Go back to Buffer::Change based implementation for SelectionList::update
However take into account the ordering of selections in insert and erase methods, so that we update selection position cheaply.
This commit is contained in:
parent
51eae8026b
commit
f49bec8021
|
@ -1163,7 +1163,7 @@ static boost::optional<SelectionList> compute_modified_ranges(Buffer& buffer, si
|
|||
const ByteCoord& end = change.end;
|
||||
if (change.type == Buffer::Change::Insert)
|
||||
{
|
||||
update_insert(ranges, begin, end, change.at_end);
|
||||
update_insert(ranges, begin, end);
|
||||
auto it = std::upper_bound(ranges.begin(), ranges.end(), begin,
|
||||
[](ByteCoord c, const Selection& sel)
|
||||
{ return c < sel.min(); });
|
||||
|
@ -1171,7 +1171,7 @@ static boost::optional<SelectionList> compute_modified_ranges(Buffer& buffer, si
|
|||
}
|
||||
else
|
||||
{
|
||||
update_erase(ranges, begin, end, change.at_end);
|
||||
update_erase(ranges, begin, end);
|
||||
auto pos = begin;
|
||||
if (change.at_end)
|
||||
pos = begin.column ? ByteCoord{begin.line, begin.column - 1}
|
||||
|
@ -1185,6 +1185,12 @@ static boost::optional<SelectionList> compute_modified_ranges(Buffer& buffer, si
|
|||
if (ranges.empty())
|
||||
return {};
|
||||
|
||||
for (auto& sel : ranges)
|
||||
{
|
||||
sel.anchor() = buffer.clamp(sel.anchor());
|
||||
sel.cursor() = buffer.clamp(sel.cursor());
|
||||
}
|
||||
|
||||
SelectionList result{buffer, std::move(ranges)};
|
||||
|
||||
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
||||
|
|
272
src/selection.cc
272
src/selection.cc
|
@ -1,7 +1,6 @@
|
|||
#include "selection.hh"
|
||||
|
||||
#include "utf8.hh"
|
||||
#include "modification.hh"
|
||||
#include "buffer_utils.hh"
|
||||
|
||||
namespace Kakoune
|
||||
|
@ -16,93 +15,6 @@ void Selection::merge_with(const Selection& range)
|
|||
m_anchor = std::max(m_anchor, range.m_anchor);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<template <bool, bool> class UpdateFunc>
|
||||
void on_buffer_change(std::vector<Selection>& sels,
|
||||
ByteCoord begin, ByteCoord end, bool at_end, LineCount end_line)
|
||||
{
|
||||
auto update_beg = std::lower_bound(sels.begin(), sels.end(), begin,
|
||||
[](const Selection& s, ByteCoord c)
|
||||
{ return s.max() < c; });
|
||||
auto update_only_line_beg = std::upper_bound(sels.begin(), sels.end(), end_line,
|
||||
[](LineCount l, const Selection& s)
|
||||
{ return l < s.min().line; });
|
||||
|
||||
if (update_beg != update_only_line_beg)
|
||||
{
|
||||
// for the first one, we are not sure if min < begin
|
||||
UpdateFunc<false, false>{}(update_beg->anchor(), begin, end, at_end);
|
||||
UpdateFunc<false, false>{}(update_beg->cursor(), begin, end, at_end);
|
||||
}
|
||||
for (auto it = update_beg+1; it < update_only_line_beg; ++it)
|
||||
{
|
||||
UpdateFunc<false, true>{}(it->anchor(), begin, end, at_end);
|
||||
UpdateFunc<false, true>{}(it->cursor(), begin, end, at_end);
|
||||
}
|
||||
if (end.line > begin.line)
|
||||
{
|
||||
for (auto it = update_only_line_beg; it != sels.end(); ++it)
|
||||
{
|
||||
UpdateFunc<true, true>{}(it->anchor(), begin, end, at_end);
|
||||
UpdateFunc<true, true>{}(it->cursor(), begin, end, at_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool assume_different_line, bool assume_greater_than_begin>
|
||||
struct UpdateInsert
|
||||
{
|
||||
void operator()(ByteCoord& coord, ByteCoord begin, ByteCoord end,
|
||||
bool at_end) const
|
||||
{
|
||||
if (assume_different_line)
|
||||
kak_assert(begin.line < coord.line);
|
||||
if (not assume_greater_than_begin and coord < begin)
|
||||
return;
|
||||
if (not assume_different_line and begin.line == coord.line)
|
||||
coord.column = end.column + coord.column - begin.column;
|
||||
|
||||
coord.line += end.line - begin.line;
|
||||
kak_assert(coord.line >= 0 and coord.column >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<bool assume_different_line, bool assume_greater_than_begin>
|
||||
struct UpdateErase
|
||||
{
|
||||
void operator()(ByteCoord& coord, ByteCoord begin, ByteCoord end,
|
||||
bool at_end) const
|
||||
{
|
||||
if (not assume_greater_than_begin and coord < begin)
|
||||
return;
|
||||
if (assume_different_line)
|
||||
kak_assert(end.line < coord.line);
|
||||
if (not assume_different_line and coord <= end)
|
||||
{
|
||||
if (not at_end or begin == ByteCoord{0,0})
|
||||
coord = begin;
|
||||
else
|
||||
coord = begin.column ? ByteCoord{begin.line, begin.column-1}
|
||||
: ByteCoord{begin.line - 1};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (not assume_different_line and end.line == coord.line)
|
||||
{
|
||||
coord.line = begin.line;
|
||||
coord.column = begin.column + coord.column - end.column;
|
||||
}
|
||||
else
|
||||
coord.line -= end.line - begin.line;
|
||||
}
|
||||
kak_assert(coord.line >= 0 and coord.column >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
SelectionList::SelectionList(Buffer& buffer, Selection s, size_t timestamp)
|
||||
: m_buffer(&buffer), m_selections({ s }), m_timestamp(timestamp)
|
||||
{
|
||||
|
@ -124,14 +36,41 @@ SelectionList::SelectionList(Buffer& buffer, std::vector<Selection> s)
|
|||
: SelectionList(buffer, std::move(s), buffer.timestamp())
|
||||
{}
|
||||
|
||||
void update_insert(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end, bool at_end)
|
||||
namespace
|
||||
{
|
||||
on_buffer_change<UpdateInsert>(sels, begin, end, at_end, begin.line);
|
||||
|
||||
ByteCoord update_insert(ByteCoord coord, ByteCoord begin, ByteCoord end)
|
||||
{
|
||||
if (coord < begin)
|
||||
return coord;
|
||||
if (begin.line == coord.line)
|
||||
coord.column += end.column - begin.column;
|
||||
coord.line += end.line - begin.line;
|
||||
kak_assert(coord.line >= 0 and coord.column >= 0);
|
||||
return coord;
|
||||
}
|
||||
|
||||
void update_erase(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end, bool at_end)
|
||||
ByteCoord update_erase(ByteCoord coord, ByteCoord begin, ByteCoord end)
|
||||
{
|
||||
on_buffer_change<UpdateErase>(sels, begin, end, at_end, end.line);
|
||||
if (coord < begin)
|
||||
return coord;
|
||||
if (coord <= end)
|
||||
return begin;
|
||||
if (end.line == coord.line)
|
||||
coord.column -= end.column - begin.column;
|
||||
coord.line -= end.line - begin.line;
|
||||
kak_assert(coord.line >= 0 and coord.column >= 0);
|
||||
return coord;
|
||||
}
|
||||
|
||||
ByteCoord update_pos(ByteCoord coord, const Buffer::Change& change)
|
||||
{
|
||||
if (change.type == Buffer::Change::Insert)
|
||||
return update_insert(coord, change.begin, change.end);
|
||||
else
|
||||
return update_erase(coord, change.begin, change.end);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SelectionList::update()
|
||||
|
@ -139,18 +78,19 @@ void SelectionList::update()
|
|||
if (m_timestamp == m_buffer->timestamp())
|
||||
return;
|
||||
|
||||
auto modifs = compute_modifications(*m_buffer, m_timestamp);
|
||||
for (auto& change : m_buffer->changes_since(m_timestamp))
|
||||
{
|
||||
for (auto& sel : m_selections)
|
||||
{
|
||||
auto anchor = update_pos(modifs, sel.anchor());
|
||||
kak_assert(m_buffer->is_valid(anchor));
|
||||
sel.anchor() = m_buffer->clamp(anchor);
|
||||
|
||||
auto cursor = update_pos(modifs, sel.cursor());
|
||||
kak_assert(m_buffer->is_valid(cursor));
|
||||
sel.cursor() = m_buffer->clamp(cursor);
|
||||
sel.anchor() = update_pos(sel.anchor(), change);
|
||||
sel.cursor() = update_pos(sel.cursor(), change);
|
||||
}
|
||||
}
|
||||
for (auto& sel : m_selections)
|
||||
{
|
||||
sel.anchor() = m_buffer->clamp(sel.anchor());
|
||||
sel.cursor() = m_buffer->clamp(sel.cursor());
|
||||
}
|
||||
|
||||
merge_overlapping(overlaps);
|
||||
check_invariant();
|
||||
|
||||
|
@ -222,17 +162,6 @@ void SelectionList::avoid_eol()
|
|||
_avoid_eol(buffer(), sel);
|
||||
}
|
||||
|
||||
void SelectionList::erase()
|
||||
{
|
||||
for (auto& sel : reversed(m_selections))
|
||||
{
|
||||
Kakoune::erase(*m_buffer, sel);
|
||||
}
|
||||
update();
|
||||
avoid_eol();
|
||||
m_buffer->check_invariant();
|
||||
}
|
||||
|
||||
BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, InsertMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
|
@ -264,42 +193,127 @@ BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, InsertMode m
|
|||
return {};
|
||||
}
|
||||
|
||||
// This tracks position changes for changes that are done
|
||||
// in a forward way (each change takes place at a position)
|
||||
// *after* the previous one.
|
||||
struct PositionChangesTracker
|
||||
{
|
||||
ByteCoord last_pos;
|
||||
ByteCoord pos_change;
|
||||
|
||||
void update(const Buffer::Change& change)
|
||||
{
|
||||
if (change.type == Buffer::Change::Insert)
|
||||
{
|
||||
pos_change.line += change.end.line - change.begin.line;
|
||||
if (change.begin.line != last_pos.line)
|
||||
pos_change.column = 0;
|
||||
pos_change.column += change.end.column - change.begin.column;
|
||||
last_pos = change.end;
|
||||
}
|
||||
else if (change.type == Buffer::Change::Erase)
|
||||
{
|
||||
pos_change.line -= change.end.line - change.begin.line;
|
||||
if (last_pos.line != change.end.line)
|
||||
pos_change.column = 0;
|
||||
pos_change.column -= change.end.column - change.begin.column;
|
||||
last_pos = change.begin;
|
||||
}
|
||||
}
|
||||
|
||||
void update(const Buffer& buffer, size_t& timestamp)
|
||||
{
|
||||
for (auto& change : buffer.changes_since(timestamp))
|
||||
update(change);
|
||||
timestamp = buffer.timestamp();
|
||||
}
|
||||
|
||||
ByteCoord get_new_coord(ByteCoord coord)
|
||||
{
|
||||
if (last_pos.line - pos_change.line == coord.line)
|
||||
coord.column += pos_change.column;
|
||||
coord.line += pos_change.line;
|
||||
return coord;
|
||||
}
|
||||
|
||||
void update_sel(Selection& sel)
|
||||
{
|
||||
sel.anchor() = get_new_coord(sel.anchor());
|
||||
sel.cursor() = get_new_coord(sel.cursor());
|
||||
}
|
||||
};
|
||||
|
||||
void SelectionList::insert(memoryview<String> strings, InsertMode mode)
|
||||
{
|
||||
if (strings.empty())
|
||||
return;
|
||||
|
||||
update();
|
||||
for (size_t i = 0; i < m_selections.size(); ++i)
|
||||
PositionChangesTracker changes_tracker;
|
||||
for (size_t index = 0; index < m_selections.size(); ++index)
|
||||
{
|
||||
size_t index = m_selections.size() - 1 - i;
|
||||
auto& sel = m_selections[index];
|
||||
|
||||
changes_tracker.update_sel(sel);
|
||||
kak_assert(m_buffer->is_valid(sel.anchor()));
|
||||
kak_assert(m_buffer->is_valid(sel.cursor()));
|
||||
|
||||
auto pos = prepare_insert(*m_buffer, sel, mode);
|
||||
changes_tracker.update(*m_buffer, m_timestamp);
|
||||
|
||||
const String& str = strings[std::min(index, strings.size()-1)];
|
||||
pos = m_buffer->insert(pos, str);
|
||||
|
||||
auto& change = m_buffer->changes_since(m_timestamp).back();
|
||||
changes_tracker.update(change);
|
||||
m_timestamp = m_buffer->timestamp();
|
||||
|
||||
if (mode == InsertMode::Replace)
|
||||
{
|
||||
if (pos == m_buffer->end())
|
||||
--pos;
|
||||
sel.anchor() = pos.coord();
|
||||
sel.cursor() = (str.empty() ?
|
||||
pos : pos + str.byte_count_to(str.char_length() - 1)).coord();
|
||||
|
||||
// update following selections
|
||||
auto changes = compute_modifications(*m_buffer, timestamp());
|
||||
for (size_t j = index+1; j < m_selections.size(); ++j)
|
||||
sel.anchor() = change.begin;
|
||||
sel.cursor() = m_buffer->char_prev(change.end);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& sel = m_selections[j];
|
||||
sel.anchor() = update_pos(changes, sel.anchor());
|
||||
sel.cursor() = update_pos(changes, sel.cursor());
|
||||
}
|
||||
update_timestamp();
|
||||
sel.anchor() = m_buffer->clamp(update_insert(sel.anchor(), change.begin, change.end));
|
||||
sel.cursor() = m_buffer->clamp(update_insert(sel.cursor(), change.begin, change.end));
|
||||
}
|
||||
}
|
||||
update();
|
||||
check_invariant();
|
||||
m_buffer->check_invariant();
|
||||
}
|
||||
|
||||
void SelectionList::erase()
|
||||
{
|
||||
update();
|
||||
PositionChangesTracker changes_tracker;
|
||||
for (auto& sel : m_selections)
|
||||
{
|
||||
changes_tracker.update_sel(sel);
|
||||
auto pos = Kakoune::erase(*m_buffer, sel);
|
||||
sel.anchor() = sel.cursor() = m_buffer->clamp(pos.coord());
|
||||
changes_tracker.update(*m_buffer, m_timestamp);
|
||||
}
|
||||
avoid_eol();
|
||||
m_buffer->check_invariant();
|
||||
}
|
||||
|
||||
void update_insert(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end)
|
||||
{
|
||||
for (auto& sel : sels)
|
||||
{
|
||||
sel.anchor() = update_insert(sel.anchor(), begin, end);
|
||||
sel.cursor() = update_insert(sel.cursor(), begin, end);
|
||||
}
|
||||
}
|
||||
|
||||
void update_erase(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end)
|
||||
{
|
||||
for (auto& sel : sels)
|
||||
{
|
||||
sel.anchor() = update_erase(sel.anchor(), begin, end);
|
||||
sel.cursor() = update_erase(sel.cursor(), begin, end);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -165,8 +165,8 @@ private:
|
|||
size_t m_timestamp;
|
||||
};
|
||||
|
||||
void update_insert(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end, bool at_end);
|
||||
void update_erase(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end, bool at_end);
|
||||
void update_insert(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end);
|
||||
void update_erase(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end);
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user