Remove Modification
Lets consider that as a failed experiment.
You see, I learned something today, for a complicated problem, it
is important to keep as much knowledge of the exact problem as
possible. the Modification approach failed because it tried to
solve the general problem, which is quite complex. The new approach,
which keeps the knowledge that selections are sorted is much simpler
and faster (see f49bec8021
).
This commit is contained in:
parent
ce469398c5
commit
9870ac22f6
|
@ -1,232 +0,0 @@
|
||||||
#include "modification.hh"
|
|
||||||
|
|
||||||
#include "buffer.hh"
|
|
||||||
|
|
||||||
namespace Kakoune
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
ByteCount change_added_column(const Buffer::Change& change)
|
|
||||||
{
|
|
||||||
kak_assert(change.type == Buffer::Change::Insert);
|
|
||||||
if (change.begin.line == change.end.line)
|
|
||||||
return change.end.column - change.begin.column;
|
|
||||||
else
|
|
||||||
return change.end.column;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteCoord Modification::added_end() const
|
|
||||||
{
|
|
||||||
if (num_added.line)
|
|
||||||
return { new_coord.line + num_added.line, num_added.column };
|
|
||||||
else
|
|
||||||
return { new_coord.line, new_coord.column + num_added.column };
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteCoord Modification::removed_end() const
|
|
||||||
{
|
|
||||||
if (num_removed.line)
|
|
||||||
return { old_coord.line + num_removed.line, num_removed.column };
|
|
||||||
else
|
|
||||||
return { old_coord.line, old_coord.column + num_removed.column };
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteCoord Modification::get_old_coord(ByteCoord coord) const
|
|
||||||
{
|
|
||||||
Modification inverse = { new_coord, old_coord, num_added, num_removed };
|
|
||||||
return inverse.get_new_coord(coord);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteCoord Modification::get_new_coord(ByteCoord coord) const
|
|
||||||
{
|
|
||||||
if (coord < old_coord)
|
|
||||||
return coord;
|
|
||||||
|
|
||||||
// apply remove
|
|
||||||
if (coord < removed_end())
|
|
||||||
coord = old_coord;
|
|
||||||
else if (coord.line == old_coord.line + num_removed.line)
|
|
||||||
{
|
|
||||||
coord.line = old_coord.line;
|
|
||||||
if (num_removed.line != 0)
|
|
||||||
coord.column += old_coord.column;
|
|
||||||
coord.column -= num_removed.column;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
coord.line -= num_removed.line;
|
|
||||||
|
|
||||||
// apply move
|
|
||||||
coord.line += new_coord.line - old_coord.line;
|
|
||||||
if (coord.line == new_coord.line)
|
|
||||||
coord.column += new_coord.column - old_coord.column;
|
|
||||||
|
|
||||||
// apply add
|
|
||||||
if (coord.line == new_coord.line)
|
|
||||||
{
|
|
||||||
if (num_added.line == 0)
|
|
||||||
coord.column += num_added.column;
|
|
||||||
else
|
|
||||||
coord.column += num_added.column - new_coord.column;
|
|
||||||
}
|
|
||||||
coord.line += num_added.line;
|
|
||||||
|
|
||||||
return coord;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Modification> compute_modifications(memoryview<Buffer::Change> changes)
|
|
||||||
{
|
|
||||||
std::vector<Modification> res;
|
|
||||||
for (auto& change : changes)
|
|
||||||
{
|
|
||||||
auto pos = std::upper_bound(res.begin(), res.end(), change.begin,
|
|
||||||
[](const ByteCoord& l, const Modification& c)
|
|
||||||
{ return l < c.new_coord; });
|
|
||||||
|
|
||||||
if (pos != res.begin())
|
|
||||||
{
|
|
||||||
auto& prev = *(pos-1);
|
|
||||||
if (change.begin <= prev.added_end())
|
|
||||||
--pos;
|
|
||||||
else
|
|
||||||
pos = res.insert(pos, {prev.get_old_coord(change.begin), change.begin, {}, {}});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pos = res.insert(pos, {change.begin, change.begin, {}, {}});
|
|
||||||
|
|
||||||
auto& modif = *pos;
|
|
||||||
auto next = pos + 1;
|
|
||||||
if (change.type == Buffer::Change::Insert)
|
|
||||||
{
|
|
||||||
const LineCount last_line = modif.new_coord.line + modif.num_added.line;
|
|
||||||
|
|
||||||
modif.num_added.line += change.end.line - change.begin.line;
|
|
||||||
|
|
||||||
if (change.begin.line == last_line)
|
|
||||||
{
|
|
||||||
if (change.end.line == change.begin.line)
|
|
||||||
modif.num_added.column += change.end.column - change.begin.column;
|
|
||||||
else
|
|
||||||
modif.num_added.column = change.end.column;
|
|
||||||
kak_assert(modif.num_added.column >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = next; it != res.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->new_coord.line == change.begin.line)
|
|
||||||
it->new_coord.column += change.end.column - change.begin.column;
|
|
||||||
it->new_coord.line += change.end.line - change.begin.line;
|
|
||||||
|
|
||||||
#ifdef KAK_DEBUG
|
|
||||||
auto ref_new_coord = (it-1)->get_new_coord(it->old_coord);
|
|
||||||
kak_assert(it->new_coord == ref_new_coord);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ByteCoord num_removed = { change.end.line - change.begin.line, 0 };
|
|
||||||
if (num_removed.line != 0)
|
|
||||||
num_removed.column = change.end.column;
|
|
||||||
else
|
|
||||||
num_removed.column = change.end.column - change.begin.column;
|
|
||||||
|
|
||||||
ByteCoord num_added;
|
|
||||||
|
|
||||||
// merge modifications lying in the erased range.
|
|
||||||
auto delend = std::upper_bound(next, res.end(), change.end,
|
|
||||||
[](const ByteCoord& l, const Modification& c)
|
|
||||||
{ return l < c.new_coord; });
|
|
||||||
|
|
||||||
for (auto it = next; it != delend; ++it)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
LineCount removed_from_it = change.end.line - it->new_coord.line;
|
|
||||||
num_removed.line += it->num_removed.line - std::min(removed_from_it, it->num_added.line);
|
|
||||||
num_added.line += std::max(0_line, it->num_added.line - removed_from_it);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it->new_coord.line == change.end.line)
|
|
||||||
num_removed.column += it->num_removed.column;
|
|
||||||
if (it->new_coord.line + it->num_added.line == change.end.line)
|
|
||||||
{
|
|
||||||
ByteCount removed_from_added = std::min(num_removed.column, it->num_added.column);
|
|
||||||
num_added.column += std::max(0_byte, it->num_added.column - removed_from_added);
|
|
||||||
num_removed.column -= removed_from_added;
|
|
||||||
}
|
|
||||||
if (it->new_coord.line == change.begin.line)
|
|
||||||
num_removed.column += it->new_coord.column - change.begin.column;
|
|
||||||
}
|
|
||||||
next = res.erase(next, delend);
|
|
||||||
|
|
||||||
// update modification with changes
|
|
||||||
if (change.end.line == modif.new_coord.line + modif.num_added.line)
|
|
||||||
{
|
|
||||||
ByteCount removed_from_added = std::min(num_removed.column, modif.num_added.column);
|
|
||||||
modif.num_added.column = std::max(0_byte, modif.num_added.column - removed_from_added) + num_added.column;
|
|
||||||
modif.num_removed.column += num_removed.column - removed_from_added;
|
|
||||||
}
|
|
||||||
else if (change.end.line > modif.new_coord.line + modif.num_added.line)
|
|
||||||
{
|
|
||||||
modif.num_added.column = num_added.column;
|
|
||||||
modif.num_removed.column = num_removed.column;
|
|
||||||
}
|
|
||||||
if (change.begin.line == modif.new_coord.line)
|
|
||||||
modif.num_added.column += change.begin.column - modif.new_coord.column;
|
|
||||||
|
|
||||||
{
|
|
||||||
LineCount change_pos_in_modif = change.begin.line - modif.new_coord.line;
|
|
||||||
LineCount removed_from_added = std::min(num_removed.line, modif.num_added.line - change_pos_in_modif);
|
|
||||||
modif.num_added.line = std::max(0_line, modif.num_added.line - removed_from_added) + num_added.line;
|
|
||||||
modif.num_removed.line += num_removed.line - removed_from_added;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = next; it != res.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->new_coord.line == change.end.line)
|
|
||||||
it->new_coord.column += change.begin.column - change.end.column;
|
|
||||||
it->new_coord.line += change.begin.line - change.end.line;
|
|
||||||
|
|
||||||
#ifdef KAK_DEBUG
|
|
||||||
auto ref_new_coord = (it-1)->get_new_coord(it->old_coord);
|
|
||||||
kak_assert(it->new_coord == ref_new_coord);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef KAK_DEBUG
|
|
||||||
for (size_t i = 0; i+1 < res.size(); ++i)
|
|
||||||
{
|
|
||||||
auto old_coord = res[i].get_old_coord(res[i+1].new_coord);
|
|
||||||
kak_assert(res[i+1].old_coord == old_coord);
|
|
||||||
auto new_coord = res[i].get_new_coord(res[i+1].old_coord);
|
|
||||||
kak_assert(res[i+1].new_coord == new_coord);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Modification> compute_modifications(const Buffer& buffer, size_t timestamp)
|
|
||||||
{
|
|
||||||
return compute_modifications(buffer.changes_since(timestamp));
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteCoord update_pos(memoryview<Modification> modifs, ByteCoord pos)
|
|
||||||
{
|
|
||||||
auto modif_it = std::upper_bound(modifs.begin(), modifs.end(), pos,
|
|
||||||
[](const ByteCoord& c, const Modification& m)
|
|
||||||
{ return c < m.old_coord; });
|
|
||||||
if (modif_it != modifs.begin())
|
|
||||||
{
|
|
||||||
auto& prev = *(modif_it-1);
|
|
||||||
return prev.get_new_coord(pos);
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
#ifndef modification_hh_INCLUDED
|
|
||||||
#define modification_hh_INCLUDED
|
|
||||||
|
|
||||||
#include "coord.hh"
|
|
||||||
#include "utils.hh"
|
|
||||||
#include "buffer.hh"
|
|
||||||
|
|
||||||
namespace Kakoune
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Modification
|
|
||||||
{
|
|
||||||
ByteCoord old_coord;
|
|
||||||
ByteCoord new_coord;
|
|
||||||
ByteCoord num_removed;
|
|
||||||
ByteCoord num_added;
|
|
||||||
|
|
||||||
ByteCoord added_end() const;
|
|
||||||
ByteCoord removed_end() const;
|
|
||||||
|
|
||||||
ByteCoord get_old_coord(ByteCoord coord) const;
|
|
||||||
ByteCoord get_new_coord(ByteCoord coord) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Modification> compute_modifications(const Buffer& buffer, size_t timestamp);
|
|
||||||
std::vector<Modification> compute_modifications(memoryview<Buffer::Change> changes);
|
|
||||||
|
|
||||||
ByteCoord update_pos(memoryview<Modification> modifs, ByteCoord pos);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // modification_hh_INCLUDED
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "user_interface.hh"
|
#include "user_interface.hh"
|
||||||
#include "utf8_iterator.hh"
|
#include "utf8_iterator.hh"
|
||||||
#include "debug.hh"
|
#include "debug.hh"
|
||||||
#include "modification.hh"
|
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
#include "selectors.hh"
|
#include "selectors.hh"
|
||||||
#include "word_db.hh"
|
#include "word_db.hh"
|
||||||
|
|
||||||
#include "modification.hh"
|
|
||||||
|
|
||||||
using namespace Kakoune;
|
using namespace Kakoune;
|
||||||
|
|
||||||
void test_buffer()
|
void test_buffer()
|
||||||
|
@ -144,144 +142,6 @@ void test_keys()
|
||||||
kak_assert(keys == parsed_keys);
|
kak_assert(keys == parsed_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_modification()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
Modification modif = { {5, 10}, {5, 10}, {0, 0}, {4, 17} };
|
|
||||||
auto pos = modif.get_new_coord({5, 10});
|
|
||||||
kak_assert(pos == ByteCoord{9 COMMA 17});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Modification modif = { {7, 10}, {7, 10}, {0, 5}, {0, 0} };
|
|
||||||
auto pos = modif.get_new_coord({7, 10});
|
|
||||||
kak_assert(pos == ByteCoord{7 COMMA 10});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Modification modif = { {8, 0}, {8, 0}, {1, 0}, {1, 0} };
|
|
||||||
auto pos = modif.get_new_coord({12, 31});
|
|
||||||
kak_assert(pos == ByteCoord{12 COMMA 31});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Modification modif = { {0, 7}, {0, 8}, {0, 0}, {0, 1} };
|
|
||||||
auto pos = modif.get_new_coord({1, 0});
|
|
||||||
kak_assert(pos == ByteCoord{1 COMMA 0});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::vector<Buffer::Change> change = {
|
|
||||||
{ Buffer::Change::Insert, {1, 0}, {5, 161}, false },
|
|
||||||
{ Buffer::Change::Insert, {5, 161}, {30, 0}, false },
|
|
||||||
{ Buffer::Change::Insert, {30, 0}, {35, 0}, false },
|
|
||||||
};
|
|
||||||
auto modifs = compute_modifications(change);
|
|
||||||
kak_assert(modifs.size() == 1);
|
|
||||||
auto& modif = modifs[0];
|
|
||||||
kak_assert(modif.old_coord == ByteCoord{1 COMMA 0});
|
|
||||||
kak_assert(modif.new_coord == ByteCoord{1 COMMA 0});
|
|
||||||
kak_assert(modif.num_added == ByteCoord{34 COMMA 0});
|
|
||||||
kak_assert(modif.num_removed == ByteCoord{0 COMMA 0});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::vector<Buffer::Change> change = {
|
|
||||||
{ Buffer::Change::Insert, {1, 20}, {2, 0}, false },
|
|
||||||
{ Buffer::Change::Insert, {1, 10}, {2, 0}, false },
|
|
||||||
{ Buffer::Change::Insert, {1, 0}, {2, 0}, false },
|
|
||||||
};
|
|
||||||
auto modifs = compute_modifications(change);
|
|
||||||
kak_assert(modifs.size() == 3);
|
|
||||||
auto& modif = modifs[2];
|
|
||||||
kak_assert(modif.old_coord == ByteCoord{1 COMMA 20});
|
|
||||||
kak_assert(modif.new_coord == ByteCoord{3 COMMA 10});
|
|
||||||
kak_assert(modif.num_added == ByteCoord{1 COMMA 0});
|
|
||||||
kak_assert(modif.num_removed == ByteCoord{0 COMMA 0});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::vector<Buffer::Change> change = {
|
|
||||||
{ Buffer::Change::Erase, {1, 10}, {2, 0}, false },
|
|
||||||
{ Buffer::Change::Erase, {0, 10}, {1, 0}, false },
|
|
||||||
};
|
|
||||||
auto modifs = compute_modifications(change);
|
|
||||||
kak_assert(modifs.size() == 2);
|
|
||||||
auto& modif = modifs[1];
|
|
||||||
kak_assert(modif.old_coord == ByteCoord{1 COMMA 10});
|
|
||||||
kak_assert(modif.new_coord == ByteCoord{0 COMMA 20});
|
|
||||||
kak_assert(modif.num_added == ByteCoord{0 COMMA 0});
|
|
||||||
kak_assert(modif.num_removed == ByteCoord{1 COMMA 0});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::vector<Buffer::Change> change = {
|
|
||||||
{ Buffer::Change::Insert, {1, 10}, {2, 0}, false },
|
|
||||||
{ Buffer::Change::Erase, {1, 20}, {2, 10}, false },
|
|
||||||
};
|
|
||||||
auto modifs = compute_modifications(change);
|
|
||||||
kak_assert(modifs.size() == 1);
|
|
||||||
auto& modif = modifs[0];
|
|
||||||
kak_assert(modif.old_coord == ByteCoord{1 COMMA 10});
|
|
||||||
kak_assert(modif.new_coord == ByteCoord{1 COMMA 10});
|
|
||||||
kak_assert(modif.num_added == ByteCoord{0 COMMA 10});
|
|
||||||
kak_assert(modif.num_removed == ByteCoord{0 COMMA 10});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::vector<Buffer::Change> change = {
|
|
||||||
{ Buffer::Change::Insert, {1, 10}, {2, 20}, false },
|
|
||||||
{ Buffer::Change::Erase, {1, 5}, {2, 10}, false },
|
|
||||||
};
|
|
||||||
auto modifs = compute_modifications(change);
|
|
||||||
kak_assert(modifs.size() == 1);
|
|
||||||
auto& modif = modifs[0];
|
|
||||||
kak_assert(modif.old_coord == ByteCoord{1 COMMA 5});
|
|
||||||
kak_assert(modif.new_coord == ByteCoord{1 COMMA 5});
|
|
||||||
kak_assert(modif.num_added == ByteCoord{0 COMMA 10});
|
|
||||||
kak_assert(modif.num_removed == ByteCoord{0 COMMA 5});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::vector<Buffer::Change> change = {
|
|
||||||
{ Buffer::Change::Insert, {1, 10}, {2, 20}, false },
|
|
||||||
{ Buffer::Change::Erase, {1, 5}, {2, 10}, false },
|
|
||||||
{ Buffer::Change::Erase, {1, 10}, {2, 0}, false },
|
|
||||||
};
|
|
||||||
auto modifs = compute_modifications(change);
|
|
||||||
kak_assert(modifs.size() == 1);
|
|
||||||
auto& modif = modifs[0];
|
|
||||||
kak_assert(modif.old_coord == ByteCoord{1 COMMA 5});
|
|
||||||
kak_assert(modif.new_coord == ByteCoord{1 COMMA 5});
|
|
||||||
kak_assert(modif.num_added == ByteCoord{0 COMMA 5});
|
|
||||||
kak_assert(modif.num_removed == ByteCoord{1 COMMA 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer buffer("test", Buffer::Flags::None,
|
|
||||||
{ "tchou mutch\n",
|
|
||||||
"tchou kanaky tchou\n",
|
|
||||||
"\n",
|
|
||||||
"tchaa tchaa\n",
|
|
||||||
"allo\n"});
|
|
||||||
|
|
||||||
size_t timestamp = buffer.timestamp();
|
|
||||||
|
|
||||||
buffer.erase(buffer.iterator_at({0,0}), buffer.iterator_at({3,0}));
|
|
||||||
buffer.insert(buffer.iterator_at({0,0}), "youuhou\nniahaha");
|
|
||||||
|
|
||||||
buffer.insert(buffer.iterator_at({2,4}), "yeehaah\n");
|
|
||||||
|
|
||||||
auto modifs = compute_modifications(buffer, timestamp);
|
|
||||||
kak_assert(modifs.size() == 2);
|
|
||||||
{
|
|
||||||
auto& modif = modifs[0];
|
|
||||||
kak_assert(modif.old_coord == ByteCoord{0 COMMA 0});
|
|
||||||
kak_assert(modif.new_coord == ByteCoord{0 COMMA 0});
|
|
||||||
kak_assert(modif.num_added == ByteCoord{1 COMMA 7});
|
|
||||||
kak_assert(modif.num_removed == ByteCoord{3 COMMA 0});
|
|
||||||
auto new_coord = modif.get_new_coord({1, 10});
|
|
||||||
kak_assert(new_coord == ByteCoord{1 COMMA 7});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto& modif = modifs[1];
|
|
||||||
kak_assert(modif.old_coord == ByteCoord{4 COMMA 4});
|
|
||||||
kak_assert(modif.new_coord == ByteCoord{2 COMMA 4});
|
|
||||||
kak_assert(modif.num_added == ByteCoord{1 COMMA 0});
|
|
||||||
kak_assert(modif.num_removed == ByteCoord{0 COMMA 0});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_unit_tests()
|
void run_unit_tests()
|
||||||
{
|
{
|
||||||
test_utf8();
|
test_utf8();
|
||||||
|
@ -289,6 +149,5 @@ void run_unit_tests()
|
||||||
test_keys();
|
test_keys();
|
||||||
test_buffer();
|
test_buffer();
|
||||||
test_undo_group_optimizer();
|
test_undo_group_optimizer();
|
||||||
test_modification();
|
|
||||||
test_word_db();
|
test_word_db();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user