From 64b20729a5e9674834b3e6f7701ddd84226f6530 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Wed, 15 May 2013 14:24:09 +0200 Subject: [PATCH] Full object selection support for paragraphs and sentences --- src/normal.cc | 18 ++-- src/selectors.cc | 209 +++++++++++++++++++++++++++-------------------- src/selectors.hh | 30 +++---- 3 files changed, 146 insertions(+), 111 deletions(-) diff --git a/src/normal.cc b/src/normal.cc index 9bebd2d2..eb43c171 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -501,7 +501,7 @@ void deindent(Context& context) editor.erase(); } -template +template void select_object(Context& context) { context.input_handler().on_next_key( @@ -522,10 +522,10 @@ void select_object(Context& context) { { Key::Modifiers::None, '>' }, std::bind(select_surrounding, _1, CodepointPair{ '<', '>' }, flags) }, { { Key::Modifiers::None, '"' }, std::bind(select_surrounding, _1, CodepointPair{ '"', '"' }, flags) }, { { Key::Modifiers::None, '\'' }, std::bind(select_surrounding, _1, CodepointPair{ '\'', '\'' }, flags) }, - { { Key::Modifiers::None, 'w' }, std::bind(select_whole_word, _1, flags & SurroundFlags::Inner) }, - { { Key::Modifiers::None, 'W' }, std::bind(select_whole_word, _1, flags & SurroundFlags::Inner) }, - { { Key::Modifiers::None, 's' }, std::bind(select_whole_sentence, _1, flags & SurroundFlags::Inner) }, - { { Key::Modifiers::None, 'p' }, std::bind(select_whole_paragraph, _1, flags & SurroundFlags::Inner) }, + { { Key::Modifiers::None, 'w' }, std::bind(select_whole_word, _1, flags) }, + { { Key::Modifiers::None, 'W' }, std::bind(select_whole_word, _1, flags) }, + { { Key::Modifiers::None, 's' }, std::bind(select_whole_sentence, _1, flags) }, + { { Key::Modifiers::None, 'p' }, std::bind(select_whole_paragraph, _1, flags) }, }; auto it = key_to_selector.find(key); @@ -783,10 +783,10 @@ KeyMap keymap = { { Key::Modifiers::None, 'u' }, repeated([](Context& context) { if (not context.editor().undo()) { context.print_status({ "nothing left to undo", get_color("Information") }); } }) }, { { Key::Modifiers::None, 'U' }, repeated([](Context& context) { if (not context.editor().redo()) { context.print_status({ "nothing left to redo", get_color("Information") }); } }) }, - { { Key::Modifiers::Alt, 'i' }, select_object }, - { { Key::Modifiers::Alt, 'a' }, select_object }, - { { Key::Modifiers::None, ']' }, select_object }, - { { Key::Modifiers::None, '[' }, select_object }, + { { Key::Modifiers::Alt, 'i' }, select_object }, + { { Key::Modifiers::Alt, 'a' }, select_object }, + { { Key::Modifiers::None, ']' }, select_object }, + { { Key::Modifiers::None, '[' }, select_object }, { { Key::Modifiers::Alt, 'j' }, join }, { { Key::Modifiers::Alt, 'J' }, join_select_spaces }, diff --git a/src/selectors.cc b/src/selectors.cc index f5095832..818822e6 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -227,10 +227,10 @@ Selection select_matching(const Selection& selection) using boost::optional; static optional find_surrounding(const BufferIterator& pos, const CodepointPair& matching, - SurroundFlags flags) + ObjectFlags flags) { - const bool to_begin = flags & SurroundFlags::ToBegin; - const bool to_end = flags & SurroundFlags::ToEnd; + const bool to_begin = flags & ObjectFlags::ToBegin; + const bool to_end = flags & ObjectFlags::ToEnd; const bool nestable = matching.first != matching.second; Utf8Iterator first = pos; if (to_begin) @@ -275,7 +275,7 @@ static optional find_surrounding(const BufferIterator& pos, return optional{}; } - if (flags & SurroundFlags::Inner) + if (flags & ObjectFlags::Inner) { if (to_begin) ++first; @@ -287,13 +287,13 @@ static optional find_surrounding(const BufferIterator& pos, Selection select_surrounding(const Selection& selection, const CodepointPair& matching, - SurroundFlags flags) + ObjectFlags flags) { auto res = find_surrounding(selection.last(), matching, flags); if (not res) return selection; - if (flags == (SurroundFlags::ToBegin | SurroundFlags::ToEnd) and + if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and matching.first != matching.second and not res->last().is_end() and (*res == selection or Range{res->last(), res->first()} == selection)) { @@ -354,105 +354,140 @@ Selection select_to_eol_reverse(const Selection& selection) } template -Selection select_whole_word(const Selection& selection, bool inner) +Selection select_whole_word(const Selection& selection, ObjectFlags flags) { Utf8Iterator first = selection.last(); Utf8Iterator last = first; - if (is_word(*first)) + if (is_word(*first)) { - skip_while_reverse(first, is_word); - if (not is_word(*first)) - ++first; - skip_while(last, is_word); - if (not inner) + if (flags & ObjectFlags::ToBegin) + { + skip_while_reverse(first, is_word); + if (not is_word(*first)) + ++first; + } + if (flags & ObjectFlags::ToEnd) + { + skip_while(last, is_word); + if (not (flags & ObjectFlags::Inner)) + skip_while(last, is_blank); + --last; + } + } + else if (not (flags & ObjectFlags::Inner)) + { + if (flags & ObjectFlags::ToBegin) + { + skip_while_reverse(first, is_blank); + if (not is_word(*first)) + return selection; + skip_while_reverse(first, is_word); + if (not is_word(*first)) + ++first; + } + if (flags & ObjectFlags::ToEnd) + { skip_while(last, is_blank); + --last; + } } - else if (not inner) - { - skip_while_reverse(first, is_blank); - if (is_blank(*first)) - ++first; - skip_while(last, is_blank); - if (not is_word(*last)) - return selection; - skip_while(last, is_word); - } - --last; - return utf8_range(first, last); + return (flags & ObjectFlags::ToEnd) ? utf8_range(first, last) + : utf8_range(last, first); } -template Selection select_whole_word(const Selection&, bool); -template Selection select_whole_word(const Selection&, bool); +template Selection select_whole_word(const Selection&, ObjectFlags); +template Selection select_whole_word(const Selection&, ObjectFlags); -Selection select_whole_sentence(const Selection& selection, bool inner) +Selection select_whole_sentence(const Selection& selection, ObjectFlags flags) { BufferIterator first = selection.last(); - - while (not is_begin(first)) - { - char cur = *first; - char prev = *(first-1); - if (is_eol(prev) and is_eol(cur)) - { - ++first; - break; - } - else if (prev == '.' or prev == ';') - break; - --first; - } - skip_while(first, is_blank); - BufferIterator last = first; - while (not is_end(last)) - { - char cur = *last; - if (cur == '.' or cur == ';' or cur == '!' or cur == '?' or - (is_eol(cur) and (is_end(last+1) or is_eol(*last+1)))) - break; - ++last; - } - if (not inner and not is_end(last)) - { - ++last; - skip_while(last, is_blank); - --last; - } - return Selection{first, last}; + if (flags & ObjectFlags::ToBegin) + { + bool saw_non_blank = false; + while (not is_begin(first)) + { + char cur = *first; + char prev = *(first-1); + if (not is_blank(cur)) + saw_non_blank = true; + if (is_eol(prev) and is_eol(cur)) + { + ++first; + break; + } + else if (prev == '.' or prev == ';' or prev == '!' or prev == '?') + { + if (saw_non_blank) + break; + else if (flags & ObjectFlags::ToEnd) + last = first-1; + } + --first; + } + skip_while(first, is_blank); + } + if (flags & ObjectFlags::ToEnd) + { + while (not is_end(last)) + { + char cur = *last; + if (cur == '.' or cur == ';' or cur == '!' or cur == '?' or + (is_eol(cur) and (is_end(last+1) or is_eol(*last+1)))) + break; + ++last; + } + if (not (flags & ObjectFlags::Inner) and not is_end(last)) + { + ++last; + skip_while(last, is_blank); + --last; + } + } + return (flags & ObjectFlags::ToEnd) ? Selection{first, last} + : Selection{last, first}; } -Selection select_whole_paragraph(const Selection& selection, bool inner) +Selection select_whole_paragraph(const Selection& selection, ObjectFlags flags) { BufferIterator first = selection.last(); - - while (not is_begin(first)) - { - char cur = *first; - char prev = *(first-1); - if (is_eol(prev) and is_eol(cur)) - { - ++first; - break; - } - --first; - } - BufferIterator last = first; - while (not is_end(last)) - { - char cur = *last; - char prev = *(last-1); - if (is_eol(cur) and is_eol(prev)) - { - if (not inner) - skip_while(last, is_eol); - --last; - break; - } - ++last; - } - return Selection{first, last}; + if (flags & ObjectFlags::ToBegin and not is_begin(first)) + { + skip_while_reverse(first, is_eol); + if (flags & ObjectFlags::ToEnd) + last = first; + while (not is_begin(first)) + { + char cur = *first; + char prev = *(first-1); + if (is_eol(prev) and is_eol(cur)) + { + ++first; + break; + } + --first; + } + } + if (flags & ObjectFlags::ToEnd) + { + while (not is_end(last)) + { + char cur = *last; + char prev = *(last-1); + if (is_eol(cur) and is_eol(prev)) + { + if (not (flags & ObjectFlags::Inner)) + skip_while(last, is_eol); + --last; + break; + } + ++last; + } + } + return (flags & ObjectFlags::ToEnd) ? Selection{first, last} + : Selection{last, first}; } Selection select_whole_lines(const Selection& selection) diff --git a/src/selectors.hh b/src/selectors.hh index f58dfc07..b7406894 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -25,10 +25,21 @@ Selection select_to_reverse(const Selection& selection, Selection select_to_eol(const Selection& selection); Selection select_to_eol_reverse(const Selection& selection); +enum class ObjectFlags +{ + ToBegin = 1, + ToEnd = 2, + Inner = 4 +}; +constexpr bool operator&(ObjectFlags lhs, ObjectFlags rhs) +{ return (bool)((int)lhs & (int) rhs); } +constexpr ObjectFlags operator|(ObjectFlags lhs, ObjectFlags rhs) +{ return (ObjectFlags)((int)lhs | (int) rhs); } + template -Selection select_whole_word(const Selection& selection, bool inner); -Selection select_whole_sentence(const Selection& selection, bool inner); -Selection select_whole_paragraph(const Selection& selection, bool inner); +Selection select_whole_word(const Selection& selection, ObjectFlags flags); +Selection select_whole_sentence(const Selection& selection, ObjectFlags flags); +Selection select_whole_paragraph(const Selection& selection, ObjectFlags flags); Selection select_whole_lines(const Selection& selection); Selection select_whole_buffer(const Selection& selection); Selection trim_partial_lines(const Selection& selection); @@ -42,21 +53,10 @@ SelectionList select_all_matches(const Selection& selection, SelectionList split_selection(const Selection& selection, const Regex& separator_regex); -enum class SurroundFlags -{ - ToBegin = 1, - ToEnd = 2, - Inner = 4 -}; -constexpr bool operator&(SurroundFlags lhs, SurroundFlags rhs) -{ return (bool)((int)lhs & (int) rhs); } -constexpr SurroundFlags operator|(SurroundFlags lhs, SurroundFlags rhs) -{ return (SurroundFlags)((int)lhs | (int) rhs); } - using CodepointPair = std::pair; Selection select_surrounding(const Selection& selection, const CodepointPair& matching, - SurroundFlags flags); + ObjectFlags flags); }