From: Kevin McCarthy Date: Sun, 4 Feb 2018 02:08:28 +0000 (-0800) Subject: Add history-search function, bound to ctrl-r. X-Git-Tag: mutt-1-10-rel~61 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=aa6903c9924127ade0219204067e3be518b7e640;p=mutt Add history-search function, bound to ctrl-r. Create a very basic "search history" functionality in the line editor. It uses the current input, and searches backward through history. If there is one match, it immediately uses that otherwise it pops up a simple menu of matches. --- diff --git a/OPS b/OPS index cc1bc3bd..f53cd63a 100644 --- a/OPS +++ b/OPS @@ -72,6 +72,7 @@ OP_EDITOR_FORWARD_CHAR "move the cursor one character to the right" OP_EDITOR_FORWARD_WORD "move the cursor to the end of the word" OP_EDITOR_HISTORY_DOWN "scroll down through the history list" OP_EDITOR_HISTORY_UP "scroll up through the history list" +OP_EDITOR_HISTORY_SEARCH "search through the history list" OP_EDITOR_KILL_EOL "delete chars from cursor to end of line" OP_EDITOR_KILL_EOW "delete chars from the cursor to the end of the word" OP_EDITOR_KILL_LINE "delete all chars on the line" diff --git a/doc/manual.xml.head b/doc/manual.xml.head index 30bcdb5f..6c1d64a5 100644 --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -557,6 +557,7 @@ descriptions. ^V<quote-char>quote the next typed key <Up><history-up>recall previous string from history <Down><history-down>recall next string from history +^R<history-search>use current input to search history <BackSpace><backspace>kill the char in front of the cursor Esc u<upcase-word>convert word to upper case Esc l<downcase-word>convert word to lower case diff --git a/enter.c b/enter.c index 188cf295..f1fb746c 100644 --- a/enter.c +++ b/enter.c @@ -351,6 +351,15 @@ int _mutt_enter_string (char *buf, size_t buflen, int col, redraw = MUTT_REDRAW_INIT; break; + case OP_EDITOR_HISTORY_SEARCH: + state->curpos = state->lastchar; + my_wcstombs (buf, buflen, state->wbuf, state->curpos); + mutt_history_complete (buf, buflen, hclass); + replace_part (state, 0, buf); + rv = 1; + goto bye; + break; + case OP_EDITOR_BACKSPACE: if (state->curpos == 0) BEEP (); diff --git a/functions.h b/functions.h index 1f7ca592..2985c6eb 100644 --- a/functions.h +++ b/functions.h @@ -447,6 +447,7 @@ const struct binding_t OpEditor[] = { /* map: editor */ { "buffy-cycle", OP_EDITOR_BUFFY_CYCLE, " " }, { "history-up", OP_EDITOR_HISTORY_UP, NULL }, { "history-down", OP_EDITOR_HISTORY_DOWN, NULL }, + { "history-search", OP_EDITOR_HISTORY_SEARCH, "\022" }, { "transpose-chars", OP_EDITOR_TRANSPOSE_CHARS, NULL }, { NULL, 0, NULL } }; diff --git a/history.c b/history.c index 4e00384d..a4b2b837 100644 --- a/history.c +++ b/history.c @@ -22,6 +22,7 @@ #include "mutt.h" #include "history.h" +#include "mutt_menu.h" #include @@ -70,6 +71,14 @@ struct history short last; }; +static const struct mapping_t HistoryHelp[] = { + { N_("Exit"), OP_EXIT }, + { N_("Select"), OP_GENERIC_SELECT_ENTRY }, + { N_("Search"), OP_SEARCH }, + { N_("Help"), OP_HELP }, + { NULL, 0 } +}; + /* global vars used for the string-history routines */ static struct history History[HC_LAST]; @@ -477,3 +486,105 @@ void mutt_history_save_scratch (history_class_t hclass, const char *s) * an old garbage value that should be overwritten */ mutt_str_replace (&h->hist[h->last], s); } + +static const char * +history_format_str (char *dest, size_t destlen, size_t col, int cols, char op, const char *src, + const char *fmt, const char *ifstring, const char *elsestring, + unsigned long data, format_flag flags) +{ + char *match = (char *)data; + + switch (op) + { + case 's': + mutt_format_s (dest, destlen, fmt, match); + break; + } + + return (src); +} + +static void history_entry (char *s, size_t slen, MUTTMENU *m, int num) +{ + char *entry = ((char **)m->data)[num]; + + mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, "%s", history_format_str, + (unsigned long) entry, MUTT_FORMAT_ARROWCURSOR); +} + +static void history_menu (char *buf, size_t buflen, char **matches, int match_count) +{ + MUTTMENU *menu; + int done = 0; + char helpstr[LONG_STRING]; + char title[STRING]; + + snprintf (title, sizeof (title), _(" History '%s'"), buf); + + menu = mutt_new_menu (MENU_GENERIC); + menu->make_entry = history_entry; + menu->title = title; + menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_GENERIC, HistoryHelp); + mutt_push_current_menu (menu); + + menu->max = match_count; + menu->data = matches; + + while (!done) + { + switch (mutt_menuLoop (menu)) + { + case OP_GENERIC_SELECT_ENTRY: + strfcpy (buf, matches[menu->current], buflen); + /* fall through */ + + case OP_EXIT: + done = 1; + break; + } + } + + mutt_pop_current_menu (menu); + mutt_menuDestroy (&menu); +} + +static int search_history (char *search_buf, history_class_t hclass, char **matches) +{ + struct history *h = GET_HISTORY(hclass); + int match_count = 0, cur; + + if (!HistSize || !h) + return 0; + + cur = h->last; + do + { + cur--; + if (cur < 0) + cur = HistSize; + if (cur == h->last) + break; + if (mutt_stristr (h->hist[cur], search_buf)) + matches[match_count++] = h->hist[cur]; + } while (match_count < HistSize); + + return match_count; +} + +void mutt_history_complete (char *buf, size_t buflen, history_class_t hclass) +{ + char **matches; + int match_count; + + matches = safe_calloc (HistSize, sizeof (char *)); + match_count = search_history (buf, hclass, matches); + if (match_count) + { + if (match_count == 1) + strfcpy (buf, matches[0], buflen); + else + history_menu (buf, buflen, matches, match_count); + } + FREE(&matches); +} + diff --git a/history.h b/history.h index 994526b6..76860056 100644 --- a/history.h +++ b/history.h @@ -44,5 +44,6 @@ char *mutt_history_prev(history_class_t); void mutt_reset_history_state (history_class_t); int mutt_history_at_scratch (history_class_t); void mutt_history_save_scratch (history_class_t, const char *); +void mutt_history_complete (char *, size_t, history_class_t); #endif