add_ed/history.rs
1//! Module for history snapshotting and management.
2
3use crate::{EdError, Result};
4use std::fmt::Debug;
5
6/// A special type of Clone for [`History`]
7///
8/// Needed because [`History`] requires a Clone that re-uses as much memory as
9/// possible, while normal Clone is usually expected to create unrelated copies
10/// of data.
11pub trait Snapshot{
12 /// Create a memory efficient copy of self
13 ///
14 /// Beware, mutation of the created copy (if even possible) may modify the
15 /// original.
16 fn create_snapshot(&self) -> Self;
17}
18
19/// A history abstraction over generic objects used by add-ed.
20///
21/// Handles snapshotting and moving over the history of snapshots. Currently
22/// uses a revert style of undo inspired by
23/// [this reasoning.](https://github.com/zaboople/klonk/blob/master/TheGURQ.md)
24///
25/// Automatically manages snapshot creation upon mutable access to the current
26/// point in history. Further allows pausing snapshot creation via
27/// [`History.dont_snapshot`] as well as manual snapshot creation via
28/// [`History.snapshot`] (for use during script/macro execution, to make each
29/// snapshot correspond to a user action).
30#[derive(Clone, Debug)]
31pub struct History<T> where
32 T: Default + Debug + Snapshot + PartialEq,
33{
34 snapshots: Vec<(String, T)>,
35 viewed_i: usize,
36 saved_i: Option<usize>,
37 /// If true all calls to [`History::snapshot`] are ignored (including the
38 /// automatic call upon running `.current_mut()`).
39 ///
40 /// Intended for macro execution, when it would be confusing to create
41 /// multiple snapshots for what the user sees as a single action.
42 ///
43 /// (If a point in history is viewed a snapshot reverting to that point in
44 /// history will be created before mutable access no matter if this variable
45 /// is set to true.)
46 pub dont_snapshot: bool,
47}
48impl<T> Default for History<T> where
49 T: Default + Debug + Snapshot + PartialEq,
50{
51 fn default() -> Self { Self::new() }
52}
53impl <T> History<T> where
54 T: Default + Debug + Snapshot + PartialEq,
55{
56 /// Create new [`History`] instance
57 ///
58 /// - Only an empty present state exists.
59 /// - Considered saved at initial empty state.
60 pub fn new() -> Self {
61 Self{
62 snapshots: vec![("Before reading in a file (empty)".to_owned(), T::default())],
63 viewed_i: 0,
64 saved_i: Some(0),
65 dont_snapshot: false,
66 }
67 }
68
69 /// Get if the buffer is saved
70 ///
71 /// It aims to be true when the viewed buffer matches the data last saved.
72 /// If it is uncertain or difficult to track it will return false.
73 pub fn saved(&self) -> bool {
74 self.saved_i == Some(self.viewed_i)
75 }
76 /// Mark the currently viewed buffer state as saved
77 ///
78 /// If `dont_snapshot` is set this instead behaves as
79 /// `set_unsaved()`, as we cannot be sure a snapshot will exist
80 /// corresponding to the state in which the buffer was saved.
81 pub fn set_saved(&mut self) {
82 if !self.dont_snapshot {
83 self.saved_i = Some(self.viewed_i);
84 } else {
85 self.saved_i = None;
86 }
87 }
88 /// Declare that no known buffer state is saved
89 ///
90 /// Mainly useful for testing, but may be relevant when knowing that file
91 /// was changed on disk.
92 pub fn set_unsaved(&mut self) {
93 self.saved_i = None;
94 }
95
96 /// Get an immutable view into the currently viewed point in history
97 pub fn current(&self) -> &T {
98 &self.snapshots[self.viewed_i].1
99 }
100 /// Get a mutable state to make new changes
101 ///
102 /// - Takes a string describing what is causing this new snapshot. (Should
103 /// generally be the full command, if not be as clear as possible.)
104 /// - If currently viewing history, will create a revert snapshot at end of
105 /// history.
106 /// - Unless self.dont_snapshot, will create a new snapshot tagged with the
107 /// given cause for modification.
108 /// - Returns mutable access to the snapshot at the end of history.
109 pub fn current_mut(&mut self,
110 modification_cause: String,
111 ) -> &mut T {
112 self.snapshot(modification_cause);
113 &mut self.snapshots[self.viewed_i].1
114 }
115
116 fn internal_create_snapshot(&mut self, label: String) {
117 // Push the current index to end of history with label
118 // (reverts if in history, snapshots if at end of history)
119 self.snapshots.push((label, self.snapshots[self.viewed_i].1.create_snapshot()));
120 // Move to end of history
121 self.viewed_i = self.snapshots.len() - 1;
122 }
123 /// Manually add a snapshot
124 ///
125 /// Takes a String as an argument that should describe what causes the
126 /// change seen in the created snapshot relative to the preceding snapshot.
127 ///
128 /// The only case this should be needed is before setting `dont_snapshot` for
129 /// a script execution. If `dont_snapshot` isn't set snapshots are created
130 /// automatically whenever [`Self.current_mut`] is executed.
131 pub fn snapshot(&mut self,
132 modification_cause: String,
133 ) {
134 // If we are in the past, create a revert snapshot
135 // This is needed even if snapshots are disabled, to not change history
136 if self.viewed_i < self.snapshots.len() - 1 {
137 self.internal_create_snapshot(format!(
138 "u{}",
139 self.snapshots.len().saturating_sub(self.viewed_i + 1),
140 ));
141 }
142 // If snapshots aren't disabled, create one
143 if !self.dont_snapshot {
144 self.internal_create_snapshot(modification_cause);
145 }
146 }
147
148 /// Checks if the last two snapshots in history are identical. If yes deletes
149 /// one of them.
150 ///
151 /// Intended for use by macros and scripts, as they have to add a snapshot
152 /// even for non-mutating scripts since they don't know if a script will
153 /// modify the buffer. By running this after macro execution the snapshot will
154 /// be deleted if extraneous and left if relevant.
155 pub fn dedup_present(&mut self) {
156 let mut last_2_iter = self.snapshots.iter().rev().take(2);
157 if last_2_iter.next().map(|x| &x.1) == last_2_iter.next().map(|x| &x.1) {
158 self.snapshots.pop();
159 self.viewed_i = self.snapshots.len() - 1;
160 }
161 }
162
163 /// Accessor to view the full list of snapshots
164 ///
165 /// - Entries are in order of creation, the first operation is first in the
166 /// list.
167 /// - The string beside the snapshot describes what caused the state in the
168 /// snapshot (relative to the preceeding snapshot).
169 pub fn snapshots(&self) -> &Vec<(String, T)> {
170 &self.snapshots
171 }
172 /// Shorthand for `.snapshots().len()`
173 pub fn len(&self) -> usize {
174 self.snapshots.len()
175 }
176 /// Getter for what index was last saved
177 ///
178 /// Returns None if no index is believed to be saved.
179 ///
180 /// Intended to be used to enrich when listing snapshots by marking the one
181 /// considered saved.
182 pub fn saved_i(&self) -> Option<usize> {
183 self.saved_i
184 }
185 /// Getter for currently viewed snapshot index
186 pub fn viewed_i(&self) -> usize {
187 self.viewed_i
188 }
189 /// Setter for currently viewed snapshot index
190 ///
191 /// Returns the modification cause for the now viewed index.
192 ///
193 /// Will return error if given index doesn't hold a snapshot (aka. is too
194 /// big).
195 pub fn set_viewed_i(&mut self, new_i: usize) -> Result<&str> {
196 if new_i < self.len() {
197 self.viewed_i = new_i;
198 Ok(&self.snapshots[self.viewed_i].0)
199 }
200 else {
201 Err(EdError::UndoIndexTooBig{
202 index: new_i,
203 history_len: self.len(),
204 relative_redo_limit: self.len() - self.viewed_i - 1,
205 })
206 }
207 }
208}