1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! A undo/redo library.
//!
//! It uses the [Command Pattern] where the user implements the `UndoCmd` trait for each command
//! and then the commands can be used with the `UndoStack`.
//!
//! The `UndoStack` has two different states, clean and dirty. The stack is in a clean state when
//! there are no more commands that can be redone, otherwise it's in a dirty state. The stack
//! can be configured to call a given method when this state changes, using the [on_clean] and
//! [on_dirty] methods.
//!
//! The `UndoStack` also supports easy merging of commands by just implementing the [id] method
//! for a given command.
//!
//! # Example
//! ```
//! use std::rc::Rc;
//! use std::cell::RefCell;
//! use undo::{UndoCmd, UndoStack};
//!
//! /// Pops an element from a vector.
//! #[derive(Clone)]
//! struct PopCmd {
//!     vec: Rc<RefCell<Vec<i32>>>,
//!     e: Option<i32>,
//! }
//!
//! impl UndoCmd for PopCmd {
//!     fn redo(&mut self) {
//!         self.e = self.vec.borrow_mut().pop();
//!     }
//!
//!     fn undo(&mut self) {
//!         self.vec.borrow_mut().push(self.e.unwrap());
//!         self.e = None;
//!     }
//! }
//!
//! // We need to use Rc<RefCell> since all commands are going to mutate the vec.
//! let vec = Rc::new(RefCell::new(vec![1, 2, 3]));
//! let mut stack = UndoStack::new()
//!     .on_clean(|| println!("This is called when the stack changes from dirty to clean!"))
//!     .on_dirty(|| println!("This is called when the stack changes from clean to dirty!"));
//!
//! let cmd = PopCmd { vec: vec.clone(), e: None };
//! stack.push(cmd.clone());
//! stack.push(cmd.clone());
//! stack.push(cmd.clone());
//!
//! assert!(vec.borrow().is_empty());
//!
//! stack.undo(); // on_dirty is going to be called here.
//! stack.undo();
//! stack.undo();
//!
//! assert_eq!(vec.borrow().len(), 3);
//! ```
//!
//! [Command Pattern]: https://en.wikipedia.org/wiki/Command_pattern
//! [on_clean]: struct.UndoStack.html#method.on_clean
//! [on_dirty]: struct.UndoStack.html#method.on_dirty
//! [id]: trait.UndoCmd.html#method.id

extern crate fnv;

mod group;
mod stack;

pub use group::{Uid, UndoGroup};
pub use stack::UndoStack;

/// Every command needs to implement the `UndoCmd` trait to be able to be used with the `UndoStack`.
pub trait UndoCmd {
    /// Executes the desired command.
    fn redo(&mut self);

    /// Restores the state as it was before [`redo`] was called.
    ///
    /// [`redo`]: trait.UndoCmd.html#tymethod.redo
    fn undo(&mut self);

    /// Used for merging of `UndoCmd`s.
    ///
    /// When two commands are merged together, undoing and redoing them are done in one step.
    /// An example where this is useful is a text editor where you might want to undo a whole word
    /// instead of each character.
    ///
    /// Two commands are merged together when a command is pushed on the `UndoStack`, and it has
    /// the same id as the top command already on the stack. It is normal to have an unique
    /// id for each implementation of `UndoCmd`, but this is not mandatory.
    ///
    /// Default implementation returns `None`, which means the command will never be merged.
    ///
    /// # Example
    /// ```
    /// use std::rc::Rc;
    /// use std::cell::RefCell;
    /// use undo::{UndoCmd, UndoStack};
    ///
    /// /// Pops an element from a vector.
    /// #[derive(Clone)]
    /// struct PopCmd {
    ///     vec: Rc<RefCell<Vec<i32>>>,
    ///     e: Option<i32>,
    /// }
    ///
    /// impl UndoCmd for PopCmd {
    ///     fn redo(&mut self) {
    ///         self.e = self.vec.borrow_mut().pop();
    ///     }
    ///
    ///     fn undo(&mut self) {
    ///         self.vec.borrow_mut().push(self.e.unwrap());
    ///         self.e = None;
    ///     }
    ///
    ///     fn id(&self) -> Option<u64> {
    ///         Some(1)
    ///     }
    /// }
    ///
    /// fn main() {
    ///     // We need to use Rc<RefCell> since all commands are going to mutate the vec.
    ///     let vec = Rc::new(RefCell::new(vec![1, 2, 3]));
    ///     let mut stack = UndoStack::new();
    ///
    ///     let cmd = PopCmd { vec: vec.clone(), e: None };
    ///     stack.push(cmd.clone());
    ///     stack.push(cmd.clone());
    ///     stack.push(cmd.clone());
    ///
    ///     assert!(vec.borrow().is_empty());
    ///
    ///     stack.undo();
    ///
    ///     assert_eq!(vec, Rc::new(RefCell::new(vec![1,2,3])));
    ///
    ///     stack.redo();
    ///
    ///     assert!(vec.borrow().is_empty());
    /// }
    /// ```
    fn id(&self) -> Option<u64> {
        None
    }
}