diff --git a/src/line_modification.cc b/src/line_modification.cc new file mode 100644 index 00000000..5da287fc --- /dev/null +++ b/src/line_modification.cc @@ -0,0 +1,105 @@ +#include "line_modification.hh" + +#include "buffer.hh" + +namespace Kakoune +{ + +namespace +{ + +struct LineChange +{ + LineChange(const Buffer::Change& change) + { + ByteCoord begin = change.begin; + ByteCoord end = change.end; + if (change.type == Buffer::Change::Insert) + { + if (change.at_end and begin != ByteCoord{0,0}) + { + kak_assert(begin.column == 0); + --begin.line; + } + pos = begin.line; + num = end.line - begin.line; + } + else + { + if (change.at_end and begin != ByteCoord{0,0}) + { + kak_assert(begin.column == 0); + --begin.line; + } + pos = begin.line; + num = begin.line - end.line; + } + } + + LineCount pos; + LineCount num; +}; + +} + +std::vector compute_line_modifications(const Buffer& buffer, size_t timestamp) +{ + std::vector res; + for (auto& buf_change : buffer.changes_since(timestamp)) + { + const LineChange change(buf_change); + + auto pos = std::upper_bound(res.begin(), res.end(), change.pos, + [](const LineCount& l, const LineModification& c) + { return l < c.new_line; }); + + if (pos != res.begin()) + { + auto& prev = *(pos-1); + if (change.pos <= prev.new_line + prev.num_added) + --pos; + else + pos = res.insert(pos, {change.pos - prev.diff(), change.pos, 0, 0}); + } + else + pos = res.insert(pos, {change.pos, change.pos, 0, 0}); + + auto& modif = *pos; + auto next = pos + 1; + if (change.num > 0) + { + modif.num_added += change.num; + for (auto it = next; it != res.end(); ++it) + it->new_line += change.num; + } + if (change.num < 0) + { + const LineCount num_removed = -change.num; + + auto delend = std::upper_bound(next, res.end(), change.pos + num_removed, + [](const LineCount& l, const LineModification& c) + { return l < c.new_line; }); + + for (auto it = next; it != delend; ++it) + { + LineCount removed_from_it = (change.pos + num_removed - it->new_line); + modif.num_removed += it->num_removed - std::min(removed_from_it, it->num_added); + modif.num_added += std::max(0_line, it->num_added - removed_from_it); + } + next = res.erase(next, delend); + + const LineCount num_added_after_pos = + modif.new_line + modif.num_added - change.pos; + const LineCount num_removed_from_added = + std::min(num_removed, num_added_after_pos); + modif.num_added -= num_removed_from_added; + modif.num_removed += num_removed - num_removed_from_added; + + for (auto it = next; it != res.end(); ++it) + it->new_line -= num_removed; + } + } + return res; +} + +} diff --git a/src/line_modification.hh b/src/line_modification.hh new file mode 100644 index 00000000..3cd2d669 --- /dev/null +++ b/src/line_modification.hh @@ -0,0 +1,26 @@ +#ifndef line_change_watcher_hh_INCLUDED +#define line_change_watcher_hh_INCLUDED + +#include "units.hh" +#include "utils.hh" + +namespace Kakoune +{ + +class Buffer; + +struct LineModification +{ + LineCount old_line; // line position in the old buffer + LineCount new_line; // new line position + LineCount num_removed; // number of lines removed after this one + LineCount num_added; // number of lines added after this one + + LineCount diff() const { return new_line - old_line + num_added - num_removed; } +}; + +std::vector compute_line_modifications(const Buffer& buffer, size_t timestamp); + +} + +#endif // line_change_watcher_hh_INCLUDED