чување c0b84583184467c41dd350d32be4d8a7e21c10de
родитељ 1959aa296ddaa36886cfd698fdb3d0f6e8f986b2
Аутор: Страхиња Радић <contact@strahinja.org>
Датум: Sat, 26 Feb 2022 23:57:18 +0100
Sort functions; add correct positioning when moving page up/down
Signed-off-by: Страхиња Радић <contact@strahinja.org>
Diffstat:
| M | TODO | | | 2 | +- |
| M | sled.c | | | 2773 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
измењених датотека: 2, додавања: 1405(+), брисања: 1370(-)
diff --git a/TODO b/TODO
@@ -1,7 +1,7 @@
TODO
====
- [ ] Investigate DrawState.first_shown_column
+ [x] Investigate DrawState.first_shown_column
[/] Add vi V
diff --git a/sled.c b/sled.c
@@ -50,155 +50,34 @@ extern const char* prompt_overwrite;
extern const char* prompt_reread_dirty;
extern const char* program_name;
-int print_error(const int code, const char* msg, ...);
-void reread_file(struct DrawState* state);
-void reread_callback(struct DrawState* state, struct tb_event* ev);
-void quit_callback(struct DrawState* state, struct tb_event* ev);
-struct BufferLine* load_info(struct DrawState* state);
-struct DrawState* load_msgstr(struct DrawState* state);
+int clear_selection(struct DrawState* state);
int create_file(struct DrawState* dstate);
-int load_file(struct DrawState* dstate, long* lineno, long* col);
-int save_file(struct DrawState* state);
+int erase_selection(struct DrawState* state);
+void go_to_line_callback(struct DrawState* state);
+void handle_key_event(struct tb_event* ev, struct DrawState* state);
+int input_insert_char(struct DrawState* state, uint32_t ch);
+int insert_char(struct DrawState* state, uint32_t ch);
struct DrawState* join_lines(struct DrawState* state, size_t first_line,
int forward);
-int toggle_selection(struct DrawState* state);
-int clear_selection(struct DrawState* state);
+int load_file(struct DrawState* dstate, long* lineno, long* col);
int position_to_selection_start(struct DrawState* state);
-int erase_selection(struct DrawState* state);
-void handle_key_event(struct tb_event* ev, struct DrawState* state);
+int print_error(const int code, const char* msg, ...);
+void reread_callback(struct DrawState* state, struct tb_event* ev);
+void reread_file(struct DrawState* state);
+void quit_callback(struct DrawState* state, struct tb_event* ev);
+int save_file(struct DrawState* state);
int
-print_error(const int code, const char* msg, ...)
-{
- char buf[MAXBUFLINE];
- va_list args;
- va_start(args, msg);
- vsnprintf(buf, sizeof(buf), msg, args);
- va_end(args);
- fprintf(stderr, "%s: %s\n", program_name, buf);
- return code;
-}
-
-void
-reread_file(struct DrawState* state)
-{
- long lineno = 1;
- long col = 1;
- size_t row = 0;
- int result;
-
- for (row = 0; row < state->buffer_size; row++)
- free_bufferline(state->buffer + row);
- free(state->buffer);
- for (row = 0; row < state->paste_size; row++)
- free_bufferline(state->paste + row);
- free(state->paste);
- init_drawstate(state, state->error, state->action, state->filename,
- state->prompt);
-
- result = load_file(state, &lineno, &col);
- switch (result)
- {
- case LOAD_ERR_CANT_ALLOC:
- strcpy(state->error, errors[ERR_CANT_ALLOC]);
- state->running = 0;
- break;
- case LOAD_ERR_CANT_OPEN_FILE:
- strcpy(state->error, errors[ERR_CANT_OPEN]);
- state->running = 0;
- break;
- default:;
- }
-}
-
-void
-go_to_line_callback(struct DrawState* state)
-{
- size_t lineno = 0;
- size_t len = 0;
- char* u8_input = NULL;
-
- if (!state)
- return;
-
- if (*state->input)
- {
- len = u32_strlen(state->input);
- u8_input = calloc(len + 1, UTF8REPMAX);
- if (!u8_input)
- {
- strcpy(state->error, errors[ERR_CANT_ALLOC]);
- state->running = 0;
- return;
- }
- unicode_string_to_u8(u8_input, state->input, len + 1);
- errno = 0;
- lineno = strtoul(u8_input, NULL, 10);
- if (errno)
- strcpy(state->error, errors[ERR_INVALID_NUM]);
-
- if (lineno > state->rows_count - 1)
- lineno = state->rows_count;
- else if (lineno < 1)
- lineno = 1;
- state->row = lineno - 1;
-
- if ((state->rows_count - 1 < (state->maxy - 2) / 2)
- || (lineno < (state->maxy - 2) / 2))
- state->first_shown_row = 0;
- else if (lineno + (state->maxy - 2) / 2 < state->rows_count - 1)
- state->first_shown_row = lineno - (state->maxy - 2) / 2;
- else
- state->first_shown_row
- = state->rows_count - (state->maxy - 2) - 1;
- if (state->saved_column == -1)
- state->saved_column = state->display_column;
- size_t dlen
- = display_length(state, state->buffer[state->row].text,
- state->buffer[state->row].length);
- if (dlen < state->saved_column)
- state->display_column = dlen;
- else
- state->display_column = state->saved_column;
- state->column = buffer_length(state,
- state->buffer[state->row].text, state->display_column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- if (state->first_shown_column + state->maxx - 1
- < state->display_column)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- free(u8_input);
- if (!*state->error)
- *state->input = 0;
- }
- if (!*state->error)
- state->input_callback = NULL;
-}
-
-void
-reread_callback(struct DrawState* state, struct tb_event* ev)
-{
- if (!state)
- return;
-
- if (ev->ch == 'y')
- reread_file(state);
- state->prompt_callback = NULL;
-}
-
-void
-quit_callback(struct DrawState* state, struct tb_event* ev)
+clear_selection(struct DrawState* state)
{
if (!state)
- return;
-
- if (ev->ch == 'y')
- write_file(state);
- state->prompt_callback = NULL;
- state->running = 0;
+ return 1;
+ state->in_selection_mode = 0;
+ state->sel_start_row = 0;
+ state->sel_start_col = 0;
+ state->sel_end_row = 0;
+ state->sel_end_col = 0;
+ return 0;
}
int
@@ -234,245 +113,224 @@ create_file(struct DrawState* state)
}
int
-load_file(struct DrawState* state, long* lineno, long* col)
+erase_selection(struct DrawState* state)
{
- struct BufferLine* newchunk = NULL;
- struct BufferLine* line = NULL;
- FILE* input = NULL;
- char input_line[MAXBUFLINE];
- uint32_t* newtext = NULL;
-
- newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine));
- if (!newchunk)
- {
- state->running = 0;
- return LOAD_ERR_CANT_ALLOC;
- }
- for (size_t row = 0; row < ALLOC_DELTA; row++)
+ if (SEL_NOT_EMPTY(state))
{
- init_bufferline(newchunk + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- {
- state->running = 0;
- return LOAD_ERR_CANT_ALLOC;
- }
- newchunk[row].text = newtext;
- }
-
- strcpy(state->action, actions[ACT_READING]);
- redraw_status(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* buf = NULL;
- state->buffer = newchunk;
- line = state->buffer;
- state->buffer_size = ALLOC_DELTA;
- state->rows_count = 0;
+ buf = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!buf)
+ exit(print_error(ENOMEM, "Memory allocation failed"));
- if (!(input = fopen(state->filename, "rt")))
- return LOAD_ERR_CANT_OPEN_FILE;
+ if (state->sel_start_row <= state->sel_end_row)
+ {
+ ssr = state->sel_start_row;
+ ssc = state->sel_start_col;
+ ser = state->sel_end_row;
+ sec = state->sel_end_col;
+ }
+ else /* Swap */
+ {
+ ssr = state->sel_end_row;
+ ssc = state->sel_end_col;
+ ser = state->sel_start_row;
+ sec = state->sel_start_col;
+ }
- while (!feof(input))
- {
- char* eol = NULL;
+ u32_strcpy(buf, state->buffer[ser].text + sec);
- if (!fgets(input_line, MAXBUFLINE, input))
- continue;
- eol = strchr(input_line, '\n');
- if (eol)
- *eol = 0;
+ *(state->buffer[ssr].text + ssc) = 0;
+ u32_strcat(state->buffer[ssr].text, buf);
+ state->buffer[ssr].length = u32_strlen(state->buffer[ssr].text);
- if (state->rows_count == state->buffer_size)
+ if (ssr != ser)
{
- state->buffer_size += ALLOC_DELTA;
- newchunk = realloc(state->buffer,
- state->buffer_size * sizeof(struct BufferLine));
- if (!newchunk)
+ for (size_t row = 0; ser + row + 1 < state->rows_count;
+ row++)
{
- state->running = 0;
- return LOAD_ERR_CANT_ALLOC;
+ u32_strcpy(state->buffer[ssr + row + 1].text,
+ state->buffer[ser + row + 1].text);
+ state->buffer[ssr + row + 1].length
+ = state->buffer[ser + row + 1].length;
}
- state->buffer = newchunk;
- for (size_t row = state->buffer_size - ALLOC_DELTA;
- row < state->buffer_size; row++)
+ for (size_t row = state->rows_count - (ser - ssr);
+ row < state->rows_count; row++)
{
- init_bufferline(state->buffer + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- {
- state->running = 0;
- return LOAD_ERR_CANT_ALLOC;
- }
- state->buffer[row].text = newtext;
+ *state->buffer[row].text = 0;
+ state->buffer[row].length = 0;
}
- line = state->buffer + state->buffer_size - ALLOC_DELTA;
+ state->rows_count -= ser - ssr;
}
- line = state->buffer + state->rows_count;
- line->length = u32_u8_strcpy(line->text, input_line);
- state->rows_count++;
+ free(buf);
+ state->dirty = 1;
}
- fclose(input);
- *state->action = 0;
-
- if (state->rows_count == 0)
- state->rows_count++;
-
- return LOAD_ERR_NONE;
+ return 0;
}
-int
-save_file(struct DrawState* state)
+void
+handle_key_event(struct tb_event* ev, struct DrawState* state)
{
- FILE* output = NULL;
- char* buf = NULL;
-#ifdef CREATE_BACKUPS
- char* backup_filename = NULL;
-#endif
- struct stat st;
+ const struct Key* current = NULL;
+ if (!state)
+ return;
- if (!(buf = calloc(MAXBUFLINE, 1)))
- {
- state->running = 0;
- return SAVE_ERR_CANT_ALLOC;
- }
-#ifdef CREATE_BACKUPS
- if (!(backup_filename = calloc(MAXPATH, 1)))
+ if (state->in_prompt)
{
- state->running = 0;
- return SAVE_ERR_CANT_ALLOC;
+ if (ev->ch == 'y' || ev->ch == 'n')
+ {
+ if (state->prompt_callback)
+ state->prompt_callback(state, ev);
+ state->in_prompt = 0;
+ *state->prompt = 0;
+ }
+ else if (ev->key == TB_KEY_ESC)
+ cancel_close(state);
+ return;
}
-#endif
- strcpy(state->action, actions[ACT_WRITING]);
- redraw_status(state);
+ /* Remember to also edit the following condition if you reassign motion
+ * keys */
+ if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP
+ && ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP
+ && ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F
+ && !(state->in_command_mode && strchr("jkgG", ev->ch)))
+ state->saved_column = -1;
-#ifdef CREATE_BACKUPS
- if (stat(state->filename, &st) != -1)
+ if (state->show_input_dialog)
{
- strcpy(backup_filename, state->filename);
- strcat(backup_filename, backup_suffix);
- if (rename(state->filename, backup_filename) == -1)
+ for (int key = 0; key < LEN(input_dialog_keys); key++)
{
- strcpy(state->error, errors[SAVE_ERR_CANT_MOVE_FILE]);
- return SAVE_ERR_CANT_MOVE_FILE;
+ current = input_dialog_keys + key;
+ if (((current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch
+ && (current->ev.ch == ev->ch)))
+ if (current->callback
+ && !current->callback(state))
+ return;
+ }
+ if (ev->ch)
+ {
+ if ((state->input_numerical
+ && u32_strchr(
+ (const uint32_t*)L"0123456789",
+ ev->ch))
+ || !state->input_numerical)
+ input_insert_char(state, ev->ch);
+ else
+ strcpy(state->error, errors[ERR_INPUT_NONNUM]);
+ return;
}
}
-#endif
-
- if (!(output = fopen(state->filename, "wt")))
- return SAVE_ERR_CANT_OPEN_FILE;
- if (state->rows_count == 1 && state->buffer[0].length == 0)
- goto save_file_cleanup;
-
- for (size_t row = 0; row < state->rows_count; row++)
+ for (int key = 0; key < LEN(top_level_keys); key++)
{
- unicode_string_to_u8(buf, state->buffer[row].text, MAXBUFLINE);
- fprintf(output, "%s\n", buf);
+ current = &top_level_keys[key];
+ if (((current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch && (current->ev.ch == ev->ch)))
+ if (current->callback && !current->callback(state))
+ return;
}
-save_file_cleanup:
- fclose(output);
-#ifdef CREATE_BACKUPS
- free(backup_filename);
-#endif
- free(buf);
- *state->action = 0;
- return SAVE_ERR_NONE;
-}
-
-int
-yank_to_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;
- struct BufferLine* newbuf = NULL;
- size_t initialize_start;
-
- if (!SEL_NOT_EMPTY(state))
- return 0;
-
- if (state->sel_start_row <= state->sel_end_row)
- {
- ssr = state->sel_start_row;
- ssc = state->sel_start_col;
- ser = state->sel_end_row;
- sec = state->sel_end_col;
- }
- else /* Swap */
+ if (state->show_help)
{
- ssr = state->sel_end_row;
- ssc = state->sel_end_col;
- ser = state->sel_start_row;
- sec = state->sel_start_col;
+ strcpy(state->error, errors[ERR_DLG_OPEN]);
+ return;
}
- state->paste_count = ser - ssr + 1;
- if (!state->paste_size)
- {
- 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)
+ for (int key = 0; key < LEN(global_keys); key++)
{
- 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));
+ current = &global_keys[key];
+ if (((current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch && (current->ev.ch == ev->ch)))
+ if (current->callback && !current->callback(state))
+ return;
}
- else
- newbuf = state->paste;
- for (size_t row = initialize_start; row < state->paste_size; row++)
+
+ if (state->in_command_mode)
{
- uint32_t* newtext = NULL;
- init_bufferline(newbuf + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
+ for (int key = 0; key < LEN(command_keys); key++)
{
- state->running = 0;
- strcpy(state->error, errors[ERR_CANT_ALLOC]);
+ current = &command_keys[key];
+ if (((current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch
+ && (current->ev.ch == ev->ch)))
+ if (current->callback
+ && !current->callback(state))
+ return;
}
- newbuf[row].text = newtext;
- newbuf[row].length = 0;
}
- state->paste = newbuf;
- if (state->paste_count == 1)
- 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);
+ for (int key = 0; key < LEN(insert_keys); key++)
+ {
+ current = &insert_keys[key];
+ if (((current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch
+ && (current->ev.ch == ev->ch)))
+ if (current->callback
+ && !current->callback(state))
+ return;
+ }
+ if (ev->ch)
+ {
+ insert_char(state, ev->ch);
+ return;
+ }
}
- clear_selection(state);
+ strcpy(state->error, errors[ERR_UNKNOWN_KEY]);
+}
+int
+input_insert_char(struct DrawState* state, uint32_t ch)
+{
+ size_t len = u32_strlen(state->input);
+ if (state->input_column < MAXINPUT - 1)
+ {
+ size_t i = len + 1;
+ int add_null = state->input[state->input_column] == 0;
+ while (i < MAXINPUT - 2 && i > state->input_column)
+ {
+ state->input[i] = state->input[i - 1];
+ i--;
+ }
+ state->input[state->input_column++] = ch;
+ if (state->input_column == MAXINPUT)
+ state->input_column = MAXINPUT - 1;
+ state->input_display_column = display_length(state,
+ state->input, state->input_column);
+ if (state->input_display_column - state->input_first_shown_column
+ > state->input_dialog_width - 4)
+ {
+ state->input_first_shown_column
+ = state->input_display_column
+ - state->input_dialog_width + 4;
+ state->input_display_column = display_length(state,
+ state->input, state->input_column);
+ }
+ if (add_null)
+ state->input[state->input_column] = 0;
+ }
return 0;
}
int
-paste_from_paste(struct DrawState* state)
+insert_char(struct DrawState* state, uint32_t ch)
{
- uint32_t* newtext = NULL;
- struct BufferLine* line = NULL;
- struct BufferLine* newbuf = NULL;
- uint32_t* rest = NULL;
+ struct BufferLine* buffer_line = NULL;
+ size_t i;
+ int add_null;
+ int len;
- if (!state->paste_count)
- return 0;
+ if (state->column >= MAXBUFLINE - 1)
+ return 1;
if (SEL_NOT_EMPTY(state))
{
@@ -481,570 +339,623 @@ paste_from_paste(struct DrawState* state)
clear_selection(state);
}
- if (state->paste_count > 1)
- {
- if (state->rows_count + state->paste_count > state->buffer_size)
+ buffer_line = &state->buffer[state->row];
+ add_null = buffer_line->text[state->column] == 0;
+ len = buffer_line->length;
+ i = len + 1;
+
+ if (!state->in_overwrite_mode)
+ while (i < MAXBUFLINE - 2 && i > state->column)
{
- size_t old_size = state->buffer_size;
- while (state->rows_count + state->paste_count
- > state->buffer_size)
- state->buffer_size += ALLOC_DELTA;
- newbuf = realloc(state->buffer,
- state->buffer_size * sizeof(struct BufferLine));
- for (size_t row = old_size; row < state->buffer_size;
- row++)
- {
- init_bufferline(newbuf + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- newbuf[row].text = newtext;
- }
- state->buffer = newbuf;
+ buffer_line->text[i] = buffer_line->text[i - 1];
+ i--;
}
+ buffer_line->text[state->column++] = ch;
+ if (!state->in_overwrite_mode)
+ buffer_line->length++;
+ if (state->column == MAXBUFLINE)
+ state->column = MAXBUFLINE - 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->display_column - state->first_shown_column > state->maxx - 1)
+ {
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
}
+ if (add_null)
+ buffer_line->text[state->column] = 0;
+ state->dirty = 1;
- line = state->buffer + state->row;
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
+ return 0;
+}
+
+struct DrawState*
+join_lines(struct DrawState* state, size_t first_line, int forward)
+{
+ if (!state)
+ return NULL;
+
+ state->column = state->buffer[first_line].length;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ u32_strncat(state->buffer[first_line].text,
+ state->buffer[first_line + 1].text, MAXBUFLINE);
+ state->buffer[first_line].length
+ = u32_strlen(state->buffer[first_line].text);
+ if (!forward)
{
- state->running = 0;
- strcpy(state->error, errors[ERR_CANT_ALLOC]);
- goto paste_from_paste_cleanup;
+ state->row--;
+ if (state->row < 0)
+ state->row = 0;
}
- u32_strncpy(newtext, line->text, state->column + 1);
- u32_strcat(newtext, state->paste[0].text);
+ for (size_t i = first_line + 1; i < state->rows_count - 1; i++)
+ {
+ u32_strncpy(state->buffer[i].text, state->buffer[i + 1].text,
+ MAXBUFLINE);
+ state->buffer[i].length = state->buffer[i + 1].length;
+ }
+ *state->buffer[state->rows_count - 1].text = 0;
+ state->buffer[state->rows_count - 1].length = 0;
+ state->rows_count--;
+ state->dirty = 1;
+ return state;
+}
- if (state->paste_count == 1)
+int
+load_file(struct DrawState* state, long* lineno, long* col)
+{
+ struct BufferLine* newchunk = NULL;
+ struct BufferLine* line = NULL;
+ FILE* input = NULL;
+ char input_line[MAXBUFLINE];
+ uint32_t* newtext = NULL;
+
+ newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine));
+ if (!newchunk)
{
- u32_strcat(newtext, line->text + state->column);
- line->length = u32_strcpy(line->text, newtext);
- state->column += state->paste[state->paste_count - 1].length;
+ state->running = 0;
+ return LOAD_ERR_CANT_ALLOC;
}
- else
+ for (size_t row = 0; row < ALLOC_DELTA; row++)
{
- state->rows_count += state->paste_count - 1;
- rest = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!rest)
+ init_bufferline(newchunk + row);
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
{
state->running = 0;
- strcpy(state->error, errors[ERR_CANT_ALLOC]);
- goto paste_from_paste_cleanup;
- }
- u32_strcpy(rest, line->text + state->column);
- line->length = u32_strcpy(line->text, newtext);
- 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;
- line->length = u32_strcpy(line->text,
- state->buffer[state->row + row - 1].text);
+ return LOAD_ERR_CANT_ALLOC;
}
- line = state->buffer + state->row;
- line->length = u32_strcpy(line->text, newtext);
- for (size_t row = 1; row < state->paste_count - 1; row++)
+ newchunk[row].text = newtext;
+ }
+
+ strcpy(state->action, actions[ACT_READING]);
+ redraw_status(state);
+
+ state->buffer = newchunk;
+ line = state->buffer;
+ state->buffer_size = ALLOC_DELTA;
+ state->rows_count = 0;
+
+ if (!(input = fopen(state->filename, "rt")))
+ return LOAD_ERR_CANT_OPEN_FILE;
+
+ while (!feof(input))
+ {
+ char* eol = NULL;
+
+ if (!fgets(input_line, MAXBUFLINE, input))
+ continue;
+ eol = strchr(input_line, '\n');
+ if (eol)
+ *eol = 0;
+
+ if (state->rows_count == state->buffer_size)
{
- line = state->buffer + state->row + row;
- line->length = u32_strcpy(line->text,
- state->paste[row].text);
+ state->buffer_size += ALLOC_DELTA;
+ newchunk = realloc(state->buffer,
+ state->buffer_size * sizeof(struct BufferLine));
+ if (!newchunk)
+ {
+ state->running = 0;
+ return LOAD_ERR_CANT_ALLOC;
+ }
+ 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)
+ {
+ state->running = 0;
+ return LOAD_ERR_CANT_ALLOC;
+ }
+ state->buffer[row].text = newtext;
+ }
+ line = state->buffer + state->buffer_size - ALLOC_DELTA;
}
- 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);
- state->column = state->paste[state->paste_count - 1].length;
- state->row += state->paste_count - 1;
+ line = state->buffer + state->rows_count;
+ line->length = u32_u8_strcpy(line->text, input_line);
+ state->rows_count++;
}
+ fclose(input);
+ *state->action = 0;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- 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;
+ if (state->rows_count == 0)
+ state->rows_count++;
-paste_from_paste_cleanup:
- free(rest);
- free(newtext);
- return 0;
+ return LOAD_ERR_NONE;
}
int
-move_up(struct DrawState* state)
+position_to_selection_start(struct DrawState* state)
{
- if (state->row == 0)
- return 0;
- state->row--;
- if (state->first_shown_row > state->row)
- state->first_shown_row = state->row;
- if (state->saved_column == -1)
- state->saved_column = state->display_column;
- size_t dlen = display_length(state, state->buffer[state->row].text,
- state->buffer[state->row].length);
- if (dlen < state->saved_column)
- state->display_column = dlen;
- else
- state->display_column = state->saved_column;
- state->column = buffer_length(state, state->buffer[state->row].text,
- state->display_column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- if (state->first_shown_column + state->maxx - 1 < state->display_column)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->in_selection_mode)
+ if (SEL_NOT_EMPTY(state))
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ state->row = state->sel_start_row;
+ state->column = state->sel_start_col;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column - 1);
+ state->saved_column = state->display_column;
+
+ if (state->first_shown_row > state->row)
+ state->first_shown_row = state->row;
+ else if (state->first_shown_row + (state->maxy - 2) < state->row)
+ state->first_shown_row = state->row - (state->maxy - 2);
+
+ if (state->display_column < state->first_shown_column)
+ state->first_shown_column = state->display_column;
+ else if (state->display_column - state->first_shown_column
+ > state->maxx - 1)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
}
return 0;
}
int
-move_down(struct DrawState* state)
+print_error(const int code, const char* msg, ...)
{
- if (state->row == state->rows_count - 1)
- return 0;
- if (state->row < state->rows_count - 1)
- state->row++;
- if (state->first_shown_row + (state->maxy - 2) < state->row)
- state->first_shown_row = state->row - (state->maxy - 2);
- if (state->saved_column == -1)
- state->saved_column = state->display_column;
- size_t dlen = display_length(state, state->buffer[state->row].text,
- state->buffer[state->row].length);
- if (dlen < state->saved_column)
- state->display_column = dlen;
- else
- state->display_column = state->saved_column;
- state->column = buffer_length(state, state->buffer[state->row].text,
- state->display_column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- if (state->first_shown_column + state->maxx - 1 < state->display_column)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->in_selection_mode)
- {
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
- }
- return 0;
+ char buf[MAXBUFLINE];
+ va_list args;
+ va_start(args, msg);
+ vsnprintf(buf, sizeof(buf), msg, args);
+ va_end(args);
+ fprintf(stderr, "%s: %s\n", program_name, buf);
+ return code;
}
-int
-move_top(struct DrawState* state)
+void
+reread_callback(struct DrawState* state, struct tb_event* ev)
{
- state->row = 0;
- state->first_shown_row = 0;
- if (state->saved_column == -1)
- state->saved_column = state->display_column;
- size_t dlen = display_length(state, state->buffer[state->row].text,
- state->buffer[state->row].length);
- if (dlen < state->saved_column)
- state->display_column = dlen;
- else
- state->display_column = state->saved_column;
- state->column = buffer_length(state, state->buffer[state->row].text,
- state->display_column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- if (state->first_shown_column + state->maxx - 1 < state->display_column)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->in_selection_mode)
- {
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
- }
- return 0;
+ if (!state)
+ return;
+
+ if (ev->ch == 'y')
+ reread_file(state);
+ state->prompt_callback = NULL;
}
-int
-move_screen_top(struct DrawState* state)
+void
+reread_file(struct DrawState* state)
{
- if (state->row == 0)
- return 0;
- state->row = state->first_shown_row;
- if (state->saved_column == -1)
- state->saved_column = state->display_column;
- size_t dlen = display_length(state, state->buffer[state->row].text,
- state->buffer[state->row].length);
- if (dlen < state->saved_column)
- state->display_column = dlen;
- else
- state->display_column = state->saved_column;
- state->column = buffer_length(state, state->buffer[state->row].text,
- state->display_column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- if (state->first_shown_column + state->maxx - 1 < state->display_column)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->in_selection_mode)
+ long lineno = 1;
+ long col = 1;
+ size_t row = 0;
+ int result;
+
+ for (row = 0; row < state->buffer_size; row++)
+ free_bufferline(state->buffer + row);
+ free(state->buffer);
+ for (row = 0; row < state->paste_size; row++)
+ free_bufferline(state->paste + row);
+ free(state->paste);
+ init_drawstate(state, state->error, state->action, state->filename,
+ state->prompt);
+
+ result = load_file(state, &lineno, &col);
+ switch (result)
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ case LOAD_ERR_CANT_ALLOC:
+ strcpy(state->error, errors[ERR_CANT_ALLOC]);
+ state->running = 0;
+ break;
+ case LOAD_ERR_CANT_OPEN_FILE:
+ strcpy(state->error, errors[ERR_CANT_OPEN]);
+ state->running = 0;
+ break;
+ default:;
}
- return 0;
+}
+
+void
+quit_callback(struct DrawState* state, struct tb_event* ev)
+{
+ if (!state)
+ return;
+
+ if (ev->ch == 'y')
+ write_file(state);
+ state->prompt_callback = NULL;
+ state->running = 0;
}
int
-move_screen_middle(struct DrawState* state)
+save_file(struct DrawState* state)
{
- if (state->rows_count < state->maxy - 2)
+ FILE* output = NULL;
+ char* buf = NULL;
+#ifdef CREATE_BACKUPS
+ char* backup_filename = NULL;
+#endif
+ struct stat st;
+
+ if (!(buf = calloc(MAXBUFLINE, 1)))
{
- state->row = state->rows_count / 2;
- state->first_shown_row = 0;
+ state->running = 0;
+ return SAVE_ERR_CANT_ALLOC;
}
- else
+#ifdef CREATE_BACKUPS
+ if (!(backup_filename = calloc(MAXPATH, 1)))
{
- state->row = state->first_shown_row + (state->maxy - 2) / 2;
- if (state->rows_count - state->row < (state->maxy - 2) / 2)
- state->row = state->rows_count - (state->maxy - 2) / 2;
+ state->running = 0;
+ return SAVE_ERR_CANT_ALLOC;
}
- if (state->saved_column == -1)
- state->saved_column = state->display_column;
- size_t dlen = display_length(state, state->buffer[state->row].text,
- state->buffer[state->row].length);
- if (dlen < state->saved_column)
- state->display_column = dlen;
- else
- state->display_column = state->saved_column;
- state->column = buffer_length(state, state->buffer[state->row].text,
- state->display_column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- if (state->first_shown_column + state->maxx - 1 < state->display_column)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->in_selection_mode)
+#endif
+
+ strcpy(state->action, actions[ACT_WRITING]);
+ redraw_status(state);
+
+#ifdef CREATE_BACKUPS
+ if (stat(state->filename, &st) != -1)
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ strcpy(backup_filename, state->filename);
+ strcat(backup_filename, backup_suffix);
+ if (rename(state->filename, backup_filename) == -1)
+ {
+ strcpy(state->error, errors[SAVE_ERR_CANT_MOVE_FILE]);
+ return SAVE_ERR_CANT_MOVE_FILE;
+ }
}
- return 0;
-}
+#endif
-int
-move_start(struct DrawState* state)
-{
- state->column = 0;
- state->display_column = 0;
- state->first_shown_column = 0;
- if (state->in_selection_mode)
+ if (!(output = fopen(state->filename, "wt")))
+ return SAVE_ERR_CANT_OPEN_FILE;
+
+ if (state->rows_count == 1 && state->buffer[0].length == 0)
+ goto save_file_cleanup;
+
+ for (size_t row = 0; row < state->rows_count; row++)
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ unicode_string_to_u8(buf, state->buffer[row].text, MAXBUFLINE);
+ fprintf(output, "%s\n", buf);
}
- return 0;
+
+save_file_cleanup:
+ fclose(output);
+#ifdef CREATE_BACKUPS
+ free(backup_filename);
+#endif
+ free(buf);
+ *state->action = 0;
+ return SAVE_ERR_NONE;
}
int
-move_left(struct DrawState* state)
+cancel_close(struct DrawState* state)
{
- if (state->column == 0)
- return 0;
+ if (!state)
+ return 1;
- if (state->buffer[state->row].text[state->column - 1] == '\t')
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column - 1);
+ if (!*state->error && !state->in_prompt && !state->show_help)
+ {
+ if (SEL_NOT_EMPTY(state))
+ clear_selection(state);
+ else
+ strcpy(state->error, errors[ERR_EXIT_KEY]);
+ }
else
- state->display_column--;
- state->column--;
-
- if (state->display_column < state->first_shown_column)
- state->first_shown_column = state->display_column;
- if (state->in_selection_mode)
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ *state->error = 0;
+ *state->prompt = 0;
+ state->in_prompt = 0;
+ state->show_help = 0;
}
+
return 0;
}
int
-move_right(struct DrawState* state)
+enter_command_mode(struct DrawState* state)
{
- size_t len = state->buffer[state->row].length;
- if (state->column == len)
- return 0;
+ if (!state)
+ return 1;
+ state->in_command_mode = 1;
+ return 0;
+}
- if (state->buffer[state->row].text[state->column] == '\t')
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column + 1);
- else
- state->display_column++;
- state->column++;
+int
+enter_insert_mode(struct DrawState* state)
+{
+ if (!state)
+ return 1;
+ state->in_command_mode = 0;
+ return 0;
+}
- if (state->display_column - state->first_shown_column > state->maxx - 1)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- if (state->in_selection_mode)
+int
+erase_backwards(struct DrawState* state)
+{
+ struct BufferLine* buffer_line = &state->buffer[state->row];
+
+ if (SEL_NOT_EMPTY(state))
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ position_to_selection_start(state);
+ erase_selection(state);
+ clear_selection(state);
+ return 0;
+ }
+
+ if (state->column > 0)
+ {
+ size_t i = state->column;
+ do
+ {
+ buffer_line->text[i - 1] = buffer_line->text[i];
+ i++;
+ } while (buffer_line->text[i]);
+ buffer_line->text[i - 1] = 0;
+ buffer_line->length--;
+ state->column--;
+ if (state->first_shown_column > 0)
+ {
+ const struct BufferLine* current_line
+ = &state->buffer[state->row];
+ if (display_length(state, current_line->text,
+ current_line->length)
+ >= state->maxx - 1)
+ state->first_shown_column--;
+ }
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->display_column < state->first_shown_column)
+ state->first_shown_column = state->display_column;
+ state->dirty = 1;
+ }
+ else if (state->row > 0)
+ {
+ move_up(state);
+ move_end(state);
+ erase_forward(state);
}
return 0;
}
int
-move_prev_word(struct DrawState* state)
+erase_forward(struct DrawState* state)
{
- int i = state->column;
- uint32_t* pline = state->buffer[state->row].text;
+ struct BufferLine* buffer_line = &state->buffer[state->row];
- if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
- i--;
- while (i > 0 && u32_is_word_boundary(pline[i], 0))
- i--;
- while (i > 0 && !u32_is_word_boundary(pline[i], 0)
- && !u32_is_word_boundary(pline[i - 1], 0))
- i--;
+ if (SEL_NOT_EMPTY(state))
+ {
+ position_to_selection_start(state);
+ erase_selection(state);
+ clear_selection(state);
+ return 0;
+ }
- state->column = i;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->display_column < state->first_shown_column)
- state->first_shown_column = state->display_column;
- if (state->in_selection_mode)
+ if (buffer_line->text[state->column])
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ size_t i = state->column;
+ while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1])
+ {
+ buffer_line->text[i] = buffer_line->text[i + 1];
+ i++;
+ }
+ buffer_line->text[i] = 0;
+ buffer_line->length--;
+ state->dirty = 1;
+ }
+ else if (state->row < state->rows_count - 1)
+ {
+ if (!join_lines(state, state->row, 1))
+ {
+ strcpy(state->error, errors[ERR_CANT_ALLOC]);
+ state->running = 0;
+ }
}
return 0;
}
int
-move_next_word(struct DrawState* state)
+erase_line(struct DrawState* state)
{
- int i = state->column;
- uint32_t* pline = state->buffer[state->row].text;
- size_t len = state->buffer[state->row].length;
-
- while (i < len && !u32_is_word_boundary(pline[i], 0))
- i++;
- while (i < len && u32_is_word_boundary(pline[i], 0))
- i++;
- state->column = i;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->display_column - state->first_shown_column > state->maxx - 1)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- if (state->in_selection_mode)
+ struct BufferLine* current = NULL;
+ if (state->rows_count == 1)
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ *state->buffer[0].text = 0;
+ state->buffer[0].length = 0;
+ goto erase_line_done;
+ }
+ for (size_t row = state->row; row < state->rows_count - 1; row++)
+ {
+ current = state->buffer + row;
+ current->length = u32_strcpy(current->text,
+ state->buffer[row + 1].text);
}
+ *state->buffer[state->rows_count - 1].text = 0;
+ state->buffer[state->rows_count - 1].length = 0;
+ state->rows_count--;
+
+erase_line_done:
+ state->column = 0;
+ state->display_column = 0;
+ state->saved_column = -1;
return 0;
}
int
-erase_prev_word(struct DrawState* state)
+erase_to_end(struct DrawState* state)
{
- int i = state->column;
- size_t len = state->buffer[state->row].length;
+ struct BufferLine* buffer_line = &state->buffer[state->row];
- uint32_t* pline = state->buffer[state->row].text;
+ if (SEL_NOT_EMPTY(state))
+ {
+ position_to_selection_start(state);
+ erase_selection(state);
+ clear_selection(state);
+ return 0;
+ }
- if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
- i--;
- while (i > 0 && u32_is_word_boundary(pline[i], 0))
- i--;
- while (i > 0 && !u32_is_word_boundary(pline[i], 0)
- && !u32_is_word_boundary(pline[i - 1], 0))
- i--;
+ buffer_line->text[state->column] = 0;
+ buffer_line->length = state->column;
+ move_end(state);
+ state->dirty = 1;
+ return 0;
+}
- int d = state->column - i;
+int
+erase_to_start(struct DrawState* state)
+{
+ size_t i = state->column;
+ struct BufferLine* buffer_line = &state->buffer[state->row];
+ size_t len = buffer_line->length;
- for (int j = i; j < len - d; j++)
- pline[j] = pline[j + d];
+ if (SEL_NOT_EMPTY(state))
+ {
+ position_to_selection_start(state);
+ erase_selection(state);
+ clear_selection(state);
+ return 0;
+ }
- len -= d;
- state->buffer[state->row].length = len;
- pline[len] = 0;
- state->column = i;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- state->dirty = 1;
+ do
+ {
+ buffer_line->text[i - state->column] = buffer_line->text[i];
+ i++;
+ } while (buffer_line->text[i]);
+ buffer_line->text[len - state->column] = 0;
+ buffer_line->length = len - state->column;
+ move_start(state);
+ state->dirty = 1;
return 0;
}
int
-move_bottom(struct DrawState* state)
+exit_program(struct DrawState* state)
{
- state->row = state->rows_count - 1;
- state->first_shown_row
- = state->row > state->maxy ? state->row - state->maxy + 2 : 0;
- if (state->saved_column == -1)
- state->saved_column = state->display_column;
- size_t dlen = display_length(state, state->buffer[state->row].text,
- state->buffer[state->row].length);
- if (dlen < state->saved_column)
- state->display_column = dlen;
+ if (state->dirty)
+ {
+ state->in_prompt = 1;
+ strcpy(state->prompt, prompt_dirty);
+ state->prompt_callback = quit_callback;
+ }
else
- state->display_column = state->saved_column;
- state->column = buffer_length(state, state->buffer[state->row].text,
- state->display_column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- if (state->first_shown_column + state->maxx - 1 < state->display_column)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->in_selection_mode)
+ state->running = 0;
+ *state->error = 0;
+ return 0;
+}
+
+int
+input_cancel(struct DrawState* state)
+{
+ if (*state->error)
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ *state->error = 0;
+ return 0;
}
+ state->show_input_dialog = 0;
+ *state->input = 0;
return 0;
}
int
-move_screen_bottom(struct DrawState* state)
+input_enter(struct DrawState* state)
{
- if (state->rows_count < state->maxy - 2)
+ if (state->input_callback)
{
- state->row = state->rows_count - 1;
- state->first_shown_row = 0;
+ state->input_callback(state);
+ if (!*state->error)
+ state->show_input_dialog = 0;
}
else
- {
- if (state->first_shown_row + state->maxy - 2 < state->rows_count)
- state->row = state->first_shown_row + state->maxy - 2;
- }
- if (state->saved_column == -1)
- state->saved_column = state->display_column;
- size_t dlen = display_length(state, state->buffer[state->row].text,
- state->buffer[state->row].length);
- if (dlen < state->saved_column)
- state->display_column = dlen;
- else
- state->display_column = state->saved_column;
- state->column = buffer_length(state, state->buffer[state->row].text,
- state->display_column);
- if (state->first_shown_column > state->display_column)
- state->first_shown_column = state->display_column;
- if (state->first_shown_column + state->maxx - 1 < state->display_column)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->in_selection_mode)
- {
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
- }
+ state->show_input_dialog = 0;
return 0;
}
int
-move_end(struct DrawState* state)
+input_erase_backwards(struct DrawState* state)
{
- size_t len;
- size_t dlen;
-
- len = state->buffer[state->row].length;
- state->column = len;
- dlen = display_length(state, state->buffer[state->row].text,
- state->column);
- state->display_column = dlen;
- if (dlen > state->maxx - 4)
- state->first_shown_column = dlen - state->maxx + 4;
- else
- state->first_shown_column = 0;
- if (state->in_selection_mode)
+ if (state->input_column > 0)
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ size_t i = state->input_column;
+ do
+ {
+ state->input[i - 1] = state->input[i];
+ i++;
+ } while (state->input[i]);
+ state->input[i - 1] = 0;
+ state->input_column--;
+ if (state->input_first_shown_column > 0)
+ {
+ if (display_length(state, state->input,
+ u32_strlen(state->input))
+ >= state->maxx - 1)
+ state->input_first_shown_column--;
+ }
+ state->input_display_column = display_length(state,
+ state->input, state->input_column);
+ if (state->input_display_column
+ < state->input_first_shown_column)
+ state->input_first_shown_column
+ = state->input_display_column;
+ state->dirty = 1;
}
return 0;
}
int
-move_page_up(struct DrawState* state)
+input_erase_forward(struct DrawState* state)
{
- if (state->row > state->maxy - 2)
- {
- state->row -= state->maxy - 2;
- if (state->row < state->first_shown_row)
- state->first_shown_row = state->row;
- }
- else
- move_top(state);
- if (state->in_selection_mode)
+ if (state->input)
{
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
+ size_t i = state->input_column;
+ while (i < MAXBUFLINE - 2 && state->input[i + 1])
+ {
+ state->input[i] = state->input[i + 1];
+ i++;
+ }
+ state->input[i] = 0;
+ state->dirty = 1;
}
return 0;
}
int
-move_page_down(struct DrawState* state)
+input_erase_to_end(struct DrawState* state)
{
- if (state->row + state->maxy - 2 < state->rows_count)
- {
- state->row += state->maxy - 2;
- if (state->row > state->first_shown_row + state->maxy - 2)
- state->first_shown_row = state->row - (state->maxy - 2);
- }
- else
- move_bottom(state);
- if (state->in_selection_mode)
- {
- state->sel_end_row = state->row;
- state->sel_end_col = state->column;
- }
+ state->input[state->input_column] = 0;
+ input_move_end(state);
+ state->dirty = 1;
return 0;
}
int
-input_insert_char(struct DrawState* state, uint32_t ch)
+input_erase_to_start(struct DrawState* state)
{
+ size_t i = state->input_column;
size_t len = u32_strlen(state->input);
- if (state->input_column < MAXINPUT - 1)
+
+ do
{
- size_t i = len + 1;
- int add_null = state->input[state->input_column] == 0;
- while (i < MAXINPUT - 2 && i > state->input_column)
- {
- state->input[i] = state->input[i - 1];
- i--;
- }
- state->input[state->input_column++] = ch;
- if (state->input_column == MAXINPUT)
- state->input_column = MAXINPUT - 1;
- state->input_display_column = display_length(state,
- state->input, state->input_column);
- if (state->input_display_column - state->input_first_shown_column
- > state->input_dialog_width - 4)
- {
- state->input_first_shown_column
- = state->input_display_column
- - state->input_dialog_width + 4;
- state->input_display_column = display_length(state,
- state->input, state->input_column);
- }
- if (add_null)
- state->input[state->input_column] = 0;
- }
+ state->input[i - state->input_column] = state->input[i];
+ i++;
+ } while (state->input[i]);
+ state->input[len - state->input_column] = 0;
+ input_move_start(state);
+ state->dirty = 1;
return 0;
}
@@ -1061,221 +972,109 @@ input_insert_tab(struct DrawState* state)
}
int
-insert_char(struct DrawState* state, uint32_t ch)
+input_move_end(struct DrawState* state)
{
- struct BufferLine* buffer_line = NULL;
- size_t i;
- int add_null;
- int len;
-
- if (state->column >= MAXBUFLINE - 1)
- return 1;
+ size_t len = u32_strlen(state->input);
+ size_t dlen;
- if (SEL_NOT_EMPTY(state))
- {
- position_to_selection_start(state);
- erase_selection(state);
- clear_selection(state);
- }
+ state->input_column = len;
+ dlen = display_length(state, state->input, state->input_column);
+ state->input_display_column = dlen;
+ if (dlen > state->maxx - 4)
+ state->input_first_shown_column = dlen - state->maxx + 4;
+ else
+ state->input_first_shown_column = 0;
+ return 0;
+}
- buffer_line = &state->buffer[state->row];
- add_null = buffer_line->text[state->column] == 0;
- len = buffer_line->length;
- i = len + 1;
+int
+input_move_left(struct DrawState* state)
+{
+ if (state->input_column == 0)
+ return 0;
- if (!state->in_overwrite_mode)
- while (i < MAXBUFLINE - 2 && i > state->column)
- {
- buffer_line->text[i] = buffer_line->text[i - 1];
- i--;
- }
- buffer_line->text[state->column++] = ch;
- if (!state->in_overwrite_mode)
- buffer_line->length++;
- if (state->column == MAXBUFLINE)
- state->column = MAXBUFLINE - 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->display_column - state->first_shown_column > state->maxx - 1)
- {
- state->first_shown_column
- = state->display_column - state->maxx + 1;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- }
- if (add_null)
- buffer_line->text[state->column] = 0;
- state->dirty = 1;
+ if (state->input[state->input_column - 1] == '\t')
+ state->input_display_column = display_length(state,
+ state->input, state->input_column - 1);
+ else
+ state->input_display_column--;
+ state->input_column--;
+ if (state->input_display_column < state->input_first_shown_column)
+ state->input_first_shown_column = state->input_display_column;
return 0;
}
int
-insert_space(struct DrawState* state)
+input_move_right(struct DrawState* state)
{
- return insert_char(state, ' ');
+ size_t len = u32_strlen(state->input);
+ if (state->input_column == len)
+ return 0;
+
+ if (state->input[state->input_column] == '\t')
+ state->input_display_column = display_length(state,
+ state->input, state->input_column + 1);
+ else
+ state->input_display_column++;
+ state->input_column++;
+
+ if (state->input_display_column - state->input_first_shown_column
+ > state->maxx - 1)
+ state->input_first_shown_column
+ = state->input_display_column - state->maxx + 1;
+ return 0;
}
int
-insert_tab(struct DrawState* state)
+input_move_start(struct DrawState* state)
{
- return insert_char(state, '\t');
+ state->input_column = 0;
+ state->input_display_column = 0;
+ state->input_first_shown_column = 0;
+ return 0;
}
int
-erase_backwards(struct DrawState* state)
+input_move_prev_word(struct DrawState* state)
{
- struct BufferLine* buffer_line = &state->buffer[state->row];
+ int i = state->input_column;
+ uint32_t* pline = state->input;
- if (SEL_NOT_EMPTY(state))
- {
- position_to_selection_start(state);
- erase_selection(state);
- clear_selection(state);
- return 0;
- }
+ if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
+ i--;
+ while (i > 0 && u32_is_word_boundary(pline[i], 0))
+ i--;
+ while (i > 0 && !u32_is_word_boundary(pline[i], 0)
+ && !u32_is_word_boundary(pline[i - 1], 0))
+ i--;
- if (state->column > 0)
- {
- size_t i = state->column;
- do
- {
- buffer_line->text[i - 1] = buffer_line->text[i];
- i++;
- } while (buffer_line->text[i]);
- buffer_line->text[i - 1] = 0;
- buffer_line->length--;
- state->column--;
- if (state->first_shown_column > 0)
- {
- const struct BufferLine* current_line
- = &state->buffer[state->row];
- if (display_length(state, current_line->text,
- current_line->length)
- >= state->maxx - 1)
- state->first_shown_column--;
- }
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column);
- if (state->display_column < state->first_shown_column)
- state->first_shown_column = state->display_column;
- state->dirty = 1;
- }
- else if (state->row > 0)
- {
- move_up(state);
- move_end(state);
- erase_forward(state);
- }
- return 0;
-}
-
-int
-erase_forward(struct DrawState* state)
-{
- struct BufferLine* buffer_line = &state->buffer[state->row];
-
- if (SEL_NOT_EMPTY(state))
- {
- position_to_selection_start(state);
- erase_selection(state);
- clear_selection(state);
- return 0;
- }
-
- if (buffer_line->text[state->column])
- {
- size_t i = state->column;
- while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1])
- {
- buffer_line->text[i] = buffer_line->text[i + 1];
- i++;
- }
- buffer_line->text[i] = 0;
- buffer_line->length--;
- state->dirty = 1;
- }
- else if (state->row < state->rows_count - 1)
- {
- if (!join_lines(state, state->row, 1))
- {
- strcpy(state->error, errors[ERR_CANT_ALLOC]);
- state->running = 0;
- }
- }
- return 0;
-}
-
-int
-erase_line(struct DrawState* state)
-{
- struct BufferLine* current = NULL;
- if (state->rows_count == 1)
- {
- *state->buffer[0].text = 0;
- state->buffer[0].length = 0;
- goto erase_line_done;
- }
- for (size_t row = state->row; row < state->rows_count - 1; row++)
- {
- current = state->buffer + row;
- current->length = u32_strcpy(current->text,
- state->buffer[row + 1].text);
- }
- *state->buffer[state->rows_count - 1].text = 0;
- state->buffer[state->rows_count - 1].length = 0;
- state->rows_count--;
-
-erase_line_done:
- state->column = 0;
- state->display_column = 0;
+ state->input_column = i;
+ state->input_display_column
+ = display_length(state, state->input, state->input_column);
+ if (state->input_display_column < state->input_first_shown_column)
+ state->input_first_shown_column = state->input_display_column;
return 0;
}
int
-erase_to_start(struct DrawState* state)
+input_move_next_word(struct DrawState* state)
{
- size_t i = state->column;
- struct BufferLine* buffer_line = &state->buffer[state->row];
- size_t len = buffer_line->length;
-
- if (SEL_NOT_EMPTY(state))
- {
- position_to_selection_start(state);
- erase_selection(state);
- clear_selection(state);
- return 0;
- }
+ int i = state->input_column;
+ uint32_t* pline = state->input;
+ size_t len = u32_strlen(state->input);
- do
- {
- buffer_line->text[i - state->column] = buffer_line->text[i];
+ while (i < len && !u32_is_word_boundary(pline[i], 0))
i++;
- } while (buffer_line->text[i]);
- buffer_line->text[len - state->column] = 0;
- buffer_line->length = len - state->column;
- move_start(state);
- state->dirty = 1;
- return 0;
-}
-
-int
-erase_to_end(struct DrawState* state)
-{
- struct BufferLine* buffer_line = &state->buffer[state->row];
-
- if (SEL_NOT_EMPTY(state))
- {
- position_to_selection_start(state);
- erase_selection(state);
- clear_selection(state);
- return 0;
- }
-
- buffer_line->text[state->column] = 0;
- buffer_line->length = state->column;
- move_end(state);
- state->dirty = 1;
+ while (i < len && u32_is_word_boundary(pline[i], 0))
+ i++;
+ state->input_column = i;
+ state->input_display_column
+ = display_length(state, state->input, state->input_column);
+ if (state->input_display_column - state->input_first_shown_column
+ > state->maxx - 1)
+ state->input_first_shown_column
+ = state->input_display_column - state->maxx + 1;
return 0;
}
@@ -1336,359 +1135,600 @@ insert_line(struct DrawState* state)
}
int
-reread_or_callback(struct DrawState* state)
+insert_space(struct DrawState* state)
{
- if (!state)
- return 1;
- if (state->dirty)
- {
- state->in_prompt = 1;
- strcpy(state->prompt, prompt_reread_dirty);
- state->prompt_callback = reread_callback;
- }
- else
- reread_file(state);
- return 0;
+ return insert_char(state, ' ');
}
int
-write_file(struct DrawState* state)
+insert_tab(struct DrawState* state)
{
- if (!state)
- return 1;
-
- int result = save_file(state);
- switch (result)
- {
- case SAVE_ERR_CANT_ALLOC:
- strcpy(state->error, errors[ERR_CANT_ALLOC]);
- state->running = 0;
- break;
- case SAVE_ERR_CANT_OPEN_FILE:
- strcpy(state->error, errors[ERR_CANT_SAVE]);
- state->running = 0;
- break;
- case SAVE_ERR_CANT_MOVE_FILE:
- strcpy(state->error, strerror(errno));
- state->running = 0;
- break;
- default:
- *state->error = 0;
- state->dirty = 0;
- }
-
- return 0;
+ return insert_char(state, '\t');
}
-struct DrawState*
-join_lines(struct DrawState* state, size_t first_line, int forward)
+int
+move_bottom(struct DrawState* state)
{
- if (!state)
- return NULL;
-
- state->column = state->buffer[first_line].length;
+ state->row = state->rows_count - 1;
+ state->first_shown_row
+ = state->row > state->maxy ? state->row - state->maxy + 2 : 0;
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state, state->buffer[state->row].text,
+ state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1 < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
state->display_column = display_length(state,
state->buffer[state->row].text, state->column);
- u32_strncat(state->buffer[first_line].text,
- state->buffer[first_line + 1].text, MAXBUFLINE);
- state->buffer[first_line].length
- = u32_strlen(state->buffer[first_line].text);
- if (!forward)
- {
- state->row--;
- if (state->row < 0)
- state->row = 0;
- }
- for (size_t i = first_line + 1; i < state->rows_count - 1; i++)
+ if (state->in_selection_mode)
{
- u32_strncpy(state->buffer[i].text, state->buffer[i + 1].text,
- MAXBUFLINE);
- state->buffer[i].length = state->buffer[i + 1].length;
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
}
- *state->buffer[state->rows_count - 1].text = 0;
- state->buffer[state->rows_count - 1].length = 0;
- state->rows_count--;
- state->dirty = 1;
- return state;
+ return 0;
}
int
-cancel_close(struct DrawState* state)
+move_down(struct DrawState* state)
{
- if (!state)
- return 1;
-
- if (!*state->error && !state->in_prompt && !state->show_help)
- {
- if (SEL_NOT_EMPTY(state))
- clear_selection(state);
- else
- strcpy(state->error, errors[ERR_EXIT_KEY]);
- }
- else
- {
- *state->error = 0;
- *state->prompt = 0;
- state->in_prompt = 0;
- state->show_help = 0;
+ if (state->row == state->rows_count - 1)
+ return 0;
+ if (state->row < state->rows_count - 1)
+ state->row++;
+ if (state->first_shown_row + (state->maxy - 2) < state->row)
+ state->first_shown_row = state->row - (state->maxy - 2);
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state, state->buffer[state->row].text,
+ state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1 < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
}
-
return 0;
}
int
-enter_command_mode(struct DrawState* state)
+move_end(struct DrawState* state)
{
- if (!state)
- return 1;
- state->in_command_mode = 1;
+ size_t len;
+ size_t dlen;
+
+ len = state->buffer[state->row].length;
+ state->column = len;
+ dlen = display_length(state, state->buffer[state->row].text,
+ state->column);
+ state->display_column = dlen;
+ if (dlen > state->maxx - 4)
+ state->first_shown_column = dlen - state->maxx + 4;
+ else
+ state->first_shown_column = 0;
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-enter_insert_mode(struct DrawState* state)
+move_left(struct DrawState* state)
{
- if (!state)
- return 1;
- state->in_command_mode = 0;
+ if (state->column == 0)
+ return 0;
+
+ if (state->buffer[state->row].text[state->column - 1] == '\t')
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column - 1);
+ else
+ state->display_column--;
+ state->column--;
+
+ if (state->display_column < state->first_shown_column)
+ state->first_shown_column = state->display_column;
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-input_cancel(struct DrawState* state)
+move_next_word(struct DrawState* state)
{
- if (*state->error)
+ int i = state->column;
+ uint32_t* pline = state->buffer[state->row].text;
+ size_t len = state->buffer[state->row].length;
+
+ while (i < len && !u32_is_word_boundary(pline[i], 0))
+ i++;
+ while (i < len && u32_is_word_boundary(pline[i], 0))
+ i++;
+ state->column = i;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->display_column - state->first_shown_column > state->maxx - 1)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ if (state->in_selection_mode)
{
- *state->error = 0;
- return 0;
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
}
- state->show_input_dialog = 0;
- *state->input = 0;
return 0;
}
int
-input_enter(struct DrawState* state)
+move_page_down(struct DrawState* state)
{
- if (state->input_callback)
+ if (state->row + state->maxy - 2 < state->rows_count)
{
- state->input_callback(state);
- if (!*state->error)
- state->show_input_dialog = 0;
+ state->row += state->maxy - 2;
+ if (state->row > state->first_shown_row + state->maxy - 2)
+ state->first_shown_row = state->row - (state->maxy - 2);
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen
+ = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state,
+ state->buffer[state->row].text, state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1
+ < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
}
else
- state->show_input_dialog = 0;
+ move_bottom(state);
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-input_erase_backwards(struct DrawState* state)
+move_page_up(struct DrawState* state)
{
- if (state->input_column > 0)
+ if (state->row > state->maxy - 2)
{
- size_t i = state->input_column;
- do
- {
- state->input[i - 1] = state->input[i];
- i++;
- } while (state->input[i]);
- state->input[i - 1] = 0;
- state->input_column--;
- if (state->input_first_shown_column > 0)
- {
- if (display_length(state, state->input,
- u32_strlen(state->input))
- >= state->maxx - 1)
- state->input_first_shown_column--;
- }
- state->input_display_column = display_length(state,
- state->input, state->input_column);
- if (state->input_display_column
- < state->input_first_shown_column)
- state->input_first_shown_column
- = state->input_display_column;
- state->dirty = 1;
+ state->row -= state->maxy - 2;
+ if (state->row < state->first_shown_row)
+ state->first_shown_row = state->row;
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen
+ = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state,
+ state->buffer[state->row].text, state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1
+ < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ }
+ else
+ move_top(state);
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
}
return 0;
}
int
-input_erase_forward(struct DrawState* state)
+move_prev_word(struct DrawState* state)
{
- if (state->input)
+ int i = state->column;
+ uint32_t* pline = state->buffer[state->row].text;
+
+ if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
+ i--;
+ while (i > 0 && u32_is_word_boundary(pline[i], 0))
+ i--;
+ while (i > 0 && !u32_is_word_boundary(pline[i], 0)
+ && !u32_is_word_boundary(pline[i - 1], 0))
+ i--;
+
+ state->column = i;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->display_column < state->first_shown_column)
+ state->first_shown_column = state->display_column;
+ if (state->in_selection_mode)
{
- size_t i = state->input_column;
- while (i < MAXBUFLINE - 2 && state->input[i + 1])
- {
- state->input[i] = state->input[i + 1];
- i++;
- }
- state->input[i] = 0;
- state->dirty = 1;
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
}
return 0;
}
-int input_move_end(struct DrawState* state);
-
int
-input_erase_to_end(struct DrawState* state)
+move_right(struct DrawState* state)
{
- state->input[state->input_column] = 0;
- input_move_end(state);
- state->dirty = 1;
- return 0;
-}
-
-int input_move_start(struct DrawState* state);
+ size_t len = state->buffer[state->row].length;
+ if (state->column == len)
+ return 0;
-int
-input_erase_to_start(struct DrawState* state)
-{
- size_t i = state->input_column;
- size_t len = u32_strlen(state->input);
+ if (state->buffer[state->row].text[state->column] == '\t')
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column + 1);
+ else
+ state->display_column++;
+ state->column++;
- do
+ if (state->display_column - state->first_shown_column > state->maxx - 1)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ if (state->in_selection_mode)
{
- state->input[i - state->input_column] = state->input[i];
- i++;
- } while (state->input[i]);
- state->input[len - state->input_column] = 0;
- input_move_start(state);
- state->dirty = 1;
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-input_move_end(struct DrawState* state)
+move_screen_bottom(struct DrawState* state)
{
- size_t len = u32_strlen(state->input);
- size_t dlen;
-
- state->input_column = len;
- dlen = display_length(state, state->input, state->input_column);
- state->input_display_column = dlen;
- if (dlen > state->maxx - 4)
- state->input_first_shown_column = dlen - state->maxx + 4;
+ if (state->rows_count < state->maxy - 2)
+ {
+ state->row = state->rows_count - 1;
+ state->first_shown_row = 0;
+ }
else
- state->input_first_shown_column = 0;
+ {
+ if (state->first_shown_row + state->maxy - 2 < state->rows_count)
+ state->row = state->first_shown_row + state->maxy - 2;
+ }
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state, state->buffer[state->row].text,
+ state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1 < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-input_move_left(struct DrawState* state)
+move_screen_middle(struct DrawState* state)
{
- if (state->input_column == 0)
- return 0;
-
- if (state->input[state->input_column - 1] == '\t')
- state->input_display_column = display_length(state,
- state->input, state->input_column - 1);
+ if (state->rows_count < state->maxy - 2)
+ {
+ state->row = state->rows_count / 2;
+ state->first_shown_row = 0;
+ }
else
- state->input_display_column--;
- state->input_column--;
-
- if (state->input_display_column < state->input_first_shown_column)
- state->input_first_shown_column = state->input_display_column;
+ {
+ state->row = state->first_shown_row + (state->maxy - 2) / 2;
+ if (state->rows_count - state->row < (state->maxy - 2) / 2)
+ state->row = state->rows_count - (state->maxy - 2) / 2;
+ }
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state, state->buffer[state->row].text,
+ state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1 < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-input_move_right(struct DrawState* state)
+move_screen_top(struct DrawState* state)
{
- size_t len = u32_strlen(state->input);
- if (state->input_column == len)
+ if (state->row == 0)
return 0;
-
- if (state->input[state->input_column] == '\t')
- state->input_display_column = display_length(state,
- state->input, state->input_column + 1);
+ state->row = state->first_shown_row;
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
else
- state->input_display_column++;
- state->input_column++;
-
- if (state->input_display_column - state->input_first_shown_column
- > state->maxx - 1)
- state->input_first_shown_column
- = state->input_display_column - state->maxx + 1;
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state, state->buffer[state->row].text,
+ state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1 < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-input_move_start(struct DrawState* state)
+move_start(struct DrawState* state)
{
- state->input_column = 0;
- state->input_display_column = 0;
- state->input_first_shown_column = 0;
+ state->column = 0;
+ state->display_column = 0;
+ state->first_shown_column = 0;
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-input_move_prev_word(struct DrawState* state)
+move_top(struct DrawState* state)
{
- int i = state->input_column;
- uint32_t* pline = state->input;
-
- if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
- i--;
- while (i > 0 && u32_is_word_boundary(pline[i], 0))
- i--;
- while (i > 0 && !u32_is_word_boundary(pline[i], 0)
- && !u32_is_word_boundary(pline[i - 1], 0))
- i--;
-
- state->input_column = i;
- state->input_display_column
- = display_length(state, state->input, state->input_column);
- if (state->input_display_column < state->input_first_shown_column)
- state->input_first_shown_column = state->input_display_column;
+ state->row = 0;
+ state->first_shown_row = 0;
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state, state->buffer[state->row].text,
+ state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1 < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-input_move_next_word(struct DrawState* state)
+move_up(struct DrawState* state)
{
- int i = state->input_column;
- uint32_t* pline = state->input;
- size_t len = u32_strlen(state->input);
-
- while (i < len && !u32_is_word_boundary(pline[i], 0))
- i++;
- while (i < len && u32_is_word_boundary(pline[i], 0))
- i++;
- state->input_column = i;
- state->input_display_column
- = display_length(state, state->input, state->input_column);
- if (state->input_display_column - state->input_first_shown_column
- > state->maxx - 1)
- state->input_first_shown_column
- = state->input_display_column - state->maxx + 1;
+ if (state->row == 0)
+ return 0;
+ state->row--;
+ if (state->first_shown_row > state->row)
+ state->first_shown_row = state->row;
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state, state->buffer[state->row].text,
+ state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1 < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->in_selection_mode)
+ {
+ state->sel_end_row = state->row;
+ state->sel_end_col = state->column;
+ }
return 0;
}
int
-exit_program(struct DrawState* state)
+paste_from_paste(struct DrawState* state)
{
- if (state->dirty)
+ uint32_t* newtext = NULL;
+ struct BufferLine* line = NULL;
+ struct BufferLine* newbuf = NULL;
+ uint32_t* rest = NULL;
+
+ if (!state->paste_count)
+ return 0;
+
+ if (SEL_NOT_EMPTY(state))
{
- state->in_prompt = 1;
- strcpy(state->prompt, prompt_dirty);
- state->prompt_callback = quit_callback;
+ position_to_selection_start(state);
+ erase_selection(state);
+ clear_selection(state);
}
- else
- state->running = 0;
- *state->error = 0;
- return 0;
-}
-int
-show_go_to_line(struct DrawState* state)
-{
- state->input_title = (char*)go_to_dialog_title;
- state->input_callback = go_to_line_callback;
- if (!state->input)
+ if (state->paste_count > 1)
{
- if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t))))
+ if (state->rows_count + state->paste_count > state->buffer_size)
+ {
+ size_t old_size = state->buffer_size;
+ while (state->rows_count + state->paste_count
+ > state->buffer_size)
+ state->buffer_size += ALLOC_DELTA;
+ newbuf = realloc(state->buffer,
+ state->buffer_size * sizeof(struct BufferLine));
+ for (size_t row = old_size; row < state->buffer_size;
+ row++)
+ {
+ init_bufferline(newbuf + row);
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newbuf[row].text = newtext;
+ }
+ state->buffer = newbuf;
+ }
+ }
+
+ line = state->buffer + state->row;
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
+ {
+ state->running = 0;
+ strcpy(state->error, errors[ERR_CANT_ALLOC]);
+ goto paste_from_paste_cleanup;
+ }
+ u32_strncpy(newtext, line->text, state->column + 1);
+ u32_strcat(newtext, state->paste[0].text);
+
+ if (state->paste_count == 1)
+ {
+ 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)
+ {
+ state->running = 0;
+ strcpy(state->error, errors[ERR_CANT_ALLOC]);
+ goto paste_from_paste_cleanup;
+ }
+ u32_strcpy(rest, line->text + state->column);
+ line->length = u32_strcpy(line->text, newtext);
+ 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;
+ line->length = u32_strcpy(line->text,
+ state->buffer[state->row + row - 1].text);
+ }
+ line = state->buffer + state->row;
+ line->length = u32_strcpy(line->text, newtext);
+ for (size_t row = 1; row < state->paste_count - 1; row++)
+ {
+ line = state->buffer + state->row + row;
+ line->length = u32_strcpy(line->text,
+ state->paste[row].text);
+ }
+ 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);
+ state->column = state->paste[state->paste_count - 1].length;
+ state->row += state->paste_count - 1;
+ }
+
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ 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:
+ free(rest);
+ free(newtext);
+ return 0;
+}
+
+int
+reread_or_callback(struct DrawState* state)
+{
+ if (!state)
+ return 1;
+ if (state->dirty)
+ {
+ state->in_prompt = 1;
+ strcpy(state->prompt, prompt_reread_dirty);
+ state->prompt_callback = reread_callback;
+ }
+ else
+ reread_file(state);
+ return 0;
+}
+
+int
+show_go_to_line(struct DrawState* state)
+{
+ state->input_title = (char*)go_to_dialog_title;
+ state->input_callback = go_to_line_callback;
+ if (!state->input)
+ {
+ if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t))))
{
state->running = 0;
strcpy(state->error, errors[ERR_CANT_ALLOC]);
@@ -1742,218 +1782,213 @@ toggle_selection(struct DrawState* state)
}
int
-clear_selection(struct DrawState* state)
+write_file(struct DrawState* state)
{
if (!state)
return 1;
- state->in_selection_mode = 0;
- state->sel_start_row = 0;
- state->sel_start_col = 0;
- state->sel_end_row = 0;
- state->sel_end_col = 0;
- return 0;
-}
-int
-position_to_selection_start(struct DrawState* state)
-{
- if (SEL_NOT_EMPTY(state))
+ int result = save_file(state);
+ switch (result)
{
- state->row = state->sel_start_row;
- state->column = state->sel_start_col;
- state->display_column = display_length(state,
- state->buffer[state->row].text, state->column - 1);
- state->saved_column = state->display_column;
-
- if (state->first_shown_row > state->row)
- state->first_shown_row = state->row;
- else if (state->first_shown_row + (state->maxy - 2) < state->row)
- state->first_shown_row = state->row - (state->maxy - 2);
-
- if (state->display_column < state->first_shown_column)
- state->first_shown_column = state->display_column;
- else if (state->display_column - state->first_shown_column
- > state->maxx - 1)
- state->first_shown_column
- = state->display_column - state->maxx + 1;
+ case SAVE_ERR_CANT_ALLOC:
+ strcpy(state->error, errors[ERR_CANT_ALLOC]);
+ state->running = 0;
+ break;
+ case SAVE_ERR_CANT_OPEN_FILE:
+ strcpy(state->error, errors[ERR_CANT_SAVE]);
+ state->running = 0;
+ break;
+ case SAVE_ERR_CANT_MOVE_FILE:
+ strcpy(state->error, strerror(errno));
+ state->running = 0;
+ break;
+ default:
+ *state->error = 0;
+ state->dirty = 0;
}
+
return 0;
}
int
-erase_selection(struct DrawState* state)
+yank_to_paste(struct DrawState* state)
{
- if (SEL_NOT_EMPTY(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* buf = NULL;
-
- buf = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!buf)
- exit(print_error(ENOMEM, "Memory allocation failed"));
-
- if (state->sel_start_row <= state->sel_end_row)
- {
- ssr = state->sel_start_row;
- ssc = state->sel_start_col;
- ser = state->sel_end_row;
- sec = state->sel_end_col;
- }
- else /* Swap */
- {
- ssr = state->sel_end_row;
- ssc = state->sel_end_col;
- ser = state->sel_start_row;
- sec = state->sel_start_col;
- }
+ /* Selection start row/col */
+ size_t ssr = 0;
+ size_t ssc = 0;
+ /* Selection end row/col */
+ size_t ser = 0;
+ size_t sec = 0;
+ struct BufferLine* newbuf = NULL;
+ size_t initialize_start;
- u32_strcpy(buf, state->buffer[ser].text + sec);
+ if (!SEL_NOT_EMPTY(state))
+ return 0;
- *(state->buffer[ssr].text + ssc) = 0;
- u32_strcat(state->buffer[ssr].text, buf);
- state->buffer[ssr].length = u32_strlen(state->buffer[ssr].text);
+ if (state->sel_start_row <= state->sel_end_row)
+ {
+ ssr = state->sel_start_row;
+ ssc = state->sel_start_col;
+ ser = state->sel_end_row;
+ sec = state->sel_end_col;
+ }
+ else /* Swap */
+ {
+ ssr = state->sel_end_row;
+ ssc = state->sel_end_col;
+ ser = state->sel_start_row;
+ sec = state->sel_start_col;
+ }
- if (ssr != ser)
+ state->paste_count = ser - ssr + 1;
+ if (!state->paste_size)
+ {
+ 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)
+ {
+ 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 = initialize_start; row < state->paste_size; row++)
+ {
+ uint32_t* newtext = NULL;
+ init_bufferline(newbuf + row);
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
{
- for (size_t row = 0; ser + row + 1 < state->rows_count;
- row++)
- {
- u32_strcpy(state->buffer[ssr + row + 1].text,
- state->buffer[ser + row + 1].text);
- state->buffer[ssr + row + 1].length
- = state->buffer[ser + row + 1].length;
- }
- for (size_t row = state->rows_count - (ser - ssr);
- row < state->rows_count; row++)
- {
- *state->buffer[row].text = 0;
- state->buffer[row].length = 0;
- }
- state->rows_count -= ser - ssr;
+ state->running = 0;
+ strcpy(state->error, errors[ERR_CANT_ALLOC]);
}
- free(buf);
- state->dirty = 1;
+ newbuf[row].text = newtext;
+ newbuf[row].length = 0;
}
+ state->paste = newbuf;
+ if (state->paste_count == 1)
+ 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);
+ }
+
+ clear_selection(state);
+
return 0;
}
void
-handle_key_event(struct tb_event* ev, struct DrawState* state)
+go_to_line_callback(struct DrawState* state)
{
- const struct Key* current = NULL;
+ size_t lineno = 0;
+ size_t len = 0;
+ char* u8_input = NULL;
+
if (!state)
return;
- if (state->in_prompt)
+ if (*state->input)
{
- if (ev->ch == 'y' || ev->ch == 'n')
+ len = u32_strlen(state->input);
+ u8_input = calloc(len + 1, UTF8REPMAX);
+ if (!u8_input)
{
- if (state->prompt_callback)
- state->prompt_callback(state, ev);
- state->in_prompt = 0;
- *state->prompt = 0;
+ strcpy(state->error, errors[ERR_CANT_ALLOC]);
+ state->running = 0;
+ return;
}
- else if (ev->key == TB_KEY_ESC)
- cancel_close(state);
- return;
- }
+ unicode_string_to_u8(u8_input, state->input, len + 1);
+ errno = 0;
+ lineno = strtoul(u8_input, NULL, 10);
+ if (errno)
+ strcpy(state->error, errors[ERR_INVALID_NUM]);
- /* Remember to also edit the following condition if you reassign motion
- * keys */
- if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP
- && ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP
- && ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F
- && !(state->in_command_mode && strchr("jkgG", ev->ch)))
- state->saved_column = -1;
+ if (lineno > state->rows_count - 1)
+ lineno = state->rows_count;
+ else if (lineno < 1)
+ lineno = 1;
+ state->row = lineno - 1;
- if (state->show_input_dialog)
- {
- for (int key = 0; key < LEN(input_dialog_keys); key++)
- {
- current = input_dialog_keys + key;
- if (((current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch
- && (current->ev.ch == ev->ch)))
- if (current->callback
- && !current->callback(state))
- return;
- }
- if (ev->ch)
- {
- if ((state->input_numerical
- && u32_strchr(
- (const uint32_t*)L"0123456789",
- ev->ch))
- || !state->input_numerical)
- input_insert_char(state, ev->ch);
- else
- strcpy(state->error, errors[ERR_INPUT_NONNUM]);
- return;
- }
+ if ((state->rows_count - 1 < (state->maxy - 2) / 2)
+ || (lineno < (state->maxy - 2) / 2))
+ state->first_shown_row = 0;
+ else if (lineno + (state->maxy - 2) / 2 < state->rows_count - 1)
+ state->first_shown_row = lineno - (state->maxy - 2) / 2;
+ else
+ state->first_shown_row
+ = state->rows_count - (state->maxy - 2) - 1;
+ if (state->saved_column == -1)
+ state->saved_column = state->display_column;
+ size_t dlen
+ = display_length(state, state->buffer[state->row].text,
+ state->buffer[state->row].length);
+ if (dlen < state->saved_column)
+ state->display_column = dlen;
+ else
+ state->display_column = state->saved_column;
+ state->column = buffer_length(state,
+ state->buffer[state->row].text, state->display_column);
+ if (state->first_shown_column > state->display_column)
+ state->first_shown_column = state->display_column;
+ if (state->first_shown_column + state->maxx - 1
+ < state->display_column)
+ state->first_shown_column
+ = state->display_column - state->maxx + 1;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ free(u8_input);
+ if (!*state->error)
+ *state->input = 0;
}
+ if (!*state->error)
+ state->input_callback = NULL;
+}
- for (int key = 0; key < LEN(top_level_keys); key++)
- {
- current = &top_level_keys[key];
- if (((current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch && (current->ev.ch == ev->ch)))
- if (current->callback && !current->callback(state))
- return;
- }
+int
+erase_prev_word(struct DrawState* state)
+{
+ int i = state->column;
+ size_t len = state->buffer[state->row].length;
- if (state->show_help)
- {
- strcpy(state->error, errors[ERR_DLG_OPEN]);
- return;
- }
+ uint32_t* pline = state->buffer[state->row].text;
- for (int key = 0; key < LEN(global_keys); key++)
- {
- current = &global_keys[key];
- if (((current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch && (current->ev.ch == ev->ch)))
- if (current->callback && !current->callback(state))
- return;
- }
+ if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
+ i--;
+ while (i > 0 && u32_is_word_boundary(pline[i], 0))
+ i--;
+ while (i > 0 && !u32_is_word_boundary(pline[i], 0)
+ && !u32_is_word_boundary(pline[i - 1], 0))
+ i--;
- if (state->in_command_mode)
- {
- for (int key = 0; key < LEN(command_keys); key++)
- {
- current = &command_keys[key];
- if (((current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch
- && (current->ev.ch == ev->ch)))
- if (current->callback
- && !current->callback(state))
- return;
- }
- }
- else
- {
- for (int key = 0; key < LEN(insert_keys); key++)
- {
- current = &insert_keys[key];
- if (((current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch
- && (current->ev.ch == ev->ch)))
- if (current->callback
- && !current->callback(state))
- return;
- }
- if (ev->ch)
- {
- insert_char(state, ev->ch);
- return;
- }
- }
+ int d = state->column - i;
- strcpy(state->error, errors[ERR_UNKNOWN_KEY]);
+ for (int j = i; j < len - d; j++)
+ pline[j] = pline[j + d];
+
+ len -= d;
+ state->buffer[state->row].length = len;
+ pline[len] = 0;
+ state->column = i;
+ state->display_column = display_length(state,
+ state->buffer[state->row].text, state->column);
+ state->dirty = 1;
+ return 0;
}
int