чување a51074a892588aedcee0d6fb0ae1f908107c596a
родитељ 7e4d7d548f8e476d588e9c4a2bda0d2613f50c36
Аутор: Страхиња Радић <contact@strahinja.org>
Датум: Thu, 30 Mar 2023 22:35:16 +0200
Add search & replace functionality
Signed-off-by: Страхиња Радић <contact@strahinja.org>
Diffstat:
| M | config.def.h | | | 4 | ++++ |
| M | draw.c | | | 26 | ++++++++++++++------------ |
| M | draw.h | | | 29 | +++++++++++++++-------------- |
| M | sled.1.in | | | 6 | ++++-- |
| M | sled.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);