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;
|
const ByteCoord& end = change.end;
|
||||||
if (change.type == Buffer::Change::Insert)
|
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,
|
auto it = std::upper_bound(ranges.begin(), ranges.end(), begin,
|
||||||
[](ByteCoord c, const Selection& sel)
|
[](ByteCoord c, const Selection& sel)
|
||||||
{ return c < sel.min(); });
|
{ return c < sel.min(); });
|
||||||
|
@ -1171,7 +1171,7 @@ static boost::optional<SelectionList> compute_modified_ranges(Buffer& buffer, si
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
update_erase(ranges, begin, end, change.at_end);
|
update_erase(ranges, begin, end);
|
||||||
auto pos = begin;
|
auto pos = begin;
|
||||||
if (change.at_end)
|
if (change.at_end)
|
||||||
pos = begin.column ? ByteCoord{begin.line, begin.column - 1}
|
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())
|
if (ranges.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
for (auto& sel : ranges)
|
||||||
|
{
|
||||||
|
sel.anchor() = buffer.clamp(sel.anchor());
|
||||||
|
sel.cursor() = buffer.clamp(sel.cursor());
|
||||||
|
}
|
||||||
|
|
||||||
SelectionList result{buffer, std::move(ranges)};
|
SelectionList result{buffer, std::move(ranges)};
|
||||||
|
|
||||||
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
||||||
|
|
272
src/selection.cc
272
src/selection.cc
|
@ -1,7 +1,6 @@
|
||||||
#include "selection.hh"
|
#include "selection.hh"
|
||||||
|
|
||||||
#include "utf8.hh"
|
#include "utf8.hh"
|
||||||
#include "modification.hh"
|
|
||||||
#include "buffer_utils.hh"
|
#include "buffer_utils.hh"
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
|
@ -16,93 +15,6 @@ void Selection::merge_with(const Selection& range)
|
||||||
m_anchor = std::max(m_anchor, range.m_anchor);
|
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)
|
SelectionList::SelectionList(Buffer& buffer, Selection s, size_t timestamp)
|
||||||
: m_buffer(&buffer), m_selections({ s }), m_timestamp(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())
|
: 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()
|
void SelectionList::update()
|
||||||
|
@ -139,18 +78,19 @@ void SelectionList::update()
|
||||||
if (m_timestamp == m_buffer->timestamp())
|
if (m_timestamp == m_buffer->timestamp())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto modifs = compute_modifications(*m_buffer, m_timestamp);
|
for (auto& change : m_buffer->changes_since(m_timestamp))
|
||||||
|
{
|
||||||
for (auto& sel : m_selections)
|
for (auto& sel : m_selections)
|
||||||
{
|
{
|
||||||
auto anchor = update_pos(modifs, sel.anchor());
|
sel.anchor() = update_pos(sel.anchor(), change);
|
||||||
kak_assert(m_buffer->is_valid(anchor));
|
sel.cursor() = update_pos(sel.cursor(), change);
|
||||||
sel.anchor() = m_buffer->clamp(anchor);
|
}
|
||||||
|
}
|
||||||
auto cursor = update_pos(modifs, sel.cursor());
|
for (auto& sel : m_selections)
|
||||||
kak_assert(m_buffer->is_valid(cursor));
|
{
|
||||||
sel.cursor() = m_buffer->clamp(cursor);
|
sel.anchor() = m_buffer->clamp(sel.anchor());
|
||||||
|
sel.cursor() = m_buffer->clamp(sel.cursor());
|
||||||
}
|
}
|
||||||
|
|
||||||
merge_overlapping(overlaps);
|
merge_overlapping(overlaps);
|
||||||
check_invariant();
|
check_invariant();
|
||||||
|
|
||||||
|
@ -222,17 +162,6 @@ void SelectionList::avoid_eol()
|
||||||
_avoid_eol(buffer(), sel);
|
_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)
|
BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, InsertMode mode)
|
||||||
{
|
{
|
||||||
switch (mode)
|
switch (mode)
|
||||||
|
@ -264,42 +193,127 @@ BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, InsertMode m
|
||||||
return {};
|
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)
|
void SelectionList::insert(memoryview<String> strings, InsertMode mode)
|
||||||
{
|
{
|
||||||
if (strings.empty())
|
if (strings.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
update();
|
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];
|
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);
|
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)];
|
const String& str = strings[std::min(index, strings.size()-1)];
|
||||||
pos = m_buffer->insert(pos, str);
|
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 (mode == InsertMode::Replace)
|
||||||
{
|
{
|
||||||
if (pos == m_buffer->end())
|
sel.anchor() = change.begin;
|
||||||
--pos;
|
sel.cursor() = m_buffer->char_prev(change.end);
|
||||||
sel.anchor() = pos.coord();
|
}
|
||||||
sel.cursor() = (str.empty() ?
|
else
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
auto& sel = m_selections[j];
|
sel.anchor() = m_buffer->clamp(update_insert(sel.anchor(), change.begin, change.end));
|
||||||
sel.anchor() = update_pos(changes, sel.anchor());
|
sel.cursor() = m_buffer->clamp(update_insert(sel.cursor(), change.begin, change.end));
|
||||||
sel.cursor() = update_pos(changes, sel.cursor());
|
|
||||||
}
|
|
||||||
update_timestamp();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update();
|
|
||||||
check_invariant();
|
check_invariant();
|
||||||
m_buffer->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;
|
size_t m_timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
void update_insert(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, bool at_end);
|
void update_erase(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user