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