Iterate in reversed order on selections when modifing buffer

This way, update only needs to be called once everything is done
as we always modify after the next selection to be used.
This commit is contained in:
Maxime Coste 2014-05-14 20:56:27 +01:00
parent c3f4ef9ba2
commit 4e280977a2
6 changed files with 72 additions and 46 deletions

View File

@ -24,20 +24,6 @@ inline CharCount char_length(const Buffer& buffer, const Selection& range)
utf8::next(buffer.iterator_at(range.max()))); utf8::next(buffer.iterator_at(range.max())));
} }
inline void avoid_eol(const Buffer& buffer, ByteCoord& coord)
{
const auto column = coord.column;
const auto& line = buffer[coord.line];
if (column != 0 and column == line.length() - 1)
coord.column = line.byte_count_to(line.char_length() - 2);
}
inline void avoid_eol(const Buffer& buffer, Selection& sel)
{
avoid_eol(buffer, sel.anchor());
avoid_eol(buffer, sel.cursor());
}
CharCount get_column(const Buffer& buffer, CharCount get_column(const Buffer& buffer,
CharCount tabstop, ByteCoord coord); CharCount tabstop, ByteCoord coord);

View File

@ -667,7 +667,7 @@ public:
} }
else if (key == Key::Backspace) else if (key == Key::Backspace)
{ {
for (auto& sel : context().selections()) for (auto& sel : reversed(context().selections()))
{ {
if (sel.cursor() == ByteCoord{0,0}) if (sel.cursor() == ByteCoord{0,0})
continue; continue;
@ -677,7 +677,7 @@ public:
} }
else if (key == Key::Erase) else if (key == Key::Erase)
{ {
for (auto& sel : context().selections()) for (auto& sel : reversed(context().selections()))
{ {
auto pos = buffer.iterator_at(sel.cursor()); auto pos = buffer.iterator_at(sel.cursor());
buffer.erase(pos, utf8::next(pos)); buffer.erase(pos, utf8::next(pos));
@ -761,11 +761,12 @@ private:
auto& selections = context().selections(); auto& selections = context().selections();
for (size_t i = 0; i < selections.size(); ++i) for (size_t i = 0; i < selections.size(); ++i)
{ {
size_t index = std::min(i, strings.size()-1); size_t index = selections.size() - 1 - i;
buffer.insert(buffer.iterator_at(selections[i].cursor()), const String& str = strings[std::min(index, strings.size()-1)];
strings[index]); buffer.insert(buffer.iterator_at(selections[index].cursor()),
selections.update(); str);
} }
selections.update();
} }
void insert(Codepoint key) void insert(Codepoint key)
@ -773,11 +774,9 @@ private:
auto str = codepoint_to_str(key); auto str = codepoint_to_str(key);
auto& buffer = context().buffer(); auto& buffer = context().buffer();
auto& selections = context().selections(); auto& selections = context().selections();
for (auto& sel : selections) for (auto& sel : reversed(selections))
{
buffer.insert(buffer.iterator_at(sel.cursor()), str); buffer.insert(buffer.iterator_at(sel.cursor()), str);
selections.update(); selections.update();
}
context().hooks().run_hook("InsertChar", str, context()); context().hooks().run_hook("InsertChar", str, context());
} }
@ -786,7 +785,7 @@ private:
SelectionList& selections = context().selections(); SelectionList& selections = context().selections();
Buffer& buffer = context().buffer(); Buffer& buffer = context().buffer();
for (auto& sel : selections) for (auto& sel : reversed(selections))
{ {
ByteCoord anchor, cursor; ByteCoord anchor, cursor;
switch (mode) switch (mode)
@ -835,10 +834,10 @@ private:
if (buffer.is_end(cursor)) if (buffer.is_end(cursor))
cursor = buffer.char_prev(cursor); cursor = buffer.char_prev(cursor);
selections.update();
sel.anchor() = anchor; sel.anchor() = anchor;
sel.cursor() = cursor; sel.cursor() = cursor;
} }
selections.update();
if (mode == InsertMode::OpenLineBelow or mode == InsertMode::OpenLineAbove) if (mode == InsertMode::OpenLineBelow or mode == InsertMode::OpenLineAbove)
{ {
insert('\n'); insert('\n');
@ -859,12 +858,13 @@ private:
void on_disabled() override void on_disabled() override
{ {
for (auto& sel : context().selections()) auto& selections = context().selections();
for (auto& sel : selections)
{ {
if (m_insert_mode == InsertMode::Append and sel.cursor().column > 0) if (m_insert_mode == InsertMode::Append and sel.cursor().column > 0)
sel.cursor() = context().buffer().char_prev(sel.cursor()); sel.cursor() = context().buffer().char_prev(sel.cursor());
avoid_eol(context().buffer(), sel);
} }
selections.avoid_eol();
} }
enum class Mode { Default, Complete, InsertReg }; enum class Mode { Default, Complete, InsertReg };

View File

@ -23,13 +23,12 @@ namespace Kakoune
void erase(Buffer& buffer, SelectionList& selections) void erase(Buffer& buffer, SelectionList& selections)
{ {
for (auto& sel : selections) for (auto& sel : reversed(selections))
{ {
erase(buffer, sel); erase(buffer, sel);
selections.update();
avoid_eol(buffer, sel);
} }
selections.check_invariant(); selections.update();
selections.avoid_eol();
buffer.check_invariant(); buffer.check_invariant();
} }
@ -66,20 +65,24 @@ BufferIterator prepare_insert(Buffer& buffer, const Selection& sel)
template<InsertMode mode> template<InsertMode mode>
void insert(Buffer& buffer, SelectionList& selections, const String& str) void insert(Buffer& buffer, SelectionList& selections, const String& str)
{ {
for (auto& sel : selections) for (auto& sel : reversed(selections))
{ {
auto pos = prepare_insert<mode>(buffer, sel); auto pos = prepare_insert<mode>(buffer, sel);
pos = buffer.insert(pos, str); pos = buffer.insert(pos, str);
selections.update(); if (mode == InsertMode::Replace)
if (mode == InsertMode::Replace and pos != buffer.end())
{ {
if (pos == buffer.end())
--pos;
sel.anchor() = pos.coord(); sel.anchor() = pos.coord();
sel.cursor() = str.empty() ? sel.cursor() = str.empty() ?
pos.coord() : (pos + str.byte_count_to(str.char_length() - 1)).coord(); pos.coord() : (pos + str.byte_count_to(str.char_length() - 1)).coord();
} }
avoid_eol(buffer, sel);
} }
selections.check_invariant(); if (mode == InsertMode::Replace)
selections.set_timestamp(buffer.timestamp());
else
selections.update();
selections.avoid_eol();
buffer.check_invariant(); buffer.check_invariant();
} }
@ -90,20 +93,25 @@ void insert(Buffer& buffer, SelectionList& selections, memoryview<String> string
return; return;
for (size_t i = 0; i < selections.size(); ++i) for (size_t i = 0; i < selections.size(); ++i)
{ {
auto& sel = selections[i]; size_t index = selections.size() - 1 - i;
auto& sel = selections[index];
auto pos = prepare_insert<mode>(buffer, sel); auto pos = prepare_insert<mode>(buffer, sel);
const String& str = strings[std::min(i, strings.size()-1)]; const String& str = strings[std::min(index, strings.size()-1)];
pos = buffer.insert(pos, str); pos = buffer.insert(pos, str);
selections.update(); if (mode == InsertMode::Replace)
if (mode == InsertMode::Replace and pos != buffer.end())
{ {
if (pos == buffer.end())
--pos;
sel.anchor() = pos.coord(); sel.anchor() = pos.coord();
sel.cursor() = (str.empty() ? sel.cursor() = (str.empty() ?
pos : pos + str.byte_count_to(str.char_length() - 1)).coord(); pos : pos + str.byte_count_to(str.char_length() - 1)).coord();
} }
avoid_eol(buffer, sel);
} }
selections.check_invariant(); if (mode == InsertMode::Replace)
selections.set_timestamp(buffer.timestamp());
else
selections.update();
selections.avoid_eol();
buffer.check_invariant(); buffer.check_invariant();
} }
@ -1352,9 +1360,9 @@ void move(Context& context, int count)
: context.buffer().offset_coord(sel.cursor(), offset); : context.buffer().offset_coord(sel.cursor(), offset);
sel.anchor() = mode == SelectMode::Extend ? sel.anchor() : cursor; sel.anchor() = mode == SelectMode::Extend ? sel.anchor() : cursor;
sel.cursor() = cursor; sel.cursor() = cursor;
avoid_eol(context.buffer(), sel);
} }
selections.avoid_eol();
selections.sort_and_merge_overlapping(); selections.sort_and_merge_overlapping();
} }

