From aa8f29eff11272112b153de95e83978ca6884fac Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Mon, 18 Jul 2022 23:29:17 +0200 Subject: [PATCH] Extract function for parsing completion switches Both "define-command" and "prompt" use the same logic, so share it. This will make it easy to implement "prompt -menu". This reveals a problem with PromptCompleterAdapter: when converting it to std::function and then to bool, it always evaluates to true because it has an operator(). However, it should evaluate to false if the adaptee holds no valid function (e.g. is a default-constructed std::function). Otherwise we try to call a non-existant function. Tweak PromptCompleterAdapter to work for empty inputs. --- src/commands.cc | 72 ++++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index fc7178142..490c63099 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -14,6 +14,7 @@ #include "hash_map.hh" #include "highlighter.hh" #include "highlighters.hh" +#include "input_handler.hh" #include "insert_completer.hh" #include "normal.hh" #include "option_manager.hh" @@ -308,10 +309,14 @@ struct PromptCompleterAdapter { PromptCompleterAdapter(Completer completer) : m_completer{std::move(completer)} {} - Completions operator()(const Context& context, CompletionFlags flags, - StringView prefix, ByteCount cursor_pos) + operator PromptCompleter() && { - return m_completer(context, flags, {String{String::NoCopy{}, prefix}}, 0, cursor_pos); + if (not m_completer) + return {}; + return [completer=std::move(m_completer)](const Context& context, CompletionFlags flags, + StringView prefix, ByteCount cursor_pos) { + return completer(context, flags, {String{String::NoCopy{}, prefix}}, 0, cursor_pos); + }; } private: @@ -1235,6 +1240,22 @@ CommandCompleter make_command_completer(StringView type, StringView param, Compl throw runtime_error(format("invalid command completion type '{}'", type)); } +static CommandCompleter parse_completion_switch(const ParametersParser& parser, Completions::Flags completions_flags) { + for (StringView completion_switch : {"file-completion", "client-completion", "buffer-completion", + "shell-script-completion", "shell-script-candidates", + "command-completion", "shell-completion"}) + { + if (auto param = parser.get_switch(completion_switch)) + { + constexpr StringView suffix = "-completion"; + if (completion_switch.ends_with(suffix)) + completion_switch = completion_switch.substr(0, completion_switch.length() - suffix.length()); + return make_command_completer(completion_switch, *param, completions_flags); + } + } + return {}; +} + void define_command(const ParametersParser& parser, Context& context, const ShellContext&) { const String& cmd_name = parser[0]; @@ -1286,20 +1307,7 @@ void define_command(const ParametersParser& parser, Context& context, const Shel }; } - CommandCompleter completer; - for (StringView completion_switch : {"file-completion", "client-completion", "buffer-completion", - "shell-script-completion", "shell-script-candidates", - "command-completion", "shell-completion"}) - { - if (auto param = parser.get_switch(completion_switch)) - { - constexpr StringView suffix = "-completion"; - if (completion_switch.ends_with(suffix)) - completion_switch = completion_switch.substr(0, completion_switch.length() - suffix.length()); - completer = make_command_completer(completion_switch, *param, completions_flags); - break; - } - } + CommandCompleter completer = parse_completion_switch(parser, completions_flags); auto docstring = trim_indent(parser.get_switch("docstring").value_or(StringView{})); cm.register_command(cmd_name, cmd, docstring, desc, flags, CommandHelper{}, std::move(completer)); @@ -2169,35 +2177,7 @@ const CommandDesc prompt_cmd = { const String& command = parser[1]; auto initstr = parser.get_switch("init").value_or(StringView{}); - PromptCompleter completer; - if (parser.get_switch("file-completion")) - completer = [](const Context& context, CompletionFlags, - StringView prefix, ByteCount cursor_pos) -> Completions { - auto& ignored_files = context.options()["ignored_files"].get(); - return { 0_byte, cursor_pos, - complete_filename(prefix, ignored_files, cursor_pos, - FilenameFlags::Expand) }; - }; - else if (parser.get_switch("client-completion")) - completer = [](const Context& context, CompletionFlags, - StringView prefix, ByteCount cursor_pos) -> Completions { - return { 0_byte, cursor_pos, - ClientManager::instance().complete_client_name(prefix, cursor_pos) }; - }; - else if (parser.get_switch("buffer-completion")) - completer = complete_buffer_name; - else if (parser.get_switch("command-completion")) - completer = [](const Context& context, CompletionFlags flags, - StringView prefix, ByteCount cursor_pos) -> Completions { - return CommandManager::instance().complete( - context, flags, prefix, cursor_pos); - }; - else if (parser.get_switch("shell-completion")) - completer = shell_complete; - else if (auto shell_script = parser.get_switch("shell-script-completion")) - completer = PromptCompleterAdapter{ShellScriptCompleter{shell_script->str()}}; - else if (auto shell_script = parser.get_switch("shell-script-candidates")) - completer = PromptCompleterAdapter{ShellCandidatesCompleter{shell_script->str()}}; + PromptCompleterAdapter completer = parse_completion_switch(parser, Completions::Flags::None); const auto flags = parser.get_switch("password") ? PromptFlags::Password : PromptFlags::None;