Commit aa6903c9 by Kevin J. McCarthy

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.
parent d5544ef6
Pipeline #17046407 passed with stages
in 17 minutes 46 seconds
......@@ -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"
......
......@@ -557,6 +557,7 @@ descriptions.
<row><entry>^V</entry><entry><literal>&lt;quote-char&gt;</literal></entry><entry>quote the next typed key</entry></row>
<row><entry>&lt;Up&gt;</entry><entry><literal>&lt;history-up&gt;</literal></entry><entry>recall previous string from history</entry></row>
<row><entry>&lt;Down&gt;</entry><entry><literal>&lt;history-down&gt;</literal></entry><entry>recall next string from history</entry></row>
<row><entry>^R</entry><entry><literal>&lt;history-search&gt;</literal></entry><entry>use current input to search history</entry></row>
<row><entry>&lt;BackSpace&gt;</entry><entry><literal>&lt;backspace&gt;</literal></entry><entry>kill the char in front of the cursor</entry></row>
<row><entry>Esc u</entry><entry><literal>&lt;upcase-word&gt;</literal></entry><entry>convert word to upper case</entry></row>
<row><entry>Esc l</entry><entry><literal>&lt;downcase-word&gt;</literal></entry><entry>convert word to lower case</entry></row>
......
......@@ -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 ();
......
......@@ -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 }
};
......
......@@ -22,6 +22,7 @@
#include "mutt.h"
#include "history.h"
#include "mutt_menu.h"
#include <stdint.h>
......@@ -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);
}
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment