Update ranges highlighter options according to buffer changes

This commit is contained in:
Maxime Coste 2017-01-13 13:45:46 +00:00
parent 5ad4499503
commit e7e72747ed
4 changed files with 215 additions and 161 deletions

97
src/changes.cc Normal file
View File

@ -0,0 +1,97 @@
#include "changes.hh"
namespace Kakoune
{
void ForwardChangesTracker::update(const Buffer::Change& change)
{
kak_assert(change.begin >= cur_pos);
if (change.type == Buffer::Change::Insert)
{
old_pos = get_old_coord(change.begin);
cur_pos = change.end;
}
else if (change.type == Buffer::Change::Erase)
{
old_pos = get_old_coord(change.end);
cur_pos = change.begin;
}
}
void ForwardChangesTracker::update(const Buffer& buffer, size_t& timestamp)
{
for (auto& change : buffer.changes_since(timestamp))
update(change);
timestamp = buffer.timestamp();
}
BufferCoord ForwardChangesTracker::get_old_coord(BufferCoord coord) const
{
kak_assert(cur_pos <= coord);
auto pos_change = cur_pos - old_pos;
if (cur_pos.line == coord.line)
{
kak_assert(pos_change.column <= coord.column);
coord.column -= pos_change.column;
}
coord.line -= pos_change.line;
kak_assert(old_pos <= coord);
return coord;
}
BufferCoord ForwardChangesTracker::get_new_coord(BufferCoord coord) const
{
kak_assert(old_pos <= coord);
auto pos_change = cur_pos - old_pos;
if (old_pos.line == coord.line)
{
kak_assert(-pos_change.column <= coord.column);
coord.column += pos_change.column;
}
coord.line += pos_change.line;
kak_assert(cur_pos <= coord);
return coord;
}
BufferCoord ForwardChangesTracker::get_new_coord_tolerant(BufferCoord coord) const
{
if (coord < old_pos)
return cur_pos;
return get_new_coord(coord);
}
bool ForwardChangesTracker::relevant(const Buffer::Change& change, BufferCoord old_coord) const
{
auto new_coord = get_new_coord_tolerant(old_coord);
return change.type == Buffer::Change::Insert ? change.begin <= new_coord
: change.begin < new_coord;
}
const Buffer::Change* forward_sorted_until(const Buffer::Change* first, const Buffer::Change* last)
{
if (first != last) {
const Buffer::Change* next = first;
while (++next != last) {
const auto& ref = first->type == Buffer::Change::Insert ? first->end : first->begin;
if (next->begin <= ref)
return next;
first = next;
}
}
return last;
}
const Buffer::Change* backward_sorted_until(const Buffer::Change* first, const Buffer::Change* last)
{
if (first != last) {
const Buffer::Change* next = first;
while (++next != last) {
if (first->begin <= next->end)
return next;
first = next;
}
}
return last;
}
}

89
src/changes.hh Normal file
View File

@ -0,0 +1,89 @@
#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 RangeContainer>
void update_forward(ConstArrayView<Buffer::Change> changes, RangeContainer& ranges)
{
ForwardChangesTracker changes_tracker;
auto change_it = changes.begin();
auto advance_while_relevant = [&](const BufferCoord& pos) mutable {
while (change_it != changes.end() and changes_tracker.relevant(*change_it, pos))
changes_tracker.update(*change_it++);
};
for (auto& range : ranges)
{
auto& first = get_first(range);
auto& last = get_last(range);
advance_while_relevant(first);
first = changes_tracker.get_new_coord_tolerant(first);
advance_while_relevant(last);
last = changes_tracker.get_new_coord_tolerant(last);
}
}
template<typename RangeContainer>
void update_backward(ConstArrayView<Buffer::Change> changes, RangeContainer& ranges)
{
ForwardChangesTracker changes_tracker;
using ReverseIt = std::reverse_iterator<const Buffer::Change*>;
auto change_it = ReverseIt(changes.end());
auto change_end = ReverseIt(changes.begin());
auto advance_while_relevant = [&](const BufferCoord& pos) mutable {
while (change_it != change_end)
{
auto change = *change_it;
change.begin = changes_tracker.get_new_coord(change.begin);
change.end = changes_tracker.get_new_coord(change.end);
if (not changes_tracker.relevant(change, pos))
break;
changes_tracker.update(change);
++change_it;
}
};
for (auto& range : ranges)
{
auto& first = get_first(range);
auto& last = get_last(range);
advance_while_relevant(first);
first = changes_tracker.get_new_coord_tolerant(first);
advance_while_relevant(last);
last = changes_tracker.get_new_coord_tolerant(last);
}
}
}
#endif // changes_hh_INCLUDED

View File

@ -2,6 +2,7 @@
#include "assert.hh"
#include "buffer_utils.hh"
#include "changes.hh"
#include "context.hh"
#include "containers.hh"
#include "command_manager.hh"
@ -992,6 +993,9 @@ HighlighterAndId create_flag_lines_highlighter(HighlighterParameters params)
return {"hlflags_" + params[1], make_simple_highlighter(func) };
}
BufferCoord& get_first(RangeAndFace& r) { return std::get<0>(r).begin; }
BufferCoord& get_last(RangeAndFace& r) { return std::get<0>(r).end; }
HighlighterAndId create_ranges_highlighter(HighlighterParameters params)
{
if (params.size() != 1)
@ -1011,8 +1015,25 @@ HighlighterAndId create_ranges_highlighter(HighlighterParameters params)
auto& buffer = context.buffer();
if (range_and_faces.prefix != buffer.timestamp())
{
// TODO: update ranges to current timestamp
return;
auto changes = buffer.changes_since(range_and_faces.prefix);
auto change_it = changes.begin();
while (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;
}
}
range_and_faces.prefix = buffer.timestamp();
}
for (auto& range : ranges)

