From 57a98880ab4ca81d1904aa0b18c6a72bc2600fb6 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Mon, 18 Jul 2022 21:14:58 +0200 Subject: [PATCH 1/6] Make completers take "StringView" instead of "const String&" for compatibility with PromptCompleter We define using PromptCompleter = std::function; Some command completers are *almost* convertible to PromptCompleter; the only difference is the string type of the prefix argument. The later commits in this series want to use menu() on the completers. Enable this by harmonizing the types. --- src/commands.cc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index 5783b0b8..3462822c 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -105,7 +105,7 @@ template auto add_flags(Completer completer, Completions::Flags completions_flags) { return [completer=std::move(completer), completions_flags] - (const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos) { + (const Context& context, CompletionFlags flags, StringView prefix, ByteCount cursor_pos) { Completions res = completer(context, flags, prefix, cursor_pos); res.flags |= completions_flags; return res; @@ -120,7 +120,7 @@ auto menu(Completer completer) template auto filename_completer = make_completer( - [](const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos) + [](const Context& context, CompletionFlags flags, StringView prefix, ByteCount cursor_pos) { return Completions{ 0_byte, cursor_pos, complete_filename(prefix, context.options()["ignored_files"].get(), @@ -179,7 +179,7 @@ auto make_single_word_completer(Func&& func) { return make_completer( [func = std::move(func)](const Context& context, CompletionFlags flags, - const String& prefix, ByteCount cursor_pos) -> Completions { + StringView prefix, ByteCount cursor_pos) -> Completions { auto candidate = { func(context) }; return { 0_byte, cursor_pos, complete(prefix, cursor_pos, candidate) }; }); } @@ -192,21 +192,21 @@ const ParameterDesc double_params{ {}, ParameterDesc::Flags::None, 2, 2 }; static constexpr auto scopes = { "global", "buffer", "window" }; static Completions complete_scope(const Context&, CompletionFlags, - const String& prefix, ByteCount cursor_pos) + StringView prefix, ByteCount cursor_pos) { return { 0_byte, cursor_pos, complete(prefix, cursor_pos, scopes) }; } static Completions complete_command_name(const Context& context, CompletionFlags, - const String& prefix, ByteCount cursor_pos) + StringView prefix, ByteCount cursor_pos) { return CommandManager::instance().complete_command_name( context, prefix.substr(0, cursor_pos)); } static Completions complete_alias_name(const Context& context, CompletionFlags, - const String& prefix, ByteCount cursor_pos) + StringView prefix, ByteCount cursor_pos) { return { 0_byte, cursor_pos, complete(prefix, cursor_pos, context.aliases().flatten_aliases() @@ -1059,7 +1059,7 @@ const CommandDesc remove_highlighter_cmd = { }; static Completions complete_hooks(const Context&, CompletionFlags, - const String& prefix, ByteCount cursor_pos) + StringView prefix, ByteCount cursor_pos) { return { 0_byte, cursor_pos, complete(prefix, cursor_pos, enum_desc(Meta::Type{}) | transform(&EnumDesc::name)) }; } @@ -1084,7 +1084,7 @@ const CommandDesc add_hook_cmd = { CommandHelper{}, make_completer(complete_scope, complete_hooks, complete_nothing, [](const Context& context, CompletionFlags flags, - const String& prefix, ByteCount cursor_pos) + StringView prefix, ByteCount cursor_pos) { return CommandManager::instance().complete( context, flags, prefix, cursor_pos); }), [](const ParametersParser& parser, Context& context, const ShellContext&) @@ -1351,7 +1351,7 @@ const CommandDesc alias_cmd = { }; static Completions complete_alias(const Context& context, CompletionFlags flags, - const String& prefix, ByteCount cursor_pos) + StringView prefix, ByteCount cursor_pos) { return {0_byte, cursor_pos, complete(prefix, cursor_pos, context.aliases().flatten_aliases() | @@ -1461,7 +1461,7 @@ const CommandDesc debug_cmd = { CommandHelper{}, make_completer( [](const Context& context, CompletionFlags flags, - const String& prefix, ByteCount cursor_pos) -> Completions { + StringView prefix, ByteCount cursor_pos) -> Completions { auto c = {"info", "buffers", "options", "memory", "shared-strings", "profile-hash-maps", "faces", "mappings", "regex", "registers"}; return { 0_byte, cursor_pos, complete(prefix, cursor_pos, c) }; @@ -1779,7 +1779,7 @@ const CommandDesc declare_option_cmd = { CommandHelper{}, make_completer( [](const Context& context, CompletionFlags flags, - const String& prefix, ByteCount cursor_pos) -> Completions { + StringView prefix, ByteCount cursor_pos) -> Completions { auto c = {"int", "bool", "str", "regex", "int-list", "str-list", "completions", "line-specs", "range-specs", "str-to-str-map"}; return { 0_byte, cursor_pos, complete(prefix, cursor_pos, c) }; }), @@ -2429,7 +2429,7 @@ const CommandDesc try_catch_cmd = { }; static Completions complete_face(const Context& context, CompletionFlags flags, - const String& prefix, ByteCount cursor_pos) + StringView prefix, ByteCount cursor_pos) { return {0_byte, cursor_pos, complete(prefix, cursor_pos, context.faces().flatten_faces() | @@ -2523,7 +2523,7 @@ const CommandDesc set_register_cmd = { CommandHelper{}, make_completer( [](const Context& context, CompletionFlags flags, - const String& prefix, ByteCount cursor_pos) -> Completions { + StringView prefix, ByteCount cursor_pos) -> Completions { return { 0_byte, cursor_pos, RegisterManager::instance().complete_register_name(prefix, cursor_pos) }; }), @@ -2572,7 +2572,7 @@ const CommandDesc change_directory_cmd = { CommandHelper{}, make_completer( [](const Context& context, CompletionFlags flags, - const String& prefix, ByteCount cursor_pos) -> Completions { + StringView prefix, ByteCount cursor_pos) -> Completions { return { 0_byte, cursor_pos, complete_filename(prefix, context.options()["ignored_files"].get(), @@ -2719,7 +2719,7 @@ const CommandDesc require_module_cmd = { CommandFlags::None, CommandHelper{}, make_completer( - [](const Context&, CompletionFlags, const String& prefix, ByteCount cursor_pos) { + [](const Context&, CompletionFlags, StringView prefix, ByteCount cursor_pos) { return CommandManager::instance().complete_module_name(prefix.substr(0, cursor_pos)); }), [](const ParametersParser& parser, Context& context, const ShellContext&) From 1358fc3ceff6b56afb4ce8bf8fd1a0df4cb99f19 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Tue, 19 Jul 2022 10:21:43 +0200 Subject: [PATCH 2/6] Deduplicate functions for completing alias names "complete_alias_name" is a better name then "complete_alias" because it's consistent with more similar names, which are: complete_client_name complete_command_name complete_module_name complete_option_name complete_register_name complete_scope complete_face --- src/commands.cc | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index 3462822c..812d8b19 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -205,14 +205,6 @@ static Completions complete_command_name(const Context& context, CompletionFlags context, prefix.substr(0, cursor_pos)); } -static Completions complete_alias_name(const Context& context, CompletionFlags, - StringView prefix, ByteCount cursor_pos) -{ - return { 0_byte, cursor_pos, complete(prefix, cursor_pos, - context.aliases().flatten_aliases() - | transform(&HashItem::key)) }; -} - struct ShellScriptCompleter { ShellScriptCompleter(String shell_script, @@ -1332,6 +1324,14 @@ const CommandDesc define_command_cmd = { define_command }; +static Completions complete_alias_name(const Context& context, CompletionFlags, + StringView prefix, ByteCount cursor_pos) +{ + return { 0_byte, cursor_pos, complete(prefix, cursor_pos, + context.aliases().flatten_aliases() + | transform(&HashItem::key))}; +} + const CommandDesc alias_cmd = { "alias", nullptr, @@ -1350,14 +1350,6 @@ const CommandDesc alias_cmd = { } }; -static Completions complete_alias(const Context& context, CompletionFlags flags, - StringView prefix, ByteCount cursor_pos) -{ - return {0_byte, cursor_pos, - complete(prefix, cursor_pos, context.aliases().flatten_aliases() | - transform([](auto& entry) -> const String& { return entry.key; }))}; -} - const CommandDesc unalias_cmd = { "unalias", nullptr, @@ -1366,7 +1358,7 @@ const CommandDesc unalias_cmd = { ParameterDesc{{}, ParameterDesc::Flags::None, 2, 3}, CommandFlags::None, CommandHelper{}, - make_completer(complete_scope, complete_alias, complete_command_name), + make_completer(complete_scope, complete_alias_name, complete_command_name), [](const ParametersParser& parser, Context& context, const ShellContext&) { AliasRegistry& aliases = get_scope(parser[0], context).aliases(); From ba557e90a26e75c9222859b63237a60e94d8d794 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Tue, 19 Jul 2022 10:04:32 +0200 Subject: [PATCH 3/6] Offer "--" as completion when completing switches The next commit will give switch completions the menu behavior, so this is necessary so we can still type "echo --" without an auto-expansion to "echo -to-file". --- src/command_manager.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/command_manager.cc b/src/command_manager.cc index 30684d85..e014d1d3 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -766,8 +766,9 @@ Completions CommandManager::complete(const Context& context, if (is_switch(token.content)) { auto switches = Kakoune::complete(token.content.substr(1_byte), pos_in_token, - command.param_desc.switches | - transform(&SwitchMap::Item::key)); + concatenated(command.param_desc.switches + | transform(&SwitchMap::Item::key), + ConstArrayView{"-"})); return switches.empty() ? Completions{} : Completions{start+1, cursor_pos, std::move(switches)}; } if (not command.completer) From 8fac31ba76e6036229baa751c41c21695f07372d Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Tue, 19 Jul 2022 09:42:20 +0200 Subject: [PATCH 4/6] Use menu behavior for completion of switches We already use the menu behavior in complete_command_name(); let's do the same for switches, since we can complete all valid inputs and it can save a Tab key in some scenarios. CommandManager::complete is fairly complex. We can't expect callers to add the menu bit when appropriate, so we currently do it here. --- src/command_manager.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/command_manager.cc b/src/command_manager.cc index e014d1d3..1fb82731 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -769,7 +769,9 @@ Completions CommandManager::complete(const Context& context, concatenated(command.param_desc.switches | transform(&SwitchMap::Item::key), ConstArrayView{"-"})); - return switches.empty() ? Completions{} : Completions{start+1, cursor_pos, std::move(switches)}; + return switches.empty() + ? Completions{} + : Completions{start+1, cursor_pos, std::move(switches), Completions::Flags::Menu}; } if (not command.completer) return Completions{}; From 7f08ac3de281c6917966dad85731b4aceeecb32c Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Tue, 19 Jul 2022 10:30:01 +0200 Subject: [PATCH 5/6] Use menu behavior for add-highlighter/remove-highlighter completion This means that typing :add-highlighter g c 80 results in :add-highlighter global/ column 80 Paths for add-highlighter do not get the menu behavior because we want to be able to type "global/foo" even if "global/foobar" exists. --- src/commands.cc | 6 ++++-- src/highlighter_group.cc | 3 ++- src/highlighters.cc | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index 812d8b19..d5d2b7ce 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -883,7 +883,8 @@ Completions highlighter_cmd_completer( StringView path = params[0]; auto sep_it = find(path, '/'); if (sep_it == path.end()) - return { 0_byte, pos_in_token, complete(path, pos_in_token, highlighter_scopes) }; + return { 0_byte, pos_in_token, complete(path, pos_in_token, highlighter_scopes), + Completions::Flags::Menu }; StringView scope{path.begin(), sep_it}; HighlighterGroup* root = nullptr; @@ -900,7 +901,8 @@ Completions highlighter_cmd_completer( else if (add and token_to_complete == 1) { StringView name = params[1]; - return { 0_byte, name.length(), complete(name, pos_in_token, HighlighterRegistry::instance() | transform(&HighlighterRegistry::Item::key)) }; + return { 0_byte, name.length(), complete(name, pos_in_token, HighlighterRegistry::instance() | transform(&HighlighterRegistry::Item::key)), + Completions::Flags::Menu }; } else return {}; diff --git a/src/highlighter_group.cc b/src/highlighter_group.cc index d5d43198..e23b5979 100644 --- a/src/highlighter_group.cc +++ b/src/highlighter_group.cc @@ -75,7 +75,8 @@ Completions HighlighterGroup::complete_child(StringView path, ByteCount cursor_p | transform([](auto& hl) { return hl.value->has_children() ? hl.key + "/" : hl.key; }) | gather>()); - return { 0, 0, std::move(candidates) }; + auto completions_flags = group ? Completions::Flags::None : Completions::Flags::Menu; + return { 0, 0, std::move(candidates), completions_flags }; } void Highlighters::highlight(HighlightContext context, DisplayBuffer& display_buffer, BufferRange range) diff --git a/src/highlighters.cc b/src/highlighters.cc index 39fd28fd..cfb24f6c 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -2089,7 +2089,8 @@ public: } auto container = m_regions | transform(&decltype(m_regions)::Item::key); - return { 0, 0, complete(path, cursor_pos, container) }; + auto completions_flags = group ? Completions::Flags::None : Completions::Flags::Menu; + return { 0, 0, complete(path, cursor_pos, container), completions_flags }; } static std::unique_ptr create(HighlighterParameters params, Highlighter*) From 3e9ca0e5c38b111bf1a68b62f6f2e44a7d12f6e9 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Tue, 19 Jul 2022 15:56:05 +0200 Subject: [PATCH 6/6] rc windowing: use menu behavior for focus We can complete every possible client argument. --- rc/windowing/iterm.kak | 2 +- rc/windowing/kitty.kak | 2 +- rc/windowing/new-client.kak | 2 +- rc/windowing/screen.kak | 2 +- rc/windowing/sway.kak | 2 +- rc/windowing/tmux.kak | 2 +- rc/windowing/wayland.kak | 2 +- rc/windowing/x11.kak | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rc/windowing/iterm.kak b/rc/windowing/iterm.kak index d99a975c..02045f13 100644 --- a/rc/windowing/iterm.kak +++ b/rc/windowing/iterm.kak @@ -136,7 +136,7 @@ If no client is passed then the current one is used' \ fi } } -complete-command iterm-focus client +complete-command -menu iterm-focus client alias global focus iterm-focus alias global terminal iterm-terminal-vertical diff --git a/rc/windowing/kitty.kak b/rc/windowing/kitty.kak index 1e4880dc..64cc224e 100644 --- a/rc/windowing/kitty.kak +++ b/rc/windowing/kitty.kak @@ -72,7 +72,7 @@ If no client is passed then the current one is used' \ fi } } -complete-command kitty-focus client +complete-command -menu kitty-focus client alias global terminal kitty-terminal alias global terminal-tab kitty-terminal-tab diff --git a/rc/windowing/new-client.kak b/rc/windowing/new-client.kak index e017344e..214a18e3 100644 --- a/rc/windowing/new-client.kak +++ b/rc/windowing/new-client.kak @@ -6,4 +6,4 @@ The optional arguments are passed as commands to the new client' \ terminal kak -c %val{session} -e "%arg{@}" } -complete-command new command +complete-command -menu new command diff --git a/rc/windowing/screen.kak b/rc/windowing/screen.kak index 8e4b6ee5..f9050886 100644 --- a/rc/windowing/screen.kak +++ b/rc/windowing/screen.kak @@ -72,7 +72,7 @@ If no client is passed then the current one is used' \ fi } } -complete-command screen-focus client +complete-command -menu screen-focus client alias global focus screen-focus alias global terminal screen-terminal-vertical diff --git a/rc/windowing/sway.kak b/rc/windowing/sway.kak index dc0c5830..4df12240 100644 --- a/rc/windowing/sway.kak +++ b/rc/windowing/sway.kak @@ -43,7 +43,7 @@ If no client is passed, then the current client is used' \ fi } } -complete-command sway-focus client +complete-command -menu sway-focus client unalias global focus alias global focus sway-focus diff --git a/rc/windowing/tmux.kak b/rc/windowing/tmux.kak index fe656cc2..f5a8fda3 100644 --- a/rc/windowing/tmux.kak +++ b/rc/windowing/tmux.kak @@ -71,7 +71,7 @@ If no client is passed then the current one is used' \ fi } } -complete-command tmux-focus client +complete-command -menu tmux-focus client ## The default behaviour for the `new` command is to open an horizontal pane in a tmux session alias global focus tmux-focus diff --git a/rc/windowing/wayland.kak b/rc/windowing/wayland.kak index 1f595a33..ea8de71d 100644 --- a/rc/windowing/wayland.kak +++ b/rc/windowing/wayland.kak @@ -51,7 +51,7 @@ If no client is passed, then the current client is used' \ %{ fail 'Focusing specific windows in most Wayland window managers is unsupported' } -complete-command wayland-focus client +complete-command -menu wayland-focus client alias global focus wayland-focus alias global terminal wayland-terminal diff --git a/rc/windowing/x11.kak b/rc/windowing/x11.kak index 4c39f7ba..a6f7da87 100644 --- a/rc/windowing/x11.kak +++ b/rc/windowing/x11.kak @@ -63,7 +63,7 @@ If no client is passed, then the current client is used' \ fi } } -complete-command x11-focus client +complete-command -menu x11-focus client alias global focus x11-focus alias global terminal x11-terminal