diff --git a/sphinx_doc_src/cmds/complete.rst b/sphinx_doc_src/cmds/complete.rst index ebfda2d07..09a402987 100644 --- a/sphinx_doc_src/cmds/complete.rst +++ b/sphinx_doc_src/cmds/complete.rst @@ -16,6 +16,7 @@ Synopsis [( -a | --arguments ) OPTION_ARGUMENTS] [( -k | --keep-order )] [( -f | --no-files )] + [( -F | --force-files )] [( -r | --require-parameter )] [( -x | --exclusive )] [( -w | --wraps ) WRAPPED_COMMAND]... @@ -56,9 +57,11 @@ the fish manual. - ``-k`` or ``--keep-order`` preserves the order of the ``OPTION_ARGUMENTS`` specified via ``-a`` or ``--arguments`` instead of sorting alphabetically. Multiple ``complete`` calls with ``-k`` result in arguments of the later ones displayed first. -- ``-f`` or ``--no-files`` specifies that the options specified by this completion may not be followed by a filename. +- ``-f`` or ``--no-files`` says that the options specified by this completion may not be followed by a filename. -- ``-r`` or ``--require-parameter`` specifies that the options specified by this completion always must have an option argument, i.e. may not be followed by another option. +- ``-F`` or ``--force-files`` says that the options specified by this completion may be followed by a filename, even if another applicable ``complete`` specified ``--no-files``. + +- ``-r`` or ``--require-parameter`` says that the options specified by this completion must have an option argument, i.e. may not be followed by another option. - ``-x`` or ``--exclusive`` implies both ``-r`` and ``-f``. diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp index 5b2fd4d8b..e17f1e34e 100644 --- a/src/builtin_complete.cpp +++ b/src/builtin_complete.cpp @@ -126,9 +126,10 @@ int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wcstring_list_t wrap_targets; bool preserve_order = false; - static const wchar_t *const short_options = L":a:c:p:s:l:o:d:frxeuAn:C::w:hk"; + 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[] = {{L"exclusive", no_argument, NULL, 'x'}, {L"no-files", no_argument, NULL, 'f'}, + {L"force-files", no_argument, NULL, 'F'}, {L"require-parameter", no_argument, NULL, 'r'}, {L"path", required_argument, NULL, 'p'}, {L"command", required_argument, NULL, 'c'}, @@ -160,6 +161,9 @@ int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) { result_mode.no_files = true; break; } + case 'F': { + result_mode.force_files = true; + } case 'r': { result_mode.requires_param = true; break; @@ -259,6 +263,11 @@ int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } } + if (result_mode.no_files && result_mode.force_files) { + streams.err.append_format(BUILTIN_ERR_COMBO2, L"complete", L"'--no-files' and '--force-files'"); + return STATUS_INVALID_ARGS; + } + if (w.woptind != argc) { // Use one left-over arg as the do-complete argument // to enable `complete -C "git check"`. diff --git a/src/complete.cpp b/src/complete.cpp index 78b2e88bf..5af617fee 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -886,7 +886,7 @@ static void complete_load(const wcstring &name) { /// Insert results into comp_out. Return true to perform file completion, false to disable it. bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, const wcstring &str, bool use_switches) { - bool use_common = 1, use_files = 1; + bool use_common = true, use_files = true, has_force = false; wcstring cmd, path; parse_cmd_string(cmd_orig, &path, &cmd, vars); @@ -955,6 +955,7 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, if (arg != NULL && this->condition_test(o.condition)) { if (o.result_mode.requires_param) use_common = false; if (o.result_mode.no_files) use_files = false; + if (o.result_mode.force_files) has_force = true; complete_from_args(arg, o.comp, o.localized_desc(), o.flags); } } @@ -971,6 +972,7 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, old_style_match = true; if (o.result_mode.requires_param) use_common = false; if (o.result_mode.no_files) use_files = false; + if (o.result_mode.force_files) has_force = true; complete_from_args(str, o.comp, o.localized_desc(), o.flags); } } @@ -989,6 +991,7 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, if (param_match(&o, popt.c_str()) && this->condition_test(o.condition)) { if (o.result_mode.requires_param) use_common = false; if (o.result_mode.no_files) use_files = false; + if (o.result_mode.force_files) has_force = true; complete_from_args(str, o.comp, o.localized_desc(), o.flags); } } @@ -1005,7 +1008,7 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, // If this entry is for the base command, check if any of the arguments match. if (!this->condition_test(o.condition)) continue; if (o.option.empty()) { - use_files = use_files && ((o.result_mode.no_files) == 0); + use_files = use_files && (!(o.result_mode.no_files)); complete_from_args(str, o.comp, o.localized_desc(), o.flags); } @@ -1067,7 +1070,7 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, } } - return use_files; + return has_force || use_files; } /// Perform generic (not command-specific) expansions on the specified string. @@ -1628,6 +1631,8 @@ wcstring complete_print() { modestr = L" --exclusive"; } else if (o.result_mode.no_files) { modestr = L" --no-files"; + } else if (o.result_mode.force_files) { + modestr = L" --force-files"; } else if (o.result_mode.requires_param) { modestr = L" --require-parameter"; } diff --git a/src/complete.h b/src/complete.h index f45888a25..90ad580d4 100644 --- a/src/complete.h +++ b/src/complete.h @@ -17,6 +17,7 @@ struct completion_mode_t { /// If set, skip file completions. bool no_files{false}; + bool force_files{false}; /// If set, require a parameter after completion. bool requires_param{false}; diff --git a/tests/complete.in b/tests/complete.in index 34bdc4ad9..9ebd88cbe 100644 --- a/tests/complete.in +++ b/tests/complete.in @@ -27,3 +27,8 @@ complete -C'myalias2 call3 ' function t --wraps t; echo t; end complete -c t -fa '(t)' complete -C't ' + +# Ensure file completion happens even though it was disabled above. +complete -c t -l fileoption -rF +# Only match this file because I don't want to touch this any time we add a test file. +complete -C't --fileoption ' | string match complete.in diff --git a/tests/complete.out b/tests/complete.out index c604f659e..264fb6a71 100644 --- a/tests/complete.out +++ b/tests/complete.out @@ -13,3 +13,4 @@ arg2 call2 complete_test_alpha1 arg1 call3 complete_test_alpha1 arg2 call3 t +complete.in