summaryrefslogtreecommitdiff
path: root/src/buffer.cc
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2016-07-20 09:41:45 +0100
committerMaxime Coste <frrrwww@gmail.com>2016-07-24 21:25:05 +0100
commit3edd2c127c9d1754a487d76de38ec9b2c690ff7c (patch)
tree8c292c3cc062b896a766468931ac60936505e5a6 /src/buffer.cc
parentb9c77e2f09f34985aec83b6f5e5e9dc4b942af2f (diff)
Support moving between arbitrary history nodes
Diffstat (limited to 'src/buffer.cc')
-rw-r--r--src/buffer.cc132
1 files changed, 117 insertions, 15 deletions
diff --git a/src/buffer.cc b/src/buffer.cc
index d0a17f74..08b857ce 100644
--- a/src/buffer.cc
+++ b/src/buffer.cc
@@ -59,8 +59,8 @@ static void apply_options(OptionManager& options, const ParsedLines& parsed_line
options.get_local_option("BOM").set(parsed_lines.bom);
}
-Buffer::HistoryNode::HistoryNode(HistoryNode* parent)
- : parent(parent), timepoint{Clock::now()}
+Buffer::HistoryNode::HistoryNode(size_t id, HistoryNode* parent)
+ : id{id}, parent(parent), timepoint{Clock::now()}
{}
Buffer::Buffer(String name, Flags flags, StringView data,
@@ -69,7 +69,7 @@ Buffer::Buffer(String name, Flags flags, StringView data,
m_name{(flags & Flags::File) ? real_path(parse_filename(name)) : std::move(name)},
m_display_name{(flags & Flags::File) ? compact_path(m_name) : m_name},
m_flags{flags | Flags::NoUndo},
- m_history{nullptr}, m_history_cursor{&m_history},
+ m_history{m_next_history_id++, nullptr}, m_history_cursor{&m_history},
m_last_save_history_cursor{&m_history},
m_fs_timestamp{fs_timestamp}
{
@@ -301,7 +301,7 @@ void Buffer::commit_undo_group()
if (m_current_undo_group.empty())
return;
- auto* node = new HistoryNode{m_history_cursor.get()};
+ auto* node = new HistoryNode{m_next_history_id++, m_history_cursor.get()};
node->undo_group = std::move(m_current_undo_group);
m_current_undo_group.clear();
@@ -310,7 +310,7 @@ void Buffer::commit_undo_group()
m_history_cursor = node;
}
-bool Buffer::undo()
+bool Buffer::undo() noexcept
{
commit_undo_group();
@@ -324,7 +324,7 @@ bool Buffer::undo()
return true;
}
-bool Buffer::redo()
+bool Buffer::redo() noexcept
{
if (not m_history_cursor->redo_child)
return false;
@@ -338,6 +338,86 @@ bool Buffer::redo()
return true;
}
+void Buffer::move_to(HistoryNode* history_node) noexcept
+{
+ commit_undo_group();
+
+ auto find_lowest_common_parent = [](HistoryNode* a, HistoryNode* b) {
+ auto depth_of = [](HistoryNode* node) {
+ size_t depth = 0;
+ for (; node->parent; node = node->parent.get())
+ ++depth;
+ return depth;
+ };
+ auto depthA = depth_of(a), depthB = depth_of(b);
+
+ for (; depthA > depthB; --depthA)
+ a = a->parent.get();
+ for (; depthB > depthA; --depthB)
+ b = b->parent.get();
+
+ while (a != b)
+ {
+ a = a->parent.get();
+ b = b->parent.get();
+ }
+
+ kak_assert(a == b and a != nullptr);
+ return a;
+ };
+
+ auto parent = find_lowest_common_parent(m_history_cursor.get(), history_node);
+
+ // undo up to common parent
+ for (auto it = m_history_cursor.get(); it != parent; it = it->parent.get())
+ {
+ for (const Modification& modification : it->undo_group | reverse())
+ apply_modification(modification.inverse());
+ }
+
+ static void (*apply_from_parent)(Buffer&, HistoryNode*, HistoryNode*) =
+ [](Buffer& buffer, HistoryNode* parent, HistoryNode* node) {
+ if (node == parent)
+ return;
+
+ apply_from_parent(buffer, parent, node->parent.get());
+
+ node->parent->redo_child = node;
+
+ for (const Modification& modification : node->undo_group)
+ buffer.apply_modification(modification);
+ };
+
+ apply_from_parent(*this, parent, history_node);
+ m_history_cursor = history_node;
+}
+
+template<typename Func>
+Buffer::HistoryNode* Buffer::find_history_node(HistoryNode* node, const Func& func)
+{
+ if (func(node))
+ return node;
+
+ for (auto&& child : node->childs)
+ {
+ if (auto res = find_history_node(child.get(), func))
+ return res;
+ }
+
+ return nullptr;
+}
+
+bool Buffer::move_to(size_t history_id) noexcept
+{
+ HistoryNode* target_node = find_history_node(&m_history, [history_id](HistoryNode* node) { return node->id == history_id; });
+
+ if (not target_node)
+ return false;
+
+ move_to(target_node);
+ return true;
+}
+
void Buffer::check_invariant() const
{
#ifdef KAK_DEBUG
@@ -735,21 +815,43 @@ UnitTest test_buffer{[]()
UnitTest test_undo{[]()
{
Buffer buffer("test", Buffer::Flags::None, "allo ?\nmais que fais la police\n hein ?\n youpi\n");
- auto pos = buffer.insert(buffer.end_coord(), "kanaky\n");
- buffer.erase(pos, buffer.end_coord());
- buffer.insert(2_line, "tchou\n");
- buffer.insert(2_line, "mutch\n");
- buffer.erase({2, 1}, {2, 5});
- buffer.replace(2_line, buffer.end_coord(), "youpi");
+ auto pos = buffer.insert(buffer.end_coord(), "kanaky\n"); // change 1
+ buffer.commit_undo_group();
+ buffer.erase(pos, buffer.end_coord()); // change 2
+ buffer.commit_undo_group();
+ buffer.insert(2_line, "tchou\n"); // change 3
+ buffer.commit_undo_group();
+ buffer.undo();
+ buffer.insert(2_line, "mutch\n"); // change 4
+ buffer.commit_undo_group();
+ buffer.erase({2, 1}, {2, 5}); // change 5
+ buffer.commit_undo_group();
buffer.undo();
buffer.redo();
buffer.undo();
+ buffer.replace(2_line, buffer.end_coord(), "foo"); // change 6
+ buffer.commit_undo_group();
+
+ kak_assert((int)buffer.line_count() == 3);
+ kak_assert(buffer[0_line] == "allo ?\n");
+ kak_assert(buffer[1_line] == "mais que fais la police\n");
+ kak_assert(buffer[2_line] == "foo\n");
+
+ buffer.move_to(3);
+ kak_assert((int)buffer.line_count() == 5);
+ kak_assert(buffer[0_line] == "allo ?\n");
+ kak_assert(buffer[1_line] == "mais que fais la police\n");
+ kak_assert(buffer[2_line] == "tchou\n");
+ kak_assert(buffer[3_line] == " hein ?\n");
+ kak_assert(buffer[4_line] == " youpi\n");
- kak_assert((int)buffer.line_count() == 4);
+ buffer.move_to(4);
+ kak_assert((int)buffer.line_count() == 5);
kak_assert(buffer[0_line] == "allo ?\n");
kak_assert(buffer[1_line] == "mais que fais la police\n");
- kak_assert(buffer[2_line] == " hein ?\n");
- kak_assert(buffer[3_line] == " youpi\n");
+ kak_assert(buffer[2_line] == "mutch\n");
+ kak_assert(buffer[3_line] == " hein ?\n");
+ kak_assert(buffer[4_line] == " youpi\n");
}};
}