diff options
Diffstat (limited to 'src/buffer.cc')
| -rw-r--r-- | src/buffer.cc | 181 |
1 files changed, 64 insertions, 117 deletions
diff --git a/src/buffer.cc b/src/buffer.cc index c968690f..8baa7423 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -20,8 +20,8 @@ namespace Kakoune { -Buffer::HistoryNode::HistoryNode(HistoryId parent) - : parent{parent}, committed{Clock::now()} +Buffer::HistoryNode::HistoryNode() + : committed{Clock::now()} {} Buffer::Buffer(String name, Flags flags, BufferLines lines, @@ -31,9 +31,9 @@ Buffer::Buffer(String name, Flags flags, BufferLines lines, 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{{HistoryId::Invalid}}, - m_history_id{HistoryId::First}, - m_last_save_history_id{HistoryId::First}, + m_history{{HistoryNode{}}}, + m_history_id{0}, + m_last_save_history_id{0}, m_fs_status{fs_status} { #ifdef KAK_DEBUG @@ -114,7 +114,7 @@ bool Buffer::set_name(String name) if (m_flags & Buffer::Flags::File and not file_exists(m_name)) { m_flags |= Buffer::Flags::New; - m_last_save_history_id = HistoryId::Invalid; + m_last_save_history_id = InvalidHistoryId; } } else @@ -201,9 +201,9 @@ void Buffer::reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, F if (not record_undo) { // Erase history about to be invalidated history - m_history_id = HistoryId::First; - m_last_save_history_id = HistoryId::First; - m_history = {HistoryNode{HistoryId::Invalid}}; + m_history_id = 0; + m_last_save_history_id = 0; + m_history = {HistoryNode{}}; m_changes.push_back({ Change::Erase, {0,0}, line_count() }); static_cast<BufferLines&>(m_lines) = std::move(lines); @@ -273,12 +273,20 @@ void Buffer::commit_undo_group() if (m_current_undo_group.empty()) return; - const HistoryId id = next_history_id(); - m_history.push_back({m_history_id}); + // walk back to current position in history, and append reverse events + for (size_t current = m_history.size() - 1; current != m_history_id ; --current) + { + m_history.emplace_back(); + m_history.back().undo_group = m_history[current].undo_group + | reverse() + | transform([](auto& modif) { return modif.inverse(); }) + | gather<UndoGroup>(); + } + m_history.emplace_back(); m_history.back().undo_group = std::move(m_current_undo_group); + m_current_undo_group.clear(); - current_history_node().redo_child = id; - m_history_id = id; + m_history_id = m_history.size() - 1; } bool Buffer::undo(size_t count) @@ -287,15 +295,15 @@ bool Buffer::undo(size_t count) commit_undo_group(); - if (current_history_node().parent == HistoryId::Invalid) + if ((m_history_id - 1) == InvalidHistoryId) return false; - while (count-- != 0 and current_history_node().parent != HistoryId::Invalid) + while (count-- != 0 and (m_history_id - 1) != InvalidHistoryId) { for (const Modification& modification : current_history_node().undo_group | reverse()) apply_modification(modification.inverse()); - m_history_id = current_history_node().parent; + m_history_id--; } return true; @@ -305,79 +313,19 @@ bool Buffer::redo(size_t count) { throw_if_read_only(); - if (current_history_node().redo_child == HistoryId::Invalid) + if (m_history_id == (m_history.size() - 1)) return false; kak_assert(m_current_undo_group.empty()); - while (count-- != 0 and current_history_node().redo_child != HistoryId::Invalid) + while (count-- != 0 and m_history_id != (m_history.size() - 1)) { - m_history_id = current_history_node().redo_child; + m_history_id++; for (const Modification& modification : current_history_node().undo_group) apply_modification(modification); } - return true; -} - -bool Buffer::move_to(HistoryId id) -{ - if (id >= next_history_id()) - return false; - - throw_if_read_only(); - - commit_undo_group(); - - auto find_lowest_common_parent = [this](HistoryId a, HistoryId b) { - auto depth_of = [this](HistoryId id) { - size_t depth = 0; - for (; history_node(id).parent != HistoryId::Invalid; id = history_node(id).parent) - ++depth; - return depth; - }; - auto depthA = depth_of(a), depthB = depth_of(b); - - for (; depthA > depthB; --depthA) - a = history_node(a).parent; - for (; depthB > depthA; --depthB) - b = history_node(b).parent; - - while (a != b) - { - a = history_node(a).parent; - b = history_node(b).parent; - } - - kak_assert(a == b and a != HistoryId::Invalid); - return a; - }; - - auto parent = find_lowest_common_parent(m_history_id, id); - - // undo up to common parent - for (auto id = m_history_id; id != parent; id = history_node(id).parent) - { - for (const Modification& modification : history_node(id).undo_group | reverse()) - apply_modification(modification.inverse()); - } - - static void (*apply_from_parent)(Buffer&, HistoryId, HistoryId) = - [](Buffer& buffer, HistoryId parent, HistoryId id) { - if (id == parent) - return; - auto& node = buffer.history_node(id); - apply_from_parent(buffer, parent, node.parent); - - buffer.history_node(node.parent).redo_child = id; - - for (const Modification& modification : node.undo_group) - buffer.apply_modification(modification); - }; - - apply_from_parent(*this, parent, id); - m_history_id = id; return true; } @@ -646,7 +594,7 @@ void Buffer::run_hook_in_own_context(Hook hook, StringView param, String client_ Optional<BufferCoord> Buffer::last_modification_coord() const { - if (m_history_id == HistoryId::First) + if (m_history_id == 0) return {}; return current_history_node().undo_group.back().coord; } @@ -734,50 +682,49 @@ UnitTest test_undo{[]() buffer.insert(2_line, "tchou\n"); // change 3 buffer.commit_undo_group(); buffer.undo(); - buffer.insert(2_line, "mutch\n"); // change 4 + buffer.insert(2_line, "mutch\n"); // change 4 (inverse of 3) and 5 buffer.commit_undo_group(); - buffer.erase({2, 1}, {2, 5}); // change 5 + buffer.erase({2, 1}, {2, 5}); // change 6 buffer.commit_undo_group(); buffer.undo(2); buffer.redo(2); buffer.undo(); - buffer.replace(2_line, buffer.end_coord(), "foo"); // change 6 + buffer.replace(2_line, buffer.end_coord(), "foo"); // change 7 (inverse of 6) and 8 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((Buffer::HistoryId)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"); - - buffer.move_to((Buffer::HistoryId)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] == "mutch\n"); - kak_assert(buffer[3_line] == " hein ?\n"); - kak_assert(buffer[4_line] == " youpi\n"); - - buffer.move_to(Buffer::HistoryId::First); - kak_assert((int)buffer.line_count() == 4); - 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(not buffer.undo()); - - buffer.move_to((Buffer::HistoryId)5); - kak_assert(not buffer.redo()); - - buffer.move_to((Buffer::HistoryId)6); - kak_assert(not buffer.redo()); + kak_assert(buffer.history().size() == 9); + + auto check_content = [&](const Vector<StringView>& lines) { + kak_assert(buffer.line_count() == lines.size()); + for (size_t i = 0; i < lines.size(); ++i) + kak_assert(buffer[i] == lines[i] + "\n"); + }; + + kak_assert(not buffer.redo(1)); + check_content({ "allo ?", "mais que fais la police", "foo" , }); + kak_assert(buffer.undo(1)); + check_content({ "allo ?", "mais que fais la police", "mutch" , " hein ?", " youpi", }); + kak_assert(buffer.undo(1)); + check_content({ "allo ?", "mais que fais la police", "m" , " hein ?", " youpi", }); + kak_assert(buffer.undo(1)); + check_content({ "allo ?", "mais que fais la police", "mutch" , " hein ?", " youpi", }); + kak_assert(buffer.undo(1)); + check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , }); + kak_assert(buffer.undo(1)); + check_content({ "allo ?", "mais que fais la police", "tchou" , " hein ?", " youpi", }); + kak_assert(buffer.undo(1)); + check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , }); + kak_assert(buffer.undo(1)); + check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , "kanaky", }); + kak_assert(buffer.undo(1)); + check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , }); + kak_assert(not buffer.undo(1)); + + kak_assert(buffer.redo(1000)); + check_content({ "allo ?", "mais que fais la police", "foo" , }); + + kak_assert(buffer.undo(1000)); + check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , }); }}; } |
