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 graph.authority_set_replay(crate::engine::authority::history::Replay::Undo);
81 let replayed = (|| {
82 let mut editor = VertexEditor::new(graph);
83 for (i, item) in batch.iter().enumerate().rev() {
84 if let Some(description) =
85 super::change_log::compound_start_description(i, |j| &batch[j].event)
86 {
87 editor.inverse_compound_end(description);
88 }
89 editor.apply_inverse(item.event.clone())?;
90 }
91 Ok::<_, EditorError>(())
92 })();
93 graph.authority_set_replay(crate::engine::authority::history::Replay::Forward);
94 replayed?;
95
96 self.undone.push(batch.clone());
98 Ok(batch)
99 }
100
101 pub(crate) fn pending_redo_events(&self) -> Vec<ChangeEvent> {
102 self.undone
103 .last()
104 .into_iter()
105 .flat_map(|batch| batch.iter().map(|item| item.event.clone()))
106 .collect()
107 }
108
109 pub fn redo(
110 &mut self,
111 graph: &mut DependencyGraph,
112 log: &mut ChangeLog,
113 ) -> Result<Vec<UndoBatchItem>, EditorError> {
114 if let Some(batch) = self.undone.pop() {
115 log.begin_compound("redo".to_string());
116 let ret = batch.clone();
119
120 graph.authority_set_replay(crate::engine::authority::history::Replay::Redo);
121 let replayed = (|| {
122 for item in batch {
123 log.record_with_meta(item.event.clone(), item.meta.clone());
125 match item.event {
126 ChangeEvent::SetValue { addr, new, .. } => {
127 let mut editor = VertexEditor::new(graph);
128 editor.set_cell_value(addr, new);
129 }
130 ChangeEvent::SetFormula { addr, new, .. } => {
131 let mut editor = VertexEditor::new(graph);
132 editor.set_cell_formula(addr, new);
133 }
134 ChangeEvent::AddVertex {
135 coord,
136 sheet_id,
137 kind,
138 ..
139 } => {
140 let mut editor = VertexEditor::new(graph);
141 let meta = crate::engine::graph::editor::vertex_editor::VertexMeta::new(
142 coord.row(),
143 coord.col(),
144 sheet_id,
145 kind.unwrap_or(crate::engine::vertex::VertexKind::Cell),
146 );
147 editor.try_add_vertex(meta)?;
148 }
149 ChangeEvent::RemoveVertex {
150 coord, sheet_id, ..
151 } => {
152 if let (Some(c), Some(sid)) = (coord, sheet_id) {
153 let mut editor = VertexEditor::new(graph);
154 let cell_ref = crate::reference::CellRef::new(
155 sid,
156 crate::reference::Coord::new(c.row(), c.col(), true, true),
157 );
158 let _ = editor.remove_vertex_at(cell_ref);
159 }
160 }
161 ChangeEvent::VertexMoved { id, new_coord, .. } => {
162 let mut editor = VertexEditor::new(graph);
163 let _ = editor.move_vertex(id, new_coord);
164 }
165 ChangeEvent::FormulaAdjusted { id, new_ast, .. } => {
166 let _ = graph.update_vertex_formula(id, new_ast);
169 graph.mark_vertex_dirty(id);
170 }
171 ChangeEvent::DefineName {
172 name,
173 scope,
174 definition,
175 } => {
176 let mut editor = VertexEditor::new(graph);
177 let _ = editor.define_name(&name, definition, scope);
178 }
179 ChangeEvent::UpdateName {
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::DeleteName { name, scope, .. } => {
189 let mut editor = VertexEditor::new(graph);
190 let _ = editor.delete_name(&name, scope);
191 }
192 ChangeEvent::NamedRangeAdjusted {
193 name,
194 scope,
195 new_definition,
196 ..
197 } => {
198 let mut editor = VertexEditor::new(graph);
199 let _ = editor.update_name(&name, new_definition, scope);
200 }
201 ChangeEvent::SpillCommitted { anchor, new, .. } => {
202 let _ = graph.restore_spill_snapshot(anchor, &new);
203 }
204 ChangeEvent::SpillCleared { anchor, .. } => {
205 graph.clear_spill_region(anchor);
206 }
207 ChangeEvent::TableResized {
208 name,
209 new_range,
210 new_empty_body,
211 ..
212 } => {
213 graph.resize_table_body(&name, new_range, new_empty_body)?;
214 }
215 ChangeEvent::SetRowVisibility { .. } => {
216 }
218 ChangeEvent::CompoundStart { description, .. } => {
219 graph.replay_structural_marker(&description, true);
222 }
223 _ => {}
224 }
225 }
226 Ok::<_, EditorError>(())
227 })();
228 graph.authority_set_replay(crate::engine::authority::history::Replay::Forward);
229 replayed?;
230 log.end_compound();
231 Ok(ret)
232 } else {
233 Ok(Vec::new())
234 }
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use crate::engine::EvalConfig;
242 use crate::engine::graph::editor::change_log::ChangeLog;
243 use crate::reference::{CellRef, Coord};
244 use formualizer_common::LiteralValue;
245
246 fn create_test_graph() -> DependencyGraph {
247 DependencyGraph::new_with_config(EvalConfig::default())
248 }
249
250 #[test]
251 fn test_undo_redo_single_value() {
252 let mut graph = create_test_graph();
253 let mut log = ChangeLog::new();
254 {
255 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
256 let cell = CellRef {
257 sheet_id: 0,
258 coord: Coord::new(1, 1, true, true),
259 };
260 editor.set_cell_value(cell, LiteralValue::Number(10.0));
261 }
262 assert_eq!(log.len(), 1);
263 let mut undo = UndoEngine::new();
264 undo.undo(&mut graph, &mut log).unwrap();
265 assert_eq!(log.len(), 0); undo.redo(&mut graph, &mut log).unwrap();
268 assert!(!log.is_empty());
269 }
270
271 #[test]
272 fn test_undo_redo_row_shift() {
273 let mut graph = create_test_graph();
274 let mut log = ChangeLog::new();
275 {
276 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
277 for r in [5u32, 6u32, 10u32] {
279 let cell = CellRef {
280 sheet_id: 0,
281 coord: Coord::new(r, 1, true, true),
282 };
283 editor.set_cell_formula(
286 cell,
287 formualizer_parse::parser::parse(format!("={r}")).unwrap(),
288 );
289 }
290 }
291 log.clear(); {
293 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
294 editor.insert_rows(0, 6, 2).unwrap(); }
296 assert!(
297 log.events()
298 .iter()
299 .any(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
300 );
301 let moved_count_before = log
302 .events()
303 .iter()
304 .filter(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
305 .count();
306 let mut undo = UndoEngine::new();
307 undo.undo(&mut graph, &mut log).unwrap();
308 assert_eq!(log.events().len(), 0); undo.redo(&mut graph, &mut log).unwrap();
310 let moved_count_after = log
311 .events()
312 .iter()
313 .filter(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
314 .count();
315 assert_eq!(moved_count_before, moved_count_after);
316 }
317
318 #[test]
319 fn test_undo_redo_spill_clear_on_scalar_edit_restores_registry_and_cells() {
320 let mut graph = create_test_graph();
321 let sheet_id = graph.sheet_id_mut("Sheet1");
322
323 let anchor_cell = CellRef::new(sheet_id, Coord::new(0, 0, true, true));
324 let anchor_vid = {
325 let mut editor = VertexEditor::new(&mut graph);
326 editor.set_cell_formula(anchor_cell, formualizer_parse::parser::parse("=0").unwrap())
329 };
330
331 let target_cells = vec![
332 CellRef::new(sheet_id, Coord::new(0, 0, true, true)),
333 CellRef::new(sheet_id, Coord::new(0, 1, true, true)),
334 CellRef::new(sheet_id, Coord::new(1, 0, true, true)),
335 CellRef::new(sheet_id, Coord::new(1, 1, true, true)),
336 ];
337 let values = vec![
338 vec![LiteralValue::Number(1.0), LiteralValue::Number(2.0)],
339 vec![LiteralValue::Number(3.0), LiteralValue::Number(4.0)],
340 ];
341 graph
342 .commit_spill_region_atomic_with_fault(anchor_vid, target_cells.clone(), values, None)
343 .unwrap();
344
345 assert!(graph.spill_registry_has_anchor(anchor_vid));
346
347 let mut log = ChangeLog::new();
348 {
349 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
350 editor.set_cell_value(anchor_cell, LiteralValue::Number(9.0));
352 }
353
354 assert!(!graph.spill_registry_has_anchor(anchor_vid));
355 assert_eq!(graph.spill_registry_counts(), (0, 0));
356
357 let mut undo = UndoEngine::new();
358 undo.undo(&mut graph, &mut log).unwrap();
359
360 assert!(graph.spill_registry_has_anchor(anchor_vid));
361 for cell in &target_cells {
362 assert_eq!(
363 graph.spill_registry_anchor_for_cell(*cell),
364 Some(anchor_vid)
365 );
366 }
367 undo.redo(&mut graph, &mut log).unwrap();
372 assert!(!graph.spill_registry_has_anchor(anchor_vid));
373 assert_eq!(graph.spill_registry_counts(), (0, 0));
374 }
375
376 #[test]
377 fn test_undo_depth_truncates_gracefully_under_changelog_cap() {
378 let mut graph = create_test_graph();
379 let sheet_id = graph.sheet_id_mut("Sheet1");
380 let mut log = ChangeLog::with_max_changelog_events(3);
381
382 for i in 0..5u32 {
384 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
385 let cell = CellRef::new(sheet_id, Coord::new(i, 0, true, true));
386 editor.set_cell_value(cell, LiteralValue::Number(i as f64));
387 }
388 assert_eq!(log.len(), 3);
389
390 let mut undo = UndoEngine::new();
391 undo.undo(&mut graph, &mut log).unwrap();
392 undo.undo(&mut graph, &mut log).unwrap();
393 undo.undo(&mut graph, &mut log).unwrap();
394 undo.undo(&mut graph, &mut log).unwrap();
396 assert_eq!(log.len(), 0);
397 }
398
399 #[test]
400 fn test_undo_redo_column_shift() {
401 let mut graph = create_test_graph();
402 let mut log = ChangeLog::new();
403 {
404 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
405 for c in [3u32, 4u32, 8u32] {
406 let cell = CellRef {
407 sheet_id: 0,
408 coord: Coord::new(1, c, true, true),
409 };
410 editor.set_cell_formula(
413 cell,
414 formualizer_parse::parser::parse(format!("={c}")).unwrap(),
415 );
416 }
417 }
418 log.clear();
419 {
420 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
421 editor.insert_columns(0, 5, 2).unwrap();
422 }
423 assert!(
424 log.events()
425 .iter()
426 .any(|e| matches!(e, ChangeEvent::VertexMoved { .. }))
427 );
428 let mut undo = UndoEngine::new();
429 undo.undo(&mut graph, &mut log).unwrap();
430 assert_eq!(log.events().len(), 0);
431 }
432
433 #[ignore = "M5 legacy-internal: legacy edge lists across RemoveVertex undo"]
437 #[test]
438 fn test_remove_vertex_dependency_roundtrip() {
439 use formualizer_parse::parser::parse;
440 let mut graph = create_test_graph();
441 let mut log = ChangeLog::new();
442 let (a1_cell, a2_cell) = (
443 CellRef {
444 sheet_id: 0,
445 coord: Coord::new(0, 0, true, true), },
447 CellRef {
448 sheet_id: 0,
449 coord: Coord::new(1, 0, true, true), },
451 );
452 let a2_id;
453 {
454 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
455 editor.set_cell_value(a1_cell, LiteralValue::Number(10.0));
456 a2_id = editor.set_cell_formula(a2_cell, parse("=A1").unwrap());
457 }
458 let deps_before = graph.get_dependencies(a2_id);
460 assert!(!deps_before.is_empty());
461 log.clear();
463 {
464 let a1_vid = graph.get_vertex_id_for_address(&a1_cell).unwrap();
466 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
467 editor.remove_vertex(a1_vid).unwrap();
468 }
469 assert!(
470 log.events()
471 .iter()
472 .any(|e| matches!(e, ChangeEvent::RemoveVertex { .. }))
473 );
474 let deps_after_remove = graph.get_dependencies(a2_id);
476 assert!(deps_after_remove.is_empty());
477 let mut undo = UndoEngine::new();
478 undo.undo(&mut graph, &mut log).unwrap();
479 let deps_after_undo = graph.get_dependencies(a2_id);
481 assert!(!deps_after_undo.is_empty());
482 undo.redo(&mut graph, &mut log).unwrap();
484 let deps_after_redo = graph.get_dependencies(a2_id);
485 assert!(deps_after_redo.is_empty());
486 }
487}