diff --git a/src/highlighters.cc b/src/highlighters.cc index 1f879b0b..20f09181 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -749,7 +749,7 @@ void update_matches(const Buffer& buffer, ArrayView modifs, if (not erase and modif_it != modifs.begin()) { auto& prev = *(modif_it-1); - erase = it->line <= prev.old_line + prev.num_removed; + erase = it->line < prev.old_line + prev.num_removed; it->line += prev.diff(); } erase = erase or (it->line >= buffer.line_count()); @@ -774,7 +774,7 @@ void update_matches(const Buffer& buffer, ArrayView modifs, for (auto& modif : modifs) { for (auto line = modif.new_line; - line < modif.new_line + modif.num_added+1 and + line < modif.new_line + modif.num_added and line < buffer.line_count(); ++line) { auto l = buffer[line]; diff --git a/src/line_modification.cc b/src/line_modification.cc index 96249856..1943350f 100644 --- a/src/line_modification.cc +++ b/src/line_modification.cc @@ -5,41 +5,33 @@ namespace Kakoune { -namespace +static LineModification make_line_modif(const Buffer::Change& change) { - -struct LineChange -{ - LineChange(const Buffer::Change& change) + LineModification res = {}; + res.old_line = change.begin.line; + res.new_line = change.begin.line; + if (change.type == Buffer::Change::Insert) { - 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; - } + res.num_added = change.end.line - change.begin.line; + // inserted a new line at buffer end but end coord is on same line + if (change.at_end and change.end.column != 0) + ++res.num_added; } - - LineCount pos; - LineCount num; -}; - + else + { + res.num_removed = change.end.line - change.begin.line; + // removed last line, but end coord is on same line + if (change.at_end and change.end.column != 0) + ++res.num_removed; + } + // modified a line + if (not change.at_end and + (change.begin.column != 0 or change.end.column != 0)) + { + ++res.num_removed; + ++res.num_added; + } + return res; } Vector compute_line_modifications(const Buffer& buffer, size_t timestamp) @@ -47,60 +39,51 @@ Vector compute_line_modifications(const Buffer& buffer, size_t Vector res; for (auto& buf_change : buffer.changes_since(timestamp)) { - const LineChange change(buf_change); + auto change = make_line_modif(buf_change); - auto pos = std::upper_bound(res.begin(), res.end(), change.pos, + auto pos = std::upper_bound(res.begin(), res.end(), change.new_line, [](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) + if (change.new_line <= prev.new_line + prev.num_added) + { --pos; + LineCount removed_from_previously_added_by_pos = clamp(pos->new_line + pos->num_added - change.new_line, 0_line, std::min(pos->num_added, change.num_removed)); + pos->num_removed += change.num_removed - removed_from_previously_added_by_pos; + pos->num_added += change.num_added - removed_from_previously_added_by_pos; + } else - pos = res.insert(pos, {change.pos - prev.diff(), change.pos, 0, 0}); + { + change.old_line -= prev.diff(); + pos = res.insert(pos, change); + } } else - pos = res.insert(pos, {change.pos, change.pos, 0, 0}); + pos = res.insert(pos, change); auto& modif = *pos; auto next = pos + 1; - if (change.num > 0) + if (buf_change.type == Buffer::Change::Erase) { - 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, + auto delend = std::upper_bound(next, res.end(), change.new_line + change.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); + LineCount removed_from_previously_added_by_it = std::min(it->num_added, change.new_line + change.num_removed - it->new_line); + modif.num_removed += it->num_removed - removed_from_previously_added_by_it; + modif.num_added += it->num_added - removed_from_previously_added_by_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); - - kak_assert(modif.num_added >= num_removed_from_added); - - 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; } + + auto diff = buf_change.end.line - buf_change.begin.line; + for (auto it = next; it != res.end(); ++it) + it->new_line += diff; } return res; } diff --git a/src/line_modification.hh b/src/line_modification.hh index d56a65d4..7c21503c 100644 --- a/src/line_modification.hh +++ b/src/line_modification.hh @@ -14,8 +14,8 @@ 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 num_removed; // number of lines removed (including this one) + LineCount num_added; // number of lines added (including this one) LineCount diff() const { return new_line - old_line + num_added - num_removed; } }; diff --git a/src/unit_tests.cc b/src/unit_tests.cc index 504c9246..ec9bb64c 100644 --- a/src/unit_tests.cc +++ b/src/unit_tests.cc @@ -3,6 +3,9 @@ #include "keys.hh" #include "selectors.hh" #include "word_db.hh" +#include "line_modification.hh" + +#include using namespace Kakoune; @@ -151,6 +154,80 @@ void test_keys() kak_assert(keys == parsed_keys); } +bool operator==(const LineModification& lhs, const LineModification& rhs) +{ + return std::tie(lhs.old_line, lhs.new_line, lhs.num_removed, lhs.num_added) == + std::tie(rhs.old_line, rhs.new_line, rhs.num_removed, rhs.num_added); +} + +void test_line_modifications() +{ + { + Buffer buffer("test", Buffer::Flags::None, { "line 1\n"_ss, "line 2\n"_ss }); + auto ts = buffer.timestamp(); + buffer.erase(buffer.iterator_at({1, 0}), buffer.iterator_at({2, 0})); + + auto modifs = compute_line_modifications(buffer, ts); + kak_assert(modifs.size() == 1 && modifs[0] == LineModification{ 1 COMMA 1 COMMA 1 COMMA 0 }); + } + + { + Buffer buffer("test", Buffer::Flags::None, { "line 1\n"_ss, "line 2\n"_ss }); + auto ts = buffer.timestamp(); + buffer.insert(buffer.iterator_at({1, 7}), "line 3"); + + auto modifs = compute_line_modifications(buffer, ts); + kak_assert(modifs.size() == 1 && modifs[0] == LineModification{ 2 COMMA 2 COMMA 0 COMMA 1 }); + } + + { + Buffer buffer("test", Buffer::Flags::None, + { "line 1\n"_ss, "line 2\n"_ss, "line 3\n"_ss }); + + auto ts = buffer.timestamp(); + buffer.insert(buffer.iterator_at({1, 4}), "hoho\nhehe"); + buffer.erase(buffer.iterator_at({0, 0}), buffer.iterator_at({1, 0})); + + auto modifs = compute_line_modifications(buffer, ts); + kak_assert(modifs.size() == 1 && modifs[0] == LineModification{ 0 COMMA 0 COMMA 2 COMMA 2 }); + } + + { + Buffer buffer("test", Buffer::Flags::None, + { "line 1\n"_ss, "line 2\n"_ss, "line 3\n"_ss, "line 4\n"_ss }); + + WordDB word_db(buffer); + word_db.find_matching("", prefix_match); + + auto ts = buffer.timestamp(); + buffer.erase(buffer.iterator_at({0,0}), buffer.iterator_at({3,0})); + buffer.insert(buffer.iterator_at({1,0}), "newline 1\nnewline 2\nnewline 3\n"); + buffer.erase(buffer.iterator_at({0,0}), buffer.iterator_at({1,0})); + { + auto modifs = compute_line_modifications(buffer, ts); + kak_assert(modifs.size() == 1 && modifs[0] == LineModification{ 0 COMMA 0 COMMA 4 COMMA 3 }); + } + buffer.insert(buffer.iterator_at({3,0}), "newline 4\n"); + + { + auto modifs = compute_line_modifications(buffer, ts); + kak_assert(modifs.size() == 1 && modifs[0] == LineModification{ 0 COMMA 0 COMMA 4 COMMA 4 }); + } + + word_db.find_matching("", prefix_match); + } + + { + Buffer buffer("test", Buffer::Flags::None, { "line 1\n"_ss }); + auto ts = buffer.timestamp(); + buffer.insert(buffer.iterator_at({0,0}), "n"); + buffer.insert(buffer.iterator_at({0,1}), "e"); + buffer.insert(buffer.iterator_at({0,2}), "w"); + auto modifs = compute_line_modifications(buffer, ts); + kak_assert(modifs.size() == 1 && modifs[0] == LineModification{ 0 COMMA 0 COMMA 1 COMMA 1 }); + } +} + void run_unit_tests() { test_utf8(); @@ -159,4 +236,5 @@ void run_unit_tests() test_buffer(); test_undo_group_optimizer(); test_word_db(); + test_line_modifications(); } diff --git a/src/word_db.cc b/src/word_db.cc index 6b0eeb0a..2786396b 100644 --- a/src/word_db.cc +++ b/src/word_db.cc @@ -108,13 +108,13 @@ void WordDB::update_db() kak_assert((int)new_lines.size() == (int)modif.new_line); - while (old_line <= modif.old_line + modif.num_removed) + while (old_line < modif.old_line + modif.num_removed) { kak_assert(old_line < m_lines.size()); remove_words(get_words(SharedString{m_lines[(int)old_line++]})); } - for (auto l = 0_line; l <= modif.num_added; ++l) + for (auto l = 0_line; l < modif.num_added; ++l) { if (modif.new_line + l >= buffer.line_count()) break;