diff --git a/src/main.cc b/src/main.cc index aa694a1a..aaa85d76 100644 --- a/src/main.cc +++ b/src/main.cc @@ -116,7 +116,7 @@ void do_replace_with_char(Context& context) Editor& editor = context.editor(); SelectionList sels = editor.selections(); auto restore_sels = on_scope_end([&]{ editor.select(std::move(sels)); }); - editor.multi_select(std::bind(select_all_matches, _1, ".")); + editor.multi_select(std::bind(select_all_matches, _1, Regex{"."})); editor.insert(codepoint_to_str(key.key), InsertMode::Replace); }); } @@ -184,20 +184,25 @@ void do_search(Context& context) if (event == PromptEvent::Abort) return; - String ex = str; + Regex ex{str}; if (event == PromptEvent::Validate) { - if (ex.empty()) - ex = RegisterManager::instance()['/'].values(context)[0]; + if (str.empty()) + ex = Regex{RegisterManager::instance()['/'].values(context)[0]}; else - RegisterManager::instance()['/'] = ex; + RegisterManager::instance()['/'] = str; context.push_jump(); } - else if (ex.empty() or not context.options()["incsearch"].get()) + else if (str.empty() or not context.options()["incsearch"].get()) return; context.editor().select(std::bind(select_next_match, _1, ex), mode); } + catch (boost::regex_error& err) + { + if (event == PromptEvent::Validate) + throw runtime_error("regex error: "_str + err.what()); + } catch (runtime_error&) { context.editor().select(selections); @@ -206,25 +211,32 @@ void do_search(Context& context) if (event == PromptEvent::Validate) throw; } - }); } template void do_search_next(Context& context) { - const String& ex = RegisterManager::instance()['/'].values(context)[0]; - if (not ex.empty()) + const String& str = RegisterManager::instance()['/'].values(context)[0]; + if (not str.empty()) { - if (mode == SelectMode::Replace) - context.push_jump(); - int count = context.numeric_param(); - do { - context.editor().select(std::bind(select_next_match, _1, ex), mode); - } while (--count > 0); + try + { + Regex ex{str}; + if (mode == SelectMode::Replace) + context.push_jump(); + int count = context.numeric_param(); + do { + context.editor().select(std::bind(select_next_match, _1, ex), mode); + } while (--count > 0); + } + catch (boost::regex_error& err) + { + throw runtime_error("regex error: "_str + err.what()); + } } else - context.print_status({ "no search pattern", get_color("Error") }); + throw runtime_error("no search pattern"); } template @@ -318,7 +330,16 @@ void regex_prompt(Context& context, const String prompt, T on_validate) context.input_handler().prompt(prompt, get_color("Prompt"), complete_nothing, [=](const String& str, PromptEvent event, Context& context) { if (event == PromptEvent::Validate) - on_validate(str, context); + { + try + { + on_validate(Regex{str}, context); + } + catch (boost::regex_error& err) + { + throw runtime_error("regex error: "_str + err.what()); + } + } else if (event == PromptEvent::Change) { const bool ok = Regex{str, boost::regex_constants::no_except}.status() == 0; @@ -329,12 +350,11 @@ void regex_prompt(Context& context, const String prompt, T on_validate) void do_select_regex(Context& context) { - regex_prompt(context, "select: ", [](const String& str, Context& context) { - String ex = str; + regex_prompt(context, "select: ", [](Regex ex, Context& context) { if (ex.empty()) - ex = RegisterManager::instance()['/'].values(context)[0]; + ex = Regex{RegisterManager::instance()['/'].values(context)[0]}; else - RegisterManager::instance()['/'] = ex; + RegisterManager::instance()['/'] = String{ex.str()}; if (not ex.empty()) context.editor().multi_select(std::bind(select_all_matches, _1, ex)); }); @@ -342,12 +362,11 @@ void do_select_regex(Context& context) void do_split_regex(Context& context) { - regex_prompt(context, "split: ", [](const String& str, Context& context) { - String ex = str; + regex_prompt(context, "split: ", [](Regex ex, Context& context) { if (ex.empty()) - ex = RegisterManager::instance()['/'].values(context)[0]; + ex = Regex{RegisterManager::instance()['/'].values(context)[0]}; else - RegisterManager::instance()['/'] = ex; + RegisterManager::instance()['/'] = String{ex.str()}; if (not ex.empty()) context.editor().multi_select(std::bind(split_selection, _1, ex)); }); @@ -355,7 +374,7 @@ void do_split_regex(Context& context) void do_split_lines(Context& context) { - context.editor().multi_select(std::bind(split_selection, _1, "^")); + context.editor().multi_select(std::bind(split_selection, _1, Regex{"^"})); } void do_join(Context& context) @@ -367,7 +386,7 @@ void do_join(Context& context) editor.select(select_to_eol, SelectMode::Extend); editor.multi_select([](const Selection& sel) { - SelectionList res = select_all_matches(sel, "\n\\h*"); + SelectionList res = select_all_matches(sel, Regex{"\n\\h*"}); // remove last end of line if selected assert(std::is_sorted(res.begin(), res.end(), [](const Selection& lhs, const Selection& rhs) @@ -383,26 +402,18 @@ template void do_keep(Context& context) { constexpr const char* prompt = matching ? "keep matching: " : "keep not matching: "; - regex_prompt(context, prompt, [](const String& str, Context& context) { - try + regex_prompt(context, prompt, [](const Regex& ex, Context& context) { + Editor& editor = context.editor(); + SelectionList sels = editor.selections(); + SelectionList keep; + for (auto& sel : sels) { - Regex re(str); - Editor& editor = context.editor(); - SelectionList sels = editor.selections(); - SelectionList keep; - for (auto& sel : sels) - { - if (boost::regex_search(sel.begin(), sel.end(), re) == matching) - keep.push_back(sel); - } - if (keep.empty()) - throw runtime_error("no selections remaining"); - editor.select(std::move(keep)); - } - catch (boost::regex_error& error) - { - throw runtime_error("regex_error: "_str + error.what()); + if (boost::regex_search(sel.begin(), sel.end(), ex) == matching) + keep.push_back(sel); } + if (keep.empty()) + throw runtime_error("no selections remaining"); + editor.select(std::move(keep)); }); } @@ -415,7 +426,7 @@ void do_indent(Context& context) DynamicSelectionList sels{editor.buffer(), editor.selections()}; auto restore_sels = on_scope_end([&]{ editor.select((SelectionList)std::move(sels)); }); editor.select(select_whole_lines); - editor.multi_select(std::bind(select_all_matches, _1, "^[^\n]")); + editor.multi_select(std::bind(select_all_matches, _1, Regex{"^[^\n]"})); editor.insert(indent, InsertMode::Insert); } @@ -427,7 +438,7 @@ void do_deindent(Context& context) auto restore_sels = on_scope_end([&]{ editor.select((SelectionList)std::move(sels)); }); editor.select(select_whole_lines); editor.multi_select(std::bind(select_all_matches, _1, - "^\\h{1," + int_to_str(width) + "}")); + Regex{"^\\h{1," + int_to_str(width) + "}"})); editor.erase(); } diff --git a/src/selectors.cc b/src/selectors.cc index 77102d59..7fa9bd56 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -419,104 +419,80 @@ bool find_match_in_buffer(const BufferIterator pos, MatchResults& matches, template -Selection select_next_match(const Selection& selection, const String& regex) +Selection select_next_match(const Selection& selection, const Regex& regex) { - try + // regex matching do not use Utf8Iterator as boost::regex handle utf8 + // decoding itself + BufferIterator begin = selection.last(); + BufferIterator end = begin; + CaptureList captures; + + MatchResults matches; + + if (find_match_in_buffer(utf8::next(begin), matches, regex)) { - // regex matching do not use Utf8Iterator as boost::regex handle utf8 - // decoding itself - BufferIterator begin = selection.last(); - BufferIterator end = begin; - CaptureList captures; - - Regex ex{regex.begin(), regex.end()}; - MatchResults matches; - - if (find_match_in_buffer(utf8::next(begin), matches, ex)) - { - begin = matches[0].first; - end = matches[0].second; - for (auto& match : matches) - captures.push_back(String(match.first, match.second)); - } - else - throw runtime_error("'" + regex + "': no matches found"); - - if (begin == end) - ++end; - - 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()); + begin = matches[0].first; + end = matches[0].second; + for (auto& match : matches) + captures.push_back(String(match.first, match.second)); } + else + throw runtime_error("'" + regex.str() + "': no matches found"); + + if (begin == end) + ++end; + + end = utf8::previous(end); + if (not forward) + std::swap(begin, end); + return Selection{begin, end, std::move(captures)}; } -template Selection select_next_match(const Selection&, const String&); -template Selection select_next_match(const Selection&, const String&); +template Selection select_next_match(const Selection&, const Regex&); +template Selection select_next_match(const Selection&, const Regex&); -SelectionList select_all_matches(const Selection& selection, const String& regex) +SelectionList select_all_matches(const Selection& selection, const Regex& regex) { - try + RegexIterator re_it(selection.begin(), selection.end(), regex); + RegexIterator re_end; + + SelectionList result; + for (; re_it != re_end; ++re_it) { - Regex ex(regex.begin(), regex.end()); - RegexIterator re_it(selection.begin(), selection.end(), ex); - RegexIterator re_end; + BufferIterator begin = (*re_it)[0].first; + BufferIterator end = (*re_it)[0].second; - SelectionList result; - for (; re_it != re_end; ++re_it) - { - BufferIterator begin = (*re_it)[0].first; - BufferIterator end = (*re_it)[0].second; + if (begin == selection.end()) + continue; - if (begin == selection.end()) - continue; + CaptureList captures; + for (auto& match : *re_it) + captures.push_back(String(match.first, match.second)); - CaptureList captures; - for (auto& match : *re_it) - captures.push_back(String(match.first, match.second)); - - result.push_back(Selection(begin, begin == end ? end : utf8::previous(end), - std::move(captures))); - } - return result; - } - catch (boost::regex_error& err) - { - throw runtime_error(String("regex error: ") + err.what()); + result.push_back(Selection(begin, begin == end ? end : utf8::previous(end), + std::move(captures))); } + return result; } SelectionList split_selection(const Selection& selection, - const String& separator_regex) + const Regex& regex) { - try - { - Regex ex(separator_regex.begin(), separator_regex.end()); - RegexIterator re_it(selection.begin(), selection.end(), ex, - boost::regex_constants::match_nosubs); - RegexIterator re_end; + RegexIterator re_it(selection.begin(), selection.end(), regex, + boost::regex_constants::match_nosubs); + RegexIterator re_end; - SelectionList result; - BufferIterator begin = selection.begin(); - for (; re_it != re_end; ++re_it) - { - BufferIterator end = (*re_it)[0].first; - - result.push_back(Selection(begin, (begin == end) ? end : utf8::previous(end))); - begin = (*re_it)[0].second; - } - result.push_back(Selection(begin, std::max(selection.first(), - selection.last()))); - return result; - } - catch (boost::regex_error& err) + SelectionList result; + BufferIterator begin = selection.begin(); + for (; re_it != re_end; ++re_it) { - throw runtime_error(String("regex error: ") + err.what()); + BufferIterator end = (*re_it)[0].first; + + result.push_back(Selection(begin, (begin == end) ? end : utf8::previous(end))); + begin = (*re_it)[0].second; } + result.push_back(Selection(begin, std::max(selection.first(), + selection.last()))); + return result; } } diff --git a/src/selectors.hh b/src/selectors.hh index a591fb4a..9c87c73c 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -31,13 +31,13 @@ Selection select_whole_lines(const Selection& selection); Selection select_whole_buffer(const Selection& selection); template -Selection select_next_match(const Selection& selection, const String& regex); +Selection select_next_match(const Selection& selection, const Regex& regex); SelectionList select_all_matches(const Selection& selection, - const String& regex); + const Regex& regex); SelectionList split_selection(const Selection& selection, - const String& separator_regex); + const Regex& separator_regex); enum class SurroundFlags { diff --git a/src/unit_tests.cc b/src/unit_tests.cc index 913693f2..b4bbead8 100644 --- a/src/unit_tests.cc +++ b/src/unit_tests.cc @@ -58,7 +58,7 @@ void test_editor() { scoped_edition edition{editor}; editor.select(select_whole_buffer); - editor.multi_select(std::bind(select_all_matches, std::placeholders::_1, "\\n\\h*")); + editor.multi_select(std::bind(select_all_matches, std::placeholders::_1, Regex{"\\n\\h*"})); for (auto& sel : editor.selections()) { assert(*sel.begin() == '\n');