add a flag to limit history search results

This adds a flag to the `history search` command to limit the number of
matching entries to the first "n". The default is unlimited. This is
mostly useful in conjunction with aliases (i.e., functions) that are
intended to report the "n" most recent matching history entries without
piping the result through the user's pager.

Fixes #3244
This commit is contained in:
Kurtis Rader 2016-09-20 22:32:38 -07:00
parent c2a8de4873
commit e9b5505169
6 changed files with 58 additions and 12 deletions

View File

@ -2,7 +2,7 @@
\subsection history-synopsis Synopsis \subsection history-synopsis Synopsis
\fish{synopsis} \fish{synopsis}
history search [ --show-time ] [ --exact | --prefix | --contains ] [ "search string"... ] history search [ --show-time ] [ --exact | --prefix | --contains ] [ --max=n ] [ "search string"... ]
history delete [ --show-time ] [ --exact | --prefix | --contains ] "search string"... history delete [ --show-time ] [ --exact | --prefix | --contains ] "search string"...
history merge history merge
history save history save
@ -30,7 +30,7 @@ The following operations (sub-commands) are available:
The following options are available: The following options are available:
These flags can appear before or immediately after on of the sub-commands listed above. These flags can appear before or immediately after one of the sub-commands listed above.
- `-c` or `--contains` searches or deletes items in the history that contain the specified text string. This is the default for the `--search` flag. This is not currently supported by the `--delete` flag. - `-c` or `--contains` searches or deletes items in the history that contain the specified text string. This is the default for the `--search` flag. This is not currently supported by the `--delete` flag.
@ -40,6 +40,8 @@ These flags can appear before or immediately after on of the sub-commands listed
- `-t` or `--show-time` prepends each history entry with the date and time the entry was recorded . By default it uses the strftime format `# %c%n`. You can specify another format; e.g., `--show-time='%Y-%m-%d %H:%M:%S '` or `--show-time='%a%I%p'`. The short option, `-t` doesn't accept a stftime format string; it only uses the default format. Any strftime format is allowed, including `%s` to get the raw UNIX seconds since the epoch. Note that `--with-time` is also allowed but is deprecated and will be removed at a future date. - `-t` or `--show-time` prepends each history entry with the date and time the entry was recorded . By default it uses the strftime format `# %c%n`. You can specify another format; e.g., `--show-time='%Y-%m-%d %H:%M:%S '` or `--show-time='%a%I%p'`. The short option, `-t` doesn't accept a stftime format string; it only uses the default format. Any strftime format is allowed, including `%s` to get the raw UNIX seconds since the epoch. Note that `--with-time` is also allowed but is deprecated and will be removed at a future date.
- `-<number>` `-n <number>` or `--max=<number>` limits the matched history items to the first "n" matching entries. This is only valid for `history search`.
- `-h` or `--help` display help for this command. - `-h` or `--help` display help for this command.
\subsection history-examples Example \subsection history-examples Example

View File

@ -10,6 +10,8 @@ complete -c history -n '__fish_seen_subcommand_from search delete' \
-s e -l exact -d "Match items identical to the string" -s e -l exact -d "Match items identical to the string"
complete -c history -n '__fish_seen_subcommand_from search delete' \ complete -c history -n '__fish_seen_subcommand_from search delete' \
-s t -l show-time -d "Output with timestamps" -s t -l show-time -d "Output with timestamps"
complete -c history -n '__fish_seen_subcommand_from search' \
-s n -l max -d "Limit output to the first 'n' matches"
# We don't include a completion for the "save" subcommand because it should not be used # We don't include a completion for the "save" subcommand because it should not be used
# interactively. # interactively.

View File

