sled

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

чување a51074a892588aedcee0d6fb0ae1f908107c596a
родитељ 7e4d7d548f8e476d588e9c4a2bda0d2613f50c36
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Thu, 30 Mar 2023 22:35:16 +0200

Add search & replace functionality

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
Mconfig.def.h | 4++++
Mdraw.c | 26++++++++++++++------------
Mdraw.h | 29+++++++++++++++--------------
Msled.1.in | 6++++--
Msled.c | 136++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
измењених датотека: 5, додавања: 168(+), брисања: 33(-)

diff --git a/config.def.h b/config.def.h @@ -99,7 +99,9 @@ int prev_match(struct DrawState* state); int reread_or_callback(struct DrawState* state); int show_go_to_line(struct DrawState* state); int show_help(struct DrawState* state); +int show_replace(struct DrawState* state); int show_search(struct DrawState* state); +int show_search_replace(struct DrawState* state); int toggle_overwrite(struct DrawState* state); int toggle_selection(struct DrawState* state); int write_file(struct DrawState* state); @@ -177,6 +179,7 @@ static const struct Key global_keys[] = { { .ev = { .key = TB_KEY_F3 }, .callback = next_match }, { .ev = { .key = TB_KEY_F3, .mod = TB_MOD_SHIFT }, .callback = prev_match }, + { .ev = { .key = TB_KEY_F4 }, .callback = show_search_replace }, { .ev = { .key = TB_KEY_F7 }, .callback = show_search }, { .ev = { .key = TB_KEY_END }, .callback = move_end }, { .ev = { .key = TB_KEY_HOME }, .callback = move_start }, @@ -244,6 +247,7 @@ static const struct Key command_keys[] = { { .ev = { .key = TB_KEY_CTRL_BACKSLASH, .mod = TB_MOD_CTRL }, .callback = enter_insert_mode }, { .ev = { .ch = '/' }, .callback = show_search }, + { .ev = { .ch = '\\' }, .callback = show_search_replace }, { .ev = { .ch = ':' }, .callback = show_go_to_line }, { .ev = { .ch = '0' }, .callback = move_start }, { .ev = { .ch = '$' }, .callback = move_end }, diff --git a/draw.c b/draw.c @@ -60,12 +60,13 @@ const char* actions[] = { [ACT_WRITING] = "Writing...", [ACT_READING] = "Reading..." }; -const char* go_to_dialog_title = "Go to line"; -const char* search_dialog_title = "Search text"; -const char* program_name = "sled"; -const char* prompt_dirty = "File changed, save [y/n/ESC]?"; -const char* prompt_overwrite = "Not empty, overwrite? [y/n/ESC]?"; -const char* prompt_reread_dirty = "File changed, lose changes [y/n/ESC]?"; +const char* go_to_dialog_title = "Go to line"; +const char* replace_dialog_title = "Replace with"; +const char* search_dialog_title = "Search text"; +const char* program_name = "sled"; +const char* prompt_dirty = "File changed, save [y/n/ESC]?"; +const char* prompt_overwrite = "Not empty, overwrite? [y/n/ESC]?"; +const char* prompt_reread_dirty = "File changed, lose changes [y/n/ESC]?"; /* clang-format on */ void @@ -90,12 +91,13 @@ void init_drawstate(struct DrawState* state, char* error, char* action, char* filename, char* prompt, int skip_paste_and_mode_init) { - state->error = error; - state->action = action; - state->filename = filename; - state->input = NULL; - state->search = NULL; - state->buffer = NULL; + state->error = error; + state->action = action; + state->filename = filename; + state->input = NULL; + state->search = NULL; + state->chain_dialog = 0; + state->buffer = NULL; init_coord(&state->coord, 0, 0); state->display_column = 0; init_coord(&state->first_shown, 0, 0); diff --git a/draw.h b/draw.h @@ -58,19 +58,20 @@ struct DrawState { char* filename; uint32_t* input; uint32_t* search; + int chain_dialog; /* show input dialogs in sequence? */ struct BufferLine* buffer; - size_t buffer_size; /* buffer parameter */ - struct Coord coord; /* buffer coordinates */ - struct Coord first_shown; /* display coordinates */ - int input_first_shown_column; /* display coordinate */ - int input_display_column; /* display coordinate */ - int input_column; /* buffer coordinates */ + size_t buffer_size; /* buffer parameter */ + struct Coord coord; /* buffer coordinates */ + struct Coord first_shown; /* display coordinates */ + int input_first_shown_column; /* display coordinate */ + int input_display_column; /* display coordinate */ + int input_column; /* buffer coordinates */ char* input_title; - size_t rows_count; /* buffer parameter */ - int display_column; /* display coordinate */ - int saved_column; /* display coordinate */ - int help_width; /* display parameter */ - int input_dialog_width; /* display parameter */ + size_t rows_count; /* buffer parameter */ + int display_column; /* display coordinate */ + int saved_column; /* display coordinate */ + int help_width; /* display parameter */ + int input_dialog_width; /* display parameter */ struct BufferLine* paste; size_t paste_count; size_t paste_size; @@ -83,10 +84,10 @@ struct DrawState { int mode; int maxx; int maxy; - struct Coord sel_start; /* buffer coordinate */ - struct Coord sel_end; /* buffer coordinate */ + struct Coord sel_start; /* buffer coordinate */ + struct Coord sel_end; /* buffer coordinate */ #ifdef CREATE_BACKUPS - int backup_possible; /* is saving backup possible? */ + int backup_possible; /* is saving backup possible? */ #endif }; diff --git a/sled.1.in b/sled.1.in @@ -92,7 +92,8 @@ _ \fCF1\fP@Show help dialog \fCF3\fP@Go to next found match \fCS-F3\fP@Go to previous found match -\fCF7\fP@Show search dialog +\fCF4\fP@Show search, then replace dialog +\fCF7\fP@Show only search dialog \fCHome\fP or \fCC-A\fP@Go to start of line \fCPgUp\fP or \fCC-B\fP@Go to previous page \fCBackspace\fP@Erase character before cursor @@ -144,7 +145,8 @@ box tab(@); l l. \fBKey\fP@\fBAction\fP _ -\fC/\fP@Show search dialog +\fC/\fP@Show only search dialog +\fC\\\fP@Show search, then replace dialog \fC:\fP@Show Go to line dialog \fC0\fP@Go to start of line \fC$\fP@Go to end of line diff --git a/sled.c b/sled.c @@ -53,6 +53,7 @@ extern const char* errors[]; extern const char* actions[]; extern const char* mode_labels[]; extern const char* go_to_dialog_title; +extern const char* replace_dialog_title; extern const char* search_dialog_title; extern const char* prompt_dirty; extern const char* prompt_overwrite; @@ -61,6 +62,7 @@ extern const char* program_name; int clear_selection(struct DrawState* state); int create_file(struct DrawState* dstate); +int do_search_replace(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); @@ -124,6 +126,43 @@ create_file(struct DrawState* state) } int +do_search_replace(struct DrawState* state) +{ + uint32_t buf[MAXBUFLINE]; + size_t search_len = u32_strlen(state->search); + + // TODO: Replace in selection? + for (int row = 0; row < state->rows_count; row++) + { + const uint32_t* before = state->buffer[row].text; + uint32_t* found = (uint32_t*)u32_strstr(before, state->search); + const uint32_t* after = before; + + *buf = 0; + + while (found) + { + state->dirty = 1; + *found = 0; + u32_strcat(buf, before); + u32_strcat(buf, state->input); + before = found + search_len; + found = (uint32_t*)u32_strstr(before, state->search); + if (found) + after = found + search_len; + else + after = before; + } + if (state->buffer[row].text + state->buffer[row].length > after) + u32_strcat(buf, after); + + u32_strcpy(state->buffer[row].text, buf); + state->buffer[row].length = u32_strlen(buf); + } + return 0; +} + +int erase_selection(struct DrawState* state) { if (SEL_NOT_EMPTY(state)) @@ -876,6 +915,27 @@ search_dialog_callback(struct DrawState* state) } void +search_replace_search_dialog_callback(struct DrawState* state) +{ + next_match(state); + if (!*state->error) + state->input_callback = NULL; + *state->input = 0; + show_replace(state); +} + +void +search_replace_replace_dialog_callback(struct DrawState* state) +{ + do_search_replace(state); + u32_strcpy(state->search, state->input); + next_match(state); + if (!*state->error) + state->input_callback = NULL; + state->chain_dialog = 0; +} + +void search_update_callback(struct DrawState* state) { u32_strcpy(state->search, state->input); @@ -1138,22 +1198,31 @@ input_cancel(struct DrawState* state) CLEAR(state->mode, M_INPUT | M_INPUT_NUM); state->input_callback = NULL; state->input_update_callback = NULL; + state->chain_dialog = 0; return 0; } int input_enter(struct DrawState* state) { + int hide_dialog = 1; + if (state->input_callback) { state->input_callback(state); - if (!*state->error) - CLEAR(state->mode, M_INPUT | M_INPUT_NUM); - state->input_callback = NULL; + hide_dialog = !*state->search || !state->chain_dialog + || *state->input; + if (hide_dialog) + { + if (!*state->error) + CLEAR(state->mode, M_INPUT | M_INPUT_NUM); + state->input_callback = NULL; + } } - else + else if (hide_dialog) CLEAR(state->mode, M_INPUT | M_INPUT_NUM); - state->input_update_callback = NULL; + if (hide_dialog) + state->input_update_callback = NULL; return 0; } @@ -1726,6 +1795,29 @@ show_go_to_line(struct DrawState* state) } int +show_replace(struct DrawState* state) +{ + state->input_title = (char*)replace_dialog_title; + state->input_callback = search_replace_replace_dialog_callback; + state->input_update_callback = NULL; + if (!state->input) + { + if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t)))) + { + state->running = 0; + strcpy(state->error, errors[ERR_CANT_ALLOC]); + return 0; + } + } + *state->input = 0; + SET(state->mode, M_INPUT); + CLEAR(state->mode, M_INPUT_NUM); + *state->error = 0; + input_position_to(state, 0); + return 0; +} + +int show_search(struct DrawState* state) { if (!state->search) @@ -1759,6 +1851,40 @@ show_search(struct DrawState* state) } int +show_search_replace(struct DrawState* state) +{ + if (!state->search) + { + if (!(state->search = calloc(MAXINPUT, sizeof(uint32_t)))) + { + state->running = 0; + strcpy(state->error, errors[ERR_CANT_ALLOC]); + return 0; + } + } + *state->search = 0; + state->input_title = (char*)search_dialog_title; + state->input_callback = search_replace_search_dialog_callback; + state->input_update_callback = search_update_callback; + if (!state->input) + { + if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t)))) + { + state->running = 0; + strcpy(state->error, errors[ERR_CANT_ALLOC]); + return 0; + } + } + *state->input = 0; + state->chain_dialog = 1; + SET(state->mode, M_INPUT); + CLEAR(state->mode, M_INPUT_NUM); + *state->error = 0; + input_position_to(state, 0); + return 0; +} + +int show_help(struct DrawState* state) { SET(state->mode, M_HELP);