Rewrite line modifications, hopefully with easier to comprehend code
This commit is contained in:
parent
b4661e37d2
commit
0b4ecef2d2
|
@ -749,7 +749,7 @@ void update_matches(const Buffer& buffer, ArrayView<LineModification> modifs,
|
||||||
if (not erase and modif_it != modifs.begin())
|
if (not erase and modif_it != modifs.begin())
|
||||||
{
|
{
|
||||||
auto& prev = *(modif_it-1);
|
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();
|
it->line += prev.diff();
|
||||||
}
|
}
|
||||||
erase = erase or (it->line >= buffer.line_count());
|
erase = erase or (it->line >= buffer.line_count());
|
||||||
|
@ -774,7 +774,7 @@ void update_matches(const Buffer& buffer, ArrayView<LineModification> modifs,
|
||||||
for (auto& modif : modifs)
|
for (auto& modif : modifs)
|
||||||
{
|
{
|
||||||
for (auto line = modif.new_line;
|
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)
|
line < buffer.line_count(); ++line)
|
||||||
{
|
{
|
||||||
auto l = buffer[line];
|
auto l = buffer[line];
|
||||||
|
|
|
@ -5,41 +5,33 @@
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace
|
static LineModification make_line_modif(const Buffer::Change& change)
|
||||||
{
|
{
|
||||||
|
LineModification res = {};
|
||||||
struct LineChange
|
res.old_line = change.begin.line;
|
||||||
{
|
res.new_line = change.begin.line;
|
||||||
LineChange(const Buffer::Change& change)
|
if (change.type == Buffer::Change::Insert)
|
||||||
{
|
{
|
||||||
ByteCoord begin = change.begin;
|
res.num_added = change.end.line - change.begin.line;
|
||||||
ByteCoord end = change.end;
|
// inserted a new line at buffer end but end coord is on same line
|
||||||
if (change.type == Buffer::Change::Insert)
|
if (change.at_end and change.end.column != 0)
|
||||||
{
|
++res.num_added;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
LineCount pos;
|
{
|
||||||
LineCount num;
|
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<LineModification> compute_line_modifications(const Buffer& buffer, size_t timestamp)
|
Vector<LineModification> compute_line_modifications(const Buffer& buffer, size_t timestamp)
|
||||||
|
@ -47,60 +39,51 @@ Vector<LineModification> compute_line_modifications(const Buffer& buffer, size_t
|
||||||
Vector<LineModification> res;
|
Vector<LineModification> res;
|
||||||
for (auto& buf_change : buffer.changes_since(timestamp))
|
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)
|
[](const LineCount& l, const LineModification& c)
|
||||||
{ return l < c.new_line; });
|
{ return l < c.new_line; });
|
||||||
|
|
||||||
if (pos != res.begin())
|
if (pos != res.begin())
|
||||||
{
|
{
|
||||||
auto& prev = *(pos-1);
|
auto& prev = *(pos-1);
|
||||||
if (change.pos <= prev.new_line + prev.num_added)
|
if (change.new_line <= prev.new_line + prev.num_added)
|
||||||
|
{
|
||||||
--pos;
|
--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
|
else
|
||||||
pos = res.insert(pos, {change.pos - prev.diff(), change.pos, 0, 0});
|
{
|
||||||
|
change.old_line -= prev.diff();
|
||||||
|
pos = res.insert(pos, change);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pos = res.insert(pos, {change.pos, change.pos, 0, 0});
|
pos = res.insert(pos, change);
|
||||||
|
|
||||||
auto& modif = *pos;
|
auto& modif = *pos;
|
||||||
auto next = pos + 1;
|
auto next = pos + 1;
|
||||||
if (change.num > 0)
|
if (buf_change.type == Buffer::Change::Erase)
|
||||||
{
|
{
|
||||||
modif.num_added += change.num;
|
auto delend = std::upper_bound(next, res.end(), change.new_line + change.num_removed,
|
||||||
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)
|
[](const LineCount& l, const LineModification& c)
|
||||||
{ return l < c.new_line; });
|
{ return l < c.new_line; });
|
||||||
|
|
||||||
for (auto it = next; it != delend; ++it)
|
for (auto it = next; it != delend; ++it)
|
||||||
{
|
{
|
||||||
LineCount removed_from_it = (change.pos + num_removed - it->new_line);
|
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 - std::min(removed_from_it, it->num_added);
|
modif.num_removed += it->num_removed - removed_from_previously_added_by_it;
|
||||||
modif.num_added += std::max(0_line, it->num_added - removed_from_it);
|
modif.num_added += it->num_added - removed_from_previously_added_by_it;
|
||||||
}
|
}
|
||||||
next = res.erase(next, delend);
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ struct LineModification
|
||||||
{
|
{
|
||||||
LineCount old_line; // line position in the old buffer
|
LineCount old_line; // line position in the old buffer
|
||||||
LineCount new_line; // new line position
|
LineCount new_line; // new line position
|
||||||
LineCount num_removed; // number of lines removed after this one
|
LineCount num_removed; // number of lines removed (including this one)
|
||||||
LineCount num_added; // number of lines added after this one
|
LineCount num_added; // number of lines added (including this one)
|
||||||
|
|
||||||
LineCount diff() const { return new_line - old_line + num_added - num_removed; }
|
LineCount diff() const { return new_line - old_line + num_added - num_removed; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
#include "keys.hh"
|
#include "keys.hh"
|
||||||
#include "selectors.hh"
|
#include "selectors.hh"
|
||||||
#include "word_db.hh"
|
#include "word_db.hh"
|
||||||
|
#include "line_modification.hh"
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
using namespace Kakoune;
|
using namespace Kakoune;
|
||||||
|
|
||||||
|
@ -151,6 +154,80 @@ void test_keys()
|
||||||
kak_assert(keys == parsed_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()
|
void run_unit_tests()
|
||||||
{
|
{
|
||||||
test_utf8();
|
test_utf8();
|
||||||
|
@ -159,4 +236,5 @@ void run_unit_tests()
|
||||||
test_buffer();
|
test_buffer();
|
||||||
test_undo_group_optimizer();
|
test_undo_group_optimizer();
|
||||||
test_word_db();
|
test_word_db();
|
||||||
|
test_line_modifications();
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,13 +108,13 @@ void WordDB::update_db()
|
||||||
|
|
||||||
kak_assert((int)new_lines.size() == (int)modif.new_line);
|
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());
|
kak_assert(old_line < m_lines.size());
|
||||||
remove_words(get_words(SharedString{m_lines[(int)old_line++]}));
|
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())
|
if (modif.new_line + l >= buffer.line_count())
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user