@ -34,6 +34,7 @@ function history --description "display or manipulate interactive command histor
set -l hist_cmd set -l hist_cmd
set -l search_mode set -l search_mode
set -l show_time set -l show_time
set -l max_count
# Check for a recognized subcommand as the first argument. # Check for a recognized subcommand as the first argument.
if set -q argv[1] if set -q argv[1]
@ -78,11 +79,24 @@ function history --description "display or manipulate interactive command histor
set search_mode --contains set search_mode --contains
case -e --exact case -e --exact
set search_mode --exact set search_mode --exact
case -n --max
if string match -- '-n?*' $argv[1]
or string match -- '--max=*' $argv[1]
set max_count $argv[1]
else
set max_count $argv[1] $argv[2]
set -e argv[1]
end
case -- case --
set -e argv[1] set -e argv[1]
break break
case '*' case '*'
break if string match -r -- '-\d+' $argv[1]
set max_count $argv[1]
set -e argv[1]
else
break
end
end end
set -e argv[1] set -e argv[1]
end end
@ -107,13 +121,14 @@ function history --description "display or manipulate interactive command histor
test -z "$search_mode" test -z "$search_mode"
and set search_mode "--contains" and set search_mode "--contains"
echo "builtin history search $search_mode $show_time $max_count -- $argv" >>/tmp/x
if isatty stdout if isatty stdout
set -l pager less set -l pager less
set -q PAGER set -q PAGER
and set pager $PAGER and set pager $PAGER
builtin history search $search_mode $show_time -- $argv | eval $pager builtin history search $search_mode $show_time $max_count -- $argv | eval $pager
else else
builtin history search $search_mode $show_time -- $argv builtin history search $search_mode $show_time $max_count -- $argv
end end
case delete # interactively delete history case delete # interactively delete history

View File

