MultiSelectors now transforms the whole selection

This commit is contained in:
Maxime Coste 2013-12-12 23:56:53 +00:00
parent 894ee0297e
commit db048a0792
5 changed files with 154 additions and 147 deletions

View File

@ -282,26 +282,7 @@ struct nothing_selected : public runtime_error
void Editor::multi_select(const MultiSelector& selector) void Editor::multi_select(const MultiSelector& selector)
{ {
SelectionList new_selections; m_selections = selector(*m_buffer, std::move(m_selections));
for (auto& sel : m_selections)
{
SelectionList res = selector(*m_buffer, sel);
new_selections.reserve(new_selections.size() + res.size());
for (auto& new_sel : res)
{
// preserve captures when selectors captures nothing.
if (new_sel.captures().empty())
new_selections.emplace_back(new_sel.first(), new_sel.last(),
sel.captures());
else
new_selections.push_back(std::move(new_sel));
}
}
if (new_selections.empty())
throw nothing_selected();
new_selections.set_main_index(new_selections.size() - 1);
new_selections.sort_and_merge_overlapping();
m_selections = std::move(new_selections);
check_invariant(); check_invariant();
} }

View File

@ -40,7 +40,7 @@ class Editor : public SafeCountable
{ {
public: public:
typedef std::function<Selection (const Buffer&, const Selection&)> Selector; typedef std::function<Selection (const Buffer&, const Selection&)> Selector;
typedef std::function<SelectionList (const Buffer&, const Selection&)> MultiSelector; typedef std::function<SelectionList (const Buffer&, SelectionList)> MultiSelector;
Editor(Buffer& buffer); Editor(Buffer& buffer);
virtual ~Editor() {} virtual ~Editor() {}

View File

@ -321,7 +321,7 @@ void search(Context& context, int)
else if (str.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<direction>, _1, _2, ex), mode); context.editor().multi_select(std::bind(select_next_match<direction, mode>, _1, _2, ex));
} }
catch (boost::regex_error& err) catch (boost::regex_error& err)
{ {
@ -351,7 +351,7 @@ void search_next(Context& context, int param)
{ {
Regex ex{str}; Regex ex{str};
do { do {
context.editor().select(std::bind(select_next_match<direction>, _1, _2, ex), mode); context.editor().multi_select(std::bind(select_next_match<direction, mode>, _1, _2, ex));
} while (--param > 0); } while (--param > 0);
} }
catch (boost::regex_error& err) catch (boost::regex_error& err)
@ -496,16 +496,20 @@ void split_regex(Context& context, int)
void split_lines(Context& context, int) void split_lines(Context& context, int)
{ {
context.editor().multi_select([](const Buffer& buffer, const Selection& sel) { context.editor().multi_select([](const Buffer& buffer,
SelectionList selections) {
SelectionList res;
for (auto& sel : selections)
{
if (sel.first().line == sel.last().line) if (sel.first().line == sel.last().line)
return SelectionList{ sel }; return SelectionList{ sel };
SelectionList res;
auto min = sel.min(); auto min = sel.min();
auto max = sel.max(); auto max = sel.max();
res.push_back({min, {min.line, buffer[min.line].length()-1}}); res.push_back({min, {min.line, buffer[min.line].length()-1}});
for (auto line = min.line+1; line < max.line; ++line) for (auto line = min.line+1; line < max.line; ++line)
res.push_back({line, {line, buffer[line].length()-1}}); res.push_back({line, {line, buffer[line].length()-1}});
res.push_back({max.line, max}); res.push_back({max.line, max});
}
return res; return res;
}); });
} }
@ -515,9 +519,10 @@ void join_select_spaces(Context& context, int)
Editor& editor = context.editor(); Editor& editor = context.editor();
editor.select(select_whole_lines); editor.select(select_whole_lines);
editor.select(select_to_eol, SelectMode::Extend); editor.select(select_to_eol, SelectMode::Extend);
editor.multi_select([](const Buffer& buffer, const Selection& sel) editor.multi_select([](const Buffer& buffer, SelectionList sel)
{ {
SelectionList res = select_all_matches(buffer, sel, Regex{"(\n\\h*)+"}); SelectionList res = select_all_matches(buffer, std::move(sel),
Regex{"(\n\\h*)+"});
// remove last end of line if selected // remove last end of line if selected
kak_assert(std::is_sorted(res.begin(), res.end(), kak_assert(std::is_sorted(res.begin(), res.end(),
[](const Selection& lhs, const Selection& rhs) [](const Selection& lhs, const Selection& rhs)
@ -568,13 +573,16 @@ void indent(Context& context, int)
Editor& editor = context.editor(); Editor& editor = context.editor();
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.multi_select([&indent](const Buffer& buf, const Selection& sel) { editor.multi_select([&indent](const Buffer& buf, SelectionList selections) {
SelectionList res; SelectionList res;
for (auto& sel : selections)
{
for (auto line = sel.min().line; line < sel.max().line+1; ++line) for (auto line = sel.min().line; line < sel.max().line+1; ++line)
{ {
if (indent_empty or buf[line].length() > 1) if (indent_empty or buf[line].length() > 1)
res.emplace_back(line, line); res.emplace_back(line, line);
} }
}
return res; return res;
}); });
editor.insert(indent, InsertMode::Insert); editor.insert(indent, InsertMode::Insert);
@ -592,8 +600,10 @@ void deindent(Context& context, int)
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.multi_select([indent_width,tabstop](const Buffer& buf, const Selection& sel) { editor.multi_select([indent_width,tabstop](const Buffer& buf, SelectionList selections) {
SelectionList res; SelectionList res;
for (auto& sel : selections)
{
for (auto line = sel.min().line; line < sel.max().line+1; ++line) for (auto line = sel.min().line; line < sel.max().line+1; ++line)
{ {
CharCount width = 0; CharCount width = 0;
@ -618,6 +628,7 @@ void deindent(Context& context, int)
} }
} }
} }
}
return res; return res;
}); });
editor.erase(); editor.erase();

View File

@ -597,107 +597,57 @@ Selection select_whole_buffer(const Buffer& buffer, const Selection&)
return Selection({0,0}, buffer.back_coord()); return Selection({0,0}, buffer.back_coord());
} }
using MatchResults = boost::match_results<BufferIterator>; SelectionList select_all_matches(const Buffer& buffer, SelectionList selections,
const Regex& regex)
static bool find_last_match(BufferIterator begin, const BufferIterator& end,
MatchResults& res, const Regex& regex)
{ {
MatchResults matches; SelectionList result;
while (boost::regex_search(begin, end, matches, regex)) for (auto& sel : selections)
{ {
if (begin == matches[0].second) auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
break; RegexIterator re_it(buffer.iterator_at(sel.min()), sel_end, regex);
begin = matches[0].second;
res.swap(matches);
}
return not res.empty();
}
template<Direction direction>
bool find_match_in_buffer(const Buffer& buffer, const BufferIterator pos,
MatchResults& matches, const Regex& ex)
{
if (direction == Forward)
return (boost::regex_search(pos, buffer.end(), matches, ex) or
boost::regex_search(buffer.begin(), pos, matches, ex));
else
return (find_last_match(buffer.begin(), pos, matches, ex) or
find_last_match(pos, buffer.end(), matches, ex));
}
template<Direction direction>
Selection select_next_match(const Buffer& buffer, const Selection& selection, const Regex& regex)
{
// regex matching do not use Utf8Iterator as boost::regex handle utf8
// decoding itself
BufferIterator begin = buffer.iterator_at(selection.last());
BufferIterator end = begin;
CaptureList captures;
MatchResults matches;
bool found = false;
if ((found = find_match_in_buffer<direction>(buffer, utf8::next(begin), matches, regex)))
{
begin = matches[0].first;
end = matches[0].second;
for (auto& match : matches)
captures.push_back(String(match.first, match.second));
}
if (not found or begin == buffer.end())
throw runtime_error("'" + regex.str() + "': no matches found");
end = (begin == end) ? end : utf8::previous(end);
if (direction == Backward)
std::swap(begin, end);
return Selection{begin.coord(), end.coord(), std::move(captures)};
}
template Selection select_next_match<Forward>(const Buffer&, const Selection&, const Regex&);
template Selection select_next_match<Backward>(const Buffer&, const Selection&, const Regex&);
SelectionList select_all_matches(const Buffer& buffer, const Selection& selection, const Regex& regex)
{
auto sel_end = utf8::next(buffer.iterator_at(selection.max()));
RegexIterator re_it(buffer.iterator_at(selection.min()), sel_end, regex);
RegexIterator re_end; RegexIterator re_end;
SelectionList result;
for (; re_it != re_end; ++re_it) for (; re_it != re_end; ++re_it)
{ {
BufferIterator begin = (*re_it)[0].first; auto& begin = (*re_it)[0].first;
BufferIterator end = (*re_it)[0].second; auto& end = (*re_it)[0].second;
if (begin == sel_end) if (begin == sel_end)
continue; continue;
CaptureList captures; CaptureList captures;
for (auto& match : *re_it) for (auto& match : *re_it)
captures.push_back(String(match.first, match.second)); captures.emplace_back(match.first, match.second);
result.push_back(Selection(begin.coord(), (begin == end ? end : utf8::previous(end)).coord(), result.emplace_back(begin.coord(),
std::move(captures))); (begin == end ? end : utf8::previous(end)).coord(),
std::move(captures));
}
} }
return result; return result;
} }
SelectionList split_selection(const Buffer& buffer, const Selection& selection, SelectionList split_selection(const Buffer& buffer, SelectionList selections,
const Regex& regex) const Regex& regex)
{ {
auto begin = buffer.iterator_at(selection.min()); SelectionList result;
auto sel_end = utf8::next(buffer.iterator_at(selection.max())); for (auto& sel : selections)
RegexIterator re_it(begin, sel_end, regex, boost::regex_constants::match_nosubs); {
auto begin = buffer.iterator_at(sel.min());
auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
RegexIterator re_it(begin, sel_end, regex,
boost::regex_constants::match_nosubs);
RegexIterator re_end; RegexIterator re_end;
SelectionList result;
for (; re_it != re_end; ++re_it) for (; re_it != re_end; ++re_it)
{ {
BufferIterator end = (*re_it)[0].first; BufferIterator end = (*re_it)[0].first;
result.push_back(Selection(begin.coord(), (begin == end) ? end.coord() : utf8::previous(end).coord())); result.emplace_back(begin.coord(), (begin == end) ? end.coord() : utf8::previous(end).coord());
begin = (*re_it)[0].second; begin = (*re_it)[0].second;
} }
result.push_back(Selection(begin.coord(), selection.max())); result.emplace_back(begin.coord(), sel.max());
}
return result; return result;
} }

View File

@ -3,6 +3,7 @@
#include "selection.hh" #include "selection.hh"
#include "unicode.hh" #include "unicode.hh"
#include "editor.hh"
namespace Kakoune namespace Kakoune
{ {
@ -58,14 +59,78 @@ Selection trim_partial_lines(const Buffer& buffer, const Selection& selection);
enum Direction { Forward, Backward }; enum Direction { Forward, Backward };
using MatchResults = boost::match_results<BufferIterator>;
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<Direction direction> template<Direction direction>
Selection select_next_match(const Buffer& buffer, const Selection& selection, bool find_match_in_buffer(const Buffer& buffer, const BufferIterator pos,
MatchResults& matches, const Regex& ex)
{
if (direction == Forward)
return (boost::regex_search(pos, buffer.end(), matches, ex) or
boost::regex_search(buffer.begin(), pos, matches, ex));
else
return (find_last_match(buffer.begin(), pos, matches, ex) or
find_last_match(pos, buffer.end(), matches, ex));
}
template<Direction direction, SelectMode mode>
SelectionList select_next_match(const Buffer& buffer, SelectionList selections,
const Regex& regex)
{
auto& sel = selections.main();
auto begin = buffer.iterator_at(sel.last());
auto end = begin;
CaptureList captures;
MatchResults matches;
bool found = false;
if ((found = find_match_in_buffer<direction>(buffer, utf8::next(begin), matches, regex)))
{
begin = matches[0].first;
end = matches[0].second;
for (auto& match : matches)
captures.emplace_back(match.first, match.second);
}
if (not found or begin == buffer.end())
throw runtime_error("'" + regex.str() + "': no matches found");
end = (begin == end) ? end : utf8::previous(end);
if (direction == Backward)
std::swap(begin, end);
Selection res{begin.coord(), end.coord(), std::move(captures)};
if (mode == SelectMode::Replace)
return SelectionList{ std::move(res) };
else if (mode == SelectMode::ReplaceMain)
sel = std::move(res);
else if (mode == SelectMode::Append)
{
selections.push_back(std::move(res));
selections.set_main_index(selections.size() - 1);
}
selections.sort_and_merge_overlapping();
return selections;
}
SelectionList select_all_matches(const Buffer& buffer, SelectionList selection,
const Regex& regex); const Regex& regex);
SelectionList select_all_matches(const Buffer& buffer, const Selection& selection, SelectionList split_selection(const Buffer& buffer, SelectionList selection,
const Regex& regex);
SelectionList split_selection(const Buffer& buffer, const Selection& selection,
const Regex& separator_regex); const Regex& separator_regex);
using CodepointPair = std::pair<Codepoint, Codepoint>; using CodepointPair = std::pair<Codepoint, Codepoint>;