From bba7152063e68801ee9a9d08c47b31216a9ce1fd Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 8 Jan 2013 18:46:45 +0100 Subject: [PATCH] Add backward search support (bound to alt-/) --- src/main.cc | 21 ++++++++++-------- src/selectors.cc | 55 ++++++++++++++++++++++++++++++++++++------------ src/selectors.hh | 4 ++-- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/main.cc b/src/main.cc index 418ead97..fcb6e759 100644 --- a/src/main.cc +++ b/src/main.cc @@ -133,7 +133,7 @@ void do_pipe(Context& context) }, context); } -template +template void do_search(Context& context) { SelectionList selections = context.editor().selections(); @@ -158,7 +158,7 @@ void do_search(Context& context) else if (not context.options()["incsearch"].as_int()) return; - context.editor().select(std::bind(select_next_match, _1, ex), mode); + context.editor().select(std::bind(select_next_match, _1, ex), mode); } catch (runtime_error&) { @@ -172,7 +172,7 @@ void do_search(Context& context) }, context); } -template +template void do_search_next(Context& context) { const String& ex = RegisterManager::instance()['/'].values(context)[0]; @@ -182,7 +182,7 @@ void do_search_next(Context& context) context.push_jump(); int count = context.numeric_param(); do { - context.editor().select(std::bind(select_next_match, _1, ex), mode); + context.editor().select(std::bind(select_next_match, _1, ex), mode); } while (--count > 0); } else @@ -526,11 +526,14 @@ std::unordered_map> keymap = { { Key::Modifiers::None, 'm' }, [](Context& context) { context.editor().select(select_matching); } }, { { Key::Modifiers::None, 'M' }, [](Context& context) { context.editor().select(select_matching, SelectMode::Extend); } }, - { { Key::Modifiers::None, '/' }, do_search }, - { { Key::Modifiers::None, '?' }, do_search }, - { { Key::Modifiers::None, 'n' }, do_search_next }, - { { Key::Modifiers::Alt, 'n' }, do_search_next }, - { { Key::Modifiers::None, 'N' }, do_search_next }, + { { Key::Modifiers::None, '/' }, do_search }, + { { Key::Modifiers::None, '?' }, do_search }, + { { Key::Modifiers::Alt, '/' }, do_search }, + { { Key::Modifiers::Alt, '?' }, do_search }, + + { { Key::Modifiers::None, 'n' }, do_search_next }, + { { Key::Modifiers::Alt, 'n' }, do_search_next }, + { { Key::Modifiers::None, 'N' }, do_search_next }, { { Key::Modifiers::None, 'u' }, repeated([](Context& context) { if (not context.editor().undo()) { context.print_status("nothing left to undo"); } }) }, { { Key::Modifiers::None, 'U' }, repeated([](Context& context) { if (not context.editor().redo()) { context.print_status("nothing left to redo"); } }) }, diff --git a/src/selectors.cc b/src/selectors.cc index c1023594..b9141554 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -385,6 +385,39 @@ Selection select_whole_buffer(const Selection& selection) return Selection(buffer.begin(), utf8::previous(buffer.end())); } +using MatchResults = boost::match_results; + +static bool find_last_match(BufferIterator begin, const BufferIterator& end, + MatchResults& res, const Regex& regex) +{ + MatchResults matches; + while (boost::regex_search(begin, end, matches, regex)) + { + if (begin == matches[0].second) + break; + begin = matches[0].second; + res.swap(matches); + } + return not res.empty(); +} + +template +bool find_match_in_buffer(const BufferIterator pos, MatchResults& matches, + const Regex& ex) +{ + auto bufbeg = pos.buffer().begin(); + auto bufend = pos.buffer().end(); + + if (forward) + return (boost::regex_search(pos, bufend, matches, ex) or + boost::regex_search(bufbeg, pos, matches, ex)); + else + return (find_last_match(bufbeg, pos, matches, ex) or + find_last_match(pos, bufend, matches, ex)); +} + + +template Selection select_next_match(const Selection& selection, const String& regex) { try @@ -395,19 +428,10 @@ Selection select_next_match(const Selection& selection, const String& regex) BufferIterator end = begin; CaptureList captures; - Regex ex(regex.begin(), regex.end()); - boost::match_results matches; + Regex ex{regex.begin(), regex.end()}; + MatchResults matches; - if (boost::regex_search(utf8::next(begin), begin.buffer().end(), - matches, ex)) - { - begin = matches[0].first; - end = matches[0].second; - for (auto& match : matches) - captures.push_back(String(match.first, match.second)); - } - else if (boost::regex_search(begin.buffer().begin(), utf8::next(begin), - matches, ex)) + if (find_match_in_buffer(utf8::next(begin), matches, ex)) { begin = matches[0].first; end = matches[0].second; @@ -420,13 +444,18 @@ Selection select_next_match(const Selection& selection, const String& regex) if (begin == end) ++end; - return Selection(begin, utf8::previous(end), std::move(captures)); + end = utf8::previous(end); + if (not forward) + std::swap(begin, end); + return Selection{begin, end, std::move(captures)}; } catch (boost::regex_error& err) { throw runtime_error(String("regex error: ") + err.what()); } } +template Selection select_next_match(const Selection&, const String&); +template Selection select_next_match(const Selection&, const String&); SelectionList select_all_matches(const Selection& selection, const String& regex) { diff --git a/src/selectors.hh b/src/selectors.hh index 2026cb4d..a591fb4a 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -30,8 +30,8 @@ Selection select_whole_word(const Selection& selection, bool inner); Selection select_whole_lines(const Selection& selection); Selection select_whole_buffer(const Selection& selection); -Selection select_next_match(const Selection& selection, - const String& regex); +template +Selection select_next_match(const Selection& selection, const String& regex); SelectionList select_all_matches(const Selection& selection, const String& regex);