From 66078243ec329a5d1d47864e9a24e6f1e44e85f3 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sun, 29 May 2022 08:06:48 +0200 Subject: [PATCH] Run InsertCompletionHide hook before insertions that close completion menu Insert mode completions are accepted by typing any key. For example, if there is a completion "somefunction()", then typing some; will insert somefunction(); and then the InsertCompletionHide hook will fire. The hook parameter is a range that contains the entire thing: the actual completion plus the trailing semicolon that closed the completion menu. The [original motivation] for the hook parameter was to support removing text inserted by completion, so we can apply text edits or expand snippets instead. One problem is that we don't want to remove the semicolon. Another problem came up in a discussion about [snippets]: let's say we have a snippet "add" that expands to add(?, ?) where ? are placeholders. After snippet expansion the cursor replaces the first placeholder. If I type "ad1" I expect to get "add(1, ?)". If the InsertCompletionHide hook only runs after processing the "1" keystroke, this is not possible without evil hacks. Fix these problems by running InsertCompletionHide when a completion is accepted _before_ inserting anything else into the buffer. This should make it much easier to fully implement [LSP text edits]. I doubt that anyone besides kak-lsp is using the hook parameter today so this should be a low-risk fix. [original motivation]: https://github.com/mawww/kakoune/issues/2898 [snippets]: https://github.com/kak-lsp/kak-lsp/pull/616#discussion_r883208858 [LSP text edits]: https://github.com/kak-lsp/kak-lsp/issues/40 --- src/input_handler.cc | 5 +++++ src/insert_completer.cc | 9 +++++++++ src/insert_completer.hh | 1 + test/hooks/completion-hide/cmd | 4 ++++ test/hooks/completion-hide/in | 1 + test/hooks/completion-hide/out | 2 ++ test/hooks/completion-hide/rc | 23 +++++++++++++++++++++++ 7 files changed, 45 insertions(+) create mode 100644 test/hooks/completion-hide/cmd create mode 100644 test/hooks/completion-hide/in create mode 100644 test/hooks/completion-hide/out create mode 100644 test/hooks/completion-hide/rc diff --git a/src/input_handler.cc b/src/input_handler.cc index 9b1857a8..d2fb8275 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -1345,7 +1345,10 @@ public: selections.sort_and_merge_overlapping(); } else if (auto cp = key.codepoint()) + { + m_completer.try_accept(); insert(*cp); + } else if (key == ctrl('r')) { on_next_key_with_autoinfo(context(), "register", KeymapMode::None, @@ -1353,6 +1356,7 @@ public: auto cp = key.codepoint(); if (not cp or key == Key::Escape) return; + m_completer.try_accept(); insert(RegisterManager::instance()[*cp].get(context())); }, "enter register name", register_doc.str()); update_completions = false; @@ -1415,6 +1419,7 @@ public: [this, transient](Key key, Context&) { if (auto cp = get_raw_codepoint(key)) { + m_completer.try_accept(); insert(*cp); context().hooks().run_hook(Hook::InsertKey, key_to_str(key), context()); if (enabled() and not transient) diff --git a/src/insert_completer.cc b/src/insert_completer.cc index ddc96cbd..d06df6f0 100644 --- a/src/insert_completer.cc +++ b/src/insert_completer.cc @@ -474,6 +474,15 @@ void InsertCompleter::update(bool allow_implicit) auto& get_first(BufferRange& range) { return range.begin; } auto& get_last(BufferRange& range) { return range.end; } +void InsertCompleter::try_accept() +{ + if (m_completions.is_valid() and m_context.has_client() + and m_current_candidate >= 0 and m_current_candidate < m_completions.candidates.size() - 1) + { + reset(); + } +} + void InsertCompleter::reset() { if (m_explicit_completer or m_completions.is_valid()) diff --git a/src/insert_completer.hh b/src/insert_completer.hh index 1d68be86..c8c266a9 100644 --- a/src/insert_completer.hh +++ b/src/insert_completer.hh @@ -84,6 +84,7 @@ public: void select(int index, bool relative, Vector& keystrokes); void update(bool allow_implicit); + void try_accept(); void reset(); void explicit_file_complete(); diff --git a/test/hooks/completion-hide/cmd b/test/hooks/completion-hide/cmd new file mode 100644 index 00000000..b49cd0f9 --- /dev/null +++ b/test/hooks/completion-hide/cmd @@ -0,0 +1,4 @@ +:update-completions +ia +:update-completions +ib diff --git a/test/hooks/completion-hide/in b/test/hooks/completion-hide/in new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/hooks/completion-hide/in @@ -0,0 +1 @@ + diff --git a/test/hooks/completion-hide/out b/test/hooks/completion-hide/out new file mode 100644 index 00000000..3ad7ab16 --- /dev/null +++ b/test/hooks/completion-hide/out @@ -0,0 +1,2 @@ +accepted completion: a +rejected completion b diff --git a/test/hooks/completion-hide/rc b/test/hooks/completion-hide/rc new file mode 100644 index 00000000..565a8e6b --- /dev/null +++ b/test/hooks/completion-hide/rc @@ -0,0 +1,23 @@ +declare-option completions line1 +declare-option completions line2 +set-option global completers \ + option=line1 \ + option=line2 \ + +define-command update-completions %{ + set-option global line1 "1.1@%val{timestamp}" foo()|| + set-option global line2 "2.1@%val{timestamp}" foo()|| +} + +hook global InsertCompletionHide .+ %{ + evaluate-commands -draft %{ + select %val{hook_param} + execute-keys ia + execute-keys i "accepted completion: " + } +} +hook global InsertCompletionHide '' %{ + evaluate-commands -draft %{ + execute-keys i "rejected completion " + } +}