2014-05-26 21:59:08 +02:00
|
|
|
#include "line_modification.hh"
|
|
|
|
|
|
|
|
#include "buffer.hh"
|
2015-05-22 14:58:56 +02:00
|
|
|
#include "unit_tests.hh"
|
2014-05-26 21:59:08 +02:00
|
|
|
|
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2015-02-01 00:50:24 +01:00
|
|
|
static LineModification make_line_modif(const Buffer::Change& change)
|
2014-05-26 21:59:08 +02:00
|
|
|
{
|
2015-02-01 23:33:59 +01:00
|
|
|
LineCount num_added = 0, num_removed = 0;
|
2015-02-01 00:50:24 +01:00
|
|
|
if (change.type == Buffer::Change::Insert)
|
2014-05-26 21:59:08 +02:00
|
|
|
{
|
2015-02-01 23:33:59 +01:00
|
|
|
num_added = change.end.line - change.begin.line;
|
2015-02-01 00:50:24 +01:00
|
|
|
// inserted a new line at buffer end but end coord is on same line
|
|
|
|
if (change.at_end and change.end.column != 0)
|
2015-02-01 23:33:59 +01:00
|
|
|
++num_added;
|
2014-05-26 21:59:08 +02:00
|
|
|
}
|
2015-02-01 00:50:24 +01:00
|
|
|
else
|
|
|
|
{
|
2015-02-01 23:33:59 +01:00
|
|
|
num_removed = change.end.line - change.begin.line;
|
2015-02-01 00:50:24 +01:00
|
|
|
// removed last line, but end coord is on same line
|
|
|
|
if (change.at_end and change.end.column != 0)
|
2015-02-01 23:33:59 +01:00
|
|
|
++num_removed;
|
2015-02-01 00:50:24 +01:00
|
|
|
}
|
|
|
|
// modified a line
|
|
|
|
if (not change.at_end and
|
|
|
|
(change.begin.column != 0 or change.end.column != 0))
|
|
|
|
{
|
2015-02-01 23:33:59 +01:00
|
|
|
++num_removed;
|
|
|
|
++num_added;
|
2015-02-01 00:50:24 +01:00
|
|
|
}
|
2015-02-01 23:33:59 +01:00
|
|
|
return { change.begin.line, change.begin.line, num_removed, num_added };
|
2014-05-26 21:59:08 +02:00
|
|
|
}
|
|
|
|
|
2015-01-12 14:58:41 +01:00
|
|
|
Vector<LineModification> compute_line_modifications(const Buffer& buffer, size_t timestamp)
|
2014-05-26 21:59:08 +02:00
|
|
|
{
|
2015-01-12 14:58:41 +01:00
|
|
|
Vector<LineModification> res;
|
2014-05-26 21:59:08 +02:00
|
|
|
for (auto& buf_change : buffer.changes_since(timestamp))
|
|
|
|
{
|
2015-02-01 00:50:24 +01:00
|
|
|
auto change = make_line_modif(buf_change);
|
2014-05-26 21:59:08 +02:00
|
|
|
|
2015-02-01 00:50:24 +01:00
|
|
|
auto pos = std::upper_bound(res.begin(), res.end(), change.new_line,
|
2014-05-26 21:59:08 +02:00
|
|
|
[](const LineCount& l, const LineModification& c)
|
|
|
|
{ return l < c.new_line; });
|
|
|
|
|
|
|
|
if (pos != res.begin())
|
|
|
|
{
|
|
|
|
auto& prev = *(pos-1);
|
2015-02-01 00:50:24 +01:00
|
|
|
if (change.new_line <= prev.new_line + prev.num_added)
|
|
|
|
{
|
2014-05-26 21:59:08 +02:00
|
|
|
--pos;
|
2015-02-01 23:33:59 +01:00
|
|
|
const 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));
|
|
|
|
|
2015-02-01 00:50:24 +01:00
|
|
|
pos->num_removed += change.num_removed - removed_from_previously_added_by_pos;
|
|
|
|
pos->num_added += change.num_added - removed_from_previously_added_by_pos;
|
|
|
|
}
|
2014-05-26 21:59:08 +02:00
|
|
|
else
|
2015-02-01 00:50:24 +01:00
|
|
|
{
|
|
|
|
change.old_line -= prev.diff();
|
|
|
|
pos = res.insert(pos, change);
|
|
|
|
}
|
2014-05-26 21:59:08 +02:00
|
|
|
}
|
|
|
|
else
|
2015-02-01 00:50:24 +01:00
|
|
|
pos = res.insert(pos, change);
|
2014-05-26 21:59:08 +02:00
|
|
|
|
|
|
|
auto next = pos + 1;
|
2015-02-01 23:33:59 +01:00
|
|
|
auto diff = buf_change.end.line - buf_change.begin.line;
|
2015-02-01 00:50:24 +01:00
|
|
|
if (buf_change.type == Buffer::Change::Erase)
|
2014-05-26 21:59:08 +02:00
|
|
|
{
|
2015-02-01 00:50:24 +01:00
|
|
|
auto delend = std::upper_bound(next, res.end(), change.new_line + change.num_removed,
|
2014-05-26 21:59:08 +02:00
|
|
|
[](const LineCount& l, const LineModification& c)
|
|
|
|
{ return l < c.new_line; });
|
|
|
|
|
|
|
|
for (auto it = next; it != delend; ++it)
|
|
|
|
{
|
2015-02-01 23:33:59 +01:00
|
|
|
const LineCount removed_from_previously_added_by_it =
|
|
|
|
std::min(it->num_added, change.new_line + change.num_removed - it->new_line);
|
|
|
|
|
|
|
|
pos->num_removed += it->num_removed - removed_from_previously_added_by_it;
|
|
|
|
pos->num_added += it->num_added - removed_from_previously_added_by_it;
|
2014-05-26 21:59:08 +02:00
|
|
|
}
|
|
|
|
next = res.erase(next, delend);
|
2015-02-01 00:50:24 +01:00
|
|
|
|
2015-02-01 23:33:59 +01:00
|
|
|
for (auto it = next; it != res.end(); ++it)
|
|
|
|
it->new_line -= diff;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (auto it = next; it != res.end(); ++it)
|
|
|
|
it->new_line += diff;
|
|
|
|
}
|
2014-05-26 21:59:08 +02:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-05-22 14:58:56 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
UnitTest 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 });
|
|
|
|
|
|
|
|
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 });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
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 });
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
|
2014-05-26 21:59:08 +02:00
|
|
|
}
|