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")) \