diff --git a/doc_src/history.txt b/doc_src/history.txt index 704c13cf7..a7456d837 100644 --- a/doc_src/history.txt +++ b/doc_src/history.txt @@ -2,7 +2,7 @@ \subsection history-synopsis 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 merge history save @@ -30,7 +30,7 @@ The following operations (sub-commands) 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. @@ -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. +- `-` `-n ` or `--max=` 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. \subsection history-examples Example diff --git a/share/completions/history.fish b/share/completions/history.fish index 6899da03f..f4d9b9114 100644 --- a/share/completions/history.fish +++ b/share/completions/history.fish @@ -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" complete -c history -n '__fish_seen_subcommand_from search delete' \ -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 # interactively. diff --git a/share/functions/history.fish b/share/functions/history.fish index 6bf228e14..c3dca2b22 100644 --- a/share/functions/history.fish +++ b/share/functions/history.fish @@ -34,6 +34,7 @@ function history --description "display or manipulate interactive command histor set -l hist_cmd set -l search_mode set -l show_time + set -l max_count # Check for a recognized subcommand as the first argument. if set -q argv[1] @@ -78,11 +79,24 @@ function history --description "display or manipulate interactive command histor set search_mode --contains case -e --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 -- set -e argv[1] break case '*' - break + if string match -r -- '-\d+' $argv[1] + set max_count $argv[1] + set -e argv[1] + else + break + end end set -e argv[1] end @@ -107,13 +121,14 @@ function history --description "display or manipulate interactive command histor test -z "$search_mode" and set search_mode "--contains" + echo "builtin history search $search_mode $show_time $max_count -- $argv" >>/tmp/x if isatty stdout set -l pager less set -q 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 - builtin history search $search_mode $show_time -- $argv + builtin history search $search_mode $show_time $max_count -- $argv end case delete # interactively delete history diff --git a/src/builtin.cpp b/src/builtin.cpp index e0eb05643..9ba131622 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -2846,18 +2846,20 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar int argc = builtin_count_args(argv); hist_cmd_t hist_cmd = HIST_NOOP; history_search_type_t search_type = (history_search_type_t)-1; + long max_items = LONG_MAX; bool history_search_type_defined = false; const wchar_t *show_time_format = NULL; // 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). - 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'}, {L"contains", no_argument, NULL, 'c'}, {L"help", no_argument, NULL, 'h'}, {L"show-time", optional_argument, NULL, 't'}, {L"with-time", optional_argument, NULL, 't'}, {L"exact", no_argument, NULL, 'e'}, + {L"max", required_argument, NULL, 'n'}, {L"delete", no_argument, NULL, 1}, {L"search", no_argument, NULL, 2}, {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"; 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': { builtin_print_help(parser, streams, cmd, streams.out); 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; } case '?': { - streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, argv[w.woptind - 1]); - return STATUS_BUILTIN_ERROR; + // Try to parse it as a number; e.g., "-123". + 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"); } } } + 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. // Note that this can be simplified after we eliminate allowing subcommands as flags. // 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; switch (hist_cmd) { 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; } break; diff --git a/src/history.cpp b/src/history.cpp index 035309027..16c8fab26 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -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, - 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); if (search_args.empty()) { // 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)) { 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)) { return false; } + if (--max_items == 0) return true; } } diff --git a/src/history.h b/src/history.h index c624d4658..db1e4615d 100644 --- a/src/history.h +++ b/src/history.h @@ -227,7 +227,7 @@ class history_t { // Searches history. 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! void disable_automatic_saving();