From 1e87fe17c6941ffc257a6afc22e6bf1f15342d19 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 6 Sep 2011 18:49:32 +0000 Subject: [PATCH] Buffer: Undo/Redo implementation Currently only a linear undo, i.e. if you undo and then make some new changes, previous undoed changes are lost. Final undo system should support an undo tree, with timestamped modifications. --- src/buffer.cc | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/buffer.hh | 37 ++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/buffer.cc b/src/buffer.cc index ef8e0e1e..5e42a295 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -126,12 +126,24 @@ Buffer::Buffer(const std::string& name, const BufferString& initial_content) } void Buffer::erase(const BufferIterator& begin, const BufferIterator& end) +{ + append_modification(Modification(Modification::Erase, begin, string(begin, end))); + do_erase(begin, end); +} + +void Buffer::insert(const BufferIterator& position, const BufferString& string) +{ + append_modification(Modification(Modification::Insert, position, string)); + do_insert(position, string); +} + +void Buffer::do_erase(const BufferIterator& begin, const BufferIterator& end) { m_content.erase(begin.m_position, end - begin); compute_lines(); } -void Buffer::insert(const BufferIterator& position, const BufferString& string) +void Buffer::do_insert(const BufferIterator& position, const BufferString& string) { m_content.insert(position.m_position, string); compute_lines(); @@ -223,4 +235,78 @@ BufferChar Buffer::at(BufferPos position) const return m_content[position]; } +void Buffer::begin_undo_group() +{ + assert(m_current_undo_group.empty()); + m_history.erase(m_history_cursor, m_history.end()); + m_history_cursor = m_history.end(); +} + +void Buffer::end_undo_group() +{ + m_history.push_back(m_current_undo_group); + m_history_cursor = m_history.end(); + + m_current_undo_group.clear(); +} + +Buffer::Modification Buffer::Modification::inverse() const +{ + Modification::Type inverse_type; + switch (type) + { + case Modification::Insert: inverse_type = Erase; break; + case Modification::Erase: inverse_type = Insert; break; + default: assert(false); + } + return Modification(inverse_type, position, content); +} + +bool Buffer::undo() +{ + if (m_history_cursor == m_history.begin()) + return false; + + --m_history_cursor; + + for (const Modification& modification : reversed(*m_history_cursor)) + replay_modification(modification.inverse()); +} + +bool Buffer::redo() +{ + if (m_history_cursor == m_history.end()) + return false; + + for (const Modification& modification : *m_history_cursor) + replay_modification(modification); + + ++m_history_cursor; +} + +void Buffer::replay_modification(const Modification& modification) +{ + switch (modification.type) + { + case Modification::Insert: + do_insert(modification.position, modification.content); + break; + case Modification::Erase: + { + BufferIterator begin = modification.position; + BufferIterator end = begin + modification.content.size(); + assert(string(begin, end) == modification.content); + do_erase(begin, end); + break; + } + default: + assert(false); + } +} + +void Buffer::append_modification(Modification&& modification) +{ + m_current_undo_group.push_back(modification); +} + } diff --git a/src/buffer.hh b/src/buffer.hh index f1441747..60306647 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -68,6 +68,12 @@ public: Buffer(const std::string& name, const BufferString& initial_content = ""); + void begin_undo_group(); + void end_undo_group(); + + bool undo(); + bool redo(); + void erase(const BufferIterator& begin, const BufferIterator& end); @@ -92,6 +98,13 @@ public: private: BufferChar at(BufferPos position) const; + + void do_erase(const BufferIterator& begin, + const BufferIterator& end); + + void do_insert(const BufferIterator& position, + const BufferString& string); + friend class BufferIterator; std::vector m_lines; @@ -103,6 +116,30 @@ private: BufferString m_content; std::string m_name; + + struct Modification + { + enum Type { Insert, Erase }; + + Type type; + BufferIterator position; + BufferString content; + + Modification(Type type, BufferIterator position, BufferString content) + : type(type), position(position), content(content) {} + + Modification inverse() const; + }; + typedef std::vector UndoGroup; + + std::vector m_history; + std::vector::iterator m_history_cursor; + UndoGroup m_current_undo_group; + + void replay_modification(const Modification& modification); + void revert_modification(const Modification& modification); + + void append_modification(Modification&& modification); }; }