diff --git a/src/normal.cc b/src/normal.cc index bd7d065c..e19f436d 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -687,7 +687,7 @@ void paste_all(Context& context, NormalParams params) selections = std::move(result); } -template +template void regex_prompt(Context& context, String prompt, String default_regex, T func) { DisplayCoord position = context.has_window() ? context.window().position() : DisplayCoord{}; @@ -719,7 +719,7 @@ void regex_prompt(Context& context, String prompt, String default_regex, T func) context.push_jump(); if (not str.empty() or event == PromptEvent::Validate) - func(str.empty() ? Regex{default_regex} : Regex{str}, event, context); + func(Regex{str.empty() ? default_regex : str, RegexCompileFlags::None, direction}, event, context); } catch (regex_error& err) { @@ -739,12 +739,12 @@ void regex_prompt(Context& context, String prompt, String default_regex, T func) }); } -template +template void search(Context& context, NormalParams params) { constexpr StringView prompt = mode == SelectMode::Extend ? - (direction == Forward ? "search (extend):" : "reverse search (extend):") - : (direction == Forward ? "search:" : "reverse search:"); + (direction == MatchDirection::Forward ? "search (extend):" : "reverse search (extend):") + : (direction == MatchDirection::Forward ? "search:" : "reverse search:"); const char reg = to_lower(params.reg ? params.reg : '/'); const int count = params.count; @@ -753,7 +753,7 @@ void search(Context& context, NormalParams params) Vector saved_reg{reg_content.begin(), reg_content.end()}; const int main_index = std::min(context.selections().main_index(), saved_reg.size()-1); - regex_prompt(context, prompt.str(), saved_reg[main_index], + regex_prompt(context, prompt.str(), saved_reg[main_index], [reg, count, saved_reg, main_index] (Regex regex, PromptEvent event, Context& context) { if (event == PromptEvent::Abort) @@ -782,14 +782,14 @@ void search(Context& context, NormalParams params) }); } -template +template void search_next(Context& context, NormalParams params) { const char reg = to_lower(params.reg ? params.reg : '/'); StringView str = context.main_sel_register_value(reg); if (not str.empty()) { - Regex regex{str}; + Regex regex{str, RegexCompileFlags::None, direction}; auto& selections = context.selections(); bool main_wrapped = false; do { @@ -1223,6 +1223,8 @@ void select_object(Context& context, NormalParams params) {{'c'}, "custom object desc"}})); } +enum Direction { Forward, Backward }; + template void scroll(Context& context, NormalParams params) { @@ -2050,14 +2052,14 @@ static const HashMap key { {'m'}, {"select to matching character", select} }, { {'M'}, {"extend to matching character", select} }, - { {'/'}, {"select next given regex match", search} }, - { {'?'}, {"extend with next given regex match", search} }, - { {alt('/')}, {"select previous given regex match", search} }, - { {alt('?')}, {"extend with previous given regex match", search} }, - { {'n'}, {"select next current search pattern match", search_next} }, - { {'N'}, {"extend with next current search pattern match", search_next} }, - { {alt('n')}, {"select previous current search pattern match", search_next} }, - { {alt('N')}, {"extend with previous current search pattern match", search_next} }, + { {'/'}, {"select next given regex match", search} }, + { {'?'}, {"extend with next given regex match", search} }, + { {alt('/')}, {"select previous given regex match", search} }, + { {alt('?')}, {"extend with previous given regex match", search} }, + { {'n'}, {"select next current search pattern match", search_next} }, + { {'N'}, {"extend with next current search pattern match", search_next} }, + { {alt('n')}, {"select previous current search pattern match", search_next} }, + { {alt('N')}, {"extend with previous current search pattern match", search_next} }, { {'*'}, {"set search pattern to main selection content", use_selection_as_search_pattern} }, { {alt('*')}, {"set search pattern to main selection content, do not detect words", use_selection_as_search_pattern} }, diff --git a/src/regex.cc b/src/regex.cc index 8809757a..5f0a5fca 100644 --- a/src/regex.cc +++ b/src/regex.cc @@ -50,13 +50,18 @@ void regex_mismatch(const Regex& re) } #endif -Regex::Regex(StringView re, RegexCompileFlags flags) try - : m_impl{new CompiledRegex{compile_regex(re, flags)}}, +Regex::Regex(StringView re, RegexCompileFlags flags, MatchDirection direction) + : m_impl{new CompiledRegex{compile_regex(re, flags, direction)}}, m_str{re.str()} +{ #ifdef REGEX_CHECK_WITH_BOOST - , m_boost_impl{Utf8It{re.begin(), re}, Utf8It{re.end(), re}, convert_flags(flags)} + if (direction == MatchDirection::Forward) try + { + m_boost_impl.assign({Utf8It{re.begin(), re}, Utf8It{re.end(), re}, convert_flags(flags)}); + } + catch (std::runtime_error& err) { throw regex_error(err.what()); } #endif -{} catch (std::runtime_error& err) { throw regex_error(err.what()); } +} String option_to_string(const Regex& re) { diff --git a/src/regex.hh b/src/regex.hh index d481ec7d..5ec88df9 100644 --- a/src/regex.hh +++ b/src/regex.hh @@ -22,7 +22,8 @@ class Regex public: Regex() = default; - explicit Regex(StringView re, RegexCompileFlags flags = RegexCompileFlags::None); + explicit Regex(StringView re, RegexCompileFlags flags = RegexCompileFlags::None, + MatchDirection direction = MatchDirection::Forward); bool empty() const { return m_str.empty(); } bool operator==(const Regex& other) const { return m_str == other.m_str; } bool operator!=(const Regex& other) const { return m_str != other.m_str; } @@ -225,24 +226,27 @@ bool regex_search(It begin, It end, const Regex& re, } } -template +template bool regex_search(It begin, It end, MatchResults& res, const Regex& re, RegexExecFlags flags = RegexExecFlags::None) { try { Vector captures; - const bool matched = regex_search(begin, end, captures, *re.impl(), flags); + const bool matched = regex_search(begin, end, captures, *re.impl(), flags); #ifdef REGEX_CHECK_WITH_BOOST - auto first = (flags & RegexExecFlags::PrevAvailable) ? begin-1 : begin; - boost::match_results> boost_res; - if (not re.boost_impl().empty() and - matched != boost::regex_search>({begin, first, end}, {end, first, end}, - boost_res, re.boost_impl(), convert_flags(flags))) - regex_mismatch(re); - if (not re.boost_impl().empty() and matched) - check_captures(re, boost_res, captures); + if (direction == MatchDirection::Forward) + { + auto first = (flags & RegexExecFlags::PrevAvailable) ? begin-1 : begin; + boost::match_results> boost_res; + if (not re.boost_impl().empty() and + matched != boost::regex_search>({begin, first, end}, {end, first, end}, + boost_res, re.boost_impl(), convert_flags(flags))) + regex_mismatch(re); + if (not re.boost_impl().empty() and matched) + check_captures(re, boost_res, captures); + } #endif res = matched ? MatchResults{std::move(captures)} : MatchResults{}; diff --git a/src/selectors.cc b/src/selectors.cc index 076edd7b..c7f237c2 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -861,36 +861,25 @@ static bool find_prev(const Buffer& buffer, const BufferIterator& pos, MatchResults& matches, const Regex& ex, bool& wrapped) { - 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)) + if (pos != buffer.begin() and + regex_search( + buffer.begin(), pos, matches, ex, + match_flags(buffer, buffer.begin(), pos))) return true; wrapped = true; - return find_last_match(buffer.end()); + return regex_search( + buffer.begin(), buffer.end(), matches, ex, + match_flags(buffer, buffer.begin(), buffer.end())); } -template +template Selection find_next_match(const Context& context, const Selection& sel, const Regex& regex, bool& wrapped) { auto& buffer = context.buffer(); MatchResults matches; - auto pos = buffer.iterator_at(direction == Backward ? sel.min() : sel.max()); + auto pos = buffer.iterator_at(direction == MatchDirection::Backward ? sel.min() : sel.max()); wrapped = false; - const bool found = (direction == Forward) ? + const bool found = (direction == MatchDirection::Forward) ? find_next(buffer, utf8::next(pos, buffer.end()), matches, regex, wrapped) : find_prev(buffer, pos, matches, regex, wrapped); @@ -903,13 +892,13 @@ Selection find_next_match(const Context& context, const Selection& sel, const Re auto begin = matches[0].first, end = matches[0].second; end = (begin == end) ? end : utf8::previous(end, begin); - if (direction == Backward) + if (direction == MatchDirection::Backward) std::swap(begin, end); return {begin.coord(), end.coord(), std::move(captures)}; } -template Selection find_next_match(const Context&, const Selection&, const Regex&, bool&); -template Selection find_next_match(const Context&, const Selection&, const Regex&, bool&); +template Selection find_next_match(const Context&, const Selection&, const Regex&, bool&); +template Selection find_next_match(const Context&, const Selection&, const Regex&, bool&); using RegexIt = RegexIterator; diff --git a/src/selectors.hh b/src/selectors.hh index e2c3444e..135a99dd 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -98,9 +98,9 @@ trim_partial_lines(const Context& context, const Selection& selection); void select_buffer(SelectionList& selections); -enum Direction { Forward, Backward }; +enum class MatchDirection; -template +template Selection find_next_match(const Context& context, const Selection& sel, const Regex& regex, bool& wrapped);