From ce17a265e71b9c73352725792df065dc87ae65a0 Mon Sep 17 00:00:00 2001 From: Kevin McCarthy Date: Sat, 3 Feb 2018 18:08:28 -0800 Subject: [PATCH] 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. --- doc/manual.xml.head | 7 +++ enter.c | 9 ++++ functions.h | 1 + history.c | 113 ++++++++++++++++++++++++++++++++++++++++++++ history.h | 1 + opcodes.h | 1 + 6 files changed, 132 insertions(+) diff --git a/doc/manual.xml.head b/doc/manual.xml.head index 6fdc48f05fc..51a6f0f8824 100644 --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -1318,6 +1318,13 @@ color sidebar_divider color8 default recall next string from history + + ^R + + <history-search> + + use current input to search history + <BackSpace> diff --git a/enter.c b/enter.c index f47575694e6..82865b91ddf 100644 --- a/enter.c +++ b/enter.c @@ -263,6 +263,15 @@ int mutt_enter_string_full(char *buf, size_t buflen, int col, int flags, int mul redraw = MUTT_REDRAW_INIT; break; + case OP_EDITOR_HISTORY_SEARCH: + state->curpos = state->lastchar; + mutt_mb_wcstombs(buf, buflen, state->wbuf, state->curpos); + mutt_history_complete(buf, buflen, hclass); + replace_part(state, 0, buf); + rc = 1; + goto bye; + break; + case OP_EDITOR_BACKSPACE: if (state->curpos == 0) BEEP(); diff --git a/functions.h b/functions.h index 23645fd5b6c..0af28f0a46e 100644 --- a/functions.h +++ b/functions.h @@ -536,6 +536,7 @@ const struct Binding 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 94835882b19..483ce9a40d8 100644 --- a/history.c +++ b/history.c @@ -95,6 +95,10 @@ #include "mutt/mutt.h" #include "history.h" #include "globals.h" +#include "keymap.h" +#include "mutt_curses.h" +#include "mutt_menu.h" +#include "opcodes.h" #include "options.h" #include "protos.h" @@ -112,6 +116,14 @@ struct History short last; }; +static const struct Mapping 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 */ short History; /**< Number of history entries stored in memory */ @@ -632,3 +644,104 @@ void mutt_hist_save_scratch(enum HistoryClass hclass, const char *str) * an old garbage value that should be overwritten */ mutt_str_replace(&h->hist[h->last], str); } + +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, enum FormatFlag 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, struct Menu *m, int num) +{ + char *entry = ((char **) m->data)[num]; + + mutt_expando_format(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) +{ + struct Menu *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_menu_loop(menu)) + { + case OP_GENERIC_SELECT_ENTRY: + mutt_str_strfcpy(buf, matches[menu->current], buflen); + /* fall through */ + + case OP_EXIT: + done = 1; + break; + } + } + + mutt_pop_current_menu(menu); + mutt_menu_destroy(&menu); +} + +static int search_history(char *search_buf, enum HistoryClass hclass, char **matches) +{ + struct History *h = get_history(hclass); + int match_count = 0, cur; + + if ((History == 0) || !h) + return 0; + + cur = h->last; + do + { + cur--; + if (cur < 0) + cur = History; + if (cur == h->last) + break; + if (mutt_str_stristr(h->hist[cur], search_buf)) + matches[match_count++] = h->hist[cur]; + } while (match_count < History); + + return match_count; +} + +void mutt_history_complete(char *buf, size_t buflen, enum HistoryClass hclass) +{ + char **matches; + int match_count; + + matches = mutt_mem_calloc(History, sizeof(char *)); + match_count = search_history(buf, hclass, matches); + if (match_count) + { + if (match_count == 1) + mutt_str_strfcpy(buf, matches[0], buflen); + else + history_menu(buf, buflen, matches, match_count); + } + FREE(&matches); +} diff --git a/history.h b/history.h index 5d08282e40e..0928e35da6c 100644 --- a/history.h +++ b/history.h @@ -54,5 +54,6 @@ char *mutt_hist_prev(enum HistoryClass hclass); void mutt_hist_read_file(void); void mutt_hist_reset_state(enum HistoryClass hclass); void mutt_hist_save_scratch(enum HistoryClass hclass, const char *str); +void mutt_history_complete(char *buf, size_t buflen, enum HistoryClass hclass); #endif /* _MUTT_HISTORY_H */ diff --git a/opcodes.h b/opcodes.h index 5dab7238b02..e1bc9cfb863 100644 --- a/opcodes.h +++ b/opcodes.h @@ -109,6 +109,7 @@ _fmt(OP_EDITOR_FORWARD_WORD, N_("move the cursor to the end of the word")) \ _fmt(OP_EDITOR_HISTORY_DOWN, N_("scroll down through the history list")) \ _fmt(OP_EDITOR_HISTORY_UP, N_("scroll up through the history list")) \ + _fmt(OP_EDITOR_HISTORY_SEARCH, N_("search through the history list")) \ _fmt(OP_EDITOR_KILL_EOL, N_("delete chars from cursor to end of line")) \ _fmt(OP_EDITOR_KILL_EOW, N_("delete chars from the cursor to the end of the word")) \ _fmt(OP_EDITOR_KILL_LINE, N_("delete all chars on the line")) \