View File

@ -188,5 +188,31 @@ void SelectionList::sort_and_merge_overlapping()
std::stable_sort(begin(), end(), compare_selections); std::stable_sort(begin(), end(), compare_selections);
merge_overlapping(overlaps); merge_overlapping(overlaps);
} }
namespace
{
inline void _avoid_eol(const Buffer& buffer, ByteCoord& coord)
{
const auto column = coord.column;
const auto& line = buffer[coord.line];
if (column != 0 and column == line.length() - 1)
coord.column = line.byte_count_to(line.char_length() - 2);
}
inline void _avoid_eol(const Buffer& buffer, Selection& sel)
{
_avoid_eol(buffer, sel.anchor());
_avoid_eol(buffer, sel.cursor());
}
}
void SelectionList::avoid_eol()
{
update();
for (auto& sel : m_selections)
_avoid_eol(buffer(), sel);
}
} }

View File

@ -73,6 +73,8 @@ struct SelectionList
void rotate_main(int count) { m_main = (m_main + count) % size(); } void rotate_main(int count) { m_main = (m_main + count) % size(); }
void avoid_eol();
void push_back(const Selection& sel) { m_selections.push_back(sel); } void push_back(const Selection& sel) { m_selections.push_back(sel); }
void push_back(Selection&& sel) { m_selections.push_back(std::move(sel)); } void push_back(Selection&& sel) { m_selections.push_back(std::move(sel)); }
@ -92,6 +94,10 @@ struct SelectionList
iterator begin() { return m_selections.begin(); } iterator begin() { return m_selections.begin(); }
iterator end() { return m_selections.end(); } iterator end() { return m_selections.end(); }
using reverse_iterator = std::vector<Selection>::reverse_iterator;
reverse_iterator rbegin() { return m_selections.rbegin(); }
reverse_iterator rend() { return m_selections.rend(); }
using const_iterator = std::vector<Selection>::const_iterator; using const_iterator = std::vector<Selection>::const_iterator;
const_iterator begin() const { return m_selections.begin(); } const_iterator begin() const { return m_selections.begin(); }
const_iterator end() const { return m_selections.end(); } const_iterator end() const { return m_selections.end(); }

View File

@ -13,8 +13,8 @@ inline void clear_selections(const Buffer& buffer, SelectionList& selections)
{ {
auto& sel = selections.main(); auto& sel = selections.main();
auto& pos = sel.cursor(); auto& pos = sel.cursor();
avoid_eol(buffer, pos);
sel.anchor() = pos; sel.anchor() = pos;
selections.avoid_eol();
selections = SelectionList{ buffer, std::move(sel) }; selections = SelectionList{ buffer, std::move(sel) };
} }