sled

Једноставни уређивач текста
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 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:
MTODO | 2+-
Msled.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