From 95c7d0efd7989f46f3a8eed8bd700c0964f5a4b6 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 | 17 +++++++
src/nano.h | 1 +
src/prompt.c | 5 ++
src/proto.h | 7 ++-
src/rcfile.c | 1 +
src/search.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
src/text.c | 2 +-
7 files changed, 177 insertions(+), 18 deletions(-)
diff --git a/src/global.c b/src/global.c
index fadd6fdc..defbc153 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 92f8e296..73de6ffa 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 c067a3fb..929d5995 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 a7d90689..5205794f 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
@@ -482,7 +484,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);
@@ -688,6 +691,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 ddfbd003..34b6663a 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 160e108b..da4e335d 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,55 @@ void search_replace_abort(void)
regexp_cleanup();
}
+/* Searches with wrap around.
+ * Return true if found, false otherwise.
+ * Sets parameter 'len' to number of characters of the search */
+bool wrap_search (char *needle)
+{
+ bool didfind;
+ bool was_full_circle = came_full_circle;
+ size_t len;
+
+ if (needle[0] == '\0') {
+ highlight(FALSE, 0);
+ return FALSE;
+ }
+
+ came_full_circle = FALSE;
+
+ if (ISSET(USE_REGEXP) && !regexp_init(needle))
+ return FALSE;
+
+ didfind = findnextstr(needle, FALSE, JUSTFIND, &len, FALSE,
+ openfile->current, openfile->current_x, FALSE);
+ came_full_circle = was_full_circle;
+
+ if (ISSET(USE_REGEXP))
+ regexp_cleanup();
+
+ if (didfind)
+ highlight(TRUE, len);
+ else {
+ beep();
+ edit_refresh();
+ }
+
+ return didfind;
+}
+
+/* Callback for incremental search. */
+void inc_search_cb(char *answer)
+{
+ filestruct *was_current = openfile->current;
+ size_t was_current_x = openfile->current_x;
+ wrap_search(answer);
+
+ /* Reposition cursor. */
+ openfile->current = was_current;
+ openfile->current_x = was_current_x;
+ openfile->placewewant = xplustabs();
+}
+
/* 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 +178,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(INCR_SEARCH)) {
char *disp = display_string(last_search, 0, COLS / 3, FALSE);
buf = charalloc(strlen(disp) + 7);
@@ -128,13 +189,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 +209,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);
@@ -153,6 +221,9 @@ int search_init(bool replacing, bool use_answer)
/* If the search was cancelled, or we have a blank answer and
* nothing was searched for yet during this session, get out. */
if (i == -1 || (i == -2 && *last_search == '\0')) {
+ /* We have to advance one more search, so cursor will be correctly positioned*/
+ if (i == -1 && ISSET(INCR_SEARCH))
+ wrap_search(answer);
statusbar(_("Cancelled"));
return -1;
}
@@ -174,7 +245,18 @@ int search_init(bool replacing, bool use_answer)
func = func_from_key(&i);
- if (func == case_sens_void) {
+ if (func == do_research_void) {
+ if (ISSET(INCR_SEARCH))
+ wrap_search(answer);
+ do_right();
+ wrap_search(answer);
+ backupstring = mallocstrcpy(backupstring, answer);
+ return 1;
+ } else 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 +285,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 +320,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..."));
@@ -307,7 +391,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;
}
@@ -353,16 +438,54 @@ int findnextstr(const char *needle, bool whole_word_only, int modus,
/* 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;
+
+ do {
+ i = search_init(FALSE, FALSE);
+
+ /* We're in incremental search and user finished the search */
+ /* Unmark highlighted text. */
+ if (i == 0) {
+ highlight(FALSE, 0);
+ break;
+ }
+
+ if (i == -1) { /* Cancelled, or some other exit reason. */
+ highlight(FALSE, 0);
+ if (ISSET(INCR_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);
+
+ /* We're in incremental search and user finished the search */
+ if (ISSET(INCR_SEARCH)) {
+ /* Allow cursor to stay if user cancels incremental search*/
+ if (i != -1) {
+ /* Reposition cursor */
+ openfile->current = was_current;
+ openfile->current_x = was_current_x;
+ openfile->placewewant = xplustabs();
+ }
- if (i == 0)
+ /* Unmark highlighted text. */
+ highlight(FALSE, 0);
+ return;
+ } else if (i == -1) {
+ /* Reposition cursor */
+ openfile->current = was_current;
+ openfile->current_x = was_current_x;
+ openfile->placewewant = xplustabs();
+ }
+
+ if (i == 0 && !ISSET(INCR_SEARCH))
go_looking();
}
@@ -432,8 +555,12 @@ void go_looking(void)
came_full_circle = FALSE;
- didfind = findnextstr(last_search, FALSE, JUSTFIND, NULL, TRUE,
- openfile->current, openfile->current_x);
+ didfind = findnextstr(last_search, FALSE, JUSTFIND, NULL, FALSE,
+ 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. */
@@ -575,7 +702,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) {
@@ -738,6 +865,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 9e0bb109..2e182e67 100644
--- a/src/text.c
+++ b/src/text.c
@@ -2584,7 +2584,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