formualizer_eval/engine/graph/editor/
undo_engine.rs1use super::change_log::{ChangeEvent, ChangeLog};
3use super::vertex_editor::VertexEditor;
4use crate::engine::graph::DependencyGraph;
5use crate::engine::graph::editor::vertex_editor::EditorError;
6
7#[derive(Debug, Default)]
8pub struct UndoEngine {
9 undone: Vec<Vec<ChangeEvent>>, }
12
13impl UndoEngine {
14 pub fn new() -> Self {
15 Self { undone: Vec::new() }
16 }
17
18 pub fn undo(
20 &mut self,
21 graph: &mut DependencyGraph,
22 log: &mut ChangeLog,
23 ) -> Result<(), EditorError> {
24 let idxs = log.last_group_indices();
25 if idxs.is_empty() {
26 return Ok(());
27 }
28 let batch: Vec<ChangeEvent> = idxs.iter().map(|i| log.events()[*i].clone()).collect();
29 let max_idx = *idxs.iter().max().unwrap();
30 if max_idx + 1 == log.events().len() {
31 let truncate_to = idxs.iter().min().copied().unwrap();
32 log.truncate(truncate_to);
33 } else {
34 return Err(EditorError::TransactionFailed {
35 reason: "Non-tail undo not supported".into(),
36 });
37 }
38 let mut editor = VertexEditor::new(graph);
39 for ev in batch.iter().rev() {
40 editor.apply_inverse(ev.clone())?;
41 }
42 self.undone.push(batch);
43 Ok(())
44 }
45
46 pub fn redo(
47 &mut self,
48 graph: &mut DependencyGraph,
49 log: &mut ChangeLog,
50 ) -> Result<(), EditorError> {
51 if let Some(batch) = self.undone.pop() {
52 let mut editor = VertexEditor::new(graph);
53 log.begin_compound("redo".to_string());
54 for ev in batch {
55 log.record(ev.clone());
57 match ev {
58 ChangeEvent::SetValue { addr, new, .. } => {
59 editor.set_cell_value(addr, new);
60 }
61 ChangeEvent::SetFormula { addr, new, .. } => {
62 editor.set_cell_formula(addr, new);
63 }
64 ChangeEvent::AddVertex {
65 coord,
66 sheet_id,
67 kind,
68 ..
69 } => {
70 let meta = crate::engine::graph::editor::vertex_editor::VertexMeta::new(
71 coord.row(),
72 coord.col(),
73 sheet_id,
74 kind.unwrap_or(crate::engine::vertex::VertexKind::Cell),
75 );
76 editor.add_vertex(meta);
77 }
78 ChangeEvent::RemoveVertex {
79 coord, sheet_id, ..
80 } => {
81 if let (Some(c), Some(sid)) = (coord, sheet_id) {
82 let cell_ref = crate::reference::CellRef::new(
83 sid,
84 crate::reference::Coord::new(c.row(), c.col(), true, true),
85 );
86 let _ = editor.remove_vertex_at(cell_ref);
87 }
88 }
89 ChangeEvent::VertexMoved {
90 id,
91 old_coord: _,
92 new_coord,
93 } => {
94 let _ = editor.move_vertex(id, new_coord);
95 }
96 ChangeEvent::DefineName {
97 name,
98 scope,
99 definition,
100 } => {
101 let _ = editor.define_name(&name, definition, scope);
102 }
103 ChangeEvent::UpdateName {
104 name,
105 scope,
106 new_definition,
107 ..
108 } => {
109 let _ = editor.update_name(&name, new_definition, scope);
110 }
111 ChangeEvent::DeleteName { name, scope, .. } => {
112 let _ = editor.delete_name(&name, scope);
113 }
114 ChangeEvent::NamedRangeAdjusted {
115 name,
116 scope,
117 new_definition,
118 ..
119 } => {
120 let _ = editor.update_name(&name, new_definition, scope);
121 }
122 _ => {}
123 }
124 }
125 log.end_compound();
126 }
127 Ok(())
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::engine::graph::editor::change_log::ChangeLog;
135 use crate::reference::{CellRef, Coord};
136 use formualizer_common::LiteralValue;
137
138 #[test]
139 fn test_undo_redo_single_value() {
140 let mut graph = DependencyGraph::new();
141 let mut log = ChangeLog::new();
142 {
143 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
144 let cell = CellRef {
145 sheet_id: 0,
146 coord: Coord::new(1, 1, true, true),
147 };
148 editor.set_cell_value(cell, LiteralValue::Number(10.0));
149 }
150 assert_eq!(log.len(), 1);
151 let mut undo = UndoEngine::new();
152 undo.undo(&mut graph, &mut log).unwrap();
153 assert_eq!(log.len(), 0); undo.redo(&mut graph, &mut log).unwrap();
156 assert!(!log.is_empty());
157 }
158
159 #[test]
160 fn test_undo_redo_row_shift() {
161 let mut graph = DependencyGraph::new();
162 let mut log = ChangeLog::new();
163 {
164 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
165 for r in [5u32, 6u32, 10u32] {
167 let cell = CellRef {
168 sheet_id: 0,
169 coord: Coord::new(r, 1, true, true),
170 };
171 editor.set_cell_value(cell, LiteralValue::Number(r as f64));
172 }
173 }
174 log.clear(); {
176 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
177 editor.insert_rows(0, 6, 2).unwrap(); }
179 assert!(
180 log.events()
181 .iter()
182 .any(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
183 );
184 let moved_count_before = log
185 .events()
186 .iter()
187 .filter(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
188 .count();
189 let mut undo = UndoEngine::new();
190 undo.undo(&mut graph, &mut log).unwrap();
191 assert_eq!(log.events().len(), 0); undo.redo(&mut graph, &mut log).unwrap();
193 let moved_count_after = log
194 .events()
195 .iter()
196 .filter(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
197 .count();
198 assert_eq!(moved_count_before, moved_count_after);
199 }
200
201 #[test]
202 fn test_undo_redo_column_shift() {
203 let mut graph = DependencyGraph::new();
204 let mut log = ChangeLog::new();
205 {
206 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
207 for c in [3u32, 4u32, 8u32] {
208 let cell = CellRef {
209 sheet_id: 0,
210 coord: Coord::new(1, c, true, true),
211 };
212 editor.set_cell_value(cell, LiteralValue::Number(c as f64));
213 }
214 }
215 log.clear();
216 {
217 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
218 editor.insert_columns(0, 5, 2).unwrap();
219 }
220 assert!(
221 log.events()
222 .iter()
223 .any(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
224 );
225 let mut undo = UndoEngine::new();
226 undo.undo(&mut graph, &mut log).unwrap();
227 assert_eq!(log.events().len(), 0);
228 }
229
230 #[test]
231 fn test_remove_vertex_dependency_roundtrip() {
232 use formualizer_parse::parser::parse;
233 let mut graph = DependencyGraph::new();
234 let mut log = ChangeLog::new();
235 let (a1_cell, a2_cell) = (
236 CellRef {
237 sheet_id: 0,
238 coord: Coord::new(0, 0, true, true), },
240 CellRef {
241 sheet_id: 0,
242 coord: Coord::new(1, 0, true, true), },
244 );
245 let a2_id;
246 {
247 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
248 editor.set_cell_value(a1_cell, LiteralValue::Number(10.0));
249 a2_id = editor.set_cell_formula(a2_cell, parse("=A1").unwrap());
250 }
251 let deps_before = graph.get_dependencies(a2_id);
253 assert!(!deps_before.is_empty());
254 log.clear();
256 {
257 let a1_vid = graph.get_vertex_id_for_address(&a1_cell).copied().unwrap();
259 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
260 editor.remove_vertex(a1_vid).unwrap();
261 }
262 assert!(
263 log.events()
264 .iter()
265 .any(|e| matches!(e, ChangeEvent::RemoveVertex { .. }))
266 );
267 let deps_after_remove = graph.get_dependencies(a2_id);
269 assert!(deps_after_remove.is_empty());
270 let mut undo = UndoEngine::new();
271 undo.undo(&mut graph, &mut log).unwrap();
272 let deps_after_undo = graph.get_dependencies(a2_id);
274 assert!(!deps_after_undo.is_empty());
275 undo.redo(&mut graph, &mut log).unwrap();
277 let deps_after_redo = graph.get_dependencies(a2_id);
278 assert!(deps_after_redo.is_empty());
279 }
280}