From 36b016226c9c3d6b31746e3980a6190214f714c6 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sat, 15 Mar 2014 03:14:23 +0000 Subject: [PATCH] Insert mode completion execute completers in order, and supports multiple option The 'completions' option is gone, just add option=completion_option_name in the completers list. --- README.asciidoc | 17 ++++++----- rc/clang.kak | 9 ++++-- rc/ctags.kak | 2 +- src/input_handler.cc | 68 ++++++++++++++++++++++++++++++------------- src/option_manager.cc | 9 ++++-- 5 files changed, 72 insertions(+), 33 deletions(-) diff --git a/README.asciidoc b/README.asciidoc index e0801da6..63fca835 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -523,15 +523,18 @@ Some options are built in Kakoune, and can be used to control it's behaviour: * +filetype+ _str_: arbitrary string defining the type of the file filetype dependant actions should hook on this option changing for activation/deactivation. - * +completions+ _str-list_: option used for external completion, the - first string should follow the format - _.[+]@_ to define where the completion - apply in the buffer, and the other strings are the candidates. * +path+ _str-list_: directories to search for gf command. * +completers+ _str-list_: completion systems to use for insert mode - completion. Support +option+ which use the +completions+ option, and - +word=all+ or +word=buffer+ which complete using words in all buffers - (+word=all+) or only the current one (+word=buffer+) + completion. given completers are tried in order until one generate some + completion candidates. Existing completers are: + - +word=all+ or +word=buffer+ which complete using words in all buffers + (+word=all+) or only the current one (+word=buffer+) + - +filename+ which tries to detect when a filename is being entered and + provides completion based on local filesystem. + - +option=+ where is a _str-list_ option. The first + element of the list should follow the format: + _.[+]@_ to define where the completion + apply in the buffer, and the other strings are the candidates. * +autoreload+ _yesnoask_: auto reload the buffers when an external modification is detected. diff --git a/rc/clang.kak b/rc/clang.kak index 1c6bdbdb..7051ba4c 100644 --- a/rc/clang.kak +++ b/rc/clang.kak @@ -1,5 +1,6 @@ decl -hidden str clang_filename decl str clang_options +decl str-list clang_completions def clang-complete %{ %sh{ @@ -23,12 +24,13 @@ def clang-complete %{ for cmp in ${output}; do completions="${completions}:${cmp}" done - echo "eval -client $kak_client %[ echo completed; set buffer completions '${completions}' ]" | kak -p ${kak_session} + echo "eval -client $kak_client %[ echo completed; set buffer clang_completions '${completions}' ]" | kak -p ${kak_session} ) > /dev/null 2>&1 < /dev/null & } } def clang-enable-autocomplete %{ + set window completers %sh{ echo "'option=clang_completions:${kak_opt_completers}'" } hook window -id clang-autocomplete InsertIdle .* %{ try %{ exec -draft (\.|->|::).$ echo 'completing...' @@ -36,4 +38,7 @@ def clang-enable-autocomplete %{ } } } -def clang-disable-autocomplete %{ rmhooks window clang-autocomplete } +def clang-disable-autocomplete %{ + set window completers %sh{ echo "'${kak_opt_completers}'" | sed -e 's/option=clang_completions://g' } + rmhooks window clang-autocomplete +} diff --git a/rc/ctags.kak b/rc/ctags.kak index 96a50322..2f60712c 100644 --- a/rc/ctags.kak +++ b/rc/ctags.kak @@ -31,7 +31,7 @@ def tag-complete %{ eval -draft %{ %sh{ ( compl=$(readtags -p "$kak_selection" | cut -f 1 | sort | uniq | sed -e 's/:/\\:/g' | sed -e 's/\n/:/g' ) compl="${kak_cursor_line}.${kak_cursor_column}+${#kak_selection}@${kak_timestamp}:${compl}" - echo "set buffer=$kak_bufname completions '${compl}'" | kak -p ${kak_session} + echo "set buffer=$kak_bufname ctags_completions '${compl}'" | kak -p ${kak_session} ) > /dev/null 2>&1 < /dev/null & } }} diff --git a/src/input_handler.cc b/src/input_handler.cc index d511c3fe..8b8582f8 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -11,6 +11,7 @@ #include "color_registry.hh" #include "file.hh" #include "word_db.hh" +#include "debug.hh" #include @@ -711,12 +712,20 @@ public: m_context.ui().menu_hide(); } - template - bool try_complete() + template + bool try_complete(CompleteFunc complete_func) { auto& buffer = m_context.buffer(); BufferCoord cursor_pos = m_context.selections().main().cursor(); - m_completions = (this->*complete_func)(buffer, cursor_pos); + try + { + m_completions = complete_func(buffer, cursor_pos); + } + catch (runtime_error& e) + { + write_debug("error while trying to run completer: "_str + e.what()); + return false; + } if (not m_completions.is_valid()) return false; @@ -812,9 +821,9 @@ public: return { begin.coord(), pos.coord(), std::move(res), buffer.timestamp() }; } - BufferCompletion complete_option(const Buffer& buffer, BufferCoord cursor_pos) + BufferCompletion complete_option(const Buffer& buffer, BufferCoord cursor_pos, const String& option_name) { - const StringList& opt = options()["completions"].get(); + const StringList& opt = options()[option_name].get();; if (opt.empty()) return {}; @@ -868,7 +877,14 @@ public: private: void on_option_changed(const Option& opt) override { - if (opt.name() == "completions") + auto& completers = options()["completers"].get(); + StringList option_names; + for (auto& completer : completers) + { + if (completer.substr(0_byte, 7_byte) == "option=") + option_names.emplace_back(completer.substr(7_byte)); + } + if (contains(option_names, opt.name())) { reset(); setup_ifn(); @@ -889,18 +905,29 @@ private: bool setup_ifn() { + using namespace std::placeholders; if (not m_completions.is_valid()) { auto& completers = options()["completers"].get(); - if (contains(completers, "option") and try_complete<&BufferCompleter::complete_option>()) - return true; - if (contains(completers, "word=buffer") and try_complete<&BufferCompleter::complete_word>()) - return true; - if (contains(completers, "word=all") and try_complete<&BufferCompleter::complete_word>()) - return true; - if (contains(completers, "filename") and try_complete<&BufferCompleter::complete_filename>()) - return true; - + for (auto& completer : completers) + { + if (completer == "filename" and + try_complete([this](const Buffer& buffer, BufferCoord cursor_pos) + { return complete_filename(buffer, cursor_pos); })) + return true; + if (completer.substr(0_byte, 7_byte) == "option=" and + try_complete([&, this](const Buffer& buffer, BufferCoord cursor_pos) + { return complete_option(buffer, cursor_pos, completer.substr(7_byte)); })) + return true; + if (completer == "word=buffer" and + try_complete([this](const Buffer& buffer, BufferCoord cursor_pos) + { return complete_word(buffer, cursor_pos); })) + return true; + if (completer == "word=all" and + try_complete([this](const Buffer& buffer, BufferCoord cursor_pos) + { return complete_word(buffer, cursor_pos); })) + return true; + } return false; } return true; @@ -946,13 +973,14 @@ public: if (m_mode == Mode::Complete) { if (key.key == 'f') - m_completer.try_complete<&BufferCompleter::complete_filename>(); + m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos) + { return m_completer.complete_filename(buffer, cursor_pos); }); if (key.key == 'w') - m_completer.try_complete<&BufferCompleter::complete_word>(); - if (key.key == 'o') - m_completer.try_complete<&BufferCompleter::complete_option>(); + m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos) + { return m_completer.complete_word(buffer, cursor_pos); }); if (key.key == 'l') - m_completer.try_complete<&BufferCompleter::complete_line>(); + m_completer.try_complete([this](const Buffer& buffer, BufferCoord cursor_pos) + { return m_completer.complete_line(buffer, cursor_pos); }); m_mode = Mode::Default; return; } diff --git a/src/option_manager.cc b/src/option_manager.cc index 58589624..cd144084 100644 --- a/src/option_manager.cc +++ b/src/option_manager.cc @@ -133,15 +133,18 @@ GlobalOptions::GlobalOptions() declare_option("aligntab", false); declare_option("ignored_files", Regex{R"(^(\..*|.*\.(o|so|a))$)"}); declare_option("filetype", ""); - declare_option>("completions", {}); declare_option>("path", { "./", "/usr/include" }); - declare_option>("completers", {"option", "filename", "word=buffer"}, + declare_option>("completers", {"filename", "word=buffer"}, Option::Flags::None, [](const std::vector& s) { - static const auto values = {"option", "word=buffer", "word=all", "filename" }; + static const auto values = {"word=buffer", "word=all", "filename" }; for (auto& v : s) + { + if (v.substr(0_byte, 7_byte) == "option=") + continue; if (not contains(values, v)) throw runtime_error(v + " is not a recognised value for completers"); + } }); declare_option("autoreload", Ask); }