2688893156
With overlapping selections, pasting after breaks assumption of SelectionList::for_each as our changes are no longer happening in increasing locations. We hence cannot rely on the ForwardChangeTracker in that case and have to rely on the more general (and more costly) ranges update logic. This interacts poorly with paste linewise pastes and we try to preserve the current behaviour by tracking the last paste position. Overall, this change really begs for overlapping selections to be removed, but we will fix them like that for now. Fixes #4779
112 lines
3.8 KiB
C++
112 lines
3.8 KiB
C++
#ifndef changes_hh_INCLUDED
|
|
#define changes_hh_INCLUDED
|
|
|
|
#include "buffer.hh"
|
|
#include "coord.hh"
|
|
|
|
namespace Kakoune
|
|
{
|
|
|
|
// 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 ForwardChangesTracker
|
|
{
|
|
BufferCoord cur_pos; // last change position at current modification
|
|
BufferCoord old_pos; // last change position at start
|
|
|
|
void update(const Buffer::Change& change);
|
|
void update(const Buffer& buffer, size_t& timestamp);
|
|
|
|
BufferCoord get_old_coord(BufferCoord coord) const;
|
|
BufferCoord get_new_coord(BufferCoord coord) const;
|
|
BufferCoord get_new_coord_tolerant(BufferCoord coord) const;
|
|
|
|
bool relevant(const Buffer::Change& change, BufferCoord old_coord) const;
|
|
};
|
|
|
|
const Buffer::Change* forward_sorted_until(const Buffer::Change* first, const Buffer::Change* last);
|
|
const Buffer::Change* backward_sorted_until(const Buffer::Change* first, const Buffer::Change* last);
|
|
|
|
template<typename Range, typename AdvanceFunc>
|
|
auto update_range(ForwardChangesTracker& changes_tracker, Range& range, AdvanceFunc&& advance_while_relevant)
|
|
{
|
|
auto& first = get_first(range);
|
|
auto& last = get_last(range);
|
|
advance_while_relevant(first);
|
|
first = changes_tracker.get_new_coord_tolerant(first);
|
|
|
|
if (last < BufferCoord{0,0})
|
|
return;
|
|
|
|
advance_while_relevant(last);
|
|
last = changes_tracker.get_new_coord_tolerant(last);
|
|
}
|
|
|
|
template<typename RangeContainer>
|
|
void update_forward(ConstArrayView<Buffer::Change> changes, RangeContainer& ranges)
|
|
{
|
|
ForwardChangesTracker changes_tracker;
|
|
auto advance_while_relevant = [&, it = changes.begin()]
|
|
(const BufferCoord& pos) mutable {
|
|
while (it != changes.end() and changes_tracker.relevant(*it, pos))
|
|
changes_tracker.update(*it++);
|
|
};
|
|
|
|
auto range_it = std::lower_bound(ranges.begin(), ranges.end(), changes.front(),
|
|
[](auto& range, const Buffer::Change& change) { return get_last(range) < change.begin; });
|
|
for (auto end = ranges.end(); range_it != end; ++range_it)
|
|
update_range(changes_tracker, *range_it, advance_while_relevant);
|
|
}
|
|
|
|
template<typename RangeContainer>
|
|
void update_backward(ConstArrayView<Buffer::Change> changes, RangeContainer& ranges)
|
|
{
|
|
ForwardChangesTracker changes_tracker;
|
|
auto advance_while_relevant = [&, it = changes.rbegin()]
|
|
(const BufferCoord& pos) mutable {
|
|
while (it != changes.rend())
|
|
{
|
|
const Buffer::Change change{it->type,
|
|
changes_tracker.get_new_coord(it->begin),
|
|
changes_tracker.get_new_coord(it->end)};
|
|
if (not changes_tracker.relevant(change, pos))
|
|
break;
|
|
changes_tracker.update(change);
|
|
++it;
|
|
}
|
|
};
|
|
|
|
for (auto& range : ranges)
|
|
update_range(changes_tracker, range, advance_while_relevant);
|
|
}
|
|
|
|
template<typename RangeContainer>
|
|
void update_ranges(Buffer& buffer, size_t timestamp, RangeContainer&& ranges)
|
|
{
|
|
if (timestamp == buffer.timestamp())
|
|
return;
|
|
|
|
auto changes = buffer.changes_since(timestamp);
|
|
for (auto change_it = changes.begin(); change_it != changes.end(); )
|
|
{
|
|
auto forward_end = forward_sorted_until(change_it, changes.end());
|
|
auto backward_end = backward_sorted_until(change_it, changes.end());
|
|
|
|
if (forward_end >= backward_end)
|
|
{
|
|
update_forward({ change_it, forward_end }, ranges);
|
|
change_it = forward_end;
|
|
}
|
|
else
|
|
{
|
|
update_backward({ change_it, backward_end }, ranges);
|
|
change_it = backward_end;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif // changes_hh_INCLUDED
|