Improve object selection support
* A count is supported for nestable objects so that we can specify the surrounding level. * more symetric behavior for select to end/ to begin
This commit is contained in:
parent
fccb954611
commit
ab925686ab
|
@ -580,33 +580,48 @@ void deindent(Context& context)
|
||||||
template<ObjectFlags flags>
|
template<ObjectFlags flags>
|
||||||
void select_object(Context& context)
|
void select_object(Context& context)
|
||||||
{
|
{
|
||||||
on_next_key_with_autoinfo(context, [](Key key, Context& context) {
|
int level = context.numeric_param() <= 0 ? 0 : context.numeric_param() - 1;
|
||||||
typedef std::function<Selection (const Buffer&, const Selection&)> Selector;
|
on_next_key_with_autoinfo(context, [level](Key key, Context& context) {
|
||||||
static const std::unordered_map<Key, Selector> key_to_selector =
|
if (key.modifiers != Key::Modifiers::None)
|
||||||
{
|
return;
|
||||||
{ { Key::Modifiers::None, '(' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '(', ')' }, flags) },
|
const Codepoint c = key.key;
|
||||||
{ { Key::Modifiers::None, ')' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '(', ')' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, 'b' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '(', ')' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, '{' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '{', '}' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, '}' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '{', '}' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, 'B' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '{', '}' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, '[' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '[', ']' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, ']' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '[', ']' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, 'r' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '[', ']' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, '<' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '<', '>' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, '>' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '<', '>' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, '"' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '"', '"' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, '\'' }, std::bind(select_surrounding, _1, _2, CodepointPair{ '\'', '\'' }, flags) },
|
|
||||||
{ { Key::Modifiers::None, 'w' }, std::bind(select_whole_word<Word>, _1, _2, flags) },
|
|
||||||
{ { Key::Modifiers::None, 'W' }, std::bind(select_whole_word<WORD>, _1, _2, flags) },
|
|
||||||
{ { Key::Modifiers::None, 's' }, std::bind(select_whole_sentence, _1, _2, flags) },
|
|
||||||
{ { Key::Modifiers::None, 'p' }, std::bind(select_whole_paragraph, _1, _2, flags) },
|
|
||||||
{ { Key::Modifiers::None, 'i' }, std::bind(select_whole_indent, _1, _2, flags) },
|
|
||||||
};
|
|
||||||
|
|
||||||
auto it = key_to_selector.find(key);
|
static constexpr struct
|
||||||
if (it != key_to_selector.end())
|
{
|
||||||
context.editor().select(it->second);
|
Codepoint key;
|
||||||
|
Selection (*func)(const Buffer&, const Selection&, ObjectFlags);
|
||||||
|
} selectors[] = {
|
||||||
|
{ 'w', select_whole_word<Word> },
|
||||||
|
{ 'W', select_whole_word<WORD> },
|
||||||
|
{ 's', select_whole_sentence },
|
||||||
|
{ 'p', select_whole_paragraph },
|
||||||
|
{ 'i', select_whole_indent },
|
||||||
|
};
|
||||||
|
for (auto& sel : selectors)
|
||||||
|
{
|
||||||
|
if (c == sel.key)
|
||||||
|
return context.editor().select(std::bind(sel.func, _1, _2, flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr struct
|
||||||
|
{
|
||||||
|
CodepointPair pair;
|
||||||
|
Codepoint name;
|
||||||
|
} surrounding_pairs[] = {
|
||||||
|
{ { '(', ')' }, 'b' },
|
||||||
|
{ { '{', '}' }, 'B' },
|
||||||
|
{ { '[', ']' }, 'r' },
|
||||||
|
{ { '<', '>' }, '\0' },
|
||||||
|
{ { '"', '"' }, '\0' },
|
||||||
|
{ { '\'', '\'' }, '\0' },
|
||||||
|
};
|
||||||
|
for (auto& sur : surrounding_pairs )
|
||||||
|
{
|
||||||
|
if (sur.pair.first == c or sur.pair.second == c or
|
||||||
|
(sur.name != 0 and sur.name == c))
|
||||||
|
return context.editor().select(std::bind(select_surrounding, _1, _2,
|
||||||
|
sur.pair, level, flags));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"╭──────┤select object├───────╮\n"
|
"╭──────┤select object├───────╮\n"
|
||||||
"│ b,(,): parenthesis block │\n"
|
"│ b,(,): parenthesis block │\n"
|
||||||
|
|
|
@ -227,7 +227,7 @@ using boost::optional;
|
||||||
static optional<Range> find_surrounding(const Buffer& buffer,
|
static optional<Range> find_surrounding(const Buffer& buffer,
|
||||||
BufferCoord coord,
|
BufferCoord coord,
|
||||||
CodepointPair matching,
|
CodepointPair matching,
|
||||||
ObjectFlags flags)
|
ObjectFlags flags, int init_level)
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
@ -236,7 +236,7 @@ static optional<Range> find_surrounding(const Buffer& buffer,
|
||||||
Utf8Iterator first = pos;
|
Utf8Iterator first = pos;
|
||||||
if (to_begin)
|
if (to_begin)
|
||||||
{
|
{
|
||||||
int level = 0;
|
int level = nestable ? init_level : 0;
|
||||||
while (first != buffer.begin())
|
while (first != buffer.begin())
|
||||||
{
|
{
|
||||||
if (nestable and first != pos and *first == matching.second)
|
if (nestable and first != pos and *first == matching.second)
|
||||||
|
@ -257,11 +257,10 @@ static optional<Range> find_surrounding(const Buffer& buffer,
|
||||||
Utf8Iterator last = pos;
|
Utf8Iterator last = pos;
|
||||||
if (to_end)
|
if (to_end)
|
||||||
{
|
{
|
||||||
int level = 0;
|
int level = nestable ? init_level : 0;
|
||||||
last = first + 1;
|
|
||||||
while (last != buffer.end())
|
while (last != buffer.end())
|
||||||
{
|
{
|
||||||
if (nestable and *last == matching.first)
|
if (nestable and last != pos and *last == matching.first)
|
||||||
++level;
|
++level;
|
||||||
else if (*last == matching.second)
|
else if (*last == matching.second)
|
||||||
{
|
{
|
||||||
|
@ -272,13 +271,13 @@ static optional<Range> find_surrounding(const Buffer& buffer,
|
||||||
}
|
}
|
||||||
++last;
|
++last;
|
||||||
}
|
}
|
||||||
if (level != 0 or *last != matching.second)
|
if (level != 0 or last == buffer.end())
|
||||||
return optional<Range>{};
|
return optional<Range>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & ObjectFlags::Inner)
|
if (flags & ObjectFlags::Inner)
|
||||||
{
|
{
|
||||||
if (to_begin)
|
if (to_begin and first != last)
|
||||||
++first;
|
++first;
|
||||||
if (to_end and first != last)
|
if (to_end and first != last)
|
||||||
--last;
|
--last;
|
||||||
|
@ -287,19 +286,32 @@ static optional<Range> find_surrounding(const Buffer& buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
||||||
CodepointPair matching,
|
CodepointPair matching, int level,
|
||||||
ObjectFlags flags)
|
ObjectFlags flags)
|
||||||
{
|
{
|
||||||
auto res = find_surrounding(buffer, selection.last(), matching, flags);
|
const bool nestable = matching.first != matching.second;
|
||||||
|
auto pos = selection.last();
|
||||||
|
if (not nestable or flags & ObjectFlags::Inner)
|
||||||
|
{
|
||||||
|
if (auto res = find_surrounding(buffer, pos, matching, flags, level))
|
||||||
|
return *res;
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto c = buffer.byte_at(pos);
|
||||||
|
if ((flags == ObjectFlags::ToBegin and c == matching.first) or
|
||||||
|
(flags == ObjectFlags::ToEnd and c == matching.second))
|
||||||
|
++level;
|
||||||
|
|
||||||
|
auto res = find_surrounding(buffer, pos, matching, flags, level);
|
||||||
if (not res)
|
if (not res)
|
||||||
return selection;
|
return selection;
|
||||||
|
|
||||||
if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and
|
if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and
|
||||||
matching.first != matching.second and not buffer.is_end(res->last()) and
|
res->min() == selection.min() and res->max() == selection.max())
|
||||||
(*res == selection or Range{res->last(), res->first()} == selection))
|
|
||||||
{
|
{
|
||||||
res = find_surrounding(buffer, buffer.next(res->last()), matching, flags);
|
if (auto res_parent = find_surrounding(buffer, pos, matching, flags, level+1))
|
||||||
return res ? Selection{*res} : selection;
|
return Selection{*res_parent};
|
||||||
}
|
}
|
||||||
return *res;
|
return *res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ SelectionList split_selection(const Buffer& buffer, const Selection& selection,
|
||||||
|
|
||||||
using CodepointPair = std::pair<Codepoint, Codepoint>;
|
using CodepointPair = std::pair<Codepoint, Codepoint>;
|
||||||
Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
||||||
CodepointPair matching, ObjectFlags flags);
|
CodepointPair matching, int level, ObjectFlags flags);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user