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:
Maxime Coste 2014-05-26 21:09:12 +01:00
parent ce469398c5
commit 9870ac22f6
4 changed files with 0 additions and 407 deletions

View File

@ -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;
}
}

View File

@ -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

View File

@ -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
{ {

View File

@ -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();
} }