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.
This commit is contained in:
Maxime Coste 2011-09-06 18:49:32 +00:00
parent f58cbf0b98
commit 1e87fe17c6
2 changed files with 124 additions and 1 deletions

View File

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

View File

@ -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<BufferPos> 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<Modification> UndoGroup;
std::vector<UndoGroup> m_history;
std::vector<UndoGroup>::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);
};
}