StringView based surround selection

This commit is contained in:
Maxime Coste 2016-01-27 08:28:25 +00:00
parent f8106690b1
commit b7530b021a
3 changed files with 122 additions and 67 deletions

View File

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

View File

@ -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);
}
}}; }};
} }

View File

@ -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);
} }