@ -2846,18 +2846,20 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
int argc = builtin_count_args(argv); int argc = builtin_count_args(argv);
hist_cmd_t hist_cmd = HIST_NOOP; hist_cmd_t hist_cmd = HIST_NOOP;
history_search_type_t search_type = (history_search_type_t)-1; history_search_type_t search_type = (history_search_type_t)-1;
long max_items = LONG_MAX;
bool history_search_type_defined = false; bool history_search_type_defined = false;
const wchar_t *show_time_format = NULL; const wchar_t *show_time_format = NULL;
// TODO: Remove the long options that correspond to subcommands (e.g., '--delete') on or after // TODO: Remove the long options that correspond to subcommands (e.g., '--delete') on or after
// 2017-10 (which will be a full year after these flags have been deprecated). // 2017-10 (which will be a full year after these flags have been deprecated).
const wchar_t *short_options = L":mepcht"; const wchar_t *short_options = L":mn:epcht";
const struct woption long_options[] = {{L"prefix", no_argument, NULL, 'p'}, const struct woption long_options[] = {{L"prefix", no_argument, NULL, 'p'},
{L"contains", no_argument, NULL, 'c'}, {L"contains", no_argument, NULL, 'c'},
{L"help", no_argument, NULL, 'h'}, {L"help", no_argument, NULL, 'h'},
{L"show-time", optional_argument, NULL, 't'}, {L"show-time", optional_argument, NULL, 't'},
{L"with-time", optional_argument, NULL, 't'}, {L"with-time", optional_argument, NULL, 't'},
{L"exact", no_argument, NULL, 'e'}, {L"exact", no_argument, NULL, 'e'},
{L"max", required_argument, NULL, 'n'},
{L"delete", no_argument, NULL, 1}, {L"delete", no_argument, NULL, 1},
{L"search", no_argument, NULL, 2}, {L"search", no_argument, NULL, 2},
{L"save", no_argument, NULL, 3}, {L"save", no_argument, NULL, 3},
@ -2923,6 +2925,17 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
show_time_format = w.woptarg ? w.woptarg : L"# %c%n"; show_time_format = w.woptarg ? w.woptarg : L"# %c%n";
break; break;
} }
case 'n': {
wchar_t *end = 0;
max_items = wcstol(w.woptarg, &end, 10);
if (!(*w.woptarg != L'\0' && *end == L'\0')) {
streams.err.append_format(
_(L"%ls: max value '%ls' is not a valid number\n"), argv[0],
w.woptarg);
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'h': { case 'h': {
builtin_print_help(parser, streams, cmd, streams.out); builtin_print_help(parser, streams, cmd, streams.out);
return STATUS_BUILTIN_OK; return STATUS_BUILTIN_OK;
@ -2932,13 +2945,26 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
} }
case '?': { case '?': {
streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, argv[w.woptind - 1]); // Try to parse it as a number; e.g., "-123".
return STATUS_BUILTIN_ERROR; wchar_t *end = 0;
max_items = wcstol(argv[w.woptind - 1] + 1, &end, 10);
if (!(argv[w.woptind - 1][1] != L'\0' && *end == L'\0')) {
streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, argv[w.woptind - 1]);
return STATUS_BUILTIN_ERROR;
}
w.nextchar = NULL;
break;
} }
default: { DIE("unexpected retval from wgetopt_long"); } default: { DIE("unexpected retval from wgetopt_long"); }
} }
} }
if (max_items <= 0) {
streams.err.append_format(_(L"%ls: max value '%ls' is not a valid number\n"), argv[0],
w.woptarg);
return STATUS_BUILTIN_ERROR;
}
// If a history command hasn't already been specified via a flag check the first word. // If a history command hasn't already been specified via a flag check the first word.
// Note that this can be simplified after we eliminate allowing subcommands as flags. // Note that this can be simplified after we eliminate allowing subcommands as flags.
// See the TODO above regarding the `long_options` array. // See the TODO above regarding the `long_options` array.
@ -2963,7 +2989,7 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
int status = STATUS_BUILTIN_OK; int status = STATUS_BUILTIN_OK;
switch (hist_cmd) { switch (hist_cmd) {
case HIST_SEARCH: { case HIST_SEARCH: {
if (!history->search(search_type, args, show_time_format, streams)) { if (!history->search(search_type, args, show_time_format, max_items, streams)) {
status = STATUS_BUILTIN_ERROR; status = STATUS_BUILTIN_ERROR;
} }
break; break;

View File

@ -1415,11 +1415,11 @@ static bool format_history_record(const history_item_t &item, const wchar_t *sho
} }
bool history_t::search(history_search_type_t search_type, wcstring_list_t search_args, bool history_t::search(history_search_type_t search_type, wcstring_list_t search_args,
const wchar_t *show_time_format, io_streams_t &streams) { const wchar_t *show_time_format, long max_items, io_streams_t &streams) {
// scoped_lock locker(lock); // scoped_lock locker(lock);
if (search_args.empty()) { if (search_args.empty()) {
// Start at one because zero is the current command. // Start at one because zero is the current command.
for (int i = 1; !this->item_at_index(i).empty(); ++i) { for (int i = 1; !this->item_at_index(i).empty() && max_items; ++i, --max_items) {
if (!format_history_record(this->item_at_index(i), show_time_format, streams)) { if (!format_history_record(this->item_at_index(i), show_time_format, streams)) {
return false; return false;
} }
@ -1439,6 +1439,7 @@ bool history_t::search(history_search_type_t search_type, wcstring_list_t search
if (!format_history_record(searcher.current_item(), show_time_format, streams)) { if (!format_history_record(searcher.current_item(), show_time_format, streams)) {
return false; return false;
} }
if (--max_items == 0) return true;
} }
} }

View File

@ -227,7 +227,7 @@ class history_t {
// Searches history. // Searches history.
bool search(history_search_type_t search_type, wcstring_list_t search_args, bool search(history_search_type_t search_type, wcstring_list_t search_args,
const wchar_t *show_time_format, io_streams_t &streams); const wchar_t *show_time_format, long max_items, io_streams_t &streams);
// Enable / disable automatic saving. Main thread only! // Enable / disable automatic saving. Main thread only!
void disable_automatic_saving(); void disable_automatic_saving();