чување 611564963cf23a4a662c7555473d3df71b7d8b7b
родитељ 99f581a62d270044e8ba9fb6791fb680a22f0b97
Аутор: Страхиња Радић <contact@strahinja.org>
Датум: Wed, 23 Feb 2022 13:38:53 +0100
Finish copy/paste WIP; more thorough directory access testing
Signed-off-by: Страхиња Радић <contact@strahinja.org>
Diffstat:
| M | README | | | 6 | ------ |
| M | TODO | | | 17 | ++--------------- |
| M | config.def.h | | | 26 | +++++++++++++++++--------- |
| M | config.h | | | 26 | +++++++++++++++++--------- |
| M | sled.1.in | | | 5 | ++--- |
| M | sled.c | | | 135 | ++++++++++++++++++++++++++++++++++++------------------------------------------- |
измењених датотека: 6, додавања: 99(+), брисања: 116(-)
diff --git a/README b/README
@@ -1,12 +1,6 @@
sled - Simple text editor
=========================
-Currently not yet functional (getting there though!). In the process of reusing
-code from poe[1].
-
-[1]: https://sr.ht/~strahinja/poe
-
-
Q: Why can't sled open the file "read only" even if modification is disallowed?
A: A text editor's purpose should be to *edit* text files. For just *viewing*
text files, there's less(1). This avoids the situation when opening a file
diff --git a/TODO b/TODO
@@ -3,25 +3,12 @@
[x] Copy/paste?
+ [ ] Overwrite mode
+
[ ] Investigate DrawState.first_shown_column
[ ] Add vi V
- [x] Add vi H/L
-
- [x] Allow creating files (error out if directory not writeable, only
- create file when saving)
-
- [x] Add handling of terminal resize event
-
- [/] Test for binary when opening? (Why?)
-
- [x] Fix goto line #0
-
- [x] Fix moving up in empty file
-
- [x] Write operation; create backup files
-
< > Search?
< > Replace?
diff --git a/config.def.h b/config.def.h
@@ -23,6 +23,14 @@ static const char* backup_suffix = "~";
#define SEL_BG TB_WHITE
#define SHADOW_FG TB_BLACK
#define SHADOW_BG TB_BLACK | TB_BOLD
+#define STATUS_1_FG TB_WHITE | TB_BOLD
+#define STATUS_1_BG TB_BLUE
+#define STATUS_2_FG TB_YELLOW | TB_BOLD
+#define STATUS_2_BG TB_BLUE
+#define STATUS_3_FG TB_WHITE | TB_BOLD
+#define STATUS_3_BG TB_BLUE
+#define STATUS_4_FG TB_WHITE
+#define STATUS_4_BG TB_BLUE
#define TEXT_FG TB_WHITE
#define TEXT_BG TB_DEFAULT
#define TITLE_FG TB_BLACK
@@ -198,7 +206,7 @@ static const struct Key command_keys[] = {
{ .ev = { .ch = 'L' }, .callback = move_screen_bottom },
{ .ev = { .ch = 'l' }, .callback = move_right },
{ .ev = { .ch = 'M' }, .callback = move_screen_middle },
- { .ev = { .ch = 'p' }, .callback = paste_from_paste },
+ { .ev = { .ch = 'P' }, .callback = paste_from_paste },
{ .ev = { .ch = 'q' }, .callback = exit_program },
{ .ev = { .ch = 'v' }, .callback = toggle_selection },
{ .ev = { .ch = 'W' }, .callback = move_prev_word },
@@ -219,23 +227,23 @@ void format_position(char* result, size_t result_size, const char* format,
static const struct StatusSegment status_segments[] = {
{.format = "%s%s",
.callback = format_filename,
- .fg = TB_WHITE,
- .bg = TB_BLUE,
+ .fg = STATUS_1_FG,
+ .bg = STATUS_1_BG,
.alignment = LEFT},
{ .format = "%s",
.callback = format_mode,
- .fg = TB_YELLOW | TB_BOLD,
- .bg = TB_BLUE,
+ .fg = STATUS_2_FG,
+ .bg = STATUS_2_BG,
.alignment = CENTER},
{.format = "%5ld:%5ld (%3ld%%)",
.callback = format_position,
- .fg = TB_WHITE | TB_BOLD,
- .bg = TB_BLUE,
+ .fg = STATUS_3_FG,
+ .bg = STATUS_3_BG,
.alignment = CENTER},
{.format = "F1=HELP, C-Q=QUIT",
.callback = NULL,
- .fg = TB_WHITE,
- .bg = TB_BLUE,
+ .fg = STATUS_4_FG,
+ .bg = STATUS_4_BG,
.alignment = RIGHT},
};
/* clang-format on */
diff --git a/config.h b/config.h
@@ -23,6 +23,14 @@ static const char* backup_suffix = "~";
#define SEL_BG TB_WHITE
#define SHADOW_FG TB_BLACK
#define SHADOW_BG TB_BLACK | TB_BOLD
+#define STATUS_1_FG TB_WHITE | TB_BOLD
+#define STATUS_1_BG TB_BLUE
+#define STATUS_2_FG TB_YELLOW | TB_BOLD
+#define STATUS_2_BG TB_BLUE
+#define STATUS_3_FG TB_WHITE | TB_BOLD
+#define STATUS_3_BG TB_BLUE
+#define STATUS_4_FG TB_WHITE
+#define STATUS_4_BG TB_BLUE
#define TEXT_FG TB_WHITE
#define TEXT_BG TB_DEFAULT
#define TITLE_FG TB_BLACK
@@ -198,7 +206,7 @@ static const struct Key command_keys[] = {
{ .ev = { .ch = 'L' }, .callback = move_screen_bottom },
{ .ev = { .ch = 'l' }, .callback = move_right },
{ .ev = { .ch = 'M' }, .callback = move_screen_middle },
- { .ev = { .ch = 'p' }, .callback = paste_from_paste },
+ { .ev = { .ch = 'P' }, .callback = paste_from_paste },
{ .ev = { .ch = 'q' }, .callback = exit_program },
{ .ev = { .ch = 'v' }, .callback = toggle_selection },
{ .ev = { .ch = 'W' }, .callback = move_prev_word },
@@ -219,23 +227,23 @@ void format_position(char* result, size_t result_size, const char* format,
static const struct StatusSegment status_segments[] = {
{.format = "%s%s",
.callback = format_filename,
- .fg = TB_WHITE,
- .bg = TB_BLUE,
+ .fg = STATUS_1_FG,
+ .bg = STATUS_1_BG,
.alignment = LEFT},
{ .format = "%s",
.callback = format_mode,
- .fg = TB_YELLOW | TB_BOLD,
- .bg = TB_BLUE,
+ .fg = STATUS_2_FG,
+ .bg = STATUS_2_BG,
.alignment = CENTER},
{.format = "%5ld:%5ld (%3ld%%)",
.callback = format_position,
- .fg = TB_WHITE | TB_BOLD,
- .bg = TB_BLUE,
+ .fg = STATUS_3_FG,
+ .bg = STATUS_3_BG,
.alignment = CENTER},
{.format = "F1=HELP, C-Q=QUIT",
.callback = NULL,
- .fg = TB_WHITE,
- .bg = TB_BLUE,
+ .fg = STATUS_4_FG,
+ .bg = STATUS_4_BG,
.alignment = RIGHT},
};
/* clang-format on */
diff --git a/sled.1.in b/sled.1.in
@@ -216,9 +216,8 @@ Only one file can be edited at a time, and no additional files can be loaded at
runtime. This is a design decision.
.
.IP \(bu
-The directory of the edited file must be writable by the user in order to write
-the changes to the file. This is due to creating backups in order to avoid the
-potential data loss under specific circumstances (loss of power during write).
+If backup creation is enabled (\fCCREATE_BACKUPS\fP in \fIconfig.h\fP), the
+directory of the edited file must be writable by the user to open the file.
.
.SH BUGS
.
diff --git a/sled.c b/sled.c
@@ -230,12 +230,19 @@ load_file(struct DrawState* state, long* lineno, long* col)
struct BufferLine* line = NULL;
FILE* input = NULL;
char input_line[MAXBUFLINE];
+ uint32_t* newtext = NULL;
newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine));
if (!newchunk)
return LOAD_ERR_CANT_ALLOC;
for (size_t row = 0; row < ALLOC_DELTA; row++)
+ {
init_bufferline(newchunk + row);
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
+ return LOAD_ERR_CANT_ALLOC;
+ newchunk[row].text = newtext;
+ }
strcpy(state->action, actions[ACT_READING]);
redraw_status(state);
@@ -250,8 +257,7 @@ load_file(struct DrawState* state, long* lineno, long* col)
while (!feof(input))
{
- char* eol = NULL;
- uint32_t* newtext = NULL;
+ char* eol = NULL;
if (!fgets(input_line, MAXBUFLINE, input))
continue;
@@ -259,8 +265,7 @@ load_file(struct DrawState* state, long* lineno, long* col)
if (eol)
*eol = 0;
- line = state->buffer + state->rows_count;
- if (line == state->buffer + state->buffer_size)
+ if (state->rows_count == state->buffer_size)
{
state->buffer_size += ALLOC_DELTA;
newchunk = realloc(state->buffer,
@@ -270,17 +275,17 @@ load_file(struct DrawState* state, long* lineno, long* col)
state->buffer = newchunk;
for (size_t row = state->buffer_size - ALLOC_DELTA;
row < state->buffer_size; row++)
+ {
init_bufferline(state->buffer + row);
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
+ return LOAD_ERR_CANT_ALLOC;
+ state->buffer[row].text = newtext;
+ }
line = state->buffer + state->buffer_size - ALLOC_DELTA;
}
- if (!line->text)
- {
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- return LOAD_ERR_CANT_ALLOC;
- line->length = u32_u8_strcpy(newtext, input_line);
- line->text = newtext;
- }
+ line = state->buffer + state->rows_count;
+ line->length = u32_u8_strcpy(line->text, input_line);
state->rows_count++;
}
fclose(input);
@@ -365,6 +370,7 @@ yank_to_paste(struct DrawState* state)
size_t ser = 0;
size_t sec = 0;
struct BufferLine* newbuf = NULL;
+ size_t initialize_start;
if (!SEL_NOT_EMPTY(state))
return 0;
@@ -387,19 +393,22 @@ yank_to_paste(struct DrawState* state)
state->paste_count = ser - ssr + 1;
if (!state->paste_size)
{
- state->paste_size = ALLOC_DELTA;
+ initialize_start = 0;
+ while (state->paste_count > state->paste_size)
+ state->paste_size += ALLOC_DELTA;
newbuf = calloc(state->paste_size, sizeof(struct BufferLine));
}
else if (state->paste_count > state->paste_size)
{
- state->paste_size += ALLOC_DELTA;
+ initialize_start = state->paste_size;
+ while (state->paste_count > state->paste_size)
+ state->paste_size += ALLOC_DELTA;
newbuf = realloc(state->paste,
state->paste_size * sizeof(struct BufferLine));
}
else
newbuf = state->paste;
- for (size_t row = state->paste_size - ALLOC_DELTA;
- row < state->paste_size; row++)
+ for (size_t row = initialize_start; row < state->paste_size; row++)
{
uint32_t* newtext = NULL;
init_bufferline(newbuf + row);
@@ -434,17 +443,10 @@ yank_to_paste(struct DrawState* state)
int
paste_from_paste(struct DrawState* state)
{
- /* Selection start row/col */
- size_t ssr = 0;
- size_t ssc = 0;
- /* Selection end row/col */
- size_t ser = 0;
- size_t sec = 0;
uint32_t* newtext = NULL;
struct BufferLine* line = NULL;
struct BufferLine* newbuf = NULL;
- size_t dlen;
- uint32_t* rest = NULL;
+ uint32_t* rest = NULL;
if (!state->paste_count)
return 0;
@@ -466,8 +468,8 @@ paste_from_paste(struct DrawState* state)
state->buffer_size += ALLOC_DELTA;
newbuf = realloc(state->buffer,
state->buffer_size * sizeof(struct BufferLine));
- for (size_t row = 0;
- row < state->buffer_size - old_size; row++)
+ for (size_t row = old_size; row < state->buffer_size;
+ row++)
{
init_bufferline(newbuf + row);
newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
@@ -491,9 +493,11 @@ paste_from_paste(struct DrawState* state)
{
u32_strcat(newtext, line->text + state->column);
line->length = u32_strcpy(line->text, newtext);
+ state->column += state->paste[state->paste_count - 1].length;
}
else
{
+ state->rows_count += state->paste_count - 1;
rest = calloc(MAXBUFLINE, sizeof(uint32_t));
if (!rest)
{
@@ -502,13 +506,12 @@ paste_from_paste(struct DrawState* state)
}
u32_strcpy(rest, line->text + state->column);
line->length = u32_strcpy(line->text, newtext);
- /* TODO: */
for (size_t row = state->rows_count - state->row
- (state->paste_count - 2) - 1;
row > 0; row--)
{
line = state->buffer + state->row + state->paste_count
- - 2 + row - 1;
+ - 2 + row;
line->length = u32_strcpy(line->text,
state->buffer[state->row + row - 1].text);
}
@@ -523,37 +526,10 @@ paste_from_paste(struct DrawState* state)
line = state->buffer + state->row + state->paste_count - 1;
u32_strcpy(newtext, state->paste[state->paste_count - 1].text);
u32_strcat(newtext, rest);
- line->length = u32_strcpy(line->text, newtext);
- }
- /*for (size_t row = state->paste_size - ALLOC_DELTA;
- row < state->paste_size; row++)
- {
- uint32_t* newtext = NULL;
- init_bufferline(newbuf + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- strcpy(state->error, errors[ERR_CANT_ALLOC]);
- newbuf[row].text = newtext;
- newbuf[row].length = 0;
+ line->length = u32_strcpy(line->text, newtext);
+ state->column = state->paste[state->paste_count - 1].length;
+ state->row += state->paste_count - 1;
}
- state->paste = newbuf;
- if (state->paste_count == 0)
- state->paste[0].length = u32_strncpy(state->paste[0].text,
- state->buffer[ssr].text + ssc, sec - ssc + 1);
- else
- {
- state->paste[0].length = u32_strcpy(state->paste[0].text,
- state->buffer[ssr].text + ssc);
- for (size_t row = 1; row < state->paste_count - 1; row++)
- state->paste[row].length
- = u32_strcpy(state->paste[row].text,
- state->buffer[ssr + row].text);
- state->paste[state->paste_count - 1].length
- = u32_strncpy(state->paste[state->paste_count - 1].text,
- state->buffer[ser].text, sec + 1);
- }*/
- state->row += state->paste_count - 1;
- state->column += state->paste[state->paste_count - 1].length;
state->display_column = display_length(state,
state->buffer[state->row].text, state->column);
@@ -562,6 +538,8 @@ paste_from_paste(struct DrawState* state)
if (state->first_shown_column + state->maxx - 1 < state->display_column)
state->first_shown_column
= state->display_column - state->maxx + 1;
+ if (state->first_shown_row + (state->maxy - 2) < state->row)
+ state->first_shown_row = state->row - (state->maxy - 2);
state->dirty = 1;
paste_from_paste_cleanup:
@@ -1921,7 +1899,11 @@ main(int argc, char** argv)
long lineno = 1;
long col = 1;
int result;
+ int dir_access;
+ int dir_access_errno;
int recalculate_size = 1;
+ char dirname[MAXPATH];
+ char* slash = NULL;
*error = 0;
*action = 0;
@@ -1952,26 +1934,24 @@ main(int argc, char** argv)
init_drawstate(&state, error, action, filename, prompt);
- if (stat(filename, &st) == -1)
+ strcpy(dirname, filename);
+ if ((slash = strrchr(dirname, '/')))
+ *slash = 0;
+ else
{
- char dirname[MAXPATH];
- char* slash = NULL;
-
- if (errno != ENOENT)
- goto file_error;
+ *dirname = '.';
+ *(dirname + 1) = 0;
+ }
- strcpy(dirname, filename);
- if ((slash = strrchr(dirname, '/')))
- *slash = 0;
- else
+ dir_access = access(dirname, W_OK);
+ dir_access_errno = errno;
+ if (stat(filename, &st) == -1)
+ {
+ if (errno != ENOENT || dir_access == -1)
{
- *dirname = '.';
- *(dirname + 1) = 0;
- }
-
- if (access(dirname, F_OK | R_OK | W_OK) == -1)
+ errno = dir_access_errno;
goto file_error;
-
+ }
result = create_file(&state);
switch (result)
{
@@ -1982,6 +1962,13 @@ main(int argc, char** argv)
}
goto init_termbox;
}
+#ifdef CREATE_BACKUPS
+ if (dir_access == -1)
+ {
+ errno = dir_access_errno;
+ goto file_error;
+ }
+#endif /* CREATE_BACKUPS */
if (access(filename, F_OK | R_OK | W_OK) == -1)
goto file_error;