#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 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 void update_forward(ConstArrayView 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 void update_backward(ConstArrayView 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 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