View File

@ -1,7 +1,8 @@
#include "selection.hh"
#include "utf8.hh"
#include "buffer_utils.hh"
#include "changes.hh"
#include "utf8.hh"
namespace Kakoune
{
@ -96,164 +97,10 @@ Iterator merge_overlapping(Iterator begin, Iterator end, size_t& main, OverlapsF
return begin + i + 1;
}
// 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)
{
kak_assert(change.begin >= cur_pos);
if (change.type == Buffer::Change::Insert)
{
old_pos = get_old_coord(change.begin);
cur_pos = change.end;
}
else if (change.type == Buffer::Change::Erase)
{
old_pos = get_old_coord(change.end);
cur_pos = change.begin;
}
}
void update(const Buffer& buffer, size_t& timestamp)
{
for (auto& change : buffer.changes_since(timestamp))
update(change);
timestamp = buffer.timestamp();
}
BufferCoord get_old_coord(BufferCoord coord) const
{
kak_assert(cur_pos <= coord);
auto pos_change = cur_pos - old_pos;
if (cur_pos.line == coord.line)
{
kak_assert(pos_change.column <= coord.column);
coord.column -= pos_change.column;
}
coord.line -= pos_change.line;
kak_assert(old_pos <= coord);
return coord;
}
BufferCoord get_new_coord(BufferCoord coord) const
{
kak_assert(old_pos <= coord);
auto pos_change = cur_pos - old_pos;
if (old_pos.line == coord.line)
{
kak_assert(-pos_change.column <= coord.column);
coord.column += pos_change.column;
}
coord.line += pos_change.line;
kak_assert(cur_pos <= coord);
return coord;
}
BufferCoord get_new_coord_tolerant(BufferCoord coord) const
{
if (coord < old_pos)
return cur_pos;
return get_new_coord(coord);
}
bool relevant(const Buffer::Change& change, BufferCoord old_coord) const
{
auto new_coord = get_new_coord_tolerant(old_coord);
return change.type == Buffer::Change::Insert ? change.begin <= new_coord
: change.begin < new_coord;
}
};
const Buffer::Change* forward_sorted_until(const Buffer::Change* first, const Buffer::Change* last)
{
if (first != last) {
const Buffer::Change* next = first;
while (++next != last) {
const auto& ref = first->type == Buffer::Change::Insert ? first->end : first->begin;
if (next->begin <= ref)
return next;
first = next;
}
}
return last;
}
const Buffer::Change* backward_sorted_until(const Buffer::Change* first, const Buffer::Change* last)
{
if (first != last) {
const Buffer::Change* next = first;
while (++next != last) {
if (first->begin <= next->end)
return next;
first = next;
}
}
return last;
}
void update_forward(ConstArrayView<Buffer::Change> changes, Vector<Selection>& selections)
{
ForwardChangesTracker changes_tracker;
auto change_it = changes.begin();
auto advance_while_relevant = [&](const BufferCoord& pos) mutable {
while (change_it != changes.end() and changes_tracker.relevant(*change_it, pos))
changes_tracker.update(*change_it++);
};
for (auto& sel : selections)
{
auto& sel_min = sel.min();
auto& sel_max = sel.max();
advance_while_relevant(sel_min);
sel_min = changes_tracker.get_new_coord_tolerant(sel_min);
advance_while_relevant(sel_max);
sel_max = changes_tracker.get_new_coord_tolerant(sel_max);
}
kak_assert(std::is_sorted(selections.begin(), selections.end(), compare_selections));
}
void update_backward(ConstArrayView<Buffer::Change> changes, Vector<Selection>& selections)
{
ForwardChangesTracker changes_tracker;
using ReverseIt = std::reverse_iterator<const Buffer::Change*>;
auto change_it = ReverseIt(changes.end());
auto change_end = ReverseIt(changes.begin());
auto advance_while_relevant = [&](const BufferCoord& pos) mutable {
while (change_it != change_end)
{
auto change = *change_it;
change.begin = changes_tracker.get_new_coord(change.begin);
change.end = changes_tracker.get_new_coord(change.end);
if (not changes_tracker.relevant(change, pos))
break;
changes_tracker.update(change);
++change_it;
}
};
for (auto& sel : selections)
{
auto& sel_min = sel.min();
auto& sel_max = sel.max();
advance_while_relevant(sel_min);
sel_min = changes_tracker.get_new_coord_tolerant(sel_min);
advance_while_relevant(sel_max);
sel_max = changes_tracker.get_new_coord_tolerant(sel_max);
}
kak_assert(std::is_sorted(selections.begin(), selections.end(), compare_selections));
}
}
BufferCoord& get_first(Selection& sel) { return sel.min(); }
BufferCoord& get_last(Selection& sel) { return sel.max(); }
Vector<Selection> compute_modified_ranges(Buffer& buffer, size_t timestamp)
{
@ -367,11 +214,11 @@ void update_selections(Vector<Selection>& selections, size_t& main, Buffer& buff
update_backward({ change_it, backward_end }, selections);
change_it = backward_end;
}
kak_assert(std::is_sorted(selections.begin(), selections.end(),
compare_selections));
selections.erase(
merge_overlapping(selections.begin(), selections.end(),
main, overlaps), selections.end());
kak_assert(std::is_sorted(selections.begin(), selections.end(),
compare_selections));
}
for (auto& sel : selections)
clamp(sel, buffer);