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 _ => {}
97 }
98 }
99 log.end_compound();
100 }
101 Ok(())
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::engine::graph::editor::change_log::ChangeLog;
109 use crate::reference::{CellRef, Coord};
110 use formualizer_common::LiteralValue;
111
112 #[test]
113 fn test_undo_redo_single_value() {
114 let mut graph = DependencyGraph::new();
115 let mut log = ChangeLog::new();
116 {
117 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
118 let cell = CellRef {
119 sheet_id: 0,
120 coord: Coord::new(1, 1, true, true),
121 };
122 editor.set_cell_value(cell, LiteralValue::Number(10.0));
123 }
124 assert_eq!(log.len(), 1);
125 let mut undo = UndoEngine::new();
126 undo.undo(&mut graph, &mut log).unwrap();
127 assert_eq!(log.len(), 0); undo.redo(&mut graph, &mut log).unwrap();
130 assert!(!log.is_empty());
131 }
132
133 #[test]
134 fn test_undo_redo_row_shift() {
135 let mut graph = DependencyGraph::new();
136 let mut log = ChangeLog::new();
137 {
138 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
139 for r in [5u32, 6u32, 10u32] {
141 let cell = CellRef {
142 sheet_id: 0,
143 coord: Coord::new(r, 1, true, true),
144 };
145 editor.set_cell_value(cell, LiteralValue::Number(r as f64));
146 }
147 }
148 log.clear(); {
150 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
151 editor.insert_rows(0, 6, 2).unwrap(); }
153 assert!(
154 log.events()
155 .iter()
156 .any(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
157 );
158 let moved_count_before = log
159 .events()
160 .iter()
161 .filter(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
162 .count();
163 let mut undo = UndoEngine::new();
164 undo.undo(&mut graph, &mut log).unwrap();
165 assert_eq!(log.events().len(), 0); undo.redo(&mut graph, &mut log).unwrap();
167 let moved_count_after = log
168 .events()
169 .iter()
170 .filter(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
171 .count();
172 assert_eq!(moved_count_before, moved_count_after);
173 }
174
175 #[test]
176 fn test_undo_redo_column_shift() {
177 let mut graph = DependencyGraph::new();
178 let mut log = ChangeLog::new();
179 {
180 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
181 for c in [3u32, 4u32, 8u32] {
182 let cell = CellRef {
183 sheet_id: 0,
184 coord: Coord::new(1, c, true, true),
185 };
186 editor.set_cell_value(cell, LiteralValue::Number(c as f64));
187 }
188 }
189 log.clear();
190 {
191 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
192 editor.insert_columns(0, 5, 2).unwrap();
193 }
194 assert!(
195 log.events()
196 .iter()
197 .any(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
198 );
199 let mut undo = UndoEngine::new();
200 undo.undo(&mut graph, &mut log).unwrap();
201 assert_eq!(log.events().len(), 0);
202 }
203
204 #[test]
205 fn test_remove_vertex_dependency_roundtrip() {
206 use formualizer_parse::parser::parse;
207 let mut graph = DependencyGraph::new();
208 let mut log = ChangeLog::new();
209 let (a1_cell, a2_cell) = (
210 CellRef {
211 sheet_id: 0,
212 coord: Coord::new(0, 0, true, true), },
214 CellRef {
215 sheet_id: 0,
216 coord: Coord::new(1, 0, true, true), },
218 );
219 let a2_id;
220 {
221 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
222 editor.set_cell_value(a1_cell, LiteralValue::Number(10.0));
223 a2_id = editor.set_cell_formula(a2_cell, parse("=A1").unwrap());
224 }
225 let deps_before = graph.get_dependencies(a2_id);
227 assert!(!deps_before.is_empty());
228 log.clear();
230 {
231 let a1_vid = graph.get_vertex_id_for_address(&a1_cell).copied().unwrap();
233 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
234 editor.remove_vertex(a1_vid).unwrap();
235 }
236 assert!(
237 log.events()
238 .iter()
239 .any(|e| matches!(e, ChangeEvent::RemoveVertex { .. }))
240 );
241 let deps_after_remove = graph.get_dependencies(a2_id);
243 assert!(deps_after_remove.is_empty());
244 let mut undo = UndoEngine::new();
245 undo.undo(&mut graph, &mut log).unwrap();
246 let deps_after_undo = graph.get_dependencies(a2_id);
248 assert!(!deps_after_undo.is_empty());
249 undo.redo(&mut graph, &mut log).unwrap();
251 let deps_after_redo = graph.get_dependencies(a2_id);
252 assert!(deps_after_redo.is_empty());
253 }
254}