From 3f0415c765699cbeefac68090f146ee80d03827d Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 21 Aug 2016 20:25:11 +0100 Subject: [PATCH] Introduce the 'completion_extra_word_chars' option This string option is used to get all the additional characters that should be considered as "word" character for the purpose of insert mode completion. Fixes #758 --- README.asciidoc | 3 +++ doc/manpages/options.asciidoc | 10 ++++--- src/insert_completer.cc | 9 ++++--- src/main.cc | 3 +++ src/word_db.cc | 51 +++++++++++++++++++++++++++++++---- src/word_db.hh | 9 +++++-- 6 files changed, 72 insertions(+), 13 deletions(-) diff --git a/README.asciidoc b/README.asciidoc index 94280239..fc1b6e13 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -899,6 +899,9 @@ Some options are built in Kakoune, and can be used to control it's behaviour: where the completion apply in the buffer. * `static_words` _str-list_: list of words that are always added to completion candidates when completing words in insert mode. + * `completions_extra_word_chars` _str_: a string containing all additional character + that should be considered as word character for the purpose of insert mode + completion. * `autoreload` _enum(yes|no|ask)_: auto reload the buffers when an external modification is detected. * `debug` _flags(hooks|shell|profile)_: dump various debug information in diff --git a/doc/manpages/options.asciidoc b/doc/manpages/options.asciidoc index a235a225..4e44b0a8 100644 --- a/doc/manpages/options.asciidoc +++ b/doc/manpages/options.asciidoc @@ -124,9 +124,13 @@ Builtin options to define where the completion apply in the buffer, and the other strings are the candidates - *static_words* 'str-list'::: - list of words that are always added to completion candidates - when completing words in insert mode +*static_words* 'str-list':: + list of words that are always added to completion candidates + when completing words in insert mode + +*completions_extra_word_chars* 'str':: + a string containing all additional character that should be considered + as word character for the purpose of insert mode completion. *autoreload* 'enum(yes|no|ask)':: auto reload the buffers when an external modification is detected diff --git a/src/insert_completer.cc b/src/insert_completer.cc index 39baee03..23731337 100644 --- a/src/insert_completer.cc +++ b/src/insert_completer.cc @@ -76,12 +76,15 @@ WordDB& get_word_db(const Buffer& buffer) template InsertCompletion complete_word(const SelectionList& sels, const OptionManager& options) { + StringView extra_word_char = options["completion_extra_word_char"].get(); + auto is_word_pred = [extra_word_char](Codepoint c) { return is_word(c) or contains(extra_word_char, c); }; + const Buffer& buffer = sels.buffer(); ByteCoord cursor_pos = sels.main().cursor(); using Utf8It = utf8::iterator; Utf8It pos{buffer.iterator_at(cursor_pos), buffer}; - if (pos == buffer.begin() or not is_word(*(pos-1))) + if (pos == buffer.begin() or not is_word_pred(*(pos-1))) return {}; ByteCoord word_begin; @@ -92,7 +95,7 @@ InsertCompletion complete_word(const SelectionList& sels, const OptionManager& o Utf8It end{buffer.iterator_at(sels[i].cursor()), buffer}; Utf8It begin = end-1; if (not skip_while_reverse(begin, buffer.begin(), - [](Codepoint c) { return is_word(c); })) + [&](Codepoint c) { return is_word_pred(c); })) ++begin; if (i == sels.main_index()) @@ -101,7 +104,7 @@ InsertCompletion complete_word(const SelectionList& sels, const OptionManager& o prefix = buffer.string(word_begin, end.base().coord()); } - skip_while(end, buffer.end(), [](Codepoint c) { return is_word(c); }); + skip_while(end, buffer.end(), [&](Codepoint c) { return is_word_pred(c); }); auto word = buffer.string(begin.base().coord(), end.base().coord()); ++sel_word_counts[word]; diff --git a/src/main.cc b/src/main.cc index 004630d4..e8fd512a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -266,6 +266,9 @@ void register_options() "%val{bufname} %val{cursor_line}:%val{cursor_char_column} "_str); reg.declare_option("debug", "various debug flags", DebugFlags::None); reg.declare_option("readonly", "prevent buffers from being modified", false); + reg.declare_option("completion_extra_word_char", + "Additional characters to be considered as words for insert completion", + ""_str); } struct convert_to_client_mode diff --git a/src/word_db.cc b/src/word_db.cc index f7ff11da..aafc0b29 100644 --- a/src/word_db.cc +++ b/src/word_db.cc @@ -10,7 +10,7 @@ namespace Kakoune using WordList = Vector; -static WordList get_words(StringView content) +static WordList get_words(StringView content, StringView extra_word_chars) { WordList res; using Utf8It = utf8::iterator; @@ -19,7 +19,7 @@ static WordList get_words(StringView content) for (Utf8It it{word_start, content}, end{content.end(), content}; it != end; ++it) { Codepoint c = *it; - const bool word = is_word(c); + const bool word = is_word(c) or contains(extra_word_chars, c); if (not in_word and word) { word_start = it.base(); @@ -36,9 +36,14 @@ static WordList get_words(StringView content) return res; } +static StringView get_extra_word_chars(const Buffer& buffer) +{ + return buffer.options()["completion_extra_word_char"].get(); +} + void WordDB::add_words(StringView line) { - for (auto& w : get_words(line)) + for (auto& w : get_words(line, get_extra_word_chars(*m_buffer))) { auto it = m_words.find(w); if (it == m_words.end()) @@ -56,7 +61,7 @@ void WordDB::add_words(StringView line) void WordDB::remove_words(StringView line) { - for (auto& w : get_words(line)) + for (auto& w : get_words(line, get_extra_word_chars(*m_buffer))) { auto it = m_words.find(w); kak_assert(it != m_words.end() and it->second.refcount > 0); @@ -66,14 +71,44 @@ void WordDB::remove_words(StringView line) } WordDB::WordDB(const Buffer& buffer) - : m_buffer{&buffer}, m_timestamp{buffer.timestamp()} + : m_buffer{&buffer} { + buffer.options().register_watcher(*this); + rebuild_db(); +} + +WordDB::WordDB(WordDB&& other) + : m_buffer{std::move(other.m_buffer)}, + m_lines{std::move(other.m_lines)}, + m_words{std::move(other.m_words)}, + m_timestamp{other.m_timestamp} +{ + kak_assert(m_buffer); + m_buffer->options().unregister_watcher(other); + other.m_buffer = nullptr; + + m_buffer->options().register_watcher(*this); +} + +WordDB::~WordDB() +{ + if (m_buffer) + m_buffer->options().unregister_watcher(*this); +} + +void WordDB::rebuild_db() +{ + auto& buffer = *m_buffer; + + m_words.clear(); + m_lines.clear(); m_lines.reserve((int)buffer.line_count()); for (auto line = 0_line, end = buffer.line_count(); line < end; ++line) { m_lines.push_back(buffer.line_storage(line)); add_words(m_lines.back()->strview()); } + m_timestamp = buffer.timestamp(); } void WordDB::update_db() @@ -118,6 +153,12 @@ void WordDB::update_db() m_lines = std::move(new_lines); } +void WordDB::on_option_changed(const Option& option) +{ + if (option.name() == "completion_extra_word_char") + rebuild_db(); +} + int WordDB::get_word_occurences(StringView word) const { auto it = m_words.find(word); diff --git a/src/word_db.hh b/src/word_db.hh index 3dcf455d..c23d266e 100644 --- a/src/word_db.hh +++ b/src/word_db.hh @@ -13,12 +13,13 @@ namespace Kakoune using RankedMatchList = Vector; // maintain a database of words available in a buffer -class WordDB +class WordDB : public OptionManagerWatcher { public: WordDB(const Buffer& buffer); + ~WordDB(); WordDB(const WordDB&) = delete; - WordDB(WordDB&&) = default; + WordDB(WordDB&&); RankedMatchList find_matching(StringView str); @@ -28,6 +29,10 @@ private: void add_words(StringView line); void remove_words(StringView line); + void rebuild_db(); + + void on_option_changed(const Option& option) override; + struct WordInfo { StringDataPtr word;