refactor regex based selectors, move regex construction out of them
This commit is contained in:
parent
e3f097ad40
commit
1ab8120147
105
src/main.cc
105
src/main.cc
|
@ -116,7 +116,7 @@ void do_replace_with_char(Context& context)
|
||||||
Editor& editor = context.editor();
|
Editor& editor = context.editor();
|
||||||
SelectionList sels = editor.selections();
|
SelectionList sels = editor.selections();
|
||||||
auto restore_sels = on_scope_end([&]{ editor.select(std::move(sels)); });
|
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);
|
editor.insert(codepoint_to_str(key.key), InsertMode::Replace);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -184,20 +184,25 @@ void do_search(Context& context)
|
||||||
if (event == PromptEvent::Abort)
|
if (event == PromptEvent::Abort)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
String ex = str;
|
Regex ex{str};
|
||||||
if (event == PromptEvent::Validate)
|
if (event == PromptEvent::Validate)
|
||||||
{
|
{
|
||||||
if (ex.empty())
|
if (str.empty())
|
||||||
ex = RegisterManager::instance()['/'].values(context)[0];
|
ex = Regex{RegisterManager::instance()['/'].values(context)[0]};
|
||||||
else
|
else
|
||||||
RegisterManager::instance()['/'] = ex;
|
RegisterManager::instance()['/'] = str;
|
||||||
context.push_jump();
|
context.push_jump();
|
||||||
}
|
}
|
||||||
else if (ex.empty() or not context.options()["incsearch"].get<bool>())
|
else if (str.empty() or not context.options()["incsearch"].get<bool>())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
context.editor().select(std::bind(select_next_match<forward>, _1, ex), mode);
|
context.editor().select(std::bind(select_next_match<forward>, _1, ex), mode);
|
||||||
}
|
}
|
||||||
|
catch (boost::regex_error& err)
|
||||||
|
{
|
||||||
|
if (event == PromptEvent::Validate)
|
||||||
|
throw runtime_error("regex error: "_str + err.what());
|
||||||
|
}
|
||||||
catch (runtime_error&)
|
catch (runtime_error&)
|
||||||
{
|
{
|
||||||
context.editor().select(selections);
|
context.editor().select(selections);
|
||||||
|
@ -206,25 +211,32 @@ void do_search(Context& context)
|
||||||
if (event == PromptEvent::Validate)
|
if (event == PromptEvent::Validate)
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<SelectMode mode, bool forward>
|
template<SelectMode mode, bool forward>
|
||||||
void do_search_next(Context& context)
|
void do_search_next(Context& context)
|
||||||
{
|
{
|
||||||
const String& ex = RegisterManager::instance()['/'].values(context)[0];
|
const String& str = RegisterManager::instance()['/'].values(context)[0];
|
||||||
if (not ex.empty())
|
if (not str.empty())
|
||||||
{
|
{
|
||||||
if (mode == SelectMode::Replace)
|
try
|
||||||
context.push_jump();
|
{
|
||||||
int count = context.numeric_param();
|
Regex ex{str};
|
||||||
do {
|
if (mode == SelectMode::Replace)
|
||||||
context.editor().select(std::bind(select_next_match<forward>, _1, ex), mode);
|
context.push_jump();
|
||||||
} while (--count > 0);
|
int count = context.numeric_param();
|
||||||
|
do {
|
||||||
|
context.editor().select(std::bind(select_next_match<forward>, _1, ex), mode);
|
||||||
|
} while (--count > 0);
|
||||||
|
}
|
||||||
|
catch (boost::regex_error& err)
|
||||||
|
{
|
||||||
|
throw runtime_error("regex error: "_str + err.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
context.print_status({ "no search pattern", get_color("Error") });
|
throw runtime_error("no search pattern");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool smart>
|
template<bool smart>
|
||||||
|
@ -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,
|
context.input_handler().prompt(prompt, get_color("Prompt"), complete_nothing,
|
||||||
[=](const String& str, PromptEvent event, Context& context) {
|
[=](const String& str, PromptEvent event, Context& context) {
|
||||||
if (event == PromptEvent::Validate)
|
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)
|
else if (event == PromptEvent::Change)
|
||||||
{
|
{
|
||||||
const bool ok = Regex{str, boost::regex_constants::no_except}.status() == 0;
|
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)
|
void do_select_regex(Context& context)
|
||||||
{
|
{
|
||||||
regex_prompt(context, "select: ", [](const String& str, Context& context) {
|
regex_prompt(context, "select: ", [](Regex ex, Context& context) {
|
||||||
String ex = str;
|
|
||||||
if (ex.empty())
|
if (ex.empty())
|
||||||
ex = RegisterManager::instance()['/'].values(context)[0];
|
ex = Regex{RegisterManager::instance()['/'].values(context)[0]};
|
||||||
else
|
else
|
||||||
RegisterManager::instance()['/'] = ex;
|
RegisterManager::instance()['/'] = String{ex.str()};
|
||||||
if (not ex.empty())
|
if (not ex.empty())
|
||||||
context.editor().multi_select(std::bind(select_all_matches, _1, ex));
|
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)
|
void do_split_regex(Context& context)
|
||||||
{
|
{
|
||||||
regex_prompt(context, "split: ", [](const String& str, Context& context) {
|
regex_prompt(context, "split: ", [](Regex ex, Context& context) {
|
||||||
String ex = str;
|
|
||||||
if (ex.empty())
|
if (ex.empty())
|
||||||
ex = RegisterManager::instance()['/'].values(context)[0];
|
ex = Regex{RegisterManager::instance()['/'].values(context)[0]};
|
||||||
else
|
else
|
||||||
RegisterManager::instance()['/'] = ex;
|
RegisterManager::instance()['/'] = String{ex.str()};
|
||||||
if (not ex.empty())
|
if (not ex.empty())
|
||||||
context.editor().multi_select(std::bind(split_selection, _1, ex));
|
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)
|
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)
|
void do_join(Context& context)
|
||||||
|
@ -367,7 +386,7 @@ void do_join(Context& context)
|
||||||
editor.select(select_to_eol, SelectMode::Extend);
|
editor.select(select_to_eol, SelectMode::Extend);
|
||||||
editor.multi_select([](const Selection& sel)
|
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
|
// remove last end of line if selected
|
||||||
assert(std::is_sorted(res.begin(), res.end(),
|
assert(std::is_sorted(res.begin(), res.end(),
|
||||||
[](const Selection& lhs, const Selection& rhs)
|
[](const Selection& lhs, const Selection& rhs)
|
||||||
|
@ -383,26 +402,18 @@ template<bool matching>
|
||||||
void do_keep(Context& context)
|
void do_keep(Context& context)
|
||||||
{
|
{
|
||||||
constexpr const char* prompt = matching ? "keep matching: " : "keep not matching: ";
|
constexpr const char* prompt = matching ? "keep matching: " : "keep not matching: ";
|
||||||
regex_prompt(context, prompt, [](const String& str, Context& context) {
|
regex_prompt(context, prompt, [](const Regex& ex, Context& context) {
|
||||||
try
|
Editor& editor = context.editor();
|
||||||
|
SelectionList sels = editor.selections();
|
||||||
|
SelectionList keep;
|
||||||
|
for (auto& sel : sels)
|
||||||
{
|
{
|
||||||
Regex re(str);
|
if (boost::regex_search(sel.begin(), sel.end(), ex) == matching)
|
||||||
Editor& editor = context.editor();
|
keep.push_back(sel);
|
||||||
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 (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()};
|
DynamicSelectionList sels{editor.buffer(), editor.selections()};
|
||||||
auto restore_sels = on_scope_end([&]{ editor.select((SelectionList)std::move(sels)); });
|
auto restore_sels = on_scope_end([&]{ editor.select((SelectionList)std::move(sels)); });
|
||||||
editor.select(select_whole_lines);
|
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);
|
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)); });
|
auto restore_sels = on_scope_end([&]{ editor.select((SelectionList)std::move(sels)); });
|
||||||
editor.select(select_whole_lines);
|
editor.select(select_whole_lines);
|
||||||
editor.multi_select(std::bind(select_all_matches, _1,
|
editor.multi_select(std::bind(select_all_matches, _1,
|
||||||
"^\\h{1," + int_to_str(width) + "}"));
|
Regex{"^\\h{1," + int_to_str(width) + "}"}));
|
||||||
editor.erase();
|
editor.erase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
136
src/selectors.cc
136
src/selectors.cc
|
@ -419,104 +419,80 @@ bool find_match_in_buffer(const BufferIterator pos, MatchResults& matches,
|
||||||
|
|
||||||
|
|
||||||
template<bool forward>
|
template<bool forward>
|
||||||
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<forward>(utf8::next(begin), matches, regex))
|
||||||
{
|
{
|
||||||
// regex matching do not use Utf8Iterator as boost::regex handle utf8
|
begin = matches[0].first;
|
||||||
// decoding itself
|
end = matches[0].second;
|
||||||
BufferIterator begin = selection.last();
|
for (auto& match : matches)
|
||||||
BufferIterator end = begin;
|
captures.push_back(String(match.first, match.second));
|
||||||
CaptureList captures;
|
|
||||||
|
|
||||||
Regex ex{regex.begin(), regex.end()};
|
|
||||||
MatchResults matches;
|
|
||||||
|
|
||||||
if (find_match_in_buffer<forward>(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());
|
|
||||||
}
|
}
|
||||||
|
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<true>(const Selection&, const String&);
|
template Selection select_next_match<true>(const Selection&, const Regex&);
|
||||||
template Selection select_next_match<false>(const Selection&, const String&);
|
template Selection select_next_match<false>(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());
|
BufferIterator begin = (*re_it)[0].first;
|
||||||
RegexIterator re_it(selection.begin(), selection.end(), ex);
|
BufferIterator end = (*re_it)[0].second;
|
||||||
RegexIterator re_end;
|
|
||||||
|
|
||||||
SelectionList result;
|
if (begin == selection.end())
|
||||||
for (; re_it != re_end; ++re_it)
|
continue;
|
||||||
{
|
|
||||||
BufferIterator begin = (*re_it)[0].first;
|
|
||||||
BufferIterator end = (*re_it)[0].second;
|
|
||||||
|
|
||||||
if (begin == selection.end())
|
CaptureList captures;
|
||||||
continue;
|
for (auto& match : *re_it)
|
||||||
|
captures.push_back(String(match.first, match.second));
|
||||||
|
|
||||||
CaptureList captures;
|
result.push_back(Selection(begin, begin == end ? end : utf8::previous(end),
|
||||||
for (auto& match : *re_it)
|
std::move(captures)));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionList split_selection(const Selection& selection,
|
SelectionList split_selection(const Selection& selection,
|
||||||
const String& separator_regex)
|
const Regex& regex)
|
||||||
{
|
{
|
||||||
try
|
RegexIterator re_it(selection.begin(), selection.end(), regex,
|
||||||
{
|
boost::regex_constants::match_nosubs);
|
||||||
Regex ex(separator_regex.begin(), separator_regex.end());
|
RegexIterator re_end;
|
||||||
RegexIterator re_it(selection.begin(), selection.end(), ex,
|
|
||||||
boost::regex_constants::match_nosubs);
|
|
||||||
RegexIterator re_end;
|
|
||||||
|
|
||||||
SelectionList result;
|
SelectionList result;
|
||||||
BufferIterator begin = selection.begin();
|
BufferIterator begin = selection.begin();
|
||||||
for (; re_it != re_end; ++re_it)
|
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)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,13 @@ Selection select_whole_lines(const Selection& selection);
|
||||||
Selection select_whole_buffer(const Selection& selection);
|
Selection select_whole_buffer(const Selection& selection);
|
||||||
|
|
||||||
template<bool forward>
|
template<bool forward>
|
||||||
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,
|
SelectionList select_all_matches(const Selection& selection,
|
||||||
const String& regex);
|
const Regex& regex);
|
||||||
|
|
||||||
SelectionList split_selection(const Selection& selection,
|
SelectionList split_selection(const Selection& selection,
|
||||||
const String& separator_regex);
|
const Regex& separator_regex);
|
||||||
|
|
||||||
enum class SurroundFlags
|
enum class SurroundFlags
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,7 +58,7 @@ void test_editor()
|
||||||
{
|
{
|
||||||
scoped_edition edition{editor};
|
scoped_edition edition{editor};
|
||||||
editor.select(select_whole_buffer);
|
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())
|
for (auto& sel : editor.selections())
|
||||||
{
|
{
|
||||||
assert(*sel.begin() == '\n');
|
assert(*sel.begin() == '\n');
|
||||||
|
|
Loading…
Reference in New Issue
Block a user