Fix commandline state for custom completions with variable overrides

Today, a command like "var=val status " has custom completions
because we skip over the var=val variable override when detecting
the command token.
However if the custom completions read the commandline state (via
"commandline -opc") they do see they variable override, which breaks
them, most likely. Try "a=b git ".

For completions of wrapped commands, we already set a transient
commandline. Do the same for commands with  leading variable overrides;
then git completions for "a=b git " will think the commandline is
"git ".
This commit is contained in:
Johannes Altmanninger 2022-01-26 15:22:18 +01:00
parent 3b41f8dfe8
commit df3b0bd89f
3 changed files with 21 additions and 3 deletions

View File

@ -175,6 +175,7 @@ Completions
- Improvements to many completions, especially for ``git`` aliases (:issue:`8129`) and subcommands (:issue:`8134`).
- Add missing completions for the ``-p`` option of ``xbps-query``.
- The ``fish_is_nth_token`` function, which is particularly useful in completions for identifying the token number within the command line, replaces various internal functions to do the same (:issue:`8008`).
- When evaluating custom completions, the command line state no longer includes variable overrides (``var=val``). This unbreaks completions that read ``commandline -op``.
Improved terminal support
^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1390,7 +1390,7 @@ void completer_t::complete_custom(const wcstring &cmd, const wcstring &cmdline,
// buitin_commandline will refer to the wrapped command. But not if
// we're doing autosuggestions.
maybe_t<cleanup_t> remove_transient{};
bool wants_transient = ad->wrap_depth > 0 && !is_autosuggest;
bool wants_transient = (ad->wrap_depth > 0 || ad->var_assignments) && !is_autosuggest;
if (wants_transient) {
ctx.parser->libdata().transient_commandlines.push_back(cmdline);
remove_transient.emplace([=] { ctx.parser->libdata().transient_commandlines.pop_back(); });
@ -1575,6 +1575,7 @@ void completer_t::perform_for_commandline(wcstring cmdline) {
// Get all the arguments.
std::vector<tok_t> tokens;
parse_util_process_extent(cmdline.c_str(), position_in_statement, nullptr, nullptr, &tokens);
size_t actual_token_count = tokens.size();
// Hack: fix autosuggestion by removing prefixing "and"s #6249.
if (is_autosuggest) {
@ -1603,6 +1604,14 @@ void completer_t::perform_for_commandline(wcstring cmdline) {
return;
}
wcstring *effective_cmdline, effective_cmdline_buf;
if (tokens.size() == actual_token_count) {
effective_cmdline = &cmdline;
} else {
effective_cmdline_buf.assign(cmdline, tokens.front().offset);
effective_cmdline = &effective_cmdline_buf;
}
const tok_t &cmd_tok = tokens.front();
const tok_t &cur_tok = tokens.back();
@ -1674,7 +1683,8 @@ void completer_t::perform_for_commandline(wcstring cmdline) {
custom_arg_data_t arg_data{&var_assignments};
arg_data.had_ddash = had_ddash;
source_range_t command_range = {cmd_tok.offset, cmd_tok.length};
source_offset_t bias = cmdline.size() - effective_cmdline->size();
source_range_t command_range = {cmd_tok.offset - bias, cmd_tok.length};
wcstring exp_command = cmd_tok.get_source(cmdline);
bool unescaped =
@ -1684,7 +1694,7 @@ void completer_t::perform_for_commandline(wcstring cmdline) {
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(exp_command, cmdline, command_range, &arg_data);
walk_wrap_chain(exp_command, *effective_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.

View File

@ -454,3 +454,10 @@ function crookshanks --wraps '$path_to_cat'
end
complete -C 'crookshanks '
# CHECK: +pet
# Custom completion works with variable overrides.
complete cmd_with_fancy_completion -xa '(commandline -opc | count)'
complete -C"a=1 b=2 cmd_with_fancy_completion "
# CHECK: 1
complete -C"a=1 b=2 cmd_with_fancy_completion 1 "
# CHECK: 2