From 4cb19e244bd9b7cb0c24c9dda35a09fb221497ab Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 26 Oct 2022 14:05:09 -0500 Subject: [PATCH] Sort and deduplicate output of `complete -C` This addresses a long-standing TODO where `complete -C` output isn't deduplicated. With this patch, the same deduplication and sort procedure that is run on actual pager completions is also executed for `complete -C` completions (with a `-C` payload specified). This makes it possible to use `complete -C` to test what completions will actually be generated by the completions pager instead of it displaying something completely divorced from reality, improving the productivity of fish completions developers. Note that completions that wouldn't be shown in the pager are also omitted from the results, e.g. `test/buildroot/` and `test/fish_expand_test/` are omitted from the check matches in `checks/complete_directories.fish` because even if they were generated, the pager wouldn't have shown them. This again makes reasoning about and debugging completions much easier and more sane. --- src/builtins/complete.cpp | 8 +++++++- tests/checks/complete_directories.fish | 2 -- tests/checks/wraps.fish | 9 ++------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/builtins/complete.cpp b/src/builtins/complete.cpp index f13b5d825..0663f56e9 100644 --- a/src/builtins/complete.cpp +++ b/src/builtins/complete.cpp @@ -388,12 +388,16 @@ maybe_t builtin_complete(parser_t &parser, io_streams_t &streams, const wch // Prevent accidental recursion (see #6171). if (!parser.libdata().builtin_complete_current_commandline) { - if (!have_do_complete_param) + if (!have_do_complete_param) { parser.libdata().builtin_complete_current_commandline = true; + } completion_list_t comp = complete( do_complete_param, completion_request_options_t::normal(), parser.context()); + // Apply the same sort and deduplication treatment as pager completions + completions_sort_and_prioritize(&comp); + for (const auto &next : comp) { // Make a fake commandline, and then apply the completion to it. const wcstring faux_cmdline = token; @@ -419,6 +423,8 @@ maybe_t builtin_complete(parser_t &parser, io_streams_t &streams, const wch // Append any description. if (!next.description.empty()) { + faux_cmdline_with_completion.reserve( + faux_cmdline_with_completion.size() + 2 + next.description.size()); faux_cmdline_with_completion.push_back(L'\t'); faux_cmdline_with_completion.append(next.description); } diff --git a/tests/checks/complete_directories.fish b/tests/checks/complete_directories.fish index a10ee3baa..e6fcdc1de 100644 --- a/tests/checks/complete_directories.fish +++ b/tests/checks/complete_directories.fish @@ -15,8 +15,6 @@ __fish_complete_directories test/z # No match - no output! __fish_complete_directories test/d #CHECK: test/data/ Directory -#CHECK: test/buildroot/ Directory -#CHECK: test/fish_expand_test/ Directory __fish_complete_directories test/data #CHECK: test/data/ Directory __fish_complete_directories test/data/ diff --git a/tests/checks/wraps.fish b/tests/checks/wraps.fish index 5700e1fd6..15a76a312 100644 --- a/tests/checks/wraps.fish +++ b/tests/checks/wraps.fish @@ -11,9 +11,6 @@ complete -c testcommand --no-files -a normal complete -C'testcommand ' # CHECK: normal -# We get the same completion twice. TODO: fix this. -# CHECK: normal - # Test double wraps. complete -c testcommand0 -x -a crosswalk complete -c testcommand1 -x --wraps testcommand0 @@ -40,8 +37,6 @@ complete -c recvar --exclusive -a recvar_comp complete -c recvar --wraps 'A=B recvar' complete -C 'recvar ' # CHECK: recvar_comp -# We get the same completion twice. TODO: fix this. -# CHECK: recvar_comp # Test that completions do not perform subcommands. # That is, `FOO=(launch_missiles) command` does not launch any missiles. @@ -64,15 +59,15 @@ function do_print_good_bad end complete -c print_good_bad -x -a '(do_print_good_bad)' complete -C 'print_good_bad ' -# CHECK: GOOD:global_good # CHECK: BAD:global_bad +# CHECK: GOOD:global_good # Key check is completions should expand GOOD but not BAD, # because GOOD is just a string but BAD contains a cmdsub # which may do arbitrary things. complete -C 'GOOD=local_good BAD=(launch_missiles) print_good_bad ' -# CHECK: GOOD:local_good # CHECK: BAD: +# CHECK: GOOD:local_good # Completion should not have launched any missiles. echo $missile_count