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:
Fabian Homborg 2020-09-09 18:03:39 +02:00
parent 34be1b458a
commit a8e237f0f9
5 changed files with 92 additions and 52 deletions

View file

@ -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``.

View file

@ -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;

View file

@ -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);

View file

@ -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,

View file

@ -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'