From 3c1f606a2202f0a60e8588c77202d7a81e1cc5b9 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 1 Jan 2017 19:14:18 +0000 Subject: [PATCH] Move some template code out of selectors.hh into selectors.cc --- src/normal.cc | 1 + src/selectors.cc | 233 +++++++++++++++++++++++++++++++++++++++++++++++ src/selectors.hh | 212 +++--------------------------------------- 3 files changed, 246 insertions(+), 200 deletions(-) diff --git a/src/normal.cc b/src/normal.cc index 835a9d32..e558fcf1 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -12,6 +12,7 @@ #include "file.hh" #include "flags.hh" #include "option_manager.hh" +#include "regex.hh" #include "register_manager.hh" #include "selectors.hh" #include "shell_manager.hh" diff --git a/src/selectors.cc b/src/selectors.cc index 3e639ba5..2ef2c2b5 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -1,14 +1,147 @@ #include "selectors.hh" +#include "buffer_utils.hh" #include "optional.hh" +#include "regex.hh" #include "string.hh" +#include "unicode.hh" #include "unit_tests.hh" +#include "utf8_iterator.hh" #include namespace Kakoune { +using Utf8Iterator = utf8::iterator; + +namespace +{ + +Selection target_eol(Selection sel) +{ + sel.cursor().target = INT_MAX; + return sel; +} + +Selection utf8_range(const BufferIterator& first, const BufferIterator& last) +{ + return {first.coord(), last.coord()}; +} + +Selection utf8_range(const Utf8Iterator& first, const Utf8Iterator& last) +{ + return {first.base().coord(), last.base().coord()}; +} + +} + +template +Selection select_to_next_word(const Buffer& buffer, const Selection& selection) +{ + Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer}; + if (begin+1 == buffer.end()) + return selection; + if (categorize(*begin) != categorize(*(begin+1))) + ++begin; + + if (not skip_while(begin, buffer.end(), + [](Codepoint c) { return is_eol(c); })) + return selection; + Utf8Iterator end = begin+1; + + if (word_type == Word and is_punctuation(*begin)) + skip_while(end, buffer.end(), is_punctuation); + else if (is_word(*begin)) + skip_while(end, buffer.end(), is_word); + + skip_while(end, buffer.end(), is_horizontal_blank); + + return utf8_range(begin, end-1); +} +template Selection select_to_next_word(const Buffer&, const Selection&); +template Selection select_to_next_word(const Buffer&, const Selection&); + +template +Selection select_to_next_word_end(const Buffer& buffer, const Selection& selection) +{ + Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer}; + if (begin+1 == buffer.end()) + return selection; + if (categorize(*begin) != categorize(*(begin+1))) + ++begin; + + if (not skip_while(begin, buffer.end(), + [](Codepoint c) { return is_eol(c); })) + return selection; + Utf8Iterator end = begin; + skip_while(end, buffer.end(), is_horizontal_blank); + + if (word_type == Word and is_punctuation(*end)) + skip_while(end, buffer.end(), is_punctuation); + else if (is_word(*end)) + skip_while(end, buffer.end(), is_word); + + return utf8_range(begin, end-1); +} +template Selection select_to_next_word_end(const Buffer&, const Selection&); +template Selection select_to_next_word_end(const Buffer&, const Selection&); + +template +Selection select_to_previous_word(const Buffer& buffer, const Selection& selection) +{ + Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer}; + if (begin == buffer.begin()) + return selection; + if (categorize(*begin) != categorize(*(begin-1))) + --begin; + + skip_while_reverse(begin, buffer.begin(), [](Codepoint c){ return is_eol(c); }); + Utf8Iterator end = begin; + + bool with_end = skip_while_reverse(end, buffer.begin(), is_horizontal_blank); + if (word_type == Word and is_punctuation(*end)) + with_end = skip_while_reverse(end, buffer.begin(), is_punctuation); + + else if (is_word(*end)) + with_end = skip_while_reverse(end, buffer.begin(), is_word); + + return utf8_range(begin, with_end ? end : end+1); +} +template Selection select_to_previous_word(const Buffer&, const Selection&); +template Selection select_to_previous_word(const Buffer&, const Selection&); + +template +Selection select_word(const Buffer& buffer, const Selection& selection, + int count, ObjectFlags flags) +{ + Utf8Iterator first{buffer.iterator_at(selection.cursor()), buffer}; + if (not is_word(*first) and + ((flags & ObjectFlags::Inner) or + not skip_while(first, buffer.end(), [](Codepoint c) + { return not is_word(c); }))) + return selection; + + Utf8Iterator last = first; + if (flags & ObjectFlags::ToBegin) + { + skip_while_reverse(first, buffer.begin(), is_word); + if (not is_word(*first)) + ++first; + } + if (flags & ObjectFlags::ToEnd) + { + skip_while(last, buffer.end(), is_word); + if (not (flags & ObjectFlags::Inner)) + skip_while(last, buffer.end(), is_horizontal_blank); + --last; + } + return (flags & ObjectFlags::ToEnd) ? utf8_range(first, last) + : utf8_range(last, first); +} +template Selection select_word(const Buffer&, const Selection&, int, ObjectFlags); +template Selection select_word(const Buffer&, const Selection&, int, ObjectFlags); + Selection select_line(const Buffer& buffer, const Selection& selection) { Utf8Iterator first{buffer.iterator_at(selection.cursor()), buffer}; @@ -24,6 +157,30 @@ Selection select_line(const Buffer& buffer, const Selection& selection) return target_eol(utf8_range(first, last)); } +template +Selection select_to_line_end(const Buffer& buffer, const Selection& selection) +{ + BufferCoord begin = selection.cursor(); + LineCount line = begin.line; + BufferCoord end = utf8::previous(buffer.iterator_at({line, buffer[line].length() - 1}), + buffer.iterator_at(line)).coord(); + if (end < begin) // Do not go backward when cursor is on eol + end = begin; + return target_eol({only_move ? end : begin, end}); +} +template Selection select_to_line_end(const Buffer&, const Selection&); +template Selection select_to_line_end(const Buffer&, const Selection&); + +template +Selection select_to_line_begin(const Buffer& buffer, const Selection& selection) +{ + BufferCoord begin = selection.cursor(); + BufferCoord end = begin.line; + return {only_move ? end : begin, end}; +} +template Selection select_to_line_begin(const Buffer&, const Selection&); +template Selection select_to_line_begin(const Buffer&, const Selection&); + Selection select_matching(const Buffer& buffer, const Selection& selection) { Vector matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' }; @@ -606,6 +763,82 @@ void select_buffer(SelectionList& selections) selections = SelectionList{ buffer, target_eol({{0,0}, buffer.back_coord()}) }; } +template +bool find_match_in_buffer(const Buffer& buffer, const BufferIterator pos, + MatchResults& matches, + const Regex& ex, bool& wrapped) +{ + wrapped = false; + if (direction == Forward) + { + if (pos != buffer.end() and + regex_search(pos, buffer.end(), matches, ex, + match_flags(is_bol(pos.coord()), true, + is_bow(buffer, pos.coord()), true))) + return true; + wrapped = true; + return regex_search(buffer.begin(), buffer.end(), matches, ex); + } + else + { + auto find_last_match = [&](const BufferIterator& pos) { + MatchResults m; + const bool is_pos_eol = is_eol(buffer, pos.coord()); + const bool is_pos_eow = is_eow(buffer, pos.coord()); + auto begin = buffer.begin(); + while (begin != pos and + regex_search(begin, pos, m, ex, + match_flags(is_bol(begin.coord()), is_pos_eol, + is_bow(buffer, begin.coord()), is_pos_eow))) + { + begin = utf8::next(m[0].first, pos); + if (matches.empty() or m[0].second > matches[0].second) + matches.swap(m); + } + return not matches.empty(); + }; + + if (find_last_match(pos)) + return true; + wrapped = true; + return find_last_match(buffer.end()); + } +} +template bool find_match_in_buffer(const Buffer&, const BufferIterator, + MatchResults&, const Regex&, bool&); +template bool find_match_in_buffer(const Buffer&, const BufferIterator, + MatchResults&, const Regex&, bool&); + +template +Selection find_next_match(const Buffer& buffer, const Selection& sel, const Regex& regex, bool& wrapped) +{ + auto begin = buffer.iterator_at(direction == Backward ? sel.min() : sel.max()); + auto end = begin; + + CaptureList captures; + MatchResults matches; + bool found = false; + auto pos = direction == Forward ? utf8::next(begin, buffer.end()) : begin; + if ((found = find_match_in_buffer(buffer, pos, matches, regex, wrapped))) + { + begin = matches[0].first; + end = matches[0].second; + for (const auto& match : matches) + captures.push_back(buffer.string(match.first.coord(), + match.second.coord())); + } + if (not found or begin == buffer.end()) + throw runtime_error(format("'{}': no matches found", regex.str())); + + end = (begin == end) ? end : utf8::previous(end, begin); + if (direction == Backward) + std::swap(begin, end); + + return {begin.coord(), end.coord(), std::move(captures)}; +} +template Selection find_next_match(const Buffer&, const Selection&, const Regex&, bool&); +template Selection find_next_match(const Buffer&, const Selection&, const Regex&, bool&); + using RegexIt = RegexIterator; void select_all_matches(SelectionList& selections, const Regex& regex, unsigned capture) diff --git a/src/selectors.hh b/src/selectors.hh index 61b01575..0811b8ae 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -3,14 +3,13 @@ #include "flags.hh" #include "selection.hh" -#include "buffer_utils.hh" -#include "unicode.hh" -#include "utf8_iterator.hh" -#include "regex.hh" namespace Kakoune { +class Regex; +template struct MatchResults; + inline Selection keep_direction(Selection res, const Selection& ref) { if ((res.cursor() < res.anchor()) != (ref.cursor() < ref.anchor())) @@ -18,92 +17,14 @@ inline Selection keep_direction(Selection res, const Selection& ref) return res; } -inline Selection target_eol(Selection sel) -{ - sel.cursor().target = INT_MAX; - return sel; -} - -using Utf8Iterator = utf8::iterator; - -inline Selection utf8_range(const BufferIterator& first, const BufferIterator& last) -{ - return {first.coord(), last.coord()}; -} - -inline Selection utf8_range(const Utf8Iterator& first, const Utf8Iterator& last) -{ - return {first.base().coord(), last.base().coord()}; -} +template +Selection select_to_next_word(const Buffer& buffer, const Selection& selection); template -Selection select_to_next_word(const Buffer& buffer, const Selection& selection) -{ - Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer}; - if (begin+1 == buffer.end()) - return selection; - if (categorize(*begin) != categorize(*(begin+1))) - ++begin; - - if (not skip_while(begin, buffer.end(), - [](Codepoint c) { return is_eol(c); })) - return selection; - Utf8Iterator end = begin+1; - - if (word_type == Word and is_punctuation(*begin)) - skip_while(end, buffer.end(), is_punctuation); - else if (is_word(*begin)) - skip_while(end, buffer.end(), is_word); - - skip_while(end, buffer.end(), is_horizontal_blank); - - return utf8_range(begin, end-1); -} +Selection select_to_next_word_end(const Buffer& buffer, const Selection& selection); template -Selection select_to_next_word_end(const Buffer& buffer, const Selection& selection) -{ - Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer}; - if (begin+1 == buffer.end()) - return selection; - if (categorize(*begin) != categorize(*(begin+1))) - ++begin; - - if (not skip_while(begin, buffer.end(), - [](Codepoint c) { return is_eol(c); })) - return selection; - Utf8Iterator end = begin; - skip_while(end, buffer.end(), is_horizontal_blank); - - if (word_type == Word and is_punctuation(*end)) - skip_while(end, buffer.end(), is_punctuation); - else if (is_word(*end)) - skip_while(end, buffer.end(), is_word); - - return utf8_range(begin, end-1); -} - -template -Selection select_to_previous_word(const Buffer& buffer, const Selection& selection) -{ - Utf8Iterator begin{buffer.iterator_at(selection.cursor()), buffer}; - if (begin == buffer.begin()) - return selection; - if (categorize(*begin) != categorize(*(begin-1))) - --begin; - - skip_while_reverse(begin, buffer.begin(), [](Codepoint c){ return is_eol(c); }); - Utf8Iterator end = begin; - - bool with_end = skip_while_reverse(end, buffer.begin(), is_horizontal_blank); - if (word_type == Word and is_punctuation(*end)) - with_end = skip_while_reverse(end, buffer.begin(), is_punctuation); - - else if (is_word(*end)) - with_end = skip_while_reverse(end, buffer.begin(), is_word); - - return utf8_range(begin, with_end ? end : end+1); -} +Selection select_to_previous_word(const Buffer& buffer, const Selection& selection); Selection select_line(const Buffer& buffer, const Selection& selection); Selection select_matching(const Buffer& buffer, const Selection& selection); @@ -114,24 +35,10 @@ Selection select_to_reverse(const Buffer& buffer, const Selection& selection, Codepoint c, int count, bool inclusive); template -Selection select_to_line_end(const Buffer& buffer, const Selection& selection) -{ - BufferCoord begin = selection.cursor(); - LineCount line = begin.line; - BufferCoord end = utf8::previous(buffer.iterator_at({line, buffer[line].length() - 1}), - buffer.iterator_at(line)).coord(); - if (end < begin) // Do not go backward when cursor is on eol - end = begin; - return target_eol({only_move ? end : begin, end}); -} +Selection select_to_line_end(const Buffer& buffer, const Selection& selection); template -Selection select_to_line_begin(const Buffer& buffer, const Selection& selection) -{ - BufferCoord begin = selection.cursor(); - BufferCoord end = begin.line; - return {only_move ? end : begin, end}; -} +Selection select_to_line_begin(const Buffer& buffer, const Selection& selection); enum class ObjectFlags { @@ -144,32 +51,7 @@ template<> struct WithBitOps : std::true_type {}; template Selection select_word(const Buffer& buffer, const Selection& selection, - int count, ObjectFlags flags) -{ - Utf8Iterator first{buffer.iterator_at(selection.cursor()), buffer}; - if (not is_word(*first) and - ((flags & ObjectFlags::Inner) or - not skip_while(first, buffer.end(), [](Codepoint c) - { return not is_word(c); }))) - return selection; - - Utf8Iterator last = first; - if (flags & ObjectFlags::ToBegin) - { - skip_while_reverse(first, buffer.begin(), is_word); - if (not is_word(*first)) - ++first; - } - if (flags & ObjectFlags::ToEnd) - { - skip_while(last, buffer.end(), is_word); - if (not (flags & ObjectFlags::Inner)) - skip_while(last, buffer.end(), is_horizontal_blank); - --last; - } - return (flags & ObjectFlags::ToEnd) ? utf8_range(first, last) - : utf8_range(last, first); -} + int count, ObjectFlags flags); Selection select_number(const Buffer& buffer, const Selection& selection, int count, ObjectFlags flags); @@ -197,83 +79,13 @@ void select_buffer(SelectionList& selections); enum Direction { Forward, Backward }; -inline bool find_last_match(const Buffer& buffer, const BufferIterator& pos, - MatchResults& res, - const Regex& regex) -{ - MatchResults matches; - const bool is_pos_eol = is_eol(buffer, pos.coord()); - const bool is_pos_eow = is_eow(buffer, pos.coord()); - auto begin = buffer.begin(); - while (begin != pos and regex_search(begin, pos, matches, regex, - match_flags(is_bol(begin.coord()), is_pos_eol, - is_bow(buffer, begin.coord()), is_pos_eow))) - { - begin = utf8::next(matches[0].first, pos); - if (res.empty() or matches[0].second > res[0].second) - res.swap(matches); - } - return not res.empty(); -} - template bool find_match_in_buffer(const Buffer& buffer, const BufferIterator pos, MatchResults& matches, - const Regex& ex, bool& wrapped) -{ - wrapped = false; - if (direction == Forward) - { - if (pos != buffer.end() and - regex_search(pos, buffer.end(), matches, ex, - match_flags(is_bol(pos.coord()), true, - is_bow(buffer, pos.coord()), true))) - return true; - wrapped = true; - return regex_search(buffer.begin(), buffer.end(), matches, ex); - } - else - { - if (find_last_match(buffer, pos, matches, ex)) - return true; - wrapped = true; - return find_last_match(buffer, buffer.end(), matches, ex); - } -} - -inline BufferIterator ensure_char_start(const Buffer& buffer, const BufferIterator& it) -{ - return it != buffer.end() ? - utf8::character_start(it, buffer.iterator_at(it.coord().line)) : it; -} + const Regex& ex, bool& wrapped); template -Selection find_next_match(const Buffer& buffer, const Selection& sel, const Regex& regex, bool& wrapped) -{ - auto begin = buffer.iterator_at(direction == Backward ? sel.min() : sel.max()); - auto end = begin; - - CaptureList captures; - MatchResults matches; - bool found = false; - auto pos = direction == Forward ? utf8::next(begin, buffer.end()) : begin; - if ((found = find_match_in_buffer(buffer, pos, matches, regex, wrapped))) - { - begin = matches[0].first; - end = matches[0].second; - for (const auto& match : matches) - captures.push_back(buffer.string(match.first.coord(), - match.second.coord())); - } - if (not found or begin == buffer.end()) - throw runtime_error(format("'{}': no matches found", regex.str())); - - end = (begin == end) ? end : utf8::previous(end, begin); - if (direction == Backward) - std::swap(begin, end); - - return {begin.coord(), end.coord(), std::move(captures)}; -} +Selection find_next_match(const Buffer& buffer, const Selection& sel, const Regex& regex, bool& wrapped); void select_all_matches(SelectionList& selections, const Regex& regex, unsigned capture = 0); void split_selections(SelectionList& selections, const Regex& separator_regex, unsigned capture = 0);