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.
|
||||
* `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
|
||||
|
|
|
@ -124,10 +124,14 @@ Builtin options
|
|||
to define where the completion apply in the buffer, and the
|
||||
other strings are the candidates
|
||||
|
||||
*static_words* 'str-list':::
|
||||
*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
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user