From 05c1419d074c32fef6b301d7c33197adc7d96f5f 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] Implement incremental search. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marco Diego Aurélio Mesquita --- src/global.c | 17 +++++++ src/nano.h | 2 + src/prompt.c | 5 ++ src/proto.h | 4 ++ src/rcfile.c | 1 + src/search.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 6 files changed, 160 insertions(+), 16 deletions(-) diff --git a/src/global.c b/src/global.c index fadd6fd..defbc15 100644 --- a/src/global.c +++ b/src/global.c @@ -267,6 +267,12 @@ size_t length_of_list(int menu) #define TOGETHER FALSE /* Just throw this here. */ +void inc_search_void(void) +{ +} +void do_research_void(void) +{ +} void case_sens_void(void) { } @@ -632,6 +638,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 = @@ -818,6 +826,13 @@ void shortcut_init(void) add_to_funcs(flip_replace, MREPLACE, N_("No Replace"), WITHORSANS(whereis_gist), BLANKAFTER, VIEW); + add_to_funcs(inc_search_void, MWHEREIS|MREPLACE|MFINDINHELP, + N_("Incremental"), WITHORSANS(inc_gist), TOGETHER, VIEW); + + add_to_funcs(do_research_void, MWHEREIS|MREPLACE|MFINDINHELP, + whereisnext_tag, WITHORSANS(whereisnext_gist), TOGETHER, VIEW); + + #ifdef ENABLE_JUSTIFY add_to_funcs(do_full_justify, MWHEREIS, fulljustify_tag, WITHORSANS(fulljustify_gist), TOGETHER, NOVIEW); @@ -1275,6 +1290,8 @@ void shortcut_init(void) add_to_sclist(MWHEREIS|MREPLACE, "M-R", 0, regexp_void, 0); add_to_sclist(MWHEREIS|MREPLACE, "M-B", 0, backwards_void, 0); add_to_sclist(MWHEREIS|MREPLACE, "^R", 0, flip_replace, 0); + add_to_sclist(MWHEREIS|MREPLACE|MFINDINHELP, "M-I", 0, inc_search_void, 0); + add_to_sclist(MWHEREIS|MREPLACE|MFINDINHELP, "M-W", 0, do_research_void, 0); add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MGOTOLINE|MFINDINHELP, "^Y", 0, to_first_line, 0); add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MGOTOLINE|MFINDINHELP, "^V", 0, to_last_line, 0); #ifdef ENABLE_JUSTIFY diff --git a/src/nano.h b/src/nano.h index 92f8e29..3da2fc7 100644 --- a/src/nano.h +++ b/src/nano.h @@ -141,6 +141,7 @@ #define JUSTFIND 0 #define REPLACING 1 #define INREGION 2 +#define STEPWISE 3 /* Enumeration types. */ typedef enum { @@ -489,6 +490,7 @@ enum enum { DONTUSE, + INCREMENTAL_SEARCH, CASE_SENSITIVE, CONSTANT_SHOW, NO_HELP, diff --git a/src/prompt.c b/src/prompt.c index c067a3f..929d599 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -27,6 +27,8 @@ static char *prompt = NULL; /* The prompt string used for statusbar questions. */ static size_t statusbar_x = HIGHEST_POSITIVE; /* The cursor position in answer. */ +void (*prompt_func)(char*) = NULL; + /* Callback for each key pressed while in prompt. */ #ifdef ENABLE_MOUSE /* Handle a mouse click on the statusbar prompt or the shortcut list. */ @@ -570,6 +572,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 a7d9068..9eda19a 100644 --- a/src/proto.h +++ b/src/proto.h @@ -125,6 +125,8 @@ extern char *word_chars; extern char *answer; +extern void (*prompt_func)(char*); + extern ssize_t tabsize; #ifndef NANO_TINY @@ -688,6 +690,8 @@ void do_credits(void); /* These are just name definitions. */ void do_cancel(void); +void inc_search_void(void); +void do_research_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 ddfbd00..7d47661 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -96,6 +96,7 @@ static const rcoption rcopts[] = { {"casesensitive", CASE_SENSITIVE}, {"cut", CUT_FROM_CURSOR}, /* deprecated form, remove in 2020 */ {"cutfromcursor", CUT_FROM_CURSOR}, + {"incremental", INCREMENTAL_SEARCH}, {"justifytrim", TRIM_BLANKS}, /* deprecated form, remove in 2020 */ {"locking", LOCKING}, {"matchbrackets", 0}, diff --git a/src/search.c b/src/search.c index 160e108..09339ef 100644 --- a/src/search.c +++ b/src/search.c @@ -30,6 +30,8 @@ static bool came_full_circle = FALSE; /* Have we reached the starting line again while searching? */ static bool regexp_compiled = FALSE; /* Have we compiled any regular expressions? */ +static bool did_research = FALSE; + /* Have we searched next while searching? */ /* Compile the given regular expression and store it in search_regexp. * Return TRUE if the expression is valid, and FALSE otherwise. */ @@ -76,6 +78,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 +103,42 @@ void search_replace_abort(void) regexp_cleanup(); } +/* Searches, highlights (or beeps) and positions the cursor. + * Return true if found, false otherwise. */ +void search_text (char *needle, bool skipone) +{ + bool didfind, was_full_circle = came_full_circle; + size_t len; + + if (needle[0] == '\0') { + highlight(FALSE, 0); + return; + } + + if (ISSET(USE_REGEXP) && !regexp_init(needle)) + return; + + came_full_circle = FALSE; + didfind = findnextstr(needle, FALSE, STEPWISE, &len, skipone, + openfile->current, openfile->current_x); + came_full_circle = was_full_circle; + + if (ISSET(USE_REGEXP)) + regexp_cleanup(); + + if (didfind) + highlight(TRUE, len); + else + beep(); +} + +/* Callback used by incremental search */ +void prompt_search (char *needle) +{ + search_text(needle, FALSE); +} + + /* 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 @@ -117,7 +167,7 @@ int search_init(bool replacing, bool use_answer) * do_search() or do_replace() and be called again. In that case, * we should put the same search string back up. */ - if (*last_search != '\0') { + if (*last_search != '\0' && !ISSET(INCREMENTAL_SEARCH)) { char *disp = display_string(last_search, 0, COLS / 3, FALSE); buf = charalloc(strlen(disp) + 7); @@ -128,13 +178,17 @@ int search_init(bool replacing, bool use_answer) } else buf = mallocstrcpy(NULL, ""); + if (ISSET(INCREMENTAL_SEARCH)) + prompt_func = prompt_search; + /* 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(INCREMENTAL_SEARCH) ? _(" [Incremental]") : "", ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : "", ISSET(USE_REGEXP) ? _(" [Regexp]") : "", ISSET(BACKWARDS_SEARCH) ? _(" [Backwards]") : "", replacing ? @@ -144,6 +198,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 +231,16 @@ int search_init(bool replacing, bool use_answer) func = func_from_key(&i); - if (func == case_sens_void) { + if (func == do_research_void) { + did_research = TRUE; + search_text(answer, TRUE); + backupstring = mallocstrcpy(backupstring, answer); + return 1; + } else if (func == inc_search_void) { + TOGGLE(INCREMENTAL_SEARCH); + backupstring = mallocstrcpy(backupstring, answer); + return 1; + } else if (func == case_sens_void) { TOGGLE(CASE_SENSITIVE); backupstring = mallocstrcpy(backupstring, answer); return 1; @@ -237,14 +303,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 (modus != STEPWISE) + statusbar(_("Cancelled")); enable_waiting(); return -2; } input = parse_kbinput(NULL); } - if (++feedback > 0) + if (++feedback > 0 && modus != STEPWISE) /* TRANSLATORS: This is shown when searching takes * more than half a second. */ statusbar(_("Searching...")); @@ -350,19 +417,45 @@ int findnextstr(const char *needle, bool whole_word_only, int modus, return 1; } +/* Repostion the cursor */ +void reposition_cursor(filestruct *was_current, size_t was_current_x) +{ + openfile->current = was_current; + openfile->current_x = was_current_x; + openfile->placewewant = xplustabs(); + edit_refresh(); +} + /* Ask what to search for and then go looking for it. */ void do_search(void) { - int i = search_init(FALSE, FALSE); + filestruct *was_current = openfile->current; + /* Initial position for incremental search */ + size_t was_current_x = openfile->current_x; + /* Initial position for incremental search */ + int i; + + did_research = FALSE; + + do { + i = search_init(FALSE, FALSE); + + if (i == -1) { /* Cancelled, or some other exit reason. */ + if (ISSET(INCREMENTAL_SEARCH)) + break; - if (i == -1) /* Cancelled, or some other exit reason. */ 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(); + }while(i == 1); - if (i == 0) + if (ISSET(INCREMENTAL_SEARCH) || did_research) + highlight(FALSE, 0); + + if (i == -1) + reposition_cursor(was_current, was_current_x); + + if (i == 0 && !ISSET(INCREMENTAL_SEARCH) && !did_research) go_looking(); } @@ -417,6 +510,8 @@ void do_research(void) /* Use the search-menu key bindings, to allow cancelling. */ currmenu = MWHEREIS; + did_research = TRUE; + go_looking(); } @@ -432,9 +527,13 @@ void go_looking(void) came_full_circle = FALSE; - didfind = findnextstr(last_search, FALSE, JUSTFIND, NULL, TRUE, + didfind = findnextstr(last_search, FALSE, JUSTFIND, NULL, did_research, openfile->current, openfile->current_x); + /* Unmark highlighted text. */ + if (didfind && ISSET(INCREMENTAL_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. */ if (didfind == 1 && openfile->current == was_current && @@ -716,6 +815,10 @@ ssize_t do_replace_loop(const char *needle, bool whole_word_only, /* Replace a string. */ void do_replace(void) { + filestruct *was_current = openfile->current; + /* Initial position for incremental search */ + size_t was_current_x = openfile->current_x; + /* Initial position for incremental search */ filestruct *edittop_save, *begin; size_t firstcolumn_save, begin_x; ssize_t numreplaced; @@ -726,14 +829,23 @@ void do_replace(void) return; } + do { i = search_init(TRUE, FALSE); - if (i == -1) /* Cancelled, or some other exit reason. */ + if (i == -1) { /* Cancelled, or some other exit reason. */ + if (ISSET(INCREMENTAL_SEARCH)) + break; + search_replace_abort(); - else if (i == -2) /* Do a search instead. */ + } else if (i == -2) /* Do a search instead. */ do_search(); - else if (i == 1) /* Toggled something. */ - do_replace(); + }while(i == 1); + + if (ISSET(INCREMENTAL_SEARCH) || did_research) + highlight(FALSE, 0); + + if (i == -1) + reposition_cursor(was_current, was_current_x); if (i != 0) return; @@ -742,6 +854,9 @@ void do_replace(void) /* TRANSLATORS: This is a prompt. */ edit_refresh, _("Replace with")); + if (i == -1) + reposition_cursor(was_current, was_current_x); + #ifdef ENABLE_HISTORIES /* If the replace string is not "", add it to the replace history list. */ if (i == 0) -- 2.7.4