mirror of
https://github.com/fish-shell/fish-shell
synced 2024-09-21 15:01:53 +00:00
Let complete
show completions for one command if just given -c
Currently only `complete` will list completions, and it will list all of them. That's a bit ridiculous, especially since `complete -c foo` just does nothing. So just make `complete -c foo` list all the completions for `foo`.
This commit is contained in:
parent
34be1b458a
commit
a8e237f0f9
5 changed files with 92 additions and 52 deletions
|
@ -98,9 +98,10 @@ The ``-w`` or ``--wraps`` options causes the specified command to inherit comple
|
|||
|
||||
When erasing completions, it is possible to either erase all completions for a specific command by specifying ``complete -c COMMAND -e``, or by specifying a specific completion option to delete by specifying either a long, short or old style option.
|
||||
|
||||
When ``complete`` is called without anything that would define or erase completions, it shows matching completions instead. So ``complete`` without any arguments shows all loaded completions, ``complete -c foo`` shows all loaded completions for ``foo``. Since completions are :ref:`autoloaded <syntax-function-autoloading>`, you will have to trigger them first.
|
||||
|
||||
Example
|
||||
-------
|
||||
Examples
|
||||
--------
|
||||
|
||||
The short style option ``-o`` for the ``gcc`` command requires that a file follows it. This can be done using writing:
|
||||
|
||||
|
@ -151,5 +152,9 @@ To implement an alias, use the ``-w`` or ``--wraps`` option:
|
|||
complete -c hub -w git
|
||||
|
||||
|
||||
Now hub inherits all of the completions from git. Note this can also be specified in a function declaration.
|
||||
Now hub inherits all of the completions from git. Note this can also be specified in a function declaration (``function thing -w otherthing``).
|
||||
|
||||
::
|
||||
complete -c git
|
||||
|
||||
Show all completions for ``git``.
|
||||
|
|
|
@ -108,6 +108,19 @@ static void builtin_complete_remove(const wcstring_list_t &cmds, const wcstring_
|
|||
}
|
||||
}
|
||||
|
||||
static void builtin_complete_print(const wcstring cmd, io_streams_t &streams, parser_t &parser) {
|
||||
const wcstring repr = complete_print(cmd);
|
||||
|
||||
// colorize if interactive
|
||||
if (!streams.out_is_redirected && isatty(STDOUT_FILENO)) {
|
||||
std::vector<highlight_spec_t> colors;
|
||||
highlight_shell(repr, colors, parser.context());
|
||||
streams.out.append(str2wcstring(colorize(repr, colors, parser.vars())));
|
||||
} else {
|
||||
streams.out.append(repr);
|
||||
}
|
||||
}
|
||||
|
||||
/// The complete builtin. Used for specifying programmable tab-completions. Calls the functions in
|
||||
// complete.cpp for any heavy lifting.
|
||||
maybe_t<int> builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
|
@ -118,7 +131,7 @@ maybe_t<int> builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t *
|
|||
completion_mode_t result_mode{};
|
||||
int remove = 0;
|
||||
wcstring short_opt;
|
||||
wcstring_list_t gnu_opt, old_opt;
|
||||
wcstring_list_t gnu_opt, old_opt, subcommand;
|
||||
const wchar_t *comp = L"", *desc = L"", *condition = L"";
|
||||
bool do_complete = false;
|
||||
bool have_do_complete_param = false;
|
||||
|
@ -139,6 +152,7 @@ maybe_t<int> builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t *
|
|||
{L"short-option", required_argument, nullptr, 's'},
|
||||
{L"long-option", required_argument, nullptr, 'l'},
|
||||
{L"old-option", required_argument, nullptr, 'o'},
|
||||
{L"subcommand", required_argument, nullptr, 'S'},
|
||||
{L"description", required_argument, nullptr, 'd'},
|
||||
{L"arguments", required_argument, nullptr, 'a'},
|
||||
{L"erase", no_argument, nullptr, 'e'},
|
||||
|
@ -225,6 +239,14 @@ maybe_t<int> builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t *
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
subcommand.push_back(w.woptarg);
|
||||
if (w.woptarg[0] == '\0') {
|
||||
streams.err.append_format(_(L"%ls: -S requires a non-empty string\n"), cmd);
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'a': {
|
||||
comp = w.woptarg;
|
||||
break;
|
||||
|
@ -381,18 +403,18 @@ maybe_t<int> builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t *
|
|||
parser.libdata().builtin_complete_recursion_level--;
|
||||
parser.libdata().builtin_complete_current_commandline = false;
|
||||
}
|
||||
} else if (cmd_to_complete.empty() && path.empty()) {
|
||||
// No arguments specified, meaning we print the definitions of all specified completions
|
||||
// to stdout.
|
||||
const wcstring repr = complete_print();
|
||||
|
||||
// colorize if interactive
|
||||
if (!streams.out_is_redirected && isatty(STDOUT_FILENO)) {
|
||||
std::vector<highlight_spec_t> colors;
|
||||
highlight_shell(repr, colors, parser.context());
|
||||
streams.out.append(str2wcstring(colorize(repr, colors, parser.vars())));
|
||||
} else if (path.empty() && gnu_opt.empty()
|
||||
&& short_opt.empty() && old_opt.empty()
|
||||
&& !remove && !*comp && !*desc && !*condition
|
||||
&& wrap_targets.empty() && !result_mode.no_files
|
||||
&& !result_mode.force_files && !result_mode.requires_param) {
|
||||
// No arguments that would add or remove anything specified, so we print the definitions of all matching completions.
|
||||
if (cmd_to_complete.empty()) {
|
||||
builtin_complete_print(L"", streams, parser);
|
||||
} else {
|
||||
streams.out.append(repr);
|
||||
for (auto& cmd : cmd_to_complete) {
|
||||
builtin_complete_print(cmd, streams, parser);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int flags = COMPLETE_AUTO_SPACE;
|
||||
|
|
|
@ -1666,8 +1666,48 @@ static void append_switch(wcstring &out, const wcstring &opt) {
|
|||
append_format(out, L" --%ls", opt.c_str());
|
||||
}
|
||||
|
||||
wcstring completion2string(const complete_entry_opt_t &o, wcstring cmd, bool is_path) {
|
||||
wcstring out;
|
||||
out.append(L"complete");
|
||||
|
||||
if (o.flags & COMPLETE_DONT_SORT) append_switch(out, L'k');
|
||||
|
||||
if (o.result_mode.no_files && o.result_mode.requires_param) {
|
||||
append_switch(out, L"exclusive");
|
||||
} else if (o.result_mode.no_files) {
|
||||
append_switch(out, L"no-files");
|
||||
} else if (o.result_mode.force_files) {
|
||||
append_switch(out, L"force-files");
|
||||
} else if (o.result_mode.requires_param) {
|
||||
append_switch(out, L"requires-param");
|
||||
}
|
||||
|
||||
append_switch(out, is_path ? L'p' : L'c', cmd);
|
||||
|
||||
switch (o.type) {
|
||||
case option_type_args_only: {
|
||||
break;
|
||||
}
|
||||
case option_type_short: {
|
||||
append_switch(out, L's', wcstring(1, o.option.at(0)));
|
||||
break;
|
||||
}
|
||||
case option_type_single_long:
|
||||
case option_type_double_long: {
|
||||
append_switch(out, o.type == option_type_single_long ? L'o' : L'l', o.option);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
append_switch(out, L'd', C_(o.desc));
|
||||
append_switch(out, L'a', o.comp);
|
||||
append_switch(out, L'n', o.condition);
|
||||
out.append(L"\n");
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Use by the bare `complete`, loaded completions are printed out as commands
|
||||
wcstring complete_print() {
|
||||
wcstring complete_print(wcstring cmd) {
|
||||
wcstring out;
|
||||
out.reserve(40); // just a guess
|
||||
auto completion_set = s_completion_set.acquire();
|
||||
|
@ -1679,43 +1719,10 @@ wcstring complete_print() {
|
|||
sort(all_completions.begin(), all_completions.end(), compare_completions_by_order);
|
||||
|
||||
for (const completion_entry_t &e : all_completions) {
|
||||
if (!cmd.empty() && e.cmd != cmd) continue;
|
||||
const option_list_t &options = e.get_options();
|
||||
for (const complete_entry_opt_t &o : options) {
|
||||
out.append(L"complete");
|
||||
|
||||
if (o.flags & COMPLETE_DONT_SORT) append_switch(out, L'k');
|
||||
|
||||
if (o.result_mode.no_files && o.result_mode.requires_param) {
|
||||
append_switch(out, L"exclusive");
|
||||
} else if (o.result_mode.no_files) {
|
||||
append_switch(out, L"no-files");
|
||||
} else if (o.result_mode.force_files) {
|
||||
append_switch(out, L"force-files");
|
||||
} else if (o.result_mode.requires_param) {
|
||||
append_switch(out, L"requires-param");
|
||||
}
|
||||
|
||||
append_switch(out, e.cmd_is_path ? L'p' : L'c', e.cmd);
|
||||
|
||||
switch (o.type) {
|
||||
case option_type_args_only: {
|
||||
break;
|
||||
}
|
||||
case option_type_short: {
|
||||
append_switch(out, L's', wcstring(1, o.option.at(0)));
|
||||
break;
|
||||
}
|
||||
case option_type_single_long:
|
||||
case option_type_double_long: {
|
||||
append_switch(out, o.type == option_type_single_long ? L'o' : L'l', o.option);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
append_switch(out, L'd', C_(o.desc));
|
||||
append_switch(out, L'a', o.comp);
|
||||
append_switch(out, L'n', o.condition);
|
||||
out.append(L"\n");
|
||||
out.append(completion2string(o, e.cmd, e.cmd_is_path));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1723,6 +1730,7 @@ wcstring complete_print() {
|
|||
auto locked_wrappers = wrapper_map.acquire();
|
||||
for (const auto &entry : *locked_wrappers) {
|
||||
const wcstring &src = entry.first;
|
||||
if (!cmd.empty() && src != cmd) continue;
|
||||
for (const wcstring &target : entry.second) {
|
||||
out.append(L"complete");
|
||||
append_switch(out, L'c', src);
|
||||
|
|
|
@ -180,7 +180,7 @@ completion_list_t complete(const wcstring &cmd, completion_request_flags_t flags
|
|||
const operation_context_t &ctx);
|
||||
|
||||
/// Return a list of all current completions.
|
||||
wcstring complete_print();
|
||||
wcstring complete_print(wcstring cmd=L"");
|
||||
|
||||
/// Tests if the specified option is defined for the specified command.
|
||||
int complete_is_valid_option(const wcstring &str, const wcstring &opt,
|
||||
|
|
|
@ -353,3 +353,8 @@ begin
|
|||
|
||||
rm -rf $parened_path
|
||||
end
|
||||
|
||||
# This should only list the completions for `banana`
|
||||
complete -c banana -a '1 2 3'
|
||||
complete -c banana
|
||||
#CHECK: complete -c banana -a '1 2 3'
|
||||
|
|
Loading…
Reference in a new issue