From d745158d607e0a963ddc85273ea63b9e9a4e85ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Diego=20Aur=C3=A9lio=20Mesquita?= Date: Wed, 1 Nov 2017 04:18:06 -0200 Subject: [PATCH] =?UTF-8?q?Implement=20incremental=20search.=20Signed-off-?= =?UTF-8?q?by:=20Marco=20Diego=20Aur=C3=A9lio=20Mesquita=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/global.c | 8 ++++++ src/nano.h | 1 + src/prompt.c | 5 ++++ src/proto.h | 5 +++- src/rcfile.c | 1 + src/search.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- src/text.c | 2 +- 7 files changed, 104 insertions(+), 12 deletions(-) diff --git a/src/global.c b/src/global.c index 10097aaf..511e91e5 100644 --- a/src/global.c +++ b/src/global.c @@ -264,6 +264,9 @@ size_t length_of_list(int menu) #define TOGETHER FALSE /* Just throw this here. */ +void inc_search_void(void) +{ +} void case_sens_void(void) { } @@ -639,6 +642,8 @@ void shortcut_init(void) #endif const char *case_gist = N_("Toggle the case sensitivity of the search"); + const char *inc_gist = + N_("Toggle incremental search"); const char *reverse_gist = N_("Reverse the direction of the search"); const char *regexp_gist = @@ -812,6 +817,8 @@ void shortcut_init(void) N_("Copy Text"), WITHORSANS(copy_gist), BLANKAFTER, NOVIEW); #endif + add_to_funcs(inc_search_void, MWHEREIS|MREPLACE|MFINDINHELP, + N_("Incremental"), WITHORSANS(inc_gist), TOGETHER, VIEW); add_to_funcs(case_sens_void, MWHEREIS|MREPLACE, N_("Case Sens"), WITHORSANS(case_gist), TOGETHER, VIEW); add_to_funcs(regexp_void, MWHEREIS|MREPLACE, @@ -1271,6 +1278,7 @@ void shortcut_init(void) add_to_sclist(((MMOST & ~MMAIN & ~MBROWSER) | MYESNO), "^C", 0, do_cancel, 0); + add_to_sclist(MWHEREIS|MREPLACE|MFINDINHELP, "M-I", 0, inc_search_void, 0); add_to_sclist(MWHEREIS|MREPLACE, "M-C", 0, case_sens_void, 0); add_to_sclist(MWHEREIS|MREPLACE, "M-R", 0, regexp_void, 0); add_to_sclist(MWHEREIS|MREPLACE, "M-B", 0, backwards_void, 0); diff --git a/src/nano.h b/src/nano.h index f47b7679..d9d1863c 100644 --- a/src/nano.h +++ b/src/nano.h @@ -489,6 +489,7 @@ enum enum { DONTUSE, + INCR_SEARCH, CASE_SENSITIVE, CONSTANT_SHOW, NO_HELP, diff --git a/src/prompt.c b/src/prompt.c index 58bc3f5c..1754f19d 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -437,6 +437,8 @@ void update_the_statusbar(void) wnoutrefresh(bottomwin); } +extern void (*prompt_func)(char*) = NULL; + /* Get a string of input at the statusbar prompt. */ functionptrtype acquire_an_answer(int *actual, bool allow_tabs, bool allow_files, bool *listed, filestruct **history_list, @@ -572,6 +574,9 @@ functionptrtype acquire_an_answer(int *actual, bool allow_tabs, if (finished) break; + if (prompt_func) + prompt_func(answer); + update_the_statusbar(); #if defined(ENABLE_HISTORIES) && defined(ENABLE_TABCOMP) diff --git a/src/proto.h b/src/proto.h index e72e6630..8ab507d1 100644 --- a/src/proto.h +++ b/src/proto.h @@ -476,6 +476,7 @@ size_t statusbar_xplustabs(void); size_t get_statusbar_page_start(size_t start_col, size_t column); void reinit_statusbar_x(void); void update_the_statusbar(void); +void (*prompt_func)(char*); int do_prompt(bool allow_tabs, bool allow_files, int menu, const char *curranswer, filestruct **history_list, void (*refresh_func)(void), const char *msg, ...); @@ -495,7 +496,8 @@ void do_rcfiles(void); void not_found_msg(const char *str); void search_replace_abort(void); int findnextstr(const char *needle, bool whole_word_only, int modus, - size_t *match_len, bool skipone, const filestruct *begin, size_t begin_x); + size_t *match_len, bool skipone, const filestruct *begin, + size_t begin_x, bool verbose); void do_search(void); void do_search_forward(void); void do_search_backward(void); @@ -697,6 +699,7 @@ void do_credits(void); /* These are just name definitions. */ void do_cancel(void); +void inc_search_void(void); void case_sens_void(void); void regexp_void(void); void backwards_void(void); diff --git a/src/rcfile.c b/src/rcfile.c index e7b21e1a..9c9df9b2 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -93,6 +93,7 @@ static const rcoption rcopts[] = { {"backup", BACKUP_FILE}, {"backupdir", 0}, {"backwards", BACKWARDS_SEARCH}, + {"incremental", INCR_SEARCH}, {"casesensitive", CASE_SENSITIVE}, {"cut", CUT_FROM_CURSOR}, /* deprecated form, remove in 2020 */ {"cutfromcursor", CUT_FROM_CURSOR}, diff --git a/src/search.c b/src/search.c index 183a1ed0..24e9eb33 100644 --- a/src/search.c +++ b/src/search.c @@ -76,6 +76,18 @@ void not_found_msg(const char *str) free(disp); } +/* Highlights len cols of text from current cursor position. */ +void highlight(bool active, size_t len) +{ + size_t from_col = xplustabs(); + size_t to_col = strnlenpt(openfile->current->data, + openfile->current_x + len); + + edit_refresh(); + if (len) + spotlight(active, from_col, to_col); +} + /* Abort the current search or replace. Clean up by displaying the main * shortcut list, updating the screen if the mark was on before, and * decompiling the compiled regular expression we used in the last @@ -89,6 +101,43 @@ void search_replace_abort(void) regexp_cleanup(); } +/* Callback for incremental search. */ +void inc_search_cb(char *answer) +{ + filestruct *was_current = openfile->current; + size_t was_current_x = openfile->current_x; + size_t len; + bool was_full_circle = came_full_circle; + int didfind; + + if (answer[0] == '\0') { + highlight(FALSE, 0); + return; + } + + came_full_circle = FALSE; + + if (ISSET(USE_REGEXP) && !regexp_init(answer)) + return; + + didfind = findnextstr(answer, FALSE, JUSTFIND, &len, FALSE, + openfile->current, openfile->current_x, FALSE); + + if (ISSET(USE_REGEXP)) + regexp_cleanup(); + + if (didfind) + highlight(TRUE, len); + else + edit_refresh(); + + /* Reposition cursor. */ + openfile->current = was_current; + openfile->current_x = was_current_x; + openfile->placewewant = xplustabs(); + came_full_circle = was_full_circle; +} + /* Set up the system variables for a search or replace. If use_answer * is TRUE, only set backupstring to answer. Return -2 to run the * opposite program (search -> replace, replace -> search), return -1 if @@ -128,13 +177,17 @@ int search_init(bool replacing, bool use_answer) } else buf = mallocstrcpy(NULL, ""); + if (ISSET(INCR_SEARCH)) + prompt_func = inc_search_cb; + /* This is now one simple call. It just does a lot. */ i = do_prompt(FALSE, FALSE, inhelp ? MFINDINHELP : (replacing ? MREPLACE : MWHEREIS), backupstring, &search_history, /* TRANSLATORS: This is the main search prompt. */ - edit_refresh, "%s%s%s%s%s%s", _("Search"), + edit_refresh, "%s%s%s%s%s%s%s", _("Search"), /* TRANSLATORS: The next three modify the search prompt. */ + ISSET(INCR_SEARCH) ? _(" [Incremental]") : "", ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : "", ISSET(USE_REGEXP) ? _(" [Regexp]") : "", ISSET(BACKWARDS_SEARCH) ? _(" [Backwards]") : "", replacing ? @@ -144,6 +197,9 @@ int search_init(bool replacing, bool use_answer) #endif _(" (to replace)") : "", buf); + /* Disable incremental search after user user answers. */ + prompt_func = NULL; + /* Release buf now that we don't need it anymore. */ free(buf); @@ -174,7 +230,11 @@ int search_init(bool replacing, bool use_answer) func = func_from_key(&i); - if (func == case_sens_void) { + if (func == inc_search_void) { + TOGGLE(INCR_SEARCH); + backupstring = mallocstrcpy(backupstring, answer); + return 1; + } else if (func == case_sens_void) { TOGGLE(CASE_SENSITIVE); backupstring = mallocstrcpy(backupstring, answer); return 1; @@ -203,7 +263,8 @@ int search_init(bool replacing, bool use_answer) * found something, 0 when nothing, and -2 on cancel. When match_len is * not NULL, set it to the length of the found string, if any. */ int findnextstr(const char *needle, bool whole_word_only, int modus, - size_t *match_len, bool skipone, const filestruct *begin, size_t begin_x) + size_t *match_len, bool skipone, const filestruct *begin, + size_t begin_x, bool verbose) { size_t found_len = strlen(needle); /* The length of a match -- will be recomputed for a regex. */ @@ -237,14 +298,15 @@ int findnextstr(const char *needle, bool whole_word_only, int modus, /* Consume all waiting keystrokes until a Cancel. */ while (input) { if (func_from_key(&input) == do_cancel) { - statusbar(_("Cancelled")); + if (verbose) + statusbar(_("Cancelled")); enable_waiting(); return -2; } input = parse_kbinput(NULL); } - if (++feedback > 0) + if (++feedback > 0 && verbose) /* TRANSLATORS: This is shown when searching takes * more than half a second. */ statusbar(_("Searching...")); @@ -312,7 +374,8 @@ int findnextstr(const char *needle, bool whole_word_only, int modus, line = openfile->fileage; if (modus == JUSTFIND) { - statusbar(_("Search Wrapped")); + if (verbose) + statusbar(_("Search Wrapped")); /* Delay the "Searching..." message for at least two seconds. */ feedback = -2; } @@ -359,9 +422,13 @@ void do_search(void) { int i = search_init(FALSE, FALSE); - if (i == -1) /* Cancelled, or some other exit reason. */ + if (i == -1) { /* Cancelled, or some other exit reason. */ + /* Unmark highlighted text. */ + if (ISSET(INCR_SEARCH)) + highlight(FALSE, 0); + search_replace_abort(); - else if (i == -2) /* Do a replace instead. */ + } else if (i == -2) /* Do a replace instead. */ do_replace(); else if (i == 1) /* Toggled something. */ do_search(); @@ -437,7 +504,11 @@ void go_looking(void) came_full_circle = FALSE; didfind = findnextstr(last_search, FALSE, JUSTFIND, NULL, FALSE, - openfile->current, openfile->current_x); + openfile->current, openfile->current_x, TRUE); + + /* Unmark highlighted text. */ + if (didfind && ISSET(INCR_SEARCH)) + highlight(FALSE, 0); /* If we found something, and we're back at the exact same spot * where we started searching, then this is the only occurrence. */ @@ -579,7 +650,7 @@ ssize_t do_replace_loop(const char *needle, bool whole_word_only, while (TRUE) { int i = 0; int result = findnextstr(needle, whole_word_only, modus, - &match_len, skipone, real_current, *real_current_x); + &match_len, skipone, real_current, *real_current_x, TRUE); /* If nothing more was found, or the user aborted, stop looping. */ if (result < 1) { @@ -744,6 +815,9 @@ void do_replace(void) if (i != 0) return; + if (ISSET(INCR_SEARCH)) + highlight(FALSE, 0); + i = do_prompt(FALSE, FALSE, MREPLACEWITH, NULL, &replace_history, /* TRANSLATORS: This is a prompt. */ edit_refresh, _("Replace with")); diff --git a/src/text.c b/src/text.c index 95392d21..28d27473 100644 --- a/src/text.c +++ b/src/text.c @@ -2619,7 +2619,7 @@ bool do_int_spell_fix(const char *word) } /* Find the first whole occurrence of word. */ - result = findnextstr(word, TRUE, INREGION, NULL, FALSE, NULL, 0); + result = findnextstr(word, TRUE, INREGION, NULL, FALSE, NULL, 0, TRUE); /* If the word isn't found, alert the user; if it is, allow correction. */ if (result == 0) { -- 2.11.0