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
This commit is contained in:
Maxime Coste 2016-08-21 20:25:11 +01:00
parent 9124851029
commit 3f0415c765
6 changed files with 72 additions and 13 deletions

View File

@ -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

View File

@ -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

View File

@ -76,12 +76,15 @@ WordDB& get_word_db(const Buffer& buffer)
template<bool other_buffers>
InsertCompletion complete_word(const SelectionList& sels, const OptionManager& options)
{
StringView extra_word_char = options["completion_extra_word_char"].get<String>();
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<BufferIterator>;
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];

View File

@ -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

View File

@ -10,7 +10,7 @@ namespace Kakoune
using WordList = Vector<StringView>;
static WordList get_words(StringView content)
static WordList get_words(StringView content, StringView extra_word_chars)
{
WordList res;
using Utf8It = utf8::iterator<const char*>;
@ -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<String>();
}
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);

View File

@ -13,12 +13,13 @@ namespace Kakoune
using RankedMatchList = Vector<RankedMatch>;
// 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;