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:
parent
9124851029
commit
3f0415c765
|
@ -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.
|
where the completion apply in the buffer.
|
||||||
* `static_words` _str-list_: list of words that are always added to completion
|
* `static_words` _str-list_: list of words that are always added to completion
|
||||||
candidates when completing words in insert mode.
|
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
|
* `autoreload` _enum(yes|no|ask)_: auto reload the buffers when an external
|
||||||
modification is detected.
|
modification is detected.
|
||||||
* `debug` _flags(hooks|shell|profile)_: dump various debug information in
|
* `debug` _flags(hooks|shell|profile)_: dump various debug information in
|
||||||
|
|
|
@ -124,9 +124,13 @@ Builtin options
|
||||||
to define where the completion apply in the buffer, and the
|
to define where the completion apply in the buffer, and the
|
||||||
other strings are the candidates
|
other strings are the candidates
|
||||||
|
|
||||||
*static_words* 'str-list':::
|
*static_words* 'str-list'::
|
||||||
list of words that are always added to completion candidates
|
list of words that are always added to completion candidates
|
||||||
when completing words in insert mode
|
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)'::
|
*autoreload* 'enum(yes|no|ask)'::
|
||||||
auto reload the buffers when an external modification is detected
|
auto reload the buffers when an external modification is detected
|
||||||
|
|
|
@ -76,12 +76,15 @@ WordDB& get_word_db(const Buffer& buffer)
|
||||||
template<bool other_buffers>
|
template<bool other_buffers>
|
||||||
InsertCompletion complete_word(const SelectionList& sels, const OptionManager& options)
|
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();
|
const Buffer& buffer = sels.buffer();
|
||||||
ByteCoord cursor_pos = sels.main().cursor();
|
ByteCoord cursor_pos = sels.main().cursor();
|
||||||
|
|
||||||
using Utf8It = utf8::iterator<BufferIterator>;
|
using Utf8It = utf8::iterator<BufferIterator>;
|
||||||
Utf8It pos{buffer.iterator_at(cursor_pos), buffer};
|
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 {};
|
return {};
|
||||||
|
|
||||||
ByteCoord word_begin;
|
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 end{buffer.iterator_at(sels[i].cursor()), buffer};
|
||||||
Utf8It begin = end-1;
|
Utf8It begin = end-1;
|
||||||
if (not skip_while_reverse(begin, buffer.begin(),
|
if (not skip_while_reverse(begin, buffer.begin(),
|
||||||
[](Codepoint c) { return is_word(c); }))
|
[&](Codepoint c) { return is_word_pred(c); }))
|
||||||
++begin;
|
++begin;
|
||||||
|
|
||||||
if (i == sels.main_index())
|
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());
|
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());
|
auto word = buffer.string(begin.base().coord(), end.base().coord());
|
||||||
++sel_word_counts[word];
|
++sel_word_counts[word];
|
||||||
|
|
|
@ -266,6 +266,9 @@ void register_options()
|
||||||
"%val{bufname} %val{cursor_line}:%val{cursor_char_column} "_str);
|
"%val{bufname} %val{cursor_line}:%val{cursor_char_column} "_str);
|
||||||
reg.declare_option("debug", "various debug flags", DebugFlags::None);
|
reg.declare_option("debug", "various debug flags", DebugFlags::None);
|
||||||
reg.declare_option("readonly", "prevent buffers from being modified", false);
|
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
|
struct convert_to_client_mode
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace Kakoune
|
||||||
|
|
||||||
using WordList = Vector<StringView>;
|
using WordList = Vector<StringView>;
|
||||||
|
|
||||||
static WordList get_words(StringView content)
|
static WordList get_words(StringView content, StringView extra_word_chars)
|
||||||
{
|
{
|
||||||
WordList res;
|
WordList res;
|
||||||
using Utf8It = utf8::iterator<const char*>;
|
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)
|
for (Utf8It it{word_start, content}, end{content.end(), content}; it != end; ++it)
|
||||||
{
|
{
|
||||||
Codepoint c = *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)
|
if (not in_word and word)
|
||||||
{
|
{
|
||||||
word_start = it.base();
|
word_start = it.base();
|
||||||
|
@ -36,9 +36,14 @@ static WordList get_words(StringView content)
|
||||||
return res;
|
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)
|
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);
|
auto it = m_words.find(w);
|
||||||
if (it == m_words.end())
|
if (it == m_words.end())
|
||||||
|
@ -56,7 +61,7 @@ void WordDB::add_words(StringView line)
|
||||||
|
|
||||||
void WordDB::remove_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);
|
auto it = m_words.find(w);
|
||||||
kak_assert(it != m_words.end() and it->second.refcount > 0);
|
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)
|
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());
|
m_lines.reserve((int)buffer.line_count());
|
||||||
for (auto line = 0_line, end = buffer.line_count(); line < end; ++line)
|
for (auto line = 0_line, end = buffer.line_count(); line < end; ++line)
|
||||||
{
|
{
|
||||||
m_lines.push_back(buffer.line_storage(line));
|
m_lines.push_back(buffer.line_storage(line));
|
||||||
add_words(m_lines.back()->strview());
|
add_words(m_lines.back()->strview());
|
||||||
}
|
}
|
||||||
|
m_timestamp = buffer.timestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WordDB::update_db()
|
void WordDB::update_db()
|
||||||
|
@ -118,6 +153,12 @@ void WordDB::update_db()
|
||||||
m_lines = std::move(new_lines);
|
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
|
int WordDB::get_word_occurences(StringView word) const
|
||||||
{
|
{
|
||||||
auto it = m_words.find(word);
|
auto it = m_words.find(word);
|
||||||
|
|
|
@ -13,12 +13,13 @@ namespace Kakoune
|
||||||
using RankedMatchList = Vector<RankedMatch>;
|
using RankedMatchList = Vector<RankedMatch>;
|
||||||
|
|
||||||
// maintain a database of words available in a buffer
|
// maintain a database of words available in a buffer
|
||||||
class WordDB
|
class WordDB : public OptionManagerWatcher
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WordDB(const Buffer& buffer);
|
WordDB(const Buffer& buffer);
|
||||||
|
~WordDB();
|
||||||
WordDB(const WordDB&) = delete;
|
WordDB(const WordDB&) = delete;
|
||||||
WordDB(WordDB&&) = default;
|
WordDB(WordDB&&);
|
||||||
|
|
||||||
RankedMatchList find_matching(StringView str);
|
RankedMatchList find_matching(StringView str);
|
||||||
|
|
||||||
|
@ -28,6 +29,10 @@ private:
|
||||||
void add_words(StringView line);
|
void add_words(StringView line);
|
||||||
void remove_words(StringView line);
|
void remove_words(StringView line);
|
||||||
|
|
||||||
|
void rebuild_db();
|
||||||
|
|
||||||
|
void on_option_changed(const Option& option) override;
|
||||||
|
|
||||||
struct WordInfo
|
struct WordInfo
|
||||||
{
|
{
|
||||||
StringDataPtr word;
|
StringDataPtr word;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user