Expand tildes and variables in command for custom completions

A «complete -C '~/fish-shell/build/fish '» fails to load custom
completions because we do not expand the ~, so
complete_param_for_command() thinks that this command is invalid.
Expand command tokens before loading custom completions.

Fixes #8442
This commit is contained in:
Johannes Altmanninger 2021-11-27 09:56:35 +01:00
parent 5a71d02d32
commit d1683958cf
2 changed files with 40 additions and 4 deletions

View File

@ -1406,6 +1406,14 @@ void completer_t::complete_custom(const wcstring &cmd, const wcstring &cmdline,
}
}
static bool expand_command_token(const operation_context_t &ctx, wcstring &cmd_tok) {
// TODO: we give up if the first token expands to more than one argument. We could handle
// that case by propagating arguments.
// Also we could expand wildcards.
return expand_one(cmd_tok, {expand_flag::skip_cmdsubst, expand_flag::skip_wildcards}, ctx,
nullptr);
}
// Invoke command-specific completions given by \p arg_data.
// Then, for each target wrapped by the given command, update the command
// line with that target and invoke this recursively.
@ -1445,6 +1453,7 @@ void completer_t::walk_wrap_chain(const wcstring &cmd, const wcstring &cmdline,
} else {
wrapped_command_offset_in_wt = tok->offset;
wrapped_command = std::move(tok_src);
expand_command_token(ctx, wrapped_command);
break;
}
}
@ -1670,15 +1679,15 @@ void completer_t::perform_for_commandline(wcstring cmdline) {
source_range_t command_range = {static_cast<uint32_t>(cmd_tok.offset),
static_cast<uint32_t>(cmd_tok.length)};
wcstring unesc_command;
wcstring exp_command = cmd_tok.get_source(cmdline);
bool unescaped =
unescape_string(cmd_tok.get_source(cmdline), &unesc_command, UNESCAPE_DEFAULT) &&
expand_command_token(ctx, exp_command) &&
unescape_string(previous_argument, &arg_data.previous_argument, UNESCAPE_DEFAULT) &&
unescape_string(current_argument, &arg_data.current_argument, UNESCAPE_INCOMPLETE);
if (unescaped) {
// Have to walk over the command and its entire wrap chain. If any command
// disables do_file, then they all do.
walk_wrap_chain(unesc_command, cmdline, command_range, &arg_data);
walk_wrap_chain(exp_command, cmdline, command_range, &arg_data);
do_file = arg_data.do_file;
// If we're autosuggesting, and the token is empty, don't do file suggestions.
@ -1689,7 +1698,7 @@ void completer_t::perform_for_commandline(wcstring cmdline) {
// Hack. If we're cd, handle it specially (issue #1059, others).
handle_as_special_cd =
(unesc_command == L"cd") || arg_data.visited_wrapped_commands.count(L"cd");
(exp_command == L"cd") || arg_data.visited_wrapped_commands.count(L"cd");
}
// Maybe apply variable assignments.

View File

@ -410,6 +410,12 @@ complete -p $PWD/command-not-in-path -xa relative-path
complete -C './command-not-in-path '
# CHECK: relative-path
# Expand variables and tildes in command.
complete -C '$PWD/command-not-in-path '
# CHECK: relative-path
HOME=$PWD complete -C '~/command-not-in-path '
# CHECK: relative-path
# Non-canonical command path
mkdir -p subdir
: >subdir/command-in-subdir
@ -427,3 +433,24 @@ end
cd -
rm -r $dir
# Expand variables and tildes in command.
complete cat -xa +pet
set -l path_to_cat (command -v cat)
complete -C '$path_to_cat '
# CHECK: +pet
HOME=$path_to_cat/.. complete -C '~/cat '
# CHECK: +pet
# Do not expand command substitutions.
complete -C '(echo cat) ' | string match +pet
# Give up if we expand to multiple arguments (we'd need to handle the arguments).
complete -C '{cat,arg1,arg2} ' | string match +pet
# Don't expand wildcards though we could.
complete -C '$path_to_cat* ' | string match +pet
# Also expand wrap targets.
function crookshanks --wraps '$path_to_cat'
end
complete -C 'crookshanks '
# CHECK: +pet