From 9e0f74eb6c04b435d1007b313ac863729daeff81 Mon Sep 17 00:00:00 2001 From: Nadav Zingerman <7372858+nzig@users.noreply.github.com> Date: Sat, 15 Jan 2022 16:57:29 +0200 Subject: [PATCH] Add --escape option to `complete -C` An example use case is an external completion pager: bind \cg "commandline -rt (complete -C --escape|fzf|cut -d\t -f1)\ " Fixes #3469 --- CHANGELOG.rst | 1 + doc_src/cmds/complete.rst | 4 +++- src/builtins/complete.cpp | 20 ++++++++++++++++---- tests/checks/complete.fish | 2 ++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 15f3ee9ac..df110c5e2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -94,6 +94,7 @@ Scripting improvements - ``command -v`` returns an exit status of 127 instead of 1 if no command was found (:issue:`8547`). - ``argparse`` with ``--ignore-unknown`` no longer breaks with multiple unknown options in a short option group (:issue:`8637`). - Comments inside command substitutions or brackets now correctly ignore parentheses, quotes, and brackets (:issue:`7866`, :issue:`8022`). +- ``complete -C`` supports a new ``--escape`` option, which turns on escaping in returned completion strings (:issue:`3469`). Interactive improvements ------------------------ diff --git a/doc_src/cmds/complete.rst b/doc_src/cmds/complete.rst index 8cd617ef3..ba495e322 100644 --- a/doc_src/cmds/complete.rst +++ b/doc_src/cmds/complete.rst @@ -9,7 +9,7 @@ Synopsis .. synopsis:: complete ((-c | --command) | (-p | --path)) COMMAND [OPTIONS] - complete ((-C | --do-complete)) STRING + complete ((-C | --do-complete)) [STRING] [--escape] Description ----------- @@ -49,6 +49,8 @@ the fish manual. - ``-C STRING`` or ``--do-complete=STRING`` makes complete try to find all possible completions for the specified string. If there is no STRING, the current commandline is used instead. +- When using ``-C``, specify ``--escape`` to escape special characters in completions. + Command specific tab-completions in ``fish`` are based on the notion of options and arguments. An option is a parameter which begins with a hyphen, such as ``-h``, ``-help`` or ``--help``. Arguments are parameters that do not begin with a hyphen. Fish recognizes three styles of options, the same styles as the GNU getopt library. These styles are: - Short options, like ``-a``. Short options are a single character long, are preceded by a single hyphen and can be grouped together (like ``-la``, which is equivalent to ``-l -a``). Option arguments may be specified by appending the option with the value (``-w32``), or, if ``--require-parameter`` is given, in the following parameter (``-w 32``). diff --git a/src/builtins/complete.cpp b/src/builtins/complete.cpp index 5d527ea72..b3e606937 100644 --- a/src/builtins/complete.cpp +++ b/src/builtins/complete.cpp @@ -122,6 +122,10 @@ static void builtin_complete_print(const wcstring &cmd, io_streams_t &streams, p } } +/// Values used for long-only options. +enum { + opt_escape = 1, +}; /// The complete builtin. Used for specifying programmable tab-completions. Calls the functions in // complete.cpp for any heavy lifting. maybe_t builtin_complete(parser_t &parser, io_streams_t &streams, const wchar_t **argv) { @@ -139,6 +143,7 @@ maybe_t builtin_complete(parser_t &parser, io_streams_t &streams, const wch wcstring_list_t path; wcstring_list_t wrap_targets; bool preserve_order = false; + bool unescape_output = true; static const wchar_t *const short_options = L":a:c:p:s:l:o:d:fFrxeuAn:C::w:hk"; static const struct woption long_options[] = { @@ -162,6 +167,7 @@ maybe_t builtin_complete(parser_t &parser, io_streams_t &streams, const wch {L"do-complete", optional_argument, nullptr, 'C'}, {L"help", no_argument, nullptr, 'h'}, {L"keep-order", no_argument, nullptr, 'k'}, + {L"escape", no_argument, nullptr, opt_escape}, {nullptr, 0, nullptr, 0}}; int opt; @@ -272,6 +278,10 @@ maybe_t builtin_complete(parser_t &parser, io_streams_t &streams, const wch if (have_do_complete_param) do_complete_param = w.woptarg; break; } + case opt_escape: { + unescape_output = false; + break; + } case 'h': { builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; @@ -388,10 +398,12 @@ maybe_t builtin_complete(parser_t &parser, io_streams_t &streams, const wch faux_cmdline_with_completion.resize(faux_cmdline_with_completion.size() - 1); } - // The input data is meant to be something like you would have on the command - // line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So - // we need to unescape the command line. See #1127. - unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT); + if (unescape_output) { + // The input data is meant to be something like you would have on the command + // line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So + // we need to unescape the command line. See #1127. + unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT); + } streams.out.append(faux_cmdline_with_completion); // Append any description. diff --git a/tests/checks/complete.fish b/tests/checks/complete.fish index 118f5bc0a..30be47359 100644 --- a/tests/checks/complete.fish +++ b/tests/checks/complete.fish @@ -9,6 +9,8 @@ complete -c complete_test_alpha3 --no-files -w 'complete_test_alpha2 extra2' complete -C'complete_test_alpha1 arg1 ' # CHECK: complete_test_alpha1 arg1 +complete --escape -C'complete_test_alpha1 arg1 ' +# CHECK: complete_test_alpha1\ arg1\ complete -C'complete_test_alpha2 arg2 ' # CHECK: complete_test_alpha1 extra1 arg2 complete -C'complete_test_alpha3 arg3 '