1use super::change_log::{ChangeEvent, ChangeEventMeta, ChangeLog};
3use super::vertex_editor::VertexEditor;
4use crate::engine::graph::DependencyGraph;
5use crate::engine::graph::editor::vertex_editor::EditorError;
6
7#[derive(Debug, Clone)]
8pub struct UndoBatchItem {
9 pub event: ChangeEvent,
10 pub meta: ChangeEventMeta,
11}
12
13#[derive(Debug, Default)]
14pub struct UndoEngine {
15 undone: Vec<Vec<UndoBatchItem>>, actions_done: Vec<crate::engine::ActionJournal>,
20 actions_undone: Vec<crate::engine::ActionJournal>,
21}
22
23impl UndoEngine {
24 pub fn new() -> Self {
25 Self {
26 undone: Vec::new(),
27 actions_done: Vec::new(),
28 actions_undone: Vec::new(),
29 }
30 }
31
32 pub fn push_action(&mut self, journal: crate::engine::ActionJournal) {
34 self.actions_done.push(journal);
35 self.actions_undone.clear();
36 }
37
38 pub fn pop_undo_action(&mut self) -> Option<crate::engine::ActionJournal> {
39 self.actions_done.pop()
40 }
41
42 pub fn push_redo_action(&mut self, journal: crate::engine::ActionJournal) {
43 self.actions_undone.push(journal);
44 }
45
46 pub fn pop_redo_action(&mut self) -> Option<crate::engine::ActionJournal> {
47 self.actions_undone.pop()
48 }
49
50 pub fn push_done_action(&mut self, journal: crate::engine::ActionJournal) {
51 self.actions_done.push(journal);
52 }
53
54 pub fn undo(
56 &mut self,
57 graph: &mut DependencyGraph,
58 log: &mut ChangeLog,
59 ) -> Result<Vec<UndoBatchItem>, EditorError> {
60 let idxs = log.last_group_indices();
61 if idxs.is_empty() {
62 return Ok(Vec::new());
63 }
64 let batch: Vec<UndoBatchItem> = idxs
65 .iter()
66 .map(|i| UndoBatchItem {
67 event: log.events()[*i].clone(),
68 meta: log.event_meta(*i).cloned().unwrap_or_default(),
69 })
70 .collect();
71 let max_idx = *idxs.iter().max().unwrap();
72 if max_idx + 1 == log.events().len() {
73 let truncate_to = idxs.iter().min().copied().unwrap();
74 log.truncate(truncate_to);
75 } else {
76 return Err(EditorError::TransactionFailed {
77 reason: "Non-tail undo not supported".into(),
78 });
79 }
80 let mut editor = VertexEditor::new(graph);
81 for item in batch.iter().rev() {
82 editor.apply_inverse(item.event.clone())?;
83 }
84
85 self.undone.push(batch.clone());
87 Ok(batch)
88 }
89
90 pub(crate) fn pending_redo_events(&self) -> Vec<ChangeEvent> {
91 self.undone
92 .last()
93 .into_iter()
94 .flat_map(|batch| batch.iter().map(|item| item.event.clone()))
95 .collect()
96 }
97
98 pub fn redo(
99 &mut self,
100 graph: &mut DependencyGraph,
101 log: &mut ChangeLog,
102 ) -> Result<Vec<UndoBatchItem>, EditorError> {
103 if let Some(batch) = self.undone.pop() {
104 log.begin_compound("redo".to_string());
105 let ret = batch.clone();
108
109 for item in batch {
110 log.record_with_meta(item.event.clone(), item.meta.clone());
112 match item.event {
113 ChangeEvent::SetValue { addr, new, .. } => {
114 let mut editor = VertexEditor::new(graph);
115 editor.set_cell_value(addr, new);
116 }
117 ChangeEvent::SetFormula { addr, new, .. } => {
118 let mut editor = VertexEditor::new(graph);
119 editor.set_cell_formula(addr, new);
120 }
121 ChangeEvent::AddVertex {
122 coord,
123 sheet_id,
124 kind,
125 ..
126 } => {
127 let mut editor = VertexEditor::new(graph);
128 let meta = crate::engine::graph::editor::vertex_editor::VertexMeta::new(
129 coord.row(),
130 coord.col(),
131 sheet_id,
132 kind.unwrap_or(crate::engine::vertex::VertexKind::Cell),
133 );
134 editor.try_add_vertex(meta)?;
135 }
136 ChangeEvent::RemoveVertex {
137 coord, sheet_id, ..
138 } => {
139 if let (Some(c), Some(sid)) = (coord, sheet_id) {
140 let mut editor = VertexEditor::new(graph);
141 let cell_ref = crate::reference::CellRef::new(
142 sid,
143 crate::reference::Coord::new(c.row(), c.col(), true, true),
144 );
145 let _ = editor.remove_vertex_at(cell_ref);
146 }
147 }
148 ChangeEvent::VertexMoved { id, new_coord, .. } => {
149 let mut editor = VertexEditor::new(graph);
150 let _ = editor.move_vertex(id, new_coord);
151 }
152 ChangeEvent::FormulaAdjusted { id, new_ast, .. } => {
153 let _ = graph.update_vertex_formula(id, new_ast);
156 graph.mark_vertex_dirty(id);
157 }
158 ChangeEvent::DefineName {
159 name,
160 scope,
161 definition,
162 } => {
163 let mut editor = VertexEditor::new(graph);
164 let _ = editor.define_name(&name, definition, scope);
165 }
166 ChangeEvent::UpdateName {
167 name,
168 scope,
169 new_definition,
170 ..
171 } => {
172 let mut editor = VertexEditor::new(graph);
173 let _ = editor.update_name(&name, new_definition, scope);
174 }
175 ChangeEvent::DeleteName { name, scope, .. } => {
176 let mut editor = VertexEditor::new(graph);
177 let _ = editor.delete_name(&name, scope);
178 }
179 ChangeEvent::NamedRangeAdjusted {
180 name,
181 scope,
182 new_definition,
183 ..
184 } => {
185 let mut editor = VertexEditor::new(graph);
186 let _ = editor.update_name(&name, new_definition, scope);
187 }
188 ChangeEvent::SpillCommitted { anchor, new, .. } => {
189 let _ = graph.commit_spill_region_atomic_with_fault(
190 anchor,
191 new.target_cells,
192 new.values,
193 None,
194 );
195 }
196 ChangeEvent::SpillCleared { anchor, .. } => {
197 graph.clear_spill_region(anchor);
198 }
199 ChangeEvent::SetRowVisibility { .. } => {
200 }
202 _ => {}
203 }
204 }
205 log.end_compound();
206 Ok(ret)
207 } else {
208 Ok(Vec::new())
209 }
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use crate::engine::EvalConfig;
217 use crate::engine::graph::editor::change_log::ChangeLog;
218 use crate::reference::{CellRef, Coord};
219 use formualizer_common::LiteralValue;
220
221 fn create_test_graph() -> DependencyGraph {
222 DependencyGraph::new_with_config(EvalConfig::default())
223 }
224
225 #[test]
226 fn test_undo_redo_single_value() {
227 let mut graph = create_test_graph();
228 let mut log = ChangeLog::new();
229 {
230 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
231 let cell = CellRef {
232 sheet_id: 0,
233 coord: Coord::new(1, 1, true, true),
234 };
235 editor.set_cell_value(cell, LiteralValue::Number(10.0));
236 }
237 assert_eq!(log.len(), 1);
238 let mut undo = UndoEngine::new();
239 undo.undo(&mut graph, &mut log).unwrap();
240 assert_eq!(log.len(), 0); undo.redo(&mut graph, &mut log).unwrap();
243 assert!(!log.is_empty());
244 }
245
246 #[test]
247 fn test_undo_redo_row_shift() {
248 let mut graph = create_test_graph();
249 let mut log = ChangeLog::new();
250 {
251 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
252 for r in [5u32, 6u32, 10u32] {
254 let cell = CellRef {
255 sheet_id: 0,
256 coord: Coord::new(r, 1, true, true),
257 };
258 editor.set_cell_value(cell, LiteralValue::Number(r as f64));
259 }
260 }
261 log.clear(); {
263 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
264 editor.insert_rows(0, 6, 2).unwrap(); }
266 assert!(
267 log.events()
268 .iter()
269 .any(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
270 );
271 let moved_count_before = log
272 .events()
273 .iter()
274 .filter(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
275 .count();
276 let mut undo = UndoEngine::new();
277 undo.undo(&mut graph, &mut log).unwrap();
278 assert_eq!(log.events().len(), 0); undo.redo(&mut graph, &mut log).unwrap();
280 let moved_count_after = log
281 .events()
282 .iter()
283 .filter(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
284 .count();
285 assert_eq!(moved_count_before, moved_count_after);
286 }
287
288 #[test]
289 fn test_undo_redo_spill_clear_on_scalar_edit_restores_registry_and_cells() {
290 let mut graph = create_test_graph();
291 let sheet_id = graph.sheet_id_mut("Sheet1");
292
293 let anchor_cell = CellRef::new(sheet_id, Coord::new(0, 0, true, true));
294 let anchor_vid = {
295 let mut editor = VertexEditor::new(&mut graph);
296 editor.set_cell_value(anchor_cell, LiteralValue::Number(0.0))
297 };
298
299 let target_cells = vec![
300 CellRef::new(sheet_id, Coord::new(0, 0, true, true)),
301 CellRef::new(sheet_id, Coord::new(0, 1, true, true)),
302 CellRef::new(sheet_id, Coord::new(1, 0, true, true)),
303 CellRef::new(sheet_id, Coord::new(1, 1, true, true)),
304 ];
305 let values = vec![
306 vec![LiteralValue::Number(1.0), LiteralValue::Number(2.0)],
307 vec![LiteralValue::Number(3.0), LiteralValue::Number(4.0)],
308 ];
309 graph
310 .commit_spill_region_atomic_with_fault(anchor_vid, target_cells.clone(), values, None)
311 .unwrap();
312
313 assert!(graph.spill_registry_has_anchor(anchor_vid));
314
315 let mut log = ChangeLog::new();
316 {
317 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
318 editor.set_cell_value(anchor_cell, LiteralValue::Number(9.0));
320 }
321
322 assert!(!graph.spill_registry_has_anchor(anchor_vid));
323 assert_eq!(graph.spill_registry_counts(), (0, 0));
324
325 let mut undo = UndoEngine::new();
326 undo.undo(&mut graph, &mut log).unwrap();
327
328 assert!(graph.spill_registry_has_anchor(anchor_vid));
329 for cell in &target_cells {
330 assert_eq!(
331 graph.spill_registry_anchor_for_cell(*cell),
332 Some(anchor_vid)
333 );
334 }
335 undo.redo(&mut graph, &mut log).unwrap();
340 assert!(!graph.spill_registry_has_anchor(anchor_vid));
341 assert_eq!(graph.spill_registry_counts(), (0, 0));
342 }
343
344 #[test]
345 fn test_undo_depth_truncates_gracefully_under_changelog_cap() {
346 let mut graph = create_test_graph();
347 let sheet_id = graph.sheet_id_mut("Sheet1");
348 let mut log = ChangeLog::with_max_changelog_events(3);
349
350 for i in 0..5u32 {
352 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
353 let cell = CellRef::new(sheet_id, Coord::new(i, 0, true, true));
354 editor.set_cell_value(cell, LiteralValue::Number(i as f64));
355 }
356 assert_eq!(log.len(), 3);
357
358 let mut undo = UndoEngine::new();
359 undo.undo(&mut graph, &mut log).unwrap();
360 undo.undo(&mut graph, &mut log).unwrap();
361 undo.undo(&mut graph, &mut log).unwrap();
362 undo.undo(&mut graph, &mut log).unwrap();
364 assert_eq!(log.len(), 0);
365 }
366
367 #[test]
368 fn test_undo_redo_column_shift() {
369 let mut graph = create_test_graph();
370 let mut log = ChangeLog::new();
371 {
372 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
373 for c in [3u32, 4u32, 8u32] {
374 let cell = CellRef {
375 sheet_id: 0,
376 coord: Coord::new(1, c, true, true),
377 };
378 editor.set_cell_value(cell, LiteralValue::Number(c as f64));
379 }
380 }
381 log.clear();
382 {
383 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
384 editor.insert_columns(0, 5, 2).unwrap();
385 }
386 assert!(
387 log.events()
388 .iter()
389 .any(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
390 );
391 let mut undo = UndoEngine::new();
392 undo.undo(&mut graph, &mut log).unwrap();
393 assert_eq!(log.events().len(), 0);
394 }
395
396 #[test]
397 fn test_remove_vertex_dependency_roundtrip() {
398 use formualizer_parse::parser::parse;
399 let mut graph = create_test_graph();
400 let mut log = ChangeLog::new();
401 let (a1_cell, a2_cell) = (
402 CellRef {
403 sheet_id: 0,
404 coord: Coord::new(0, 0, true, true), },
406 CellRef {
407 sheet_id: 0,
408 coord: Coord::new(1, 0, true, true), },
410 );
411 let a2_id;
412 {
413 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
414 editor.set_cell_value(a1_cell, LiteralValue::Number(10.0));
415 a2_id = editor.set_cell_formula(a2_cell, parse("=A1").unwrap());
416 }
417 let deps_before = graph.get_dependencies(a2_id);
419 assert!(!deps_before.is_empty());
420 log.clear();
422 {
423 let a1_vid = graph.get_vertex_id_for_address(&a1_cell).copied().unwrap();
425 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
426 editor.remove_vertex(a1_vid).unwrap();
427 }
428 assert!(
429 log.events()
430 .iter()
431 .any(|e| matches!(e, ChangeEvent::RemoveVertex { .. }))
432 );
433 let deps_after_remove = graph.get_dependencies(a2_id);
435 assert!(deps_after_remove.is_empty());
436 let mut undo = UndoEngine::new();
437 undo.undo(&mut graph, &mut log).unwrap();
438 let deps_after_undo = graph.get_dependencies(a2_id);
440 assert!(!deps_after_undo.is_empty());
441 undo.redo(&mut graph, &mut log).unwrap();
443 let deps_after_redo = graph.get_dependencies(a2_id);
444 assert!(deps_after_redo.is_empty());
445 }
446}