mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-12 21:18:53 +00:00
alter history sub-command handling
This deprecates the use of long options for history sub-commands (e.g., `history --delete`) in favor of proper sub-commands (e.g., `history delete`). It also eliminates the short options for those sub-commands. Also change option processing to allow options anywhere on the command line to match how the vast majority of fish builtins handle flags. Replace --with-time with --show-time. Fixes #3367
This commit is contained in:
parent
02ba7933e0
commit
76c73aa8ce
9 changed files with 288 additions and 154 deletions
|
@ -2,39 +2,45 @@
|
|||
|
||||
\subsection history-synopsis Synopsis
|
||||
\fish{synopsis}
|
||||
history ( -s | --search ) [ -t | --with-time ] [ -e | --exact | -p | --prefix | -c | --contains ] [ "search string"... ]
|
||||
history ( -d | --delete ) [ -t | --with-time ] [ -e | --exact | -p | --prefix | -c | --contains ] "search string"...
|
||||
history ( -m | --merge )
|
||||
history ( -s | --save )
|
||||
history ( -l | --clear )
|
||||
history search [ --show-time ] [ --exact | --prefix | --contains ] [ "search string"... ]
|
||||
history delete [ --show-time ] [ --exact | --prefix | --contains ] "search string"...
|
||||
history merge
|
||||
history save
|
||||
history clear
|
||||
history ( -h | --help )
|
||||
\endfish
|
||||
|
||||
\subsection history-description Description
|
||||
|
||||
`history` is used to list, search and delete the history of commands used.
|
||||
`history` is used to search, delete, and otherwise manipulate the history of interactive commands.
|
||||
|
||||
The following commands are available:
|
||||
Note that for backwards compatibility each subcommand can also be specified as a long option. For example, rather than `history search` you can type `history --search`. Those long options are deprecated and will be removed in a future release.
|
||||
|
||||
- `-s` or `--search` returns history items matching the search string. If no search string is provided it returns all history items. This is the default operation if no other operation is specified. The `--contains` search option will be used if you don't specify a different search option. Entries are ordered newest to oldest. If stdout is attached to a tty the output will be piped through your pager by the history function. The history builtin simply writes the results to stdout.
|
||||
The following operations (sub-commands) are available:
|
||||
|
||||
- `-d` or `--delete` deletes history items. Without the `--prefix` or `--contains` options, the exact match will be deleted. With either of these options, a prompt will be displayed before any items are deleted asking you which entries are to be deleted. You can enter the word "all" to delete all matching entries. You can enter a single ID (the number in square brackets) to delete just that single entry. You can enter more than one ID separated by a space to delete multiple entries. Just press [enter] to not delete anything. Note that the interactive delete behavior is a feature of the history function. The history builtin only supports bulk deletion.
|
||||
- `search` returns history items matching the search string. If no search string is provided it returns all history items. This is the default operation if no other operation is specified. You only have to explicitly say `history search` if you wish to search for one of the subcommands. The `--contains` search option will be used if you don't specify a different search option. Entries are ordered newest to oldest. If stdout is attached to a tty the output will be piped through your pager by the history function. The history builtin simply writes the results to stdout.
|
||||
|
||||
- `-m` or `--merge` immediately incorporates history changes from other sessions. Ordinarily `fish` ignores history changes from sessions started after the current one. This command applies those changes immediately.
|
||||
- `delete` deletes history items. Without the `--prefix` or `--contains` options, the exact match will be deleted. With either of these options, a prompt will be displayed before any items are deleted asking you which entries are to be deleted. You can enter the word "all" to delete all matching entries. You can enter a single ID (the number in square brackets) to delete just that single entry. You can enter more than one ID separated by a space to delete multiple entries. Just press [enter] to not delete anything. Note that the interactive delete behavior is a feature of the history function. The history builtin only supports bulk deletion.
|
||||
|
||||
- `-v` or `--save` saves all changes in the history file. The shell automatically saves the history file; this option is provided for internal use.
|
||||
- `merge` immediately incorporates history changes from other sessions. Ordinarily `fish` ignores history changes from sessions started after the current one. This command applies those changes immediately.
|
||||
|
||||
- `-l` or `--clear` clears the history file. A prompt is displayed before the history is erased asking you to confirm you really want to clear all history unless `builtin history` is used.
|
||||
- `save` immediately writes all changes to the history file. The shell automatically saves the history file; this option is provided for internal use and should not normally need to be used by the user.
|
||||
|
||||
- `clear` clears the history file. A prompt is displayed before the history is erased asking you to confirm you really want to clear all history unless `builtin history` is used.
|
||||
|
||||
The following options are available:
|
||||
|
||||
These flags can appear before or immediately after on 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.
|
||||
|
||||
- `-e` or `--exact` searches or deletes items in the history that exactly match the specified text string. This is the default for the `--delete` flag.
|
||||
|
||||
- `-p` or `--prefix` searches or deletes items in the history that begin with the specified text string. This is not currently supported by the `--delete` flag.
|
||||
|
||||
- `-t` or `--with-time` outputs the date and time ("%Y-%m-%d %H:%M:%S") history items were recorded at on a line starting with "#" before each history entry.
|
||||
- `-t` or `--show-time` outputs the date and time ("%Y-%m-%d %H:%M:%S") history items were recorded at on a line starting with "#" before each history entry. Note that `--with-time` is also allowed but is deprecated and will be removed at a future date.
|
||||
|
||||
- `-h` or `--help` display help for this command.
|
||||
|
||||
\subsection history-examples Example
|
||||
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
complete -c history -r -l prefix --description "Match items starting with prefix"
|
||||
complete -c history -r -l contains --description "Match items containing string"
|
||||
complete -c history -l search -s s --description "Prints commands from history matching query"
|
||||
complete -c history -l delete -s d --description "Deletes commands from history matching query"
|
||||
complete -c history -l clear --description "Clears history file"
|
||||
complete -c history -l merge -s m --description "Incorporate history changes from other sessions"
|
||||
complete -c history -l exact -s e --description "Match items in the history that are identicial"
|
||||
complete -c history -l with-time -s t --description "Output with timestamps"
|
||||
# Note that when a completion file is sourced a new block scope is created so `set -l` works.
|
||||
set -l __fish_history_all_commands search delete save merge clear
|
||||
|
||||
# --save is not completed; it is for internal use
|
||||
# Note that these options are only valid with the "search" and "delete" subcommands.
|
||||
complete -c history -n '__fish_seen_subcommand_from search delete' \
|
||||
-s p -l prefix -d "Match items beginning with the string"
|
||||
complete -c history -n '__fish_seen_subcommand_from search delete' \
|
||||
-s c -l contains -d "Match items containing the string"
|
||||
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"
|
||||
|
||||
# We don't include a completion for the "save" subcommand because it should not be used
|
||||
# interactively.
|
||||
complete -f -c history -n "not __fish_seen_subcommand_from $__fish_history_all_commands" \
|
||||
-a search -d "Prints commands from history matching the strings"
|
||||
complete -f -c history -n "not __fish_seen_subcommand_from $__fish_history_all_commands" \
|
||||
-a delete -d "Deletes commands from history matching the strings"
|
||||
complete -f -c history -n "not __fish_seen_subcommand_from $__fish_history_all_commands" \
|
||||
-a merge -d "Incorporate history changes from other sessions"
|
||||
complete -f -c history -n "not __fish_seen_subcommand_from $__fish_history_all_commands" \
|
||||
-a clear -d "Clears history file"
|
||||
|
|
|
@ -1,29 +1,78 @@
|
|||
#
|
||||
# Wrap the builtin history command to provide additional functionality.
|
||||
#
|
||||
|
||||
# This function is meant to mimic the `set_hist_cmd` function in *src/builtin.cpp*.
|
||||
# In particular the error message should be identical in both locations.
|
||||
function __fish_set_hist_cmd --no-scope-shadowing
|
||||
if set -q hist_cmd[1]
|
||||
set -l msg (printf (_ "you cannot do both '%ls' and '%ls' in the same invocation") \
|
||||
$hist_cmd $argv[1])
|
||||
printf (_ "%ls: Invalid combination of options,\n%ls\n") $cmd $msg >&2
|
||||
return 1
|
||||
end
|
||||
set hist_cmd $argv[1]
|
||||
return 0
|
||||
end
|
||||
|
||||
function __fish_unexpected_hist_args --no-scope-shadowing
|
||||
if test -n "$search_mode"
|
||||
or test -n "$show_time"
|
||||
printf (_ "%ls: you cannot use any options with the %ls command\n") $cmd $hist_cmd >&2
|
||||
return 0
|
||||
end
|
||||
if set -q argv[1]
|
||||
printf (_ "%ls: %ls command expected %d args, got %d\n") \
|
||||
$cmd $hist_cmd 0 (count $argv) >&2
|
||||
return 0
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
function history --description "display or manipulate interactive command history"
|
||||
set -l cmd
|
||||
set -l cmd $_
|
||||
set -l cmd history
|
||||
set -l hist_cmd
|
||||
set -l search_mode
|
||||
set -l with_time
|
||||
set -l show_time
|
||||
|
||||
# Check for a recognized subcommand as the first argument.
|
||||
if set -q argv[1]
|
||||
and not string match -q -- '-*' $argv[1]
|
||||
switch $argv[1]
|
||||
case search delete merge save clear
|
||||
set hist_cmd $argv[1]
|
||||
set -e argv[1]
|
||||
end
|
||||
end
|
||||
|
||||
# The "set cmd $cmd xyz" lines are to make it easy to detect if the user specifies more than one
|
||||
# subcommand.
|
||||
#
|
||||
# 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).
|
||||
while set -q argv[1]
|
||||
switch $argv[1]
|
||||
case -d --delete
|
||||
set cmd $cmd delete
|
||||
case -v --save
|
||||
set cmd $cmd save
|
||||
case -l --clear
|
||||
set cmd $cmd clear
|
||||
case -s --search
|
||||
set cmd $cmd search
|
||||
case -m --merge
|
||||
set cmd $cmd merge
|
||||
case --delete
|
||||
__fish_set_hist_cmd delete
|
||||
or return
|
||||
case --save
|
||||
__fish_set_hist_cmd save
|
||||
or return
|
||||
case --clear
|
||||
__fish_set_hist_cmd clear
|
||||
or return
|
||||
case --search
|
||||
__fish_set_hist_cmd search
|
||||
or return
|
||||
case --merge
|
||||
__fish_set_hist_cmd merge
|
||||
or return
|
||||
case -h --help
|
||||
set cmd $cmd help
|
||||
case -t --with-time
|
||||
set with_time -t
|
||||
builtin history --help
|
||||
return
|
||||
case -t --show-time --with-time
|
||||
set show_time -t
|
||||
case -p --prefix
|
||||
set search_mode --prefix
|
||||
case -c --contains
|
||||
|
@ -39,15 +88,23 @@ function history --description "display or manipulate interactive command histor
|
|||
set -e argv[1]
|
||||
end
|
||||
|
||||
if not set -q cmd[1]
|
||||
set cmd search # default to "search" if the user didn't explicitly specify a command
|
||||
else if set -q cmd[2]
|
||||
printf (_ "You cannot specify multiple commands: %s\n") "$cmd"
|
||||
return 1
|
||||
# If a history command has not already been specified check the first non-flag argument for a
|
||||
# command. This allows the flags to appear before or after the subcommand.
|
||||
if not set -q hist_cmd[1]
|
||||
and set -q argv[1]
|
||||
switch $argv[1]
|
||||
case search delete merge save clear
|
||||
set hist_cmd $argv[1]
|
||||
set -e argv[1]
|
||||
end
|
||||
end
|
||||
|
||||
switch $cmd
|
||||
case search
|
||||
if not set -q hist_cmd[1]
|
||||
set hist_cmd search # default to "search" if the user didn't specify a subcommand
|
||||
end
|
||||
|
||||
switch $hist_cmd
|
||||
case search # search the interactive command history
|
||||
test -z "$search_mode"
|
||||
and set search_mode "--contains"
|
||||
|
||||
|
@ -55,12 +112,12 @@ function history --description "display or manipulate interactive command histor
|
|||
set -l pager less
|
||||
set -q PAGER
|
||||
and set pager $PAGER
|
||||
builtin history --search $search_mode $with_time -- $argv | eval $pager
|
||||
builtin history search $search_mode $show_time -- $argv | eval $pager
|
||||
else
|
||||
builtin history --search $search_mode $with_time -- $argv
|
||||
builtin history search $search_mode $show_time -- $argv
|
||||
end
|
||||
|
||||
case delete # Interactively delete history
|
||||
case delete # interactively delete history
|
||||
# TODO: Fix this to deal with history entries that have multiple lines.
|
||||
if not set -q argv[1]
|
||||
printf (_ "You must specify at least one search term when deleting entries\n") >&2
|
||||
|
@ -71,13 +128,13 @@ function history --description "display or manipulate interactive command histor
|
|||
and set search_mode "--exact"
|
||||
|
||||
if test $search_mode = "--exact"
|
||||
builtin history --delete $search_mode $argv
|
||||
builtin history delete $search_mode $argv
|
||||
return
|
||||
end
|
||||
|
||||
# TODO: Fix this so that requesting history entries with a timestamp works:
|
||||
# set -l found_items (builtin history --search $search_mode $with_time -- $argv)
|
||||
set -l found_items (builtin history --search $search_mode -- $argv)
|
||||
# set -l found_items (builtin history search $search_mode $show_time -- $argv)
|
||||
set -l found_items (builtin history search $search_mode -- $argv)
|
||||
if set -q found_items[1]
|
||||
set -l found_items_count (count $found_items)
|
||||
for i in (seq $found_items_count)
|
||||
|
@ -98,8 +155,8 @@ function history --description "display or manipulate interactive command histor
|
|||
|
||||
if test "$choice" = "all"
|
||||
printf "Deleting all matching entries!\n"
|
||||
builtin history --delete $search_mode -- $argv
|
||||
builtin history --save
|
||||
builtin history delete $search_mode -- $argv
|
||||
builtin history save
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -112,45 +169,38 @@ function history --description "display or manipulate interactive command histor
|
|||
end
|
||||
|
||||
printf "Deleting history entry %s: \"%s\"\n" $i $found_items[$i]
|
||||
builtin history --delete "$found_items[$i]"
|
||||
builtin history delete "$found_items[$i]"
|
||||
end
|
||||
builtin history --save
|
||||
builtin history save
|
||||
end
|
||||
|
||||
case save
|
||||
if test -n "$search_mode"
|
||||
or test -n "$with_time"
|
||||
printf (_ "history: you cannot use any options with %s command\n") save >&2
|
||||
return 1
|
||||
end
|
||||
builtin history --save -- $argv
|
||||
case save # save our interactive command history to the persistent history
|
||||
__fish_unexpected_hist_args $argv
|
||||
and return 1
|
||||
|
||||
case merge
|
||||
if test -n "$search_mode"
|
||||
or test -n "$with_time"
|
||||
printf (_ "history: you cannot use any options with %s command\n") merge >&2
|
||||
return 1
|
||||
end
|
||||
builtin history --merge -- $argv
|
||||
builtin history save -- $argv
|
||||
|
||||
case help
|
||||
builtin history --help
|
||||
case merge # merge the persistent interactive command history with our history
|
||||
__fish_unexpected_hist_args $argv
|
||||
and return 1
|
||||
|
||||
case clear
|
||||
# Erase the entire history.
|
||||
if test -n "$search_mode"
|
||||
or test -n "$with_time"
|
||||
printf (_ "history: you cannot use any options with %s command\n") clear >&2
|
||||
return 1
|
||||
end
|
||||
builtin history merge -- $argv
|
||||
|
||||
case clear # clear the interactive command history
|
||||
__fish_unexpected_hist_args $argv
|
||||
and return 1
|
||||
|
||||
printf (_ "If you enter 'yes' your entire interactive command history will be erased\n")
|
||||
read --local --prompt "echo 'Are you sure you want to clear history? (yes/no) '" choice
|
||||
if test "$choice" = "yes"
|
||||
builtin history --clear -- $argv
|
||||
builtin history clear -- $argv
|
||||
and printf (_ "Command history cleared!")
|
||||
else
|
||||
printf (_ "You did not say 'yes' so I will not clear your command history\n")
|
||||
end
|
||||
|
||||
case '*'
|
||||
printf "%ls: unexpected subcommand '%ls'\n" $cmd $hist_cmd
|
||||
return 2
|
||||
end
|
||||
end
|
||||
|
|
|
@ -455,7 +455,7 @@ class BindingParser:
|
|||
readable_command += 'CTRL - '
|
||||
if alt:
|
||||
readable_command += 'ALT - '
|
||||
|
||||
|
||||
if result == '':
|
||||
return 'unknown-control-sequence'
|
||||
|
||||
|
@ -654,7 +654,9 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
|
||||
def do_delete_history_item(self, history_item_text):
|
||||
# It's really lame that we always return success here
|
||||
out, err = run_fish_cmd('builtin history --save --delete -- ' + escape_fish_cmd(history_item_text))
|
||||
cmd = ('builtin history delete --exact -- %s; builtin history save' %
|
||||
escape_fish_cmd(history_item_text))
|
||||
out, err = run_fish_cmd(cmd)
|
||||
return True
|
||||
|
||||
def do_set_prompt_function(self, prompt_func):
|
||||
|
|
141
src/builtin.cpp
141
src/builtin.cpp
|
@ -471,7 +471,6 @@ static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv)
|
|||
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
||||
long_options[opt_index].name);
|
||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
case 'a': {
|
||||
|
@ -2776,6 +2775,21 @@ static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **arg
|
|||
|
||||
enum hist_cmd_t { HIST_NOOP, HIST_SEARCH, HIST_DELETE, HIST_CLEAR, HIST_MERGE, HIST_SAVE };
|
||||
|
||||
static hist_cmd_t hist_string_to_cmd(const wchar_t *hist_command) {
|
||||
if (wcscmp(hist_command, L"search") == 0) {
|
||||
return HIST_SEARCH;
|
||||
} else if (wcscmp(hist_command, L"delete") == 0) {
|
||||
return HIST_DELETE;
|
||||
} else if (wcscmp(hist_command, L"merge") == 0) {
|
||||
return HIST_MERGE;
|
||||
} else if (wcscmp(hist_command, L"save") == 0) {
|
||||
return HIST_SAVE;
|
||||
} else if (wcscmp(hist_command, L"clear") == 0) {
|
||||
return HIST_CLEAR;
|
||||
}
|
||||
return HIST_NOOP;
|
||||
}
|
||||
|
||||
static const wcstring hist_cmd_to_string(hist_cmd_t hist_cmd) {
|
||||
switch (hist_cmd) {
|
||||
case HIST_NOOP:
|
||||
|
@ -2802,8 +2816,8 @@ static bool set_hist_cmd(wchar_t *const cmd, hist_cmd_t *hist_cmd, hist_cmd_t su
|
|||
if (*hist_cmd != HIST_NOOP) {
|
||||
wchar_t err_text[1024];
|
||||
swprintf(err_text, sizeof(err_text) / sizeof(wchar_t),
|
||||
_(L"You cannot do both '%ls' and '%ls' in the same '%ls' invocation\n"),
|
||||
hist_cmd_to_string(*hist_cmd).c_str(), hist_cmd_to_string(sub_cmd).c_str(), cmd);
|
||||
_(L"you cannot do both '%ls' and '%ls' in the same invocation"),
|
||||
hist_cmd_to_string(*hist_cmd).c_str(), hist_cmd_to_string(sub_cmd).c_str());
|
||||
streams.err.append_format(BUILTIN_ERR_COMBO2, cmd, err_text);
|
||||
return false;
|
||||
}
|
||||
|
@ -2812,15 +2826,14 @@ static bool set_hist_cmd(wchar_t *const cmd, hist_cmd_t *hist_cmd, hist_cmd_t su
|
|||
return true;
|
||||
}
|
||||
|
||||
#define CHECK_FOR_UNEXPECTED_HIST_OPTIONS(hist_cmd) \
|
||||
if (history_search_type_defined || with_time) { \
|
||||
streams.err.append_format(_(L"history: you cannot use any options with %ls command\n"), \
|
||||
hist_cmd_to_string(hist_cmd).c_str()); \
|
||||
status = STATUS_BUILTIN_ERROR; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define CHECK_FOR_UNEXPECTED_HIST_ARGS(hist_cmd) \
|
||||
if (history_search_type_defined || show_time) { \
|
||||
streams.err.append_format( \
|
||||
_(L"%ls: you cannot use any options with the %ls command\n"), \
|
||||
cmd, hist_cmd_to_string(hist_cmd).c_str()); \
|
||||
status = STATUS_BUILTIN_ERROR; \
|
||||
break; \
|
||||
} \
|
||||
if (args.size() != 0) { \
|
||||
streams.err.append_format(BUILTIN_ERR_ARG_COUNT, cmd, \
|
||||
hist_cmd_to_string(hist_cmd).c_str(), 0, args.size()); \
|
||||
|
@ -2835,60 +2848,63 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
|
|||
hist_cmd_t hist_cmd = HIST_NOOP;
|
||||
history_search_type_t search_type = (history_search_type_t)-1;
|
||||
bool history_search_type_defined = false;
|
||||
bool with_time = false;
|
||||
bool show_time = false;
|
||||
|
||||
static const struct woption long_options[] = {{L"delete", no_argument, 0, 'd'},
|
||||
{L"search", no_argument, 0, 's'},
|
||||
{L"prefix", no_argument, 0, 'p'},
|
||||
{L"contains", no_argument, 0, 'c'},
|
||||
{L"save", no_argument, 0, 'v'},
|
||||
{L"clear", no_argument, 0, 'l'},
|
||||
{L"merge", no_argument, 0, 'm'},
|
||||
{L"help", no_argument, 0, 'h'},
|
||||
{L"with-time", no_argument, 0, 't'},
|
||||
{L"exact", no_argument, 0, 'e'},
|
||||
{0, 0, 0, 0}};
|
||||
// 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 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", no_argument, NULL, 't'},
|
||||
{L"with-time", no_argument, NULL, 't'},
|
||||
{L"exact", no_argument, NULL, 'e'},
|
||||
{L"delete", no_argument, NULL, 1},
|
||||
{L"search", no_argument, NULL, 2},
|
||||
{L"save", no_argument, NULL, 3},
|
||||
{L"clear", no_argument, NULL, 4},
|
||||
{L"merge", no_argument, NULL, 5},
|
||||
{NULL, 0, NULL, 0}};
|
||||
|
||||
history_t *history = reader_get_history();
|
||||
// Use the default history if we have none (which happens if invoked non-interactively, e.g.
|
||||
// from webconfig.py.
|
||||
if (!history) history = &history_t::history_with_name(L"fish");
|
||||
|
||||
int opt = 0;
|
||||
int opt_index = 0;
|
||||
int opt;
|
||||
wgetopter_t w;
|
||||
while ((opt = w.wgetopt_long(argc, argv, L"+despcvlmht", long_options, &opt_index)) != EOF) {
|
||||
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 's': {
|
||||
if (!set_hist_cmd(cmd, &hist_cmd, HIST_SEARCH, streams)) {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
if (!set_hist_cmd(cmd, &hist_cmd, HIST_MERGE, streams)) {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
if (!set_hist_cmd(cmd, &hist_cmd, HIST_SAVE, streams)) {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
case 1: {
|
||||
if (!set_hist_cmd(cmd, &hist_cmd, HIST_DELETE, streams)) {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
case 2: {
|
||||
if (!set_hist_cmd(cmd, &hist_cmd, HIST_SEARCH, streams)) {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (!set_hist_cmd(cmd, &hist_cmd, HIST_SAVE, streams)) {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (!set_hist_cmd(cmd, &hist_cmd, HIST_CLEAR, streams)) {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
if (!set_hist_cmd(cmd, &hist_cmd, HIST_MERGE, streams)) {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
search_type = HISTORY_SEARCH_TYPE_PREFIX;
|
||||
history_search_type_defined = true;
|
||||
|
@ -2905,28 +2921,40 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
|
|||
break;
|
||||
}
|
||||
case 't': {
|
||||
with_time = true;
|
||||
show_time = true;
|
||||
break;
|
||||
}
|
||||
case 'h': {
|
||||
builtin_print_help(parser, streams, cmd, streams.out);
|
||||
return STATUS_BUILTIN_OK;
|
||||
}
|
||||
case ':': {
|
||||
streams.err.append_format(BUILTIN_ERR_MISSING, cmd, argv[w.woptind - 1]);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
case '?': {
|
||||
streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, argv[w.woptind - 1]);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
default: {
|
||||
streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, argv[w.woptind - 1]);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
default: { DIE("unexpected retval from wgetopt_long"); }
|
||||
}
|
||||
}
|
||||
|
||||
// Everything after the flags is an argument for a subcommand (e.g., a search term).
|
||||
// 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.
|
||||
if (hist_cmd == HIST_NOOP && w.woptind < argc) {
|
||||
hist_cmd = hist_string_to_cmd(argv[w.woptind]);
|
||||
if (hist_cmd != HIST_NOOP) {
|
||||
w.woptind++;
|
||||
}
|
||||
}
|
||||
|
||||
// Every argument that we haven't consumed already is an argument for a subcommand (e.g., a
|
||||
// search term).
|
||||
const wcstring_list_t args(argv + w.woptind, argv + argc);
|
||||
|
||||
// Establish appropriate defaults for unspecified options.
|
||||
// Establish appropriate defaults.
|
||||
if (hist_cmd == HIST_NOOP) hist_cmd = HIST_SEARCH;
|
||||
if (!history_search_type_defined) {
|
||||
if (hist_cmd == HIST_SEARCH) search_type = HISTORY_SEARCH_TYPE_CONTAINS;
|
||||
|
@ -2936,7 +2964,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, with_time, streams)) {
|
||||
if (!history->search(search_type, args, show_time, streams)) {
|
||||
status = STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
break;
|
||||
|
@ -2946,7 +2974,7 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
|
|||
// this time we expect the non-exact deletions to be handled only by the history
|
||||
// function's interactive delete feature.
|
||||
if (search_type != HISTORY_SEARCH_TYPE_EXACT) {
|
||||
streams.err.append_format(_(L"builtin history --delete only supports --exact\n"));
|
||||
streams.err.append_format(_(L"builtin history delete only supports --exact\n"));
|
||||
status = STATUS_BUILTIN_ERROR;
|
||||
break;
|
||||
}
|
||||
|
@ -2960,20 +2988,17 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
|
|||
break;
|
||||
}
|
||||
case HIST_CLEAR: {
|
||||
CHECK_FOR_UNEXPECTED_HIST_OPTIONS(hist_cmd)
|
||||
CHECK_FOR_UNEXPECTED_HIST_ARGS(hist_cmd)
|
||||
history->clear();
|
||||
history->save();
|
||||
break;
|
||||
}
|
||||
case HIST_MERGE: {
|
||||
CHECK_FOR_UNEXPECTED_HIST_OPTIONS(hist_cmd)
|
||||
CHECK_FOR_UNEXPECTED_HIST_ARGS(hist_cmd)
|
||||
history->incorporate_external_changes();
|
||||
break;
|
||||
}
|
||||
case HIST_SAVE: {
|
||||
CHECK_FOR_UNEXPECTED_HIST_OPTIONS(hist_cmd)
|
||||
CHECK_FOR_UNEXPECTED_HIST_ARGS(hist_cmd)
|
||||
history->save();
|
||||
break;
|
||||
|
|
|
@ -1,7 +1,24 @@
|
|||
history: you cannot use any options with clear command
|
||||
history: you cannot use any options with merge command
|
||||
history: Invalid combination of options,
|
||||
you cannot do both 'search' and 'merge' in the same invocation
|
||||
history: you cannot use any options with the clear command
|
||||
history: you cannot use any options with the merge command
|
||||
history: save command expected 0 args, got 1
|
||||
history: you cannot use any options with save command
|
||||
history: you cannot use any options with clear command
|
||||
history: you cannot use any options with the save command
|
||||
history: you cannot use any options with the clear command
|
||||
history: merge command expected 0 args, got 1
|
||||
history: clear command expected 0 args, got 2
|
||||
|
||||
history: you cannot use any options with the clear command
|
||||
history: you cannot use any options with the merge command
|
||||
history: save command expected 0 args, got 1
|
||||
history: you cannot use any options with the clear command
|
||||
history: you cannot use any options with the merge command
|
||||
|
||||
history: Invalid combination of options,
|
||||
you cannot do both 'search' and 'merge' in the same invocation
|
||||
history: you cannot use any options with the save command
|
||||
history: you cannot use any options with the clear command
|
||||
history: merge command expected 0 args, got 1
|
||||
history: clear command expected 0 args, got 2
|
||||
history: you cannot use any options with the save command
|
||||
history: you cannot use any options with the merge command
|
||||
|
|
|
@ -43,15 +43,6 @@ expect_prompt -re {start2\r\necho start1; builtin history; echo end1\r\nend2\r\n
|
|||
puts stderr "first history command not detected as expected"
|
||||
}
|
||||
|
||||
# ==========
|
||||
# Verify asking for two different actions produces an error.
|
||||
send "builtin history --search --merge\r"
|
||||
expect_prompt -re {\r\nYou cannot do both 'search' and 'merge' in the same 'history' invocation\r\n} {
|
||||
puts "invalid attempt at multiple history commands detected"
|
||||
} unmatched {
|
||||
puts stderr "invalid attempt at multiple history commands not detected"
|
||||
}
|
||||
|
||||
# ==========
|
||||
# The following tests verify the behavior of the history function.
|
||||
# ==========
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
empty history detected as expected
|
||||
first history command detected as expected
|
||||
invalid attempt at multiple history commands detected
|
||||
history function explicit search succeeded
|
||||
history function implicit search succeeded
|
||||
history function implicit search with timestamps succeeded
|
||||
|
|
|
@ -1,13 +1,44 @@
|
|||
# Verify that specifying unexpected options or arguments results in an error.
|
||||
|
||||
# First using the legacy, now deprecated, long options to specify a
|
||||
# subcommand.
|
||||
|
||||
# First with the history function.
|
||||
history --search --merge
|
||||
history --clear --contains
|
||||
history --merge -t
|
||||
history --save xyz
|
||||
|
||||
# Now with the history builtin.
|
||||
builtin history --save --prefix
|
||||
builtin history --clear --with-time
|
||||
builtin history --merge xyz
|
||||
builtin history --clear abc def
|
||||
|
||||
# Now do a history command that should succeed.
|
||||
builtin history --merge
|
||||
# Put a blank line in the stderr output to separate the above sequence from
|
||||
# the following sequence of tests.
|
||||
echo >&2
|
||||
|
||||
# Now using the preferred subcommand form. Note that we support flags before
|
||||
# or after the subcommand name so test both variants.
|
||||
|
||||
# First with the history function.
|
||||
history clear --contains
|
||||
history merge -t
|
||||
history save xyz
|
||||
history --prefix clear
|
||||
history --with-time merge
|
||||
echo >&2
|
||||
|
||||
# Now with the history builtin.
|
||||
builtin history --search --merge
|
||||
builtin history save --prefix
|
||||
builtin history clear --with-time
|
||||
builtin history merge xyz
|
||||
builtin history clear abc def
|
||||
builtin history --contains save
|
||||
builtin history -t merge
|
||||
|
||||
# Now do a history command that should succeed so we exit with a zero,
|
||||
# success, status.
|
||||
builtin history save
|
||||
|
|
Loading…
Reference in a new issue