StringView based surround selection
This commit is contained in:
parent
f8106690b1
commit
b7530b021a
|
@ -941,23 +941,26 @@ void select_object(Context& context, NormalParams params)
|
||||||
|
|
||||||
static constexpr struct
|
static constexpr struct
|
||||||
{
|
{
|
||||||
MatchingPair pair;
|
StringView opening;
|
||||||
|
StringView closing;
|
||||||
Codepoint name;
|
Codepoint name;
|
||||||
} surrounding_pairs[] = {
|
} surrounding_pairs[] = {
|
||||||
{ { '(', ')' }, 'b' },
|
{ "(", ")", 'b' },
|
||||||
{ { '{', '}' }, 'B' },
|
{ "{", "}", 'B' },
|
||||||
{ { '[', ']' }, 'r' },
|
{ "[", "]", 'r' },
|
||||||
{ { '<', '>' }, 'a' },
|
{ "<", ">", 'a' },
|
||||||
{ { '"', '"' }, 'Q' },
|
{ "\"", "\"", 'Q' },
|
||||||
{ { '\'', '\'' }, 'q' },
|
{ "'", "'", 'q' },
|
||||||
{ { '`', '`' }, 'g' },
|
{ "`", "`", 'g' },
|
||||||
};
|
};
|
||||||
for (auto& sur : surrounding_pairs)
|
for (auto& sur : surrounding_pairs)
|
||||||
{
|
{
|
||||||
if (sur.pair.opening == *cp or sur.pair.closing == *cp or
|
if (sur.opening[0_char] == *cp or
|
||||||
|
sur.closing[0_char] == *cp or
|
||||||
(sur.name != 0 and sur.name == *cp))
|
(sur.name != 0 and sur.name == *cp))
|
||||||
return select<mode>(context, std::bind(select_surrounding, _1, _2,
|
return select<mode>(context, std::bind(select_surrounding, _1, _2,
|
||||||
sur.pair, level, flags));
|
sur.opening, sur.closing,
|
||||||
|
level, flags));
|
||||||
}
|
}
|
||||||
}, "select object",
|
}, "select object",
|
||||||
"b,(,): parenthesis block\n"
|
"b,(,): parenthesis block\n"
|
||||||
|
|
162
src/selectors.cc
162
src/selectors.cc
|
@ -74,100 +74,121 @@ Selection select_matching(const Buffer& buffer, const Selection& selection)
|
||||||
return selection;
|
return selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Iterator, typename Container>
|
||||||
|
Optional<Iterator> find_closing(Iterator pos, Iterator end,
|
||||||
|
Container opening, Container closing,
|
||||||
|
int init_level, bool nestable)
|
||||||
|
{
|
||||||
|
const auto opening_len = opening.end() - opening.begin();
|
||||||
|
const auto closing_len = closing.end() - closing.begin();
|
||||||
|
|
||||||
|
int level = nestable ? init_level : 0;
|
||||||
|
|
||||||
|
if (end - pos >= opening_len and
|
||||||
|
std::equal(opening.begin(), opening.end(), pos))
|
||||||
|
pos += opening_len;
|
||||||
|
|
||||||
|
while (pos != end)
|
||||||
|
{
|
||||||
|
auto close = std::search(pos, end, closing.begin(), closing.end());
|
||||||
|
if (close == end)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (nestable)
|
||||||
|
{
|
||||||
|
for (auto open = pos; open != close; open += opening_len)
|
||||||
|
{
|
||||||
|
open = std::search(open, close, opening.begin(), opening.end());
|
||||||
|
if (open == close)
|
||||||
|
break;
|
||||||
|
++level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = close + closing_len;
|
||||||
|
if (level == 0)
|
||||||
|
return pos-1;
|
||||||
|
--level;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Iterator>
|
template<typename Iterator>
|
||||||
Optional<std::pair<Iterator, Iterator>>
|
Optional<std::pair<Iterator, Iterator>>
|
||||||
find_surrounding(Iterator begin, Iterator end,
|
find_surrounding(Iterator begin, Iterator end,
|
||||||
Iterator pos, MatchingPair matching,
|
Iterator pos, StringView opening, StringView closing,
|
||||||
ObjectFlags flags, int init_level)
|
ObjectFlags flags, int init_level)
|
||||||
{
|
{
|
||||||
using Utf8It = utf8::iterator<Iterator>;
|
|
||||||
|
|
||||||
const bool to_begin = flags & ObjectFlags::ToBegin;
|
const bool to_begin = flags & ObjectFlags::ToBegin;
|
||||||
const bool to_end = flags & ObjectFlags::ToEnd;
|
const bool to_end = flags & ObjectFlags::ToEnd;
|
||||||
const bool nestable = matching.opening != matching.closing;
|
const bool nestable = opening != closing;
|
||||||
Utf8It first{pos, begin, end};
|
|
||||||
|
auto first = pos;
|
||||||
if (to_begin)
|
if (to_begin)
|
||||||
{
|
{
|
||||||
int level = nestable ? init_level : 0;
|
using RevIt = std::reverse_iterator<Iterator>;
|
||||||
while (first != begin)
|
auto res = find_closing(RevIt{pos+1}, RevIt{begin},
|
||||||
{
|
reversed(closing), reversed(opening),
|
||||||
if (nestable and first != pos and *first == matching.closing)
|
init_level, nestable);
|
||||||
++level;
|
if (not res)
|
||||||
else if (*first == matching.opening)
|
|
||||||
{
|
|
||||||
if (level == 0)
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
--level;
|
|
||||||
}
|
|
||||||
--first;
|
|
||||||
}
|
|
||||||
if (level != 0 or *first != matching.opening)
|
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
first = res->base() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utf8It last{pos, begin, end};
|
auto last = pos;
|
||||||
if (to_end)
|
if (to_end)
|
||||||
{
|
{
|
||||||
int level = nestable ? init_level : 0;
|
auto res = find_closing(pos, end, opening, closing,
|
||||||
while (last != end)
|
init_level, nestable);
|
||||||
{
|
if (not res)
|
||||||
if (nestable and last != pos and *last == matching.opening)
|
|
||||||
++level;
|
|
||||||
else if (*last == matching.closing)
|
|
||||||
{
|
|
||||||
if (level == 0)
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
--level;
|
|
||||||
}
|
|
||||||
++last;
|
|
||||||
}
|
|
||||||
if (level != 0 or last == end)
|
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
last = *res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & ObjectFlags::Inner)
|
if (flags & ObjectFlags::Inner)
|
||||||
{
|
{
|
||||||
if (to_begin and first != last)
|
if (to_begin and first != last)
|
||||||
++first;
|
first += (int)opening.length();
|
||||||
if (to_end and first != last)
|
if (to_end and first != last)
|
||||||
--last;
|
last -= (int)closing.length();
|
||||||
}
|
}
|
||||||
return to_end ? std::pair<Iterator, Iterator>{first.base(), last.base()}
|
return to_end ? std::pair<Iterator, Iterator>{first, last}
|
||||||
: std::pair<Iterator, Iterator>{last.base(), first.base()};
|
: std::pair<Iterator, Iterator>{last, first};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container, typename Iterator>
|
template<typename Container, typename Iterator>
|
||||||
Optional<std::pair<Iterator, Iterator>>
|
Optional<std::pair<Iterator, Iterator>>
|
||||||
find_surrounding(const Container& container, Iterator pos,
|
find_surrounding(const Container& container, Iterator pos,
|
||||||
MatchingPair matching, ObjectFlags flags, int init_level)
|
StringView opening, StringView closing,
|
||||||
|
ObjectFlags flags, int init_level)
|
||||||
{
|
{
|
||||||
return find_surrounding(begin(container), end(container), pos,
|
return find_surrounding(begin(container), end(container), pos,
|
||||||
matching, flags, init_level);
|
opening, closing, flags, init_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
||||||
MatchingPair matching, int level,
|
StringView opening, StringView closing, int level,
|
||||||
ObjectFlags flags)
|
ObjectFlags flags)
|
||||||
{
|
{
|
||||||
const bool nestable = matching.opening != matching.closing;
|
const bool nestable = opening != closing;
|
||||||
auto pos = selection.cursor();
|
auto pos = selection.cursor();
|
||||||
if (not nestable or flags & ObjectFlags::Inner)
|
if (not nestable or flags & ObjectFlags::Inner)
|
||||||
{
|
{
|
||||||
if (auto res = find_surrounding(buffer, buffer.iterator_at(pos),
|
if (auto res = find_surrounding(buffer, buffer.iterator_at(pos),
|
||||||
matching, flags, level))
|
opening, closing, flags, level))
|
||||||
return utf8_range(res->first, res->second);
|
return utf8_range(res->first, res->second);
|
||||||
return selection;
|
return selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto c = buffer.byte_at(pos);
|
auto c = buffer.byte_at(pos);
|
||||||
if ((flags == ObjectFlags::ToBegin and c == matching.opening) or
|
if ((flags == ObjectFlags::ToBegin and c == opening) or
|
||||||
(flags == ObjectFlags::ToEnd and c == matching.closing))
|
(flags == ObjectFlags::ToEnd and c == closing))
|
||||||
++level;
|
++level;
|
||||||
|
|
||||||
auto res = find_surrounding(buffer, buffer.iterator_at(pos),
|
auto res = find_surrounding(buffer, buffer.iterator_at(pos),
|
||||||
matching, flags, level);
|
opening, closing, flags, level);
|
||||||
if (not res)
|
if (not res)
|
||||||
return selection;
|
return selection;
|
||||||
|
|
||||||
|
@ -176,9 +197,8 @@ Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
||||||
if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and
|
if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and
|
||||||
sel.min() == selection.min() and sel.max() == selection.max())
|
sel.min() == selection.min() and sel.max() == selection.max())
|
||||||
{
|
{
|
||||||
if (auto res_parent = find_surrounding(buffer.begin(), buffer.end(),
|
if (auto res_parent = find_surrounding(buffer, buffer.iterator_at(pos),
|
||||||
buffer.iterator_at(pos),
|
opening, closing, flags, level+1))
|
||||||
matching, flags, level+1))
|
|
||||||
return utf8_range(res_parent->first, res_parent->second);
|
return utf8_range(res_parent->first, res_parent->second);
|
||||||
}
|
}
|
||||||
return sel;
|
return sel;
|
||||||
|
@ -665,17 +685,49 @@ UnitTest test_find_surrounding{[]()
|
||||||
{
|
{
|
||||||
StringView s("[salut { toi[] }]");
|
StringView s("[salut { toi[] }]");
|
||||||
{
|
{
|
||||||
auto res = find_surrounding(s, s.begin() + 10, { '{', '}' },
|
auto res = find_surrounding(s, s.begin() + 10, '{', '}',
|
||||||
ObjectFlags::ToBegin | ObjectFlags::ToEnd, 0);
|
ObjectFlags::ToBegin | ObjectFlags::ToEnd, 0);
|
||||||
|
|
||||||
kak_assert(res and StringView{res->first COMMA res->second+1} == "{ toi[] }");
|
kak_assert(res and StringView{res->first COMMA res->second+1} == "{ toi[] }");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto res = find_surrounding(s, s.begin() + 10, { '[', ']' },
|
auto res = find_surrounding(s, s.begin() + 10, '[', ']',
|
||||||
ObjectFlags::ToBegin | ObjectFlags::ToEnd | ObjectFlags::Inner, 0);
|
ObjectFlags::ToBegin | ObjectFlags::ToEnd | ObjectFlags::Inner, 0);
|
||||||
|
|
||||||
kak_assert(res and StringView{res->first COMMA res->second+1} == "salut { toi[] }");
|
kak_assert(res and StringView{res->first COMMA res->second+1} == "salut { toi[] }");
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto res = find_surrounding(s, s.begin(), '[', ']',
|
||||||
|
ObjectFlags::ToBegin | ObjectFlags::ToEnd, 0);
|
||||||
|
kak_assert(res and StringView{res->first COMMA res->second+1} == "[salut { toi[] }]");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto res = find_surrounding(s, s.begin()+7, '{', '}',
|
||||||
|
ObjectFlags::ToBegin | ObjectFlags::ToEnd, 0);
|
||||||
|
kak_assert(res and StringView{res->first COMMA res->second+1} == "{ toi[] }");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto res = find_surrounding(s, s.begin() + 12, '[', ']',
|
||||||
|
ObjectFlags::ToBegin | ObjectFlags::ToEnd | ObjectFlags::Inner, 0);
|
||||||
|
kak_assert(res and StringView{res->first COMMA res->second+1} == "]");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto res = find_surrounding(s, s.begin() + 14, '[', ']',
|
||||||
|
ObjectFlags::ToBegin | ObjectFlags::ToEnd, 0);
|
||||||
|
kak_assert(res and StringView{res->first COMMA res->second+1} == "[salut { toi[] }]");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto res = find_surrounding(s, s.begin() + 1, '[', ']', ObjectFlags::ToBegin, 0);
|
||||||
|
kak_assert(res and StringView{res->second COMMA res->first+1} == "[s");
|
||||||
|
}
|
||||||
|
s = "[]";
|
||||||
|
{
|
||||||
|
auto res = find_surrounding(s, s.begin() + 1, '[', ']', ObjectFlags::ToBegin | ObjectFlags::ToEnd, 0);
|
||||||
|
kak_assert(res and StringView{res->first COMMA res->second+1} == "[]");
|
||||||
|
}
|
||||||
|
s = "[*][] hehe";
|
||||||
|
{
|
||||||
|
auto res = find_surrounding(s, s.begin() + 6, '[', ']', ObjectFlags::ToBegin, 0);
|
||||||
|
kak_assert(not res);
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,9 +304,9 @@ Selection find_next_match(const Buffer& buffer, const Selection& sel, const Rege
|
||||||
void select_all_matches(SelectionList& selections, const Regex& regex, unsigned capture = 0);
|
void select_all_matches(SelectionList& selections, const Regex& regex, unsigned capture = 0);
|
||||||
void split_selections(SelectionList& selections, const Regex& separator_regex, unsigned capture = 0);
|
void split_selections(SelectionList& selections, const Regex& separator_regex, unsigned capture = 0);
|
||||||
|
|
||||||
struct MatchingPair { Codepoint opening, closing; };
|
|
||||||
Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
||||||
MatchingPair matching, int level, ObjectFlags flags);
|
StringView opening, StringView closing, int level,
|
||||||
|
ObjectFlags flags);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user