Move compute_modified_ranges to selection.cc and use an optimized approach
This commit is contained in:
parent
23a1914d7e
commit
d33c27acdf
|
@ -1155,55 +1155,6 @@ void spaces_to_tabs(Context& context, int ts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<Selection> compute_modified_ranges(Buffer& buffer, size_t timestamp)
|
|
||||||
{
|
|
||||||
std::vector<Selection> ranges;
|
|
||||||
for (auto& change : buffer.changes_since(timestamp))
|
|
||||||
{
|
|
||||||
const ByteCoord& begin = change.begin;
|
|
||||||
const ByteCoord& end = change.end;
|
|
||||||
if (change.type == Buffer::Change::Insert)
|
|
||||||
{
|
|
||||||
update_insert(ranges, begin, end);
|
|
||||||
auto it = std::upper_bound(ranges.begin(), ranges.end(), begin,
|
|
||||||
[](ByteCoord c, const Selection& sel)
|
|
||||||
{ return c < sel.min(); });
|
|
||||||
ranges.insert(it, Selection{begin, end});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
update_erase(ranges, begin, end);
|
|
||||||
auto pos = begin;
|
|
||||||
if (change.at_end)
|
|
||||||
pos = begin.column ? ByteCoord{begin.line, begin.column - 1}
|
|
||||||
: ByteCoord{begin.line - 1};
|
|
||||||
auto it = std::upper_bound(ranges.begin(), ranges.end(), pos,
|
|
||||||
[](ByteCoord c, const Selection& sel)
|
|
||||||
{ return c < sel.min(); });
|
|
||||||
ranges.insert(it, Selection{pos, pos});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto& sel : ranges)
|
|
||||||
{
|
|
||||||
sel.anchor() = buffer.clamp(sel.anchor());
|
|
||||||
sel.cursor() = buffer.clamp(sel.cursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
|
||||||
return lhs.min() <= rhs.min() ? buffer.char_next(lhs.max()) >= rhs.min()
|
|
||||||
: lhs.min() <= buffer.char_next(rhs.max());
|
|
||||||
};
|
|
||||||
size_t main = 0;
|
|
||||||
merge_overlapping(ranges.begin(), ranges.end(), main, touches);
|
|
||||||
|
|
||||||
for (auto& sel : ranges)
|
|
||||||
{
|
|
||||||
if (sel.anchor() != sel.cursor())
|
|
||||||
sel.cursor() = buffer.char_prev(sel.cursor());
|
|
||||||
}
|
|
||||||
return ranges;
|
|
||||||
}
|
|
||||||
|
|
||||||
void undo(Context& context, int)
|
void undo(Context& context, int)
|
||||||
{
|
{
|
||||||
Buffer& buffer = context.buffer();
|
Buffer& buffer = context.buffer();
|
||||||
|
|
135
src/selection.cc
135
src/selection.cc
|
@ -63,12 +63,33 @@ ByteCoord update_erase(ByteCoord coord, ByteCoord begin, ByteCoord end)
|
||||||
return coord;
|
return coord;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteCoord update_pos(ByteCoord coord, const Buffer::Change& change)
|
static bool compare_selections(const Selection& lhs, const Selection& rhs)
|
||||||
{
|
{
|
||||||
if (change.type == Buffer::Change::Insert)
|
return lhs.min() < rhs.min();
|
||||||
return update_insert(coord, change.begin, change.end);
|
}
|
||||||
|
|
||||||
|
template<typename Iterator, typename OverlapsFunc>
|
||||||
|
Iterator merge_overlapping(Iterator begin, Iterator end, size_t& main, OverlapsFunc overlaps)
|
||||||
|
{
|
||||||
|
kak_assert(std::is_sorted(begin, end, compare_selections));
|
||||||
|
size_t size = end - begin;
|
||||||
|
size_t i = 0;
|
||||||
|
for (size_t j = 1; j < size; ++j)
|
||||||
|
{
|
||||||
|
if (overlaps(begin[i], begin[j]))
|
||||||
|
{
|
||||||
|
begin[i].merge_with(begin[j]);
|
||||||
|
if (i < main)
|
||||||
|
--main;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return update_erase(coord, change.begin, change.end);
|
{
|
||||||
|
++i;
|
||||||
|
if (i != j)
|
||||||
|
begin[i] = std::move(begin[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return begin + i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tracks position changes for changes that are done
|
// This tracks position changes for changes that are done
|
||||||
|
@ -109,7 +130,7 @@ struct PositionChangesTracker
|
||||||
ByteCoord get_new_coord(ByteCoord coord)
|
ByteCoord get_new_coord(ByteCoord coord)
|
||||||
{
|
{
|
||||||
if (last_pos.line - pos_change.line == coord.line)
|
if (last_pos.line - pos_change.line == coord.line)
|
||||||
coord.column += pos_change.column;
|
coord.column = std::max(0_byte, coord.column + pos_change.column);
|
||||||
coord.line += pos_change.line;
|
coord.line += pos_change.line;
|
||||||
return coord;
|
return coord;
|
||||||
}
|
}
|
||||||
|
@ -127,6 +148,16 @@ bool relevant(const Buffer::Change& change, ByteCoord coord)
|
||||||
: change.begin < coord;
|
: change.begin < coord;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool forward_sorted(const Buffer::Change& lhs, const Buffer::Change& rhs)
|
||||||
|
{
|
||||||
|
return lhs.begin < rhs.begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool backward_sorted(const Buffer::Change& lhs, const Buffer::Change& rhs)
|
||||||
|
{
|
||||||
|
return lhs.begin > rhs.end;
|
||||||
|
}
|
||||||
|
|
||||||
void update_forward(memoryview<Buffer::Change> changes, std::vector<Selection>& selections)
|
void update_forward(memoryview<Buffer::Change> changes, std::vector<Selection>& selections)
|
||||||
{
|
{
|
||||||
PositionChangesTracker changes_tracker;
|
PositionChangesTracker changes_tracker;
|
||||||
|
@ -184,22 +215,84 @@ void update_backward(memoryview<Buffer::Change> changes, std::vector<Selection>&
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Selection> compute_modified_ranges(Buffer& buffer, size_t timestamp)
|
||||||
|
{
|
||||||
|
std::vector<Selection> ranges;
|
||||||
|
auto changes = buffer.changes_since(timestamp);
|
||||||
|
auto change_it = changes.begin();
|
||||||
|
while (change_it != changes.end())
|
||||||
|
{
|
||||||
|
auto forward_end = std::is_sorted_until(change_it, changes.end(), forward_sorted);
|
||||||
|
auto backward_end = std::is_sorted_until(change_it, changes.end(), backward_sorted);
|
||||||
|
|
||||||
|
size_t prev_size = ranges.size();
|
||||||
|
if (forward_end >= backward_end)
|
||||||
|
{
|
||||||
|
update_forward({ change_it, forward_end }, ranges);
|
||||||
|
|
||||||
|
PositionChangesTracker changes_tracker;
|
||||||
|
for (; change_it != forward_end; ++change_it)
|
||||||
|
{
|
||||||
|
if (change_it->type == Buffer::Change::Insert)
|
||||||
|
ranges.push_back({ change_it->begin, change_it->end });
|
||||||
|
else
|
||||||
|
ranges.push_back({ change_it->begin });
|
||||||
|
changes_tracker.update(*change_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
update_backward({ change_it, backward_end }, ranges);
|
||||||
|
|
||||||
|
using ReverseIt = std::reverse_iterator<const Buffer::Change*>;
|
||||||
|
PositionChangesTracker changes_tracker;
|
||||||
|
for (ReverseIt it{backward_end}, end{change_it}; it != end; ++it)
|
||||||
|
{
|
||||||
|
if (it->type == Buffer::Change::Insert)
|
||||||
|
ranges.push_back({ changes_tracker.get_new_coord(it->begin),
|
||||||
|
changes_tracker.get_new_coord(it->end) });
|
||||||
|
else
|
||||||
|
ranges.push_back({ changes_tracker.get_new_coord(it->begin) });
|
||||||
|
changes_tracker.update(*it);
|
||||||
|
}
|
||||||
|
change_it = backward_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
kak_assert(std::is_sorted(ranges.begin(), ranges.begin() + prev_size, compare_selections));
|
||||||
|
kak_assert(std::is_sorted(ranges.begin() + prev_size, ranges.end(), compare_selections));
|
||||||
|
std::inplace_merge(ranges.begin(), ranges.begin() + prev_size, ranges.end(), compare_selections);
|
||||||
|
}
|
||||||
|
for (auto& sel : ranges)
|
||||||
|
{
|
||||||
|
sel.anchor() = buffer.clamp(sel.anchor());
|
||||||
|
sel.cursor() = buffer.clamp(sel.cursor());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
||||||
|
return buffer.char_next(lhs.max()) >= rhs.min();
|
||||||
|
};
|
||||||
|
size_t dummy = 0;
|
||||||
|
ranges.erase(merge_overlapping(ranges.begin(), ranges.end(), dummy, touches), ranges.end());
|
||||||
|
|
||||||
|
for (auto& sel : ranges)
|
||||||
|
{
|
||||||
|
if (sel.anchor() != sel.cursor())
|
||||||
|
sel.cursor() = buffer.char_prev(sel.cursor());
|
||||||
|
}
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
void SelectionList::update()
|
void SelectionList::update()
|
||||||
{
|
{
|
||||||
if (m_timestamp == m_buffer->timestamp())
|
if (m_timestamp == m_buffer->timestamp())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto forward = [](const Buffer::Change& lhs, const Buffer::Change& rhs)
|
|
||||||
{ return lhs.begin < rhs.begin; };
|
|
||||||
auto backward = [](const Buffer::Change& lhs, const Buffer::Change& rhs)
|
|
||||||
{ return lhs.begin > rhs.end; };
|
|
||||||
|
|
||||||
auto changes = m_buffer->changes_since(m_timestamp);
|
auto changes = m_buffer->changes_since(m_timestamp);
|
||||||
auto change_it = changes.begin();
|
auto change_it = changes.begin();
|
||||||
while (change_it != changes.end())
|
while (change_it != changes.end())
|
||||||
{
|
{
|
||||||
auto forward_end = std::is_sorted_until(change_it, changes.end(), forward);
|
auto forward_end = std::is_sorted_until(change_it, changes.end(), forward_sorted);
|
||||||
auto backward_end = std::is_sorted_until(change_it, changes.end(), backward);
|
auto backward_end = std::is_sorted_until(change_it, changes.end(), backward_sorted);
|
||||||
|
|
||||||
if (forward_end >= backward_end)
|
if (forward_end >= backward_end)
|
||||||
{
|
{
|
||||||
|
@ -375,22 +468,4 @@ void SelectionList::erase()
|
||||||
m_buffer->check_invariant();
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,11 +53,6 @@ inline bool overlaps(const Selection& lhs, const Selection& rhs)
|
||||||
: lhs.min() <= rhs.max();
|
: lhs.min() <= rhs.max();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool compare_selections(const Selection& lhs, const Selection& rhs)
|
|
||||||
{
|
|
||||||
return lhs.min() < rhs.min();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class InsertMode : unsigned
|
enum class InsertMode : unsigned
|
||||||
{
|
{
|
||||||
Insert,
|
Insert,
|
||||||
|
@ -71,30 +66,6 @@ enum class InsertMode : unsigned
|
||||||
OpenLineAbove
|
OpenLineAbove
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Iterator, typename OverlapsFunc>
|
|
||||||
Iterator merge_overlapping(Iterator begin, Iterator end, size_t& main, OverlapsFunc overlaps)
|
|
||||||
{
|
|
||||||
kak_assert(std::is_sorted(begin, end, compare_selections));
|
|
||||||
size_t size = end - begin;
|
|
||||||
size_t i = 0;
|
|
||||||
for (size_t j = 1; j < size; ++j)
|
|
||||||
{
|
|
||||||
if (overlaps(begin[i], begin[j]))
|
|
||||||
{
|
|
||||||
begin[i].merge_with(begin[j]);
|
|
||||||
if (i < main)
|
|
||||||
--main;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
if (i != j)
|
|
||||||
begin[i] = std::move(begin[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return begin + i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SelectionList
|
struct SelectionList
|
||||||
{
|
{
|
||||||
SelectionList(Buffer& buffer, Selection s);
|
SelectionList(Buffer& buffer, Selection s);
|
||||||
|
@ -164,8 +135,7 @@ private:
|
||||||
size_t m_timestamp;
|
size_t m_timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
void update_insert(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end);
|
std::vector<Selection> compute_modified_ranges(Buffer& buffer, size_t timestamp);
|
||||||
void update_erase(std::vector<Selection>& sels, ByteCoord begin, ByteCoord end);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user