diff --git a/src/command_manager.cc b/src/command_manager.cc index c3eb141e..f7d094bb 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -719,10 +719,34 @@ static Completions complete_expand(const Context& context, CompletionFlags flags return {}; } -Completions CommandManager::complete(const Context& context, - CompletionFlags flags, - StringView command_line, - ByteCount cursor_pos) + +static Completions requote(Completions completions, Token::Type token_type) +{ + if (completions.flags & Completions::Flags::Quoted) + return completions; + + if (token_type == Token::Type::Raw) + { + const bool at_token_start = completions.start == 0; + for (auto& candidate : completions.candidates) + { + const StringView to_escape = ";\n \t"; + if ((at_token_start and candidate.substr(0_byte, 1_byte) == "%") or + any_of(candidate, [&](auto c) { return contains(to_escape, c); })) + candidate = at_token_start ? quote(candidate) : escape(candidate, to_escape, '\\'); + } + } + else if (token_type == Token::Type::RawQuoted) + completions.flags |= Completions::Flags::Quoted; + else + kak_assert(false); + + return completions; +} + +Completions CommandManager::Completer::operator()( + const Context& context, CompletionFlags flags, + StringView command_line, ByteCount cursor_pos) { auto prefix = command_line.substr(0_byte, cursor_pos); CommandParser parser{prefix}; @@ -754,29 +778,6 @@ Completions CommandManager::complete(const Context& context, if (token.terminated) // do not complete past explicit token close return Completions{}; - auto requote = [](Completions completions, Token::Type token_type) { - if (completions.flags & Completions::Flags::Quoted) - return completions; - - if (token_type == Token::Type::Raw) - { - const bool at_token_start = completions.start == 0; - for (auto& candidate : completions.candidates) - { - const StringView to_escape = ";\n \t"; - if ((at_token_start and candidate.substr(0_byte, 1_byte) == "%") or - any_of(candidate, [&](auto c) { return contains(to_escape, c); })) - candidate = at_token_start ? quote(candidate) : escape(candidate, to_escape, '\\'); - } - } - else if (token_type == Token::Type::RawQuoted) - completions.flags |= Completions::Flags::Quoted; - else - kak_assert(false); - - return completions; - }; - const ByteCount start = token.pos; const ByteCount pos_in_token = cursor_pos - start; @@ -784,9 +785,11 @@ Completions CommandManager::complete(const Context& context, if (tokens.size() == 1 and (token.type == Token::Type::Raw or token.type == Token::Type::RawQuoted)) { - return offset_pos(requote(complete_command_name(context, prefix), token.type), start); + return offset_pos(requote(CommandManager::instance().complete_command_name(context, prefix), token.type), start); } + auto& commands = CommandManager::instance().m_commands; + switch (token.type) { case Token::Type::RegisterExpand: @@ -800,17 +803,16 @@ Completions CommandManager::complete(const Context& context, case Token::Type::RawQuoted: { StringView command_name = tokens.front().content; - if (command_name != m_last_complete_command) - { - m_last_complete_command = command_name.str(); - flags |= CompletionFlags::Start; - } - - auto command_it = m_commands.find(resolve_alias(context, command_name)); - if (command_it == m_commands.end()) + auto command_it = commands.find(resolve_alias(context, command_name)); + if (command_it == commands.end()) return Completions{}; auto& command = command_it->value; + if (command_name != m_last_complete_command) + { + m_last_complete_command = command_name.str(); + m_command_completer = command.completer; + } auto raw_params = tokens | skip(1) | transform(&Token::content) | gather>(); ParametersParser parser{raw_params, command.param_desc, true}; @@ -841,13 +843,13 @@ Completions CommandManager::complete(const Context& context, break; } - if (not command.completer) + if (not m_command_completer) return Completions{}; Vector params{parser.begin(), parser.end()}; auto index = params.size() - 1; - return offset_pos(requote(command.completer(context, flags, params, index, pos_in_token), token.type), start); + return offset_pos(requote(m_command_completer(context, flags, params, index, pos_in_token), token.type), start); } case Token::Type::Expand: return complete_expand(context, flags, token.content, start, cursor_pos, pos_in_token); @@ -857,26 +859,26 @@ Completions CommandManager::complete(const Context& context, return Completions{}; } -Completions CommandManager::complete(const Context& context, - CompletionFlags flags, - CommandParameters params, - size_t token_to_complete, - ByteCount pos_in_token) +Completions CommandManager::NestedCompleter::operator()( + const Context& context, CompletionFlags flags, CommandParameters params, + size_t token_to_complete, ByteCount pos_in_token) { StringView prefix = params[token_to_complete].substr(0, pos_in_token); if (token_to_complete == 0) - return complete_command_name(context, prefix); + return CommandManager::instance().complete_command_name(context, prefix); StringView command_name = params[0]; + auto& commands = CommandManager::instance().m_commands; if (command_name != m_last_complete_command) { m_last_complete_command = command_name.str(); - flags |= CompletionFlags::Start; + auto it = commands.find(resolve_alias(context, command_name)); + if (it != commands.end()) + m_command_completer = it->value.completer; } - auto it = m_commands.find(resolve_alias(context, command_name)); - return (it != m_commands.end() and it->value.completer) - ? it->value.completer(context, flags, params.subrange(1), token_to_complete-1, pos_in_token) + return m_command_completer + ? m_command_completer(context, flags, params.subrange(1), token_to_complete-1, pos_in_token) : Completions{}; } diff --git a/src/command_manager.hh b/src/command_manager.hh index 8804b5b2..878ca129 100644 --- a/src/command_manager.hh +++ b/src/command_manager.hh @@ -93,13 +93,6 @@ public: const ShellContext& shell_context); - Completions complete(const Context& context, CompletionFlags flags, - StringView command_line, ByteCount cursor_pos); - - Completions complete(const Context& context, CompletionFlags flags, - CommandParameters params, - size_t token_to_complete, ByteCount pos_in_token); - Optional command_info(const Context& context, StringView command_line) const; @@ -116,8 +109,6 @@ public: Completions complete_command_name(const Context& context, StringView query) const; - void clear_last_complete_command() { m_last_complete_command = String{}; } - bool module_defined(StringView module_name) const; void register_module(String module_name, String commands); @@ -127,6 +118,26 @@ public: Completions complete_module_name(StringView query) const; + struct Completer + { + Completions operator()(const Context& context, CompletionFlags flags, + StringView command_line, ByteCount cursor_pos); + + private: + String m_last_complete_command; + CommandCompleter m_command_completer; + }; + + struct NestedCompleter + { + Completions operator()(const Context& context, CompletionFlags flags, + CommandParameters params, size_t token_to_complete, ByteCount pos_in_token); + + private: + String m_last_complete_command; + CommandCompleter m_command_completer; + }; + private: struct Command { @@ -139,7 +150,6 @@ private: }; using CommandMap = HashMap; CommandMap m_commands; - String m_last_complete_command; int m_command_depth = 0; struct Module diff --git a/src/commands.cc b/src/commands.cc index 94c61241..92e11e1b 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -80,7 +80,7 @@ struct PerArgumentCommandCompleter : PerArgumentCommandCompl Completions operator()(const Context& context, CompletionFlags flags, CommandParameters params, size_t token_to_complete, - ByteCount pos_in_token) const + ByteCount pos_in_token) { if (token_to_complete == 0) { @@ -284,9 +284,6 @@ struct ShellCandidatesCompleter CommandParameters params, size_t token_to_complete, ByteCount pos_in_token) { - if (flags & CompletionFlags::Start) - m_token = -1; - if (m_token != token_to_complete) { ShellContext shell_context{ @@ -1140,11 +1137,7 @@ const CommandDesc add_hook_cmd = { }, CommandFlags::None, CommandHelper{}, - make_completer(menu(complete_scope), menu(complete_hooks), complete_nothing, - [](const Context& context, CompletionFlags flags, - StringView prefix, ByteCount cursor_pos) - { return CommandManager::instance().complete( - context, flags, prefix, cursor_pos); }), + make_completer(menu(complete_scope), menu(complete_hooks), complete_nothing, CommandManager::Completer{}), [](const ParametersParser& parser, Context& context, const ShellContext&) { auto descs = enum_desc(Meta::Type{}); @@ -1270,15 +1263,7 @@ CommandCompleter make_command_completer(StringView type, StringView param, Compl return ShellCandidatesCompleter{param.str(), completions_flags}; } else if (type == "command") - { - return [](const Context& context, CompletionFlags flags, - CommandParameters params, - size_t token_to_complete, ByteCount pos_in_token) - { - return CommandManager::instance().complete( - context, flags, params, token_to_complete, pos_in_token); - }; - } + return CommandManager::NestedCompleter{}; else if (type == "shell") { return [=](const Context& context, CompletionFlags flags, diff --git a/src/completion.hh b/src/completion.hh index d3aeca2b..e9321d5a 100644 --- a/src/completion.hh +++ b/src/completion.hh @@ -47,7 +47,6 @@ enum class CompletionFlags { None = 0, Fast = 1 << 0, - Start = 1 << 2, }; constexpr bool with_bit_ops(Meta::Type) { return true; } diff --git a/src/normal.cc b/src/normal.cc index ebe86ebc..e70d0680 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -471,17 +471,15 @@ void command(const Context& context, EnvVarMap env_vars, char reg = 0) if (not CommandManager::has_instance()) throw runtime_error{"commands are not supported"}; - CommandManager::instance().clear_last_complete_command(); - String default_command = context.main_sel_register_value(reg ? reg : ':').str(); context.input_handler().prompt( ":", {}, default_command, context.faces()["Prompt"], PromptFlags::DropHistoryEntriesWithBlankPrefix, ':', - [](const Context& context, CompletionFlags flags, - StringView cmd_line, ByteCount pos) { - return CommandManager::instance().complete(context, flags, cmd_line, pos); + [completer=CommandManager::Completer{}](const Context& context, CompletionFlags flags, + StringView cmd_line, ByteCount pos) mutable { + return completer(context, flags, cmd_line, pos); }, [env_vars = std::move(env_vars), default_command](StringView cmdline, PromptEvent event, Context& context) { if (context.has_client())