Use the extra_word_chars option in word based normal commands
the completion_extra_word_chars is now gone, superseeded by extra_word_chars that gets used both for completion and for normal mode. Fixes #1304
This commit is contained in:
parent
dc378aed72
commit
f41d78083a
|
@ -57,7 +57,7 @@ def -hidden haskell-indent-on-new-line %{
|
||||||
hook -group haskell-highlight global WinSetOption filetype=haskell %{ add-highlighter ref haskell }
|
hook -group haskell-highlight global WinSetOption filetype=haskell %{ add-highlighter ref haskell }
|
||||||
|
|
||||||
hook global WinSetOption filetype=haskell %{
|
hook global WinSetOption filetype=haskell %{
|
||||||
set buffer completion_extra_word_char "'"
|
set window extra_word_chars "'"
|
||||||
hook window InsertEnd .* -group haskell-hooks haskell-filter-around-selections
|
hook window InsertEnd .* -group haskell-hooks haskell-filter-around-selections
|
||||||
hook window InsertChar \n -group haskell-indent haskell-indent-on-new-line
|
hook window InsertChar \n -group haskell-indent haskell-indent-on-new-line
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ add-highlighter -group / regions -default code kakrc \
|
||||||
# Add the language's grammar to the static completion list
|
# Add the language's grammar to the static completion list
|
||||||
printf %s\\n "hook global WinSetOption filetype=kak %{
|
printf %s\\n "hook global WinSetOption filetype=kak %{
|
||||||
set window static_words '${keywords}:${attributes}:${types}:${values}'
|
set window static_words '${keywords}:${attributes}:${types}:${values}'
|
||||||
set -- window completion_extra_word_char '-'
|
set -- window extra_word_chars '-'
|
||||||
}" | sed 's,|,:,g'
|
}" | sed 's,|,:,g'
|
||||||
|
|
||||||
# Highlight keywords. Teach \b that - does not create a word boundary
|
# Highlight keywords. Teach \b that - does not create a word boundary
|
||||||
|
|
|
@ -79,8 +79,8 @@ 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)
|
||||||
{
|
{
|
||||||
auto& extra_word_char = options["completion_extra_word_char"].get<Vector<Codepoint, MemoryDomain::Options>>();
|
ConstArrayView<Codepoint> extra_word_chars = options["extra_word_chars"].get<Vector<Codepoint, MemoryDomain::Options>>();
|
||||||
auto is_word_pred = [extra_word_char](Codepoint c) { return is_word(c) or contains(extra_word_char, c); };
|
auto is_word_pred = [extra_word_chars](Codepoint c) { return is_word(c, extra_word_chars); };
|
||||||
|
|
||||||
const Buffer& buffer = sels.buffer();
|
const Buffer& buffer = sels.buffer();
|
||||||
BufferCoord cursor_pos = sels.main().cursor();
|
BufferCoord cursor_pos = sels.main().cursor();
|
||||||
|
|
10
src/main.cc
10
src/main.cc
|
@ -48,7 +48,9 @@ static const char* startup_info =
|
||||||
" * `*` will now strip surrounding whitespaces from the selection\n"
|
" * `*` will now strip surrounding whitespaces from the selection\n"
|
||||||
" * lint/grep/make next/prev commands have been renamed to more\n"
|
" * lint/grep/make next/prev commands have been renamed to more\n"
|
||||||
" explicit names (lint-next-error, grep-previous-match, ...)\n"
|
" explicit names (lint-next-error, grep-previous-match, ...)\n"
|
||||||
" * ctags commands have been renamed to use the ctags- prefix\n";
|
" * ctags commands have been renamed to use the ctags- prefix\n"
|
||||||
|
" * completion_extra_word_char option is now extra_word_chars (note the plural form)\n"
|
||||||
|
" and is used for word selection commands\n";
|
||||||
|
|
||||||
struct startup_error : runtime_error
|
struct startup_error : runtime_error
|
||||||
{
|
{
|
||||||
|
@ -251,7 +253,7 @@ static void check_timeout(const int& timeout)
|
||||||
throw runtime_error{"the minimum acceptable timeout is 50 milliseconds"};
|
throw runtime_error{"the minimum acceptable timeout is 50 milliseconds"};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_extra_word_char(const Vector<Codepoint, MemoryDomain::Options>& extra_chars)
|
static void check_extra_word_chars(const Vector<Codepoint, MemoryDomain::Options>& extra_chars)
|
||||||
{
|
{
|
||||||
if (contains_that(extra_chars, is_blank))
|
if (contains_that(extra_chars, is_blank))
|
||||||
throw runtime_error{"blanks are not accepted for extra completion characters"};
|
throw runtime_error{"blanks are not accepted for extra completion characters"};
|
||||||
|
@ -324,8 +326,8 @@ void register_options()
|
||||||
|
|
||||||
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<Vector<Codepoint, MemoryDomain::Options>, check_extra_word_char>(
|
reg.declare_option<Vector<Codepoint, MemoryDomain::Options>, check_extra_word_chars>(
|
||||||
"completion_extra_word_char",
|
"extra_word_chars",
|
||||||
"Additional characters to be considered as words for insert completion",
|
"Additional characters to be considered as words for insert completion",
|
||||||
{});
|
{});
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,17 +36,24 @@ Selection utf8_range(const Utf8Iterator& first, const Utf8Iterator& last)
|
||||||
return {first.base().coord(), last.base().coord()};
|
return {first.base().coord(), last.base().coord()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConstArrayView<Codepoint> get_extra_word_chars(const Context& context)
|
||||||
|
{
|
||||||
|
return context.options()["extra_word_chars"].get<Vector<Codepoint, MemoryDomain::Options>>();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<WordType word_type>
|
template<WordType word_type>
|
||||||
Optional<Selection>
|
Optional<Selection>
|
||||||
select_to_next_word(const Context& context, const Selection& selection)
|
select_to_next_word(const Context& context, const Selection& selection)
|
||||||
{
|
{
|
||||||
|
auto extra_word_chars = get_extra_word_chars(context);
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer};
|
Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer};
|
||||||
if (begin+1 == buffer.end())
|
if (begin+1 == buffer.end())
|
||||||
return {};
|
return {};
|
||||||
if (categorize<word_type>(*begin) != categorize<word_type>(*(begin+1)))
|
if (categorize<word_type>(*begin, extra_word_chars) !=
|
||||||
|
categorize<word_type>(*(begin+1), extra_word_chars))
|
||||||
++begin;
|
++begin;
|
||||||
|
|
||||||
if (not skip_while(begin, buffer.end(),
|
if (not skip_while(begin, buffer.end(),
|
||||||
|
@ -54,10 +61,12 @@ select_to_next_word(const Context& context, const Selection& selection)
|
||||||
return {};
|
return {};
|
||||||
Utf8Iterator end = begin+1;
|
Utf8Iterator end = begin+1;
|
||||||
|
|
||||||
if (word_type == Word and is_punctuation(*begin))
|
auto is_word = [&](Codepoint c) { return Kakoune::is_word<word_type>(c, extra_word_chars); };
|
||||||
|
|
||||||
|
if (is_word(*begin))
|
||||||
|
skip_while(end, buffer.end(), is_word);
|
||||||
|
else if (is_punctuation(*begin))
|
||||||
skip_while(end, buffer.end(), is_punctuation);
|
skip_while(end, buffer.end(), is_punctuation);
|
||||||
else if (is_word<word_type>(*begin))
|
|
||||||
skip_while(end, buffer.end(), is_word<word_type>);
|
|
||||||
|
|
||||||
skip_while(end, buffer.end(), is_horizontal_blank);
|
skip_while(end, buffer.end(), is_horizontal_blank);
|
||||||
|
|
||||||
|
@ -70,11 +79,13 @@ template<WordType word_type>
|
||||||
Optional<Selection>
|
Optional<Selection>
|
||||||
select_to_next_word_end(const Context& context, const Selection& selection)
|
select_to_next_word_end(const Context& context, const Selection& selection)
|
||||||
{
|
{
|
||||||
|
auto extra_word_chars = get_extra_word_chars(context);
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer};
|
Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer};
|
||||||
if (begin+1 == buffer.end())
|
if (begin+1 == buffer.end())
|
||||||
return {};
|
return {};
|
||||||
if (categorize<word_type>(*begin) != categorize<word_type>(*(begin+1)))
|
if (categorize<word_type>(*begin, extra_word_chars) !=
|
||||||
|
categorize<word_type>(*(begin+1), extra_word_chars))
|
||||||
++begin;
|
++begin;
|
||||||
|
|
||||||
if (not skip_while(begin, buffer.end(),
|
if (not skip_while(begin, buffer.end(),
|
||||||
|
@ -83,10 +94,12 @@ select_to_next_word_end(const Context& context, const Selection& selection)
|
||||||
Utf8Iterator end = begin;
|
Utf8Iterator end = begin;
|
||||||
skip_while(end, buffer.end(), is_horizontal_blank);
|
skip_while(end, buffer.end(), is_horizontal_blank);
|
||||||
|
|
||||||
if (word_type == Word and is_punctuation(*end))
|
auto is_word = [&](Codepoint c) { return Kakoune::is_word<word_type>(c, extra_word_chars); };
|
||||||
|
|
||||||
|
if (is_word(*end))
|
||||||
|
skip_while(end, buffer.end(), is_word);
|
||||||
|
else if (is_punctuation(*end))
|
||||||
skip_while(end, buffer.end(), is_punctuation);
|
skip_while(end, buffer.end(), is_punctuation);
|
||||||
else if (is_word<word_type>(*end))
|
|
||||||
skip_while(end, buffer.end(), is_word<word_type>);
|
|
||||||
|
|
||||||
return utf8_range(begin, end-1);
|
return utf8_range(begin, end-1);
|
||||||
}
|
}
|
||||||
|
@ -97,22 +110,25 @@ template<WordType word_type>
|
||||||
Optional<Selection>
|
Optional<Selection>
|
||||||
select_to_previous_word(const Context& context, const Selection& selection)
|
select_to_previous_word(const Context& context, const Selection& selection)
|
||||||
{
|
{
|
||||||
|
auto extra_word_chars = get_extra_word_chars(context);
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer};
|
Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer};
|
||||||
if (begin == buffer.begin())
|
if (begin == buffer.begin())
|
||||||
return {};
|
return {};
|
||||||
if (categorize<word_type>(*begin) != categorize<word_type>(*(begin-1)))
|
if (categorize<word_type>(*begin, extra_word_chars) !=
|
||||||
|
categorize<word_type>(*(begin-1), extra_word_chars))
|
||||||
--begin;
|
--begin;
|
||||||
|
|
||||||
skip_while_reverse(begin, buffer.begin(), [](Codepoint c){ return is_eol(c); });
|
skip_while_reverse(begin, buffer.begin(), [](Codepoint c){ return is_eol(c); });
|
||||||
Utf8Iterator end = begin;
|
Utf8Iterator end = begin;
|
||||||
|
|
||||||
bool with_end = skip_while_reverse(end, buffer.begin(), is_horizontal_blank);
|
auto is_word = [&](Codepoint c) { return Kakoune::is_word<word_type>(c, extra_word_chars); };
|
||||||
if (word_type == Word and is_punctuation(*end))
|
|
||||||
with_end = skip_while_reverse(end, buffer.begin(), is_punctuation);
|
|
||||||
|
|
||||||
else if (is_word<word_type>(*end))
|
bool with_end = skip_while_reverse(end, buffer.begin(), is_horizontal_blank);
|
||||||
with_end = skip_while_reverse(end, buffer.begin(), is_word<word_type>);
|
if (is_word(*end))
|
||||||
|
with_end = skip_while_reverse(end, buffer.begin(), is_word);
|
||||||
|
else if (is_punctuation(*end))
|
||||||
|
with_end = skip_while_reverse(end, buffer.begin(), is_punctuation);
|
||||||
|
|
||||||
return utf8_range(begin, with_end ? end : end+1);
|
return utf8_range(begin, with_end ? end : end+1);
|
||||||
}
|
}
|
||||||
|
@ -124,21 +140,25 @@ Optional<Selection>
|
||||||
select_word(const Context& context, const Selection& selection,
|
select_word(const Context& context, const Selection& selection,
|
||||||
int count, ObjectFlags flags)
|
int count, ObjectFlags flags)
|
||||||
{
|
{
|
||||||
|
auto extra_word_chars = get_extra_word_chars(context);
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
|
|
||||||
|
auto is_word = [&](Codepoint c) { return Kakoune::is_word<word_type>(c, extra_word_chars); };
|
||||||
|
|
||||||
Utf8Iterator first{buffer.iterator_at(selection.cursor()), buffer};
|
Utf8Iterator first{buffer.iterator_at(selection.cursor()), buffer};
|
||||||
if (not is_word<word_type>(*first))
|
if (not is_word(*first))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
Utf8Iterator last = first;
|
Utf8Iterator last = first;
|
||||||
if (flags & ObjectFlags::ToBegin)
|
if (flags & ObjectFlags::ToBegin)
|
||||||
{
|
{
|
||||||
skip_while_reverse(first, buffer.begin(), is_word<word_type>);
|
skip_while_reverse(first, buffer.begin(), is_word);
|
||||||
if (not is_word<word_type>(*first))
|
if (not is_word(*first))
|
||||||
++first;
|
++first;
|
||||||
}
|
}
|
||||||
if (flags & ObjectFlags::ToEnd)
|
if (flags & ObjectFlags::ToEnd)
|
||||||
{
|
{
|
||||||
skip_while(last, buffer.end(), is_word<word_type>);
|
skip_while(last, buffer.end(), is_word);
|
||||||
if (not (flags & ObjectFlags::Inner))
|
if (not (flags & ObjectFlags::Inner))
|
||||||
skip_while(last, buffer.end(), is_horizontal_blank);
|
skip_while(last, buffer.end(), is_horizontal_blank);
|
||||||
--last;
|
--last;
|
||||||
|
|
|
@ -429,7 +429,7 @@ Vector<StringView> wrap_lines(StringView text, ColumnCount max_width)
|
||||||
Vector<StringView> lines;
|
Vector<StringView> lines;
|
||||||
while (it != end)
|
while (it != end)
|
||||||
{
|
{
|
||||||
const CharCategories cat = categorize(*it);
|
const CharCategories cat = categorize(*it, {});
|
||||||
if (cat == CharCategories::EndOfLine)
|
if (cat == CharCategories::EndOfLine)
|
||||||
{
|
{
|
||||||
lines.emplace_back(line_begin.base(), it.base());
|
lines.emplace_back(line_begin.base(), it.base());
|
||||||
|
@ -438,7 +438,7 @@ Vector<StringView> wrap_lines(StringView text, ColumnCount max_width)
|
||||||
}
|
}
|
||||||
|
|
||||||
Utf8It word_end = it+1;
|
Utf8It word_end = it+1;
|
||||||
while (word_end != end and categorize(*word_end) == cat)
|
while (word_end != end and categorize(*word_end, {}) == cat)
|
||||||
++word_end;
|
++word_end;
|
||||||
|
|
||||||
while (word_end > line_begin and
|
while (word_end > line_begin and
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
|
||||||
#include "units.hh"
|
#include "units.hh"
|
||||||
|
#include "array_view.hh"
|
||||||
|
#include "containers.hh"
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
@ -30,13 +32,13 @@ inline bool is_blank(Codepoint c) noexcept
|
||||||
enum WordType { Word, WORD };
|
enum WordType { Word, WORD };
|
||||||
|
|
||||||
template<WordType word_type = Word>
|
template<WordType word_type = Word>
|
||||||
inline bool is_word(Codepoint c) noexcept
|
inline bool is_word(Codepoint c, ConstArrayView<Codepoint> extra_word_chars = {}) noexcept
|
||||||
{
|
{
|
||||||
return c == '_' or iswalnum((wchar_t)c);
|
return c == '_' or iswalnum((wchar_t)c) or contains(extra_word_chars, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool is_word<WORD>(Codepoint c) noexcept
|
inline bool is_word<WORD>(Codepoint c, ConstArrayView<Codepoint>) noexcept
|
||||||
{
|
{
|
||||||
return not is_blank(c);
|
return not is_blank(c);
|
||||||
}
|
}
|
||||||
|
@ -65,13 +67,13 @@ enum class CharCategories
|
||||||
};
|
};
|
||||||
|
|
||||||
template<WordType word_type = Word>
|
template<WordType word_type = Word>
|
||||||
inline CharCategories categorize(Codepoint c) noexcept
|
inline CharCategories categorize(Codepoint c, ConstArrayView<Codepoint> extra_word_chars) noexcept
|
||||||
{
|
{
|
||||||
if (is_eol(c))
|
if (is_eol(c))
|
||||||
return CharCategories::EndOfLine;
|
return CharCategories::EndOfLine;
|
||||||
if (is_horizontal_blank(c))
|
if (is_horizontal_blank(c))
|
||||||
return CharCategories::Blank;
|
return CharCategories::Blank;
|
||||||
if (word_type == WORD or is_word(c))
|
if (word_type == WORD or is_word(c, extra_word_chars))
|
||||||
return CharCategories::Word;
|
return CharCategories::Word;
|
||||||
return CharCategories::Punctuation;
|
return CharCategories::Punctuation;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ static WordList get_words(StringView content, ConstArrayView<Codepoint> extra_wo
|
||||||
|
|
||||||
static ConstArrayView<Codepoint> get_extra_word_chars(const Buffer& buffer)
|
static ConstArrayView<Codepoint> get_extra_word_chars(const Buffer& buffer)
|
||||||
{
|
{
|
||||||
return buffer.options()["completion_extra_word_char"].get<Vector<Codepoint, MemoryDomain::Options>>();
|
return buffer.options()["extra_word_chars"].get<Vector<Codepoint, MemoryDomain::Options>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WordDB::add_words(StringView line)
|
void WordDB::add_words(StringView line)
|
||||||
|
@ -147,7 +147,7 @@ void WordDB::update_db()
|
||||||
|
|
||||||
void WordDB::on_option_changed(const Option& option)
|
void WordDB::on_option_changed(const Option& option)
|
||||||
{
|
{
|
||||||
if (option.name() == "completion_extra_word_char")
|
if (option.name() == "extra_word_chars")
|
||||||
rebuild_db();
|
rebuild_db();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
test/normal/extra-word-chars/cmd
Normal file
1
test/normal/extra-word-chars/cmd
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ww
|
1
test/normal/extra-word-chars/in
Normal file
1
test/normal/extra-word-chars/in
Normal file
|
@ -0,0 +1 @@
|
||||||
|
a-word another'one
|
1
test/normal/extra-word-chars/rc
Normal file
1
test/normal/extra-word-chars/rc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
set buffer extra_word_chars "-:'"
|
1
test/normal/extra-word-chars/selections
Normal file
1
test/normal/extra-word-chars/selections
Normal file
|
@ -0,0 +1 @@
|
||||||
|
another'one
|
Loading…
Reference in New Issue
Block a user