1use crate::SheetId;
2use crate::engine::addr::GridAddr;
3use crate::engine::graph::DependencyGraph;
4use crate::engine::graph::editor::reference_adjuster::{
5 MoveReferenceAdjuster, ReferenceAdjuster, ReferenceContext, RelativeReferenceAdjuster,
6 ShiftOperation,
7};
8use crate::engine::named_range::{NameScope, NamedDefinition};
9use crate::engine::{ChangeEvent, ChangeLogger, VertexId, VertexKind};
10use crate::reference::{CellRef, Coord};
11use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
12use formualizer_parse::parser::ASTNode;
13use rustc_hash::FxHashMap;
14use std::sync::atomic::{AtomicU64, Ordering};
15
16#[derive(Debug, Clone)]
18pub struct VertexMeta {
19 pub coord: GridAddr,
20 pub sheet_id: SheetId,
21 pub kind: VertexKind,
22 pub flags: u8,
23}
24
25impl VertexMeta {
26 pub fn new(row: u32, col: u32, sheet_id: SheetId, kind: VertexKind) -> Self {
27 Self {
28 coord: GridAddr::new(row, col),
29 sheet_id,
30 kind,
31 flags: 0,
32 }
33 }
34
35 pub fn with_flags(mut self, flags: u8) -> Self {
36 self.flags = flags;
37 self
38 }
39
40 pub fn dirty(mut self) -> Self {
41 self.flags |= 0x01;
42 self
43 }
44
45 pub fn volatile(mut self) -> Self {
46 self.flags |= 0x02;
47 self
48 }
49}
50
51#[derive(Debug, Clone)]
53pub struct VertexMetaPatch {
54 pub kind: Option<VertexKind>,
55 pub coord: Option<GridAddr>,
56 pub dirty: Option<bool>,
57 pub volatile: Option<bool>,
58}
59
60#[derive(Debug, Clone)]
62pub struct VertexDataPatch {
63 pub value: Option<LiteralValue>,
64 pub formula: Option<ASTNode>,
65}
66
67#[derive(Debug, Clone, Default)]
69pub struct MetaUpdateSummary {
70 pub coord_changed: bool,
71 pub kind_changed: bool,
72 pub flags_changed: bool,
73}
74
75#[derive(Debug, Clone, Default)]
77pub struct DataUpdateSummary {
78 pub value_changed: bool,
79 pub formula_changed: bool,
80 pub dependents_marked_dirty: Vec<VertexId>,
81}
82
83#[derive(Debug, Clone, Default)]
85pub struct ShiftSummary {
86 pub vertices_moved: Vec<VertexId>,
87 pub vertices_deleted: Vec<VertexId>,
88 pub references_adjusted: usize,
89 pub formulas_updated: usize,
90 #[cfg(test)]
91 pub(crate) structural_dependents_dirtied: Vec<VertexId>,
92}
93
94#[derive(Debug, Clone, Default)]
96pub struct RangeSummary {
97 pub cells_affected: usize,
98 pub vertices_created: Vec<VertexId>,
99 pub vertices_updated: Vec<VertexId>,
100 pub cells_moved: usize,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub struct TransactionId(u64);
106
107impl TransactionId {
108 fn new() -> Self {
109 static COUNTER: AtomicU64 = AtomicU64::new(0);
110 TransactionId(COUNTER.fetch_add(1, Ordering::Relaxed))
111 }
112}
113
114#[derive(Debug)]
116struct Transaction {
117 id: TransactionId,
118 start_index: usize, }
120
121#[derive(Debug, Clone)]
123pub enum EditorError {
124 TargetOccupied { cell: CellRef },
125 OutOfBounds { row: u32, col: u32 },
126 InvalidName { name: String, reason: String },
127 TransactionFailed { reason: String },
128 TransactionUnsupported { reason: String },
129 NoActiveTransaction,
130 VertexNotFound { id: VertexId },
131 Excel(ExcelError),
132}
133
134impl From<ExcelError> for EditorError {
135 fn from(e: ExcelError) -> Self {
136 EditorError::Excel(e)
137 }
138}
139
140impl std::fmt::Display for EditorError {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 match self {
143 EditorError::TargetOccupied { cell } => {
144 write!(
145 f,
146 "Target cell occupied at row {}, col {}",
147 cell.coord.row(),
148 cell.coord.col()
149 )
150 }
151 EditorError::OutOfBounds { row, col } => {
152 write!(f, "Cell position out of bounds: row {row}, col {col}")
153 }
154 EditorError::InvalidName { name, reason } => {
155 write!(f, "Invalid name '{name}': {reason}")
156 }
157 EditorError::TransactionFailed { reason } => {
158 write!(f, "Transaction failed: {reason}")
159 }
160 EditorError::TransactionUnsupported { reason } => {
161 write!(f, "Transaction unsupported: {reason}")
162 }
163 EditorError::NoActiveTransaction => {
164 write!(f, "No active transaction")
165 }
166 EditorError::VertexNotFound { id } => {
167 write!(f, "Vertex not found: {id:?}")
168 }
169 EditorError::Excel(e) => write!(f, "Excel error: {e:?}"),
170 }
171 }
172}
173
174impl std::error::Error for EditorError {}
175
176pub trait SpillValueReader {
211 fn read_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue>;
212}
213
214pub struct VertexEditor<'g> {
215 graph: &'g mut DependencyGraph,
216 change_logger: Option<&'g mut dyn ChangeLogger>,
217 spill_value_reader: Option<&'g dyn SpillValueReader>,
218 structural_occupancy: Option<crate::engine::graph::StructuralOccupancy>,
219 batch_mode: bool,
220}
221
222impl<'g> VertexEditor<'g> {
223 pub fn new(graph: &'g mut DependencyGraph) -> Self {
225 Self {
226 graph,
227 change_logger: None,
228 spill_value_reader: None,
229 structural_occupancy: None,
230 batch_mode: false,
231 }
232 }
233
234 pub(crate) fn with_structural_occupancy(
236 mut self,
237 occupancy: crate::engine::graph::StructuralOccupancy,
238 ) -> Self {
239 self.structural_occupancy = Some(occupancy);
240 self
241 }
242
243 pub(crate) fn set_structural_occupancy(
244 &mut self,
245 occupancy: crate::engine::graph::StructuralOccupancy,
246 ) {
247 self.structural_occupancy = Some(occupancy);
248 }
249
250 pub fn with_logger<L: ChangeLogger + 'g>(
252 graph: &'g mut DependencyGraph,
253 logger: &'g mut L,
254 ) -> Self {
255 Self {
256 graph,
257 change_logger: Some(logger as &'g mut dyn ChangeLogger),
258 spill_value_reader: None,
259 structural_occupancy: None,
260 batch_mode: false,
261 }
262 }
263
264 pub fn with_logger_and_spill_reader<L: ChangeLogger + 'g>(
266 graph: &'g mut DependencyGraph,
267 logger: &'g mut L,
268 spill_value_reader: &'g dyn SpillValueReader,
269 ) -> Self {
270 Self {
271 graph,
272 change_logger: Some(logger as &'g mut dyn ChangeLogger),
273 spill_value_reader: Some(spill_value_reader),
274 structural_occupancy: None,
275 batch_mode: false,
276 }
277 }
278
279 pub fn begin_batch(&mut self) {
281 if !self.batch_mode {
282 self.graph.begin_batch();
283 self.batch_mode = true;
284 }
285 }
286
287 pub fn commit_batch(&mut self) {
289 if self.batch_mode {
290 self.graph.end_batch();
291 self.batch_mode = false;
292 }
293 }
294
295 fn log_change(&mut self, event: ChangeEvent) {
297 if let Some(logger) = &mut self.change_logger {
298 logger.record(event);
299 }
300 }
301
302 fn snapshot_spill_for_anchor(
303 &self,
304 anchor: VertexId,
305 ) -> Option<crate::engine::graph::editor::change_log::SpillSnapshot> {
306 let cells = self.graph.spill_cells_for_anchor(anchor)?.to_vec();
307 if cells.is_empty() {
308 return None;
309 }
310
311 let max = self.graph.get_config().spill.max_spill_cells as usize;
313 let mut cells = cells;
314 if cells.len() > max {
315 cells.truncate(max);
316 }
317
318 let first = *cells.first().expect("non-empty spill cells");
319 let sheet_name = self.graph.sheet_name(first.sheet_id).to_string();
320 let row0 = first.coord.row();
321 let col0 = first.coord.col();
322
323 let mut max_row = row0;
324 let mut max_col = col0;
325 let mut by_coord: FxHashMap<(u32, u32), LiteralValue> = FxHashMap::default();
326 for cell in &cells {
327 max_row = max_row.max(cell.coord.row());
328 max_col = max_col.max(cell.coord.col());
329 let v = if let Some(reader) = self.spill_value_reader {
330 reader
331 .read_cell_value(&sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
332 .unwrap_or(LiteralValue::Empty)
333 } else {
334 self.graph
335 .get_cell_value(&sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
336 .unwrap_or(LiteralValue::Empty)
337 };
338 by_coord.insert((cell.coord.row(), cell.coord.col()), v);
339 }
340
341 let rows = (max_row - row0 + 1) as usize;
342 let cols = (max_col - col0 + 1) as usize;
343 let mut values: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
344 for r in 0..rows {
345 let mut row: Vec<LiteralValue> = Vec::with_capacity(cols);
346 for c in 0..cols {
347 row.push(
348 by_coord
349 .get(&(row0 + r as u32, col0 + c as u32))
350 .cloned()
351 .unwrap_or(LiteralValue::Empty),
352 );
353 }
354 values.push(row);
355 }
356
357 Some(crate::engine::graph::editor::change_log::SpillSnapshot {
358 target_cells: cells,
359 values,
360 })
361 }
362
363 pub fn commit_spill_region(
365 &mut self,
366 anchor: VertexId,
367 target_cells: Vec<CellRef>,
368 values: Vec<Vec<LiteralValue>>,
369 ) -> Result<(), EditorError> {
370 let old = self.snapshot_spill_for_anchor(anchor);
371 self.graph
372 .commit_spill_region_atomic_with_fault(
373 anchor,
374 target_cells.clone(),
375 values.clone(),
376 None,
377 )
378 .map_err(EditorError::Excel)?;
379 self.log_change(ChangeEvent::SpillCommitted {
380 anchor,
381 old,
382 new: crate::engine::graph::editor::change_log::SpillSnapshot {
383 target_cells,
384 values,
385 },
386 });
387 Ok(())
388 }
389
390 pub fn clear_spill_region(&mut self, anchor: VertexId) {
392 let Some(old) = self.snapshot_spill_for_anchor(anchor) else {
393 return;
394 };
395 self.graph.clear_spill_region(anchor);
396 self.log_change(ChangeEvent::SpillCleared { anchor, old });
397 }
398
399 pub fn has_logger(&self) -> bool {
401 self.change_logger.is_some()
402 }
403
404 fn get_formula_ast(&self, id: VertexId) -> Option<ASTNode> {
405 self.graph.get_formula_id(id).and_then(|ast_id| {
406 self.graph
407 .data_store()
408 .retrieve_ast(ast_id, self.graph.sheet_reg())
409 })
410 }
411
412 fn snapshot_named_definitions(&self) -> FxHashMap<(NameScope, String), NamedDefinition> {
413 let mut out: FxHashMap<(NameScope, String), NamedDefinition> = FxHashMap::default();
414 for (name, nr) in self.graph.named_ranges_iter() {
415 out.insert((NameScope::Workbook, name.clone()), nr.definition.clone());
416 }
417 for ((sheet_id, name), nr) in self.graph.sheet_named_ranges_iter() {
418 out.insert(
419 (NameScope::Sheet(*sheet_id), name.clone()),
420 nr.definition.clone(),
421 );
422 }
423 out
424 }
425
426 pub fn apply_inverse(&mut self, change: ChangeEvent) -> Result<(), EditorError> {
433 match change {
434 ChangeEvent::SetValue {
435 addr,
436 old_value,
437 old_formula,
438 new: _,
439 } => {
440 if let Some(old_formula) = old_formula {
442 self.set_cell_formula(addr, old_formula);
443 } else if let Some(old_value) = old_value {
444 self.set_cell_value(addr, old_value);
445 } else if let Some(&id) = self.graph.get_vertex_id_for_address(&addr) {
446 self.remove_vertex(id)?;
447 }
448 }
449 ChangeEvent::SetFormula {
450 addr,
451 old_value,
452 old_formula,
453 new: _,
454 } => {
455 if let Some(old_formula) = old_formula {
457 self.set_cell_formula(addr, old_formula);
458 } else if let Some(old_value) = old_value {
459 self.set_cell_value(addr, old_value);
460 } else if let Some(&id) = self.graph.get_vertex_id_for_address(&addr) {
461 self.remove_vertex(id)?;
462 }
463 }
464 ChangeEvent::SetRowVisibility { .. } => {
465 }
467 ChangeEvent::AddVertex { id, .. } => {
468 let _ = self.remove_vertex(id); }
471 ChangeEvent::RemoveVertex {
472 id: _,
473 old_value,
474 old_formula,
475 old_dependencies,
476 old_dependents,
477 coord,
478 sheet_id,
479 kind,
480 ..
481 } => {
482 if let (Some(c), Some(sid)) = (coord, sheet_id) {
483 let meta =
484 VertexMeta::new(c.row(), c.col(), sid, kind.unwrap_or(VertexKind::Cell));
485 let new_id = self.try_add_vertex(meta)?;
486 if let Some(v) = old_value {
487 let cell_ref = self.graph.make_cell_ref_internal(sid, c.row(), c.col());
488 self.set_cell_value(cell_ref, v);
489 }
490 if let Some(f) = old_formula {
491 let cell_ref = self.graph.make_cell_ref_internal(sid, c.row(), c.col());
492 self.set_cell_formula(cell_ref, f);
493 }
494 for dep in old_dependencies {
495 self.graph.add_dependency_edge(new_id, dep)?;
496 }
497 for parent in old_dependents {
498 self.graph.add_dependency_edge(parent, new_id)?;
499 }
500 }
501 }
502 ChangeEvent::DefineName { name, scope, .. } => {
503 self.graph.delete_name(&name, scope)?;
505 }
506 ChangeEvent::UpdateName {
507 name,
508 scope,
509 old_definition,
510 ..
511 } => {
512 self.graph.update_name(&name, old_definition, scope)?;
514 }
515 ChangeEvent::DeleteName {
516 name,
517 scope,
518 old_definition,
519 } => {
520 if let Some(def) = old_definition {
521 self.graph.define_name(&name, def, scope)?;
522 } else {
523 return Err(EditorError::TransactionFailed {
524 reason: "Missing old definition for name deletion rollback".to_string(),
525 });
526 }
527 }
528 ChangeEvent::SpillCommitted { anchor, old, .. } => {
529 if let Some(old) = old {
531 self.graph
532 .commit_spill_region_atomic_with_fault(
533 anchor,
534 old.target_cells,
535 old.values,
536 None,
537 )
538 .map_err(EditorError::Excel)?;
539 } else {
540 self.graph.clear_spill_region(anchor);
541 }
542 }
543 ChangeEvent::SpillCleared { anchor, old } => {
544 self.graph
546 .commit_spill_region_atomic_with_fault(
547 anchor,
548 old.target_cells,
549 old.values,
550 None,
551 )
552 .map_err(EditorError::Excel)?;
553 }
554 ChangeEvent::StagedFormulaCellChanged { .. } => {
555 }
557 ChangeEvent::CompoundStart { .. } | ChangeEvent::CompoundEnd { .. } => {
559 }
561 ChangeEvent::VertexMoved {
562 id,
563 sheet_id: _,
564 old_coord,
565 ..
566 } => {
567 self.move_vertex(id, old_coord)?;
569 }
570 ChangeEvent::FormulaAdjusted { id, old_ast, .. } => {
571 self.graph
573 .update_vertex_formula(id, old_ast)
574 .map_err(EditorError::Excel)?;
575 self.graph.mark_vertex_dirty(id);
576 }
577 ChangeEvent::NamedRangeAdjusted {
578 name,
579 scope,
580 old_definition,
581 ..
582 } => {
583 self.graph.update_name(&name, old_definition, scope)?;
585 }
586 ChangeEvent::EdgeAdded { from, to } => {
587 return Err(EditorError::TransactionFailed {
590 reason: "Cannot rollback edge addition yet".to_string(),
591 });
592 }
593 ChangeEvent::EdgeRemoved { from, to } => {
594 return Err(EditorError::TransactionFailed {
597 reason: "Cannot rollback edge removal yet".to_string(),
598 });
599 }
600 }
601 Ok(())
602 }
603
604 pub fn add_vertex(&mut self, meta: VertexMeta) -> VertexId {
609 self.try_add_vertex(meta)
610 .unwrap_or_else(|_| VertexId::new(0))
611 }
612
613 pub fn try_add_vertex(&mut self, meta: VertexMeta) -> Result<VertexId, EditorError> {
614 let sheet_name = self.graph.sheet_name(meta.sheet_id).to_string();
617
618 let id = self
621 .graph
622 .set_cell_value(
623 &sheet_name,
624 meta.coord.row() + 1,
625 meta.coord.col() + 1,
626 LiteralValue::Empty,
627 )
628 .map_err(EditorError::Excel)?
629 .affected_vertices
630 .into_iter()
631 .next()
632 .ok_or_else(|| EditorError::TransactionFailed {
633 reason: "vertex addition produced no affected vertex".to_string(),
634 })?;
635
636 if self.has_logger() && id.0 != 0 {
637 self.log_change(ChangeEvent::AddVertex {
638 id,
639 coord: meta.coord,
640 sheet_id: meta.sheet_id,
641 value: Some(LiteralValue::Empty),
642 formula: None,
643 kind: Some(meta.kind),
644 flags: Some(meta.flags),
645 });
646 }
647 Ok(id)
648 }
649
650 pub fn remove_vertex(&mut self, id: VertexId) -> Result<(), EditorError> {
652 if !self.graph.vertex_exists(id) {
654 return Err(EditorError::Excel(
655 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
656 ));
657 }
658
659 let spill_snapshot = self.snapshot_spill_for_anchor(id);
662 let did_spill_clear = spill_snapshot.is_some();
663 if let Some(old_spill) = spill_snapshot {
664 if let Some(logger) = &mut self.change_logger {
665 logger.begin_compound(format!("RemoveVertexWithSpillClear id={}", id.0));
666 }
667 self.graph.clear_spill_region(id);
668 self.log_change(ChangeEvent::SpillCleared {
669 anchor: id,
670 old: old_spill,
671 });
672 }
673
674 let dependents = self.graph.get_dependents(id);
676
677 let (
679 old_value,
680 old_formula,
681 old_dependencies,
682 old_dependents,
683 coord,
684 sheet_id_opt,
685 kind,
686 flags,
687 ) = if self.has_logger() {
688 let coord = self.graph.get_grid_addr(id);
689 let sheet_id = self.graph.get_sheet_id(id);
690 let kind = self.graph.get_vertex_kind(id);
691 let flags = 0u8;
693 (
694 self.graph.get_value(id),
695 self.get_formula_ast(id),
696 self.graph.get_dependencies(id), dependents.clone(), coord,
699 Some(sheet_id),
700 Some(kind),
701 Some(flags),
702 )
703 } else {
704 (None, None, vec![], vec![], None, None, None, None)
705 };
706
707 if let Some(cell_ref) = self.graph.get_cell_ref_for_vertex(id) {
709 self.graph.remove_cell_mapping(&cell_ref);
710 }
711
712 self.graph.vertex_formulas.remove(&id);
716 self.graph.vertex_values.remove(&id);
717 self.graph.clear_formula_vertex_dirty(id);
718 self.graph.mark_volatile(id, false);
719 self.graph.store.set_kind(id, VertexKind::Empty);
720 self.graph.store.set_dynamic(id, false);
721
722 self.graph.remove_all_edges(id);
724
725 for dep_id in &dependents {
727 self.graph.mark_as_ref_error(*dep_id);
728 }
729
730 self.graph.mark_deleted(id, true);
732
733 self.log_change(ChangeEvent::RemoveVertex {
735 id,
736 old_value,
737 old_formula,
738 old_dependencies,
739 old_dependents,
740 coord,
741 sheet_id: sheet_id_opt,
742 kind,
743 flags,
744 });
745
746 if did_spill_clear && let Some(logger) = &mut self.change_logger {
747 logger.end_compound();
748 }
749
750 Ok(())
751 }
752
753 pub fn remove_vertex_at(&mut self, cell: CellRef) -> Result<(), EditorError> {
755 if let Some(id) = self.graph.get_vertex_for_cell(&cell) {
756 self.remove_vertex(id)
757 } else {
758 Ok(())
759 }
760 }
761
762 pub fn move_vertex(&mut self, id: VertexId, new_coord: GridAddr) -> Result<(), EditorError> {
770 if !self.graph.vertex_exists(id) {
772 return Err(EditorError::Excel(
773 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
774 ));
775 }
776 if self.graph.get_grid_addr(id).is_none() {
777 return Err(EditorError::Excel(
778 ExcelError::new(ExcelErrorKind::Ref).with_message(
779 "Symbol vertices have no position and cannot be moved onto the grid",
780 ),
781 ));
782 }
783
784 let old_cell_ref = self.graph.get_cell_ref_for_vertex(id);
786
787 let sheet_id = self.graph.get_sheet_id(id);
789 let new_cell_ref = CellRef::new(
790 sheet_id,
791 Coord::new(new_coord.row(), new_coord.col(), true, true),
792 );
793
794 self.graph.set_grid_addr(id, new_coord);
796
797 self.graph.update_edge_grid_addr(id, new_coord);
799
800 self.graph
802 .update_cell_mapping(id, old_cell_ref, new_cell_ref);
803
804 self.graph.mark_dependents_dirty(id);
806
807 Ok(())
808 }
809
810 pub fn patch_vertex_meta(
812 &mut self,
813 id: VertexId,
814 patch: VertexMetaPatch,
815 ) -> Result<MetaUpdateSummary, EditorError> {
816 if !self.graph.vertex_exists(id) {
817 return Err(EditorError::Excel(
818 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
819 ));
820 }
821
822 let mut summary = MetaUpdateSummary::default();
823
824 if let Some(coord) = patch.coord {
825 if self.graph.get_grid_addr(id).is_none() {
827 return Err(EditorError::Excel(
828 ExcelError::new(ExcelErrorKind::Ref).with_message(
829 "Symbol vertices have no position and cannot be moved onto the grid",
830 ),
831 ));
832 }
833 self.graph.set_grid_addr(id, coord);
834 self.graph.update_edge_grid_addr(id, coord);
835 summary.coord_changed = true;
836 }
837
838 if let Some(kind) = patch.kind {
839 self.graph.set_kind(id, kind);
840 summary.kind_changed = true;
841 }
842
843 if let Some(dirty) = patch.dirty {
844 self.graph.set_dirty(id, dirty);
845 summary.flags_changed = true;
846 }
847
848 if let Some(volatile) = patch.volatile {
849 self.graph.mark_volatile(id, volatile);
850 summary.flags_changed = true;
851 }
852
853 Ok(summary)
854 }
855
856 pub fn patch_vertex_data(
858 &mut self,
859 id: VertexId,
860 patch: VertexDataPatch,
861 ) -> Result<DataUpdateSummary, EditorError> {
862 if !self.graph.vertex_exists(id) {
863 return Err(EditorError::Excel(
864 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
865 ));
866 }
867
868 let mut summary = DataUpdateSummary::default();
869
870 if let Some(value) = patch.value {
871 self.graph.update_vertex_value(id, value);
872 summary.value_changed = true;
873
874 let dependents = self.graph.get_dependents(id);
877 for dep in &dependents {
878 self.graph.set_dirty(*dep, true);
879 }
880 summary.dependents_marked_dirty = dependents;
881 }
882
883 if let Some(_formula) = patch.formula {
884 summary.formula_changed = true;
887 }
888
889 Ok(summary)
890 }
891
892 pub fn add_edge(&mut self, from: VertexId, to: VertexId) -> bool {
894 if from == to {
895 return false; }
897
898 true
901 }
902
903 pub fn remove_edge(&mut self, _from: VertexId, _to: VertexId) -> bool {
905 true
907 }
908
909 pub fn insert_rows(
911 &mut self,
912 sheet_id: SheetId,
913 before: u32,
914 count: u32,
915 ) -> Result<ShiftSummary, EditorError> {
916 if count == 0 {
917 return Ok(ShiftSummary::default());
918 }
919
920 let mut summary = ShiftSummary::default();
921
922 self.begin_batch();
924
925 let conservative = crate::engine::graph::StructuralOccupancy::conservative();
926 let occupancy = self.structural_occupancy.as_ref().unwrap_or(&conservative);
927 let range_dependents = self.graph.compressed_range_dependents_for_structural_edit(
928 sheet_id,
929 crate::engine::graph::StructuralEdit::InsertRows { before },
930 occupancy,
931 );
932 #[cfg(test)]
933 {
934 summary.structural_dependents_dirtied = range_dependents.clone();
935 }
936 self.graph.mark_dirty_many(&range_dependents);
937
938 let vertices_to_shift: Vec<(VertexId, GridAddr)> = self
940 .graph
941 .grid_vertices_in_sheet(sheet_id)
942 .filter(|(_, coord)| coord.row() >= before)
943 .collect();
944
945 if let Some(logger) = &mut self.change_logger {
946 logger.begin_compound(format!(
947 "InsertRows sheet={sheet_id} before={before} count={count}"
948 ));
949 }
950 for (id, old_coord) in vertices_to_shift {
952 let new_coord = GridAddr::new(old_coord.row() + count, old_coord.col());
953 if self.has_logger() {
954 self.log_change(ChangeEvent::VertexMoved {
955 id,
956 sheet_id,
957 old_coord,
958 new_coord,
959 });
960 }
961 self.move_vertex(id, new_coord)?;
962 summary.vertices_moved.push(id);
963 }
964
965 let op = ShiftOperation::InsertRows {
967 sheet_id,
968 before,
969 count,
970 };
971 let adjuster = ReferenceAdjuster::new();
972
973 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
975
976 for id in formula_vertices {
977 if let Some(ast) = self.get_formula_ast(id)
978 && let Some(adjusted) = adjuster.adjust_ast_if_changed_in_context(
979 &ast,
980 &op,
981 &ReferenceContext::new(self.graph.get_sheet_id(id), self.graph.sheet_reg()),
982 )
983 {
984 if self.has_logger() {
985 self.log_change(ChangeEvent::FormulaAdjusted {
986 id,
987 addr: self.graph.get_cell_ref_for_vertex(id),
988 old_ast: ast.clone(),
989 new_ast: adjusted.clone(),
990 });
991 }
992 self.graph.update_vertex_formula(id, adjusted)?;
993 self.graph.mark_vertex_dirty(id);
994 summary.formulas_updated += 1;
995 }
996 }
997
998 let old_names = if self.has_logger() {
1000 Some(self.snapshot_named_definitions())
1001 } else {
1002 None
1003 };
1004 self.graph.adjust_named_ranges(&op)?;
1005 if let Some(old_names) = old_names {
1006 let new_names = self.snapshot_named_definitions();
1007 for ((scope, name), old_definition) in old_names {
1008 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1009 && *new_definition != old_definition
1010 {
1011 self.log_change(ChangeEvent::NamedRangeAdjusted {
1012 name,
1013 scope,
1014 old_definition,
1015 new_definition: new_definition.clone(),
1016 });
1017 }
1018 }
1019 }
1020
1021 if let Some(logger) = &mut self.change_logger {
1023 logger.end_compound();
1024 }
1025
1026 self.commit_batch();
1027
1028 Ok(summary)
1029 }
1030
1031 pub fn delete_rows(
1033 &mut self,
1034 sheet_id: SheetId,
1035 start: u32,
1036 count: u32,
1037 ) -> Result<ShiftSummary, EditorError> {
1038 if count == 0 {
1039 return Ok(ShiftSummary::default());
1040 }
1041
1042 let mut summary = ShiftSummary::default();
1043
1044 self.begin_batch();
1045
1046 if let Some(logger) = &mut self.change_logger {
1047 logger.begin_compound(format!(
1048 "DeleteRows sheet={sheet_id} start={start} count={count}"
1049 ));
1050 }
1051
1052 let vertices_to_delete: Vec<VertexId> = self
1054 .graph
1055 .grid_vertices_in_sheet(sheet_id)
1056 .filter(|(_, coord)| coord.row() >= start && coord.row() < start + count)
1057 .map(|(id, _)| id)
1058 .collect();
1059 let conservative = crate::engine::graph::StructuralOccupancy::conservative();
1060 let occupancy = self.structural_occupancy.as_ref().unwrap_or(&conservative);
1061 let range_dependents = self.graph.compressed_range_dependents_for_structural_edit(
1062 sheet_id,
1063 crate::engine::graph::StructuralEdit::DeleteRows {
1064 start,
1065 end: start.saturating_add(count).saturating_sub(1).max(start),
1066 },
1067 occupancy,
1068 );
1069 #[cfg(test)]
1070 {
1071 summary.structural_dependents_dirtied = range_dependents.clone();
1072 }
1073 self.graph.mark_dirty_many(&range_dependents);
1074
1075 for id in vertices_to_delete {
1076 self.remove_vertex(id)?;
1077 summary.vertices_deleted.push(id);
1078 }
1079 let vertices_to_shift: Vec<(VertexId, GridAddr)> = self
1081 .graph
1082 .grid_vertices_in_sheet(sheet_id)
1083 .filter(|(_, coord)| coord.row() >= start + count)
1084 .collect();
1085
1086 for (id, old_coord) in vertices_to_shift {
1087 let new_coord = GridAddr::new(old_coord.row() - count, old_coord.col());
1088 if self.has_logger() {
1089 self.log_change(ChangeEvent::VertexMoved {
1090 id,
1091 sheet_id,
1092 old_coord,
1093 new_coord,
1094 });
1095 }
1096 self.move_vertex(id, new_coord)?;
1097 summary.vertices_moved.push(id);
1098 }
1099
1100 let op = ShiftOperation::DeleteRows {
1102 sheet_id,
1103 start,
1104 count,
1105 };
1106 let adjuster = ReferenceAdjuster::new();
1107
1108 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1109
1110 for id in formula_vertices {
1111 if let Some(ast) = self.get_formula_ast(id)
1112 && let Some(adjusted) = adjuster.adjust_ast_if_changed_in_context(
1113 &ast,
1114 &op,
1115 &ReferenceContext::new(self.graph.get_sheet_id(id), self.graph.sheet_reg()),
1116 )
1117 {
1118 if self.has_logger() {
1119 self.log_change(ChangeEvent::FormulaAdjusted {
1120 id,
1121 addr: self.graph.get_cell_ref_for_vertex(id),
1122 old_ast: ast.clone(),
1123 new_ast: adjusted.clone(),
1124 });
1125 }
1126 self.graph.update_vertex_formula(id, adjusted)?;
1127 self.graph.mark_vertex_dirty(id);
1128 summary.formulas_updated += 1;
1129 }
1130 }
1131
1132 let old_names = if self.has_logger() {
1134 Some(self.snapshot_named_definitions())
1135 } else {
1136 None
1137 };
1138 self.graph.adjust_named_ranges(&op)?;
1139 if let Some(old_names) = old_names {
1140 let new_names = self.snapshot_named_definitions();
1141 for ((scope, name), old_definition) in old_names {
1142 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1143 && *new_definition != old_definition
1144 {
1145 self.log_change(ChangeEvent::NamedRangeAdjusted {
1146 name,
1147 scope,
1148 old_definition,
1149 new_definition: new_definition.clone(),
1150 });
1151 }
1152 }
1153 }
1154
1155 if let Some(logger) = &mut self.change_logger {
1157 logger.end_compound();
1158 }
1159
1160 self.commit_batch();
1161
1162 Ok(summary)
1163 }
1164
1165 pub fn insert_columns(
1167 &mut self,
1168 sheet_id: SheetId,
1169 before: u32,
1170 count: u32,
1171 ) -> Result<ShiftSummary, EditorError> {
1172 if count == 0 {
1173 return Ok(ShiftSummary::default());
1174 }
1175
1176 let mut summary = ShiftSummary::default();
1177
1178 self.begin_batch();
1180
1181 let conservative = crate::engine::graph::StructuralOccupancy::conservative();
1182 let occupancy = self.structural_occupancy.as_ref().unwrap_or(&conservative);
1183 let range_dependents = self.graph.compressed_range_dependents_for_structural_edit(
1184 sheet_id,
1185 crate::engine::graph::StructuralEdit::InsertColumns { before },
1186 occupancy,
1187 );
1188 #[cfg(test)]
1189 {
1190 summary.structural_dependents_dirtied = range_dependents.clone();
1191 }
1192 self.graph.mark_dirty_many(&range_dependents);
1193
1194 let vertices_to_shift: Vec<(VertexId, GridAddr)> = self
1196 .graph
1197 .grid_vertices_in_sheet(sheet_id)
1198 .filter(|(_, coord)| coord.col() >= before)
1199 .collect();
1200
1201 if let Some(logger) = &mut self.change_logger {
1202 logger.begin_compound(format!(
1203 "InsertColumns sheet={sheet_id} before={before} count={count}"
1204 ));
1205 }
1206 for (id, old_coord) in vertices_to_shift {
1208 let new_coord = GridAddr::new(old_coord.row(), old_coord.col() + count);
1209 if self.has_logger() {
1210 self.log_change(ChangeEvent::VertexMoved {
1211 id,
1212 sheet_id,
1213 old_coord,
1214 new_coord,
1215 });
1216 }
1217 self.move_vertex(id, new_coord)?;
1218 summary.vertices_moved.push(id);
1219 }
1220
1221 let op = ShiftOperation::InsertColumns {
1223 sheet_id,
1224 before,
1225 count,
1226 };
1227 let adjuster = ReferenceAdjuster::new();
1228
1229 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1231
1232 for id in formula_vertices {
1233 if let Some(ast) = self.get_formula_ast(id)
1234 && let Some(adjusted) = adjuster.adjust_ast_if_changed_in_context(
1235 &ast,
1236 &op,
1237 &ReferenceContext::new(self.graph.get_sheet_id(id), self.graph.sheet_reg()),
1238 )
1239 {
1240 if self.has_logger() {
1241 self.log_change(ChangeEvent::FormulaAdjusted {
1242 id,
1243 addr: self.graph.get_cell_ref_for_vertex(id),
1244 old_ast: ast.clone(),
1245 new_ast: adjusted.clone(),
1246 });
1247 }
1248 self.graph.update_vertex_formula(id, adjusted)?;
1249 self.graph.mark_vertex_dirty(id);
1250 summary.formulas_updated += 1;
1251 }
1252 }
1253
1254 let old_names = if self.has_logger() {
1256 Some(self.snapshot_named_definitions())
1257 } else {
1258 None
1259 };
1260 self.graph.adjust_named_ranges(&op)?;
1261 if let Some(old_names) = old_names {
1262 let new_names = self.snapshot_named_definitions();
1263 for ((scope, name), old_definition) in old_names {
1264 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1265 && *new_definition != old_definition
1266 {
1267 self.log_change(ChangeEvent::NamedRangeAdjusted {
1268 name,
1269 scope,
1270 old_definition,
1271 new_definition: new_definition.clone(),
1272 });
1273 }
1274 }
1275 }
1276
1277 if let Some(logger) = &mut self.change_logger {
1279 logger.end_compound();
1280 }
1281
1282 self.commit_batch();
1283
1284 Ok(summary)
1285 }
1286
1287 pub fn delete_columns(
1289 &mut self,
1290 sheet_id: SheetId,
1291 start: u32,
1292 count: u32,
1293 ) -> Result<ShiftSummary, EditorError> {
1294 if count == 0 {
1295 return Ok(ShiftSummary::default());
1296 }
1297
1298 let mut summary = ShiftSummary::default();
1299
1300 self.begin_batch();
1301
1302 if let Some(logger) = &mut self.change_logger {
1303 logger.begin_compound(format!(
1304 "DeleteColumns sheet={sheet_id} start={start} count={count}"
1305 ));
1306 }
1307
1308 let vertices_to_delete: Vec<VertexId> = self
1310 .graph
1311 .grid_vertices_in_sheet(sheet_id)
1312 .filter(|(_, coord)| coord.col() >= start && coord.col() < start + count)
1313 .map(|(id, _)| id)
1314 .collect();
1315 let conservative = crate::engine::graph::StructuralOccupancy::conservative();
1316 let occupancy = self.structural_occupancy.as_ref().unwrap_or(&conservative);
1317 let range_dependents = self.graph.compressed_range_dependents_for_structural_edit(
1318 sheet_id,
1319 crate::engine::graph::StructuralEdit::DeleteColumns {
1320 start,
1321 end: start.saturating_add(count).saturating_sub(1).max(start),
1322 },
1323 occupancy,
1324 );
1325 #[cfg(test)]
1326 {
1327 summary.structural_dependents_dirtied = range_dependents.clone();
1328 }
1329 self.graph.mark_dirty_many(&range_dependents);
1330
1331 for id in vertices_to_delete {
1332 self.remove_vertex(id)?;
1333 summary.vertices_deleted.push(id);
1334 }
1335 let vertices_to_shift: Vec<(VertexId, GridAddr)> = self
1337 .graph
1338 .grid_vertices_in_sheet(sheet_id)
1339 .filter(|(_, coord)| coord.col() >= start + count)
1340 .collect();
1341
1342 for (id, old_coord) in vertices_to_shift {
1343 let new_coord = GridAddr::new(old_coord.row(), old_coord.col() - count);
1344 if self.has_logger() {
1345 self.log_change(ChangeEvent::VertexMoved {
1346 id,
1347 sheet_id,
1348 old_coord,
1349 new_coord,
1350 });
1351 }
1352 self.move_vertex(id, new_coord)?;
1353 summary.vertices_moved.push(id);
1354 }
1355
1356 let op = ShiftOperation::DeleteColumns {
1358 sheet_id,
1359 start,
1360 count,
1361 };
1362 let adjuster = ReferenceAdjuster::new();
1363
1364 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1365
1366 for id in formula_vertices {
1367 if let Some(ast) = self.get_formula_ast(id)
1368 && let Some(adjusted) = adjuster.adjust_ast_if_changed_in_context(
1369 &ast,
1370 &op,
1371 &ReferenceContext::new(self.graph.get_sheet_id(id), self.graph.sheet_reg()),
1372 )
1373 {
1374 if self.has_logger() {
1375 self.log_change(ChangeEvent::FormulaAdjusted {
1376 id,
1377 addr: self.graph.get_cell_ref_for_vertex(id),
1378 old_ast: ast.clone(),
1379 new_ast: adjusted.clone(),
1380 });
1381 }
1382 self.graph.update_vertex_formula(id, adjusted)?;
1383 self.graph.mark_vertex_dirty(id);
1384 summary.formulas_updated += 1;
1385 }
1386 }
1387
1388 let old_names = if self.has_logger() {
1390 Some(self.snapshot_named_definitions())
1391 } else {
1392 None
1393 };
1394 self.graph.adjust_named_ranges(&op)?;
1395 if let Some(old_names) = old_names {
1396 let new_names = self.snapshot_named_definitions();
1397 for ((scope, name), old_definition) in old_names {
1398 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1399 && *new_definition != old_definition
1400 {
1401 self.log_change(ChangeEvent::NamedRangeAdjusted {
1402 name,
1403 scope,
1404 old_definition,
1405 new_definition: new_definition.clone(),
1406 });
1407 }
1408 }
1409 }
1410
1411 if let Some(logger) = &mut self.change_logger {
1413 logger.end_compound();
1414 }
1415
1416 self.commit_batch();
1417
1418 Ok(summary)
1419 }
1420
1421 pub fn shift_rows(&mut self, sheet_id: SheetId, start_row: u32, delta: i32) {
1423 if delta == 0 {
1424 return;
1425 }
1426
1427 let change_event = ChangeEvent::SetValue {
1429 addr: CellRef {
1430 sheet_id,
1431 coord: Coord::new(start_row, 0, true, true),
1432 },
1433 old_value: None,
1434 old_formula: None,
1435 new: LiteralValue::Text(format!("Row shift: start={start_row}, delta={delta}")),
1436 };
1437 self.log_change(change_event);
1438
1439 }
1442
1443 pub fn shift_columns(&mut self, sheet_id: SheetId, start_col: u32, delta: i32) {
1445 if delta == 0 {
1446 return;
1447 }
1448
1449 let change_event = ChangeEvent::SetValue {
1451 addr: CellRef {
1452 sheet_id,
1453 coord: Coord::new(0, start_col, true, true),
1454 },
1455 old_value: None,
1456 old_formula: None,
1457 new: LiteralValue::Text(format!("Column shift: start={start_col}, delta={delta}")),
1458 };
1459 self.log_change(change_event);
1460
1461 }
1464
1465 pub fn set_cell_value(&mut self, cell_ref: CellRef, value: LiteralValue) -> VertexId {
1467 self.set_cell_value_with_old_state(cell_ref, value, None, None)
1468 }
1469
1470 pub fn set_cell_value_with_old_state(
1480 &mut self,
1481 cell_ref: CellRef,
1482 value: LiteralValue,
1483 fallback_old_value: Option<LiteralValue>,
1484 fallback_old_formula: Option<ASTNode>,
1485 ) -> VertexId {
1486 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1487
1488 let old_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1491 let old_value = old_id
1492 .and_then(|id| self.graph.get_value(id))
1493 .or(fallback_old_value);
1494 let old_formula = old_id
1495 .and_then(|id| self.get_formula_ast(id))
1496 .or(fallback_old_formula);
1497
1498 let spill_snapshot =
1501 old_id.and_then(|id| self.snapshot_spill_for_anchor(id).map(|s| (id, s)));
1502 let did_spill_clear = spill_snapshot.is_some();
1503 if let Some((anchor, old_spill)) = spill_snapshot {
1504 if let Some(logger) = &mut self.change_logger {
1505 logger.begin_compound(format!(
1506 "SetValueWithSpillClear sheet={} row={} col={}",
1507 cell_ref.sheet_id,
1508 cell_ref.coord.row(),
1509 cell_ref.coord.col()
1510 ));
1511 }
1512 self.graph.clear_spill_region(anchor);
1513 self.log_change(ChangeEvent::SpillCleared {
1514 anchor,
1515 old: old_spill,
1516 });
1517 }
1518
1519 match self.graph.set_cell_value(
1522 &sheet_name,
1523 cell_ref.coord.row() + 1,
1524 cell_ref.coord.col() + 1,
1525 value.clone(),
1526 ) {
1527 Ok(summary) => {
1528 let change_event = ChangeEvent::SetValue {
1530 addr: cell_ref,
1531 old_value,
1532 old_formula,
1533 new: value,
1534 };
1535 self.log_change(change_event);
1536
1537 if did_spill_clear && let Some(logger) = &mut self.change_logger {
1538 logger.end_compound();
1539 }
1540
1541 summary
1542 .affected_vertices
1543 .into_iter()
1544 .next()
1545 .unwrap_or(VertexId::new(0))
1546 }
1547 Err(_) => VertexId::new(0),
1548 }
1549 }
1550
1551 pub fn set_cell_formula(&mut self, cell_ref: CellRef, formula: ASTNode) -> VertexId {
1553 self.set_cell_formula_with_old_state(cell_ref, formula, None, None)
1554 }
1555
1556 pub fn set_cell_formula_with_old_state(
1562 &mut self,
1563 cell_ref: CellRef,
1564 formula: ASTNode,
1565 fallback_old_value: Option<LiteralValue>,
1566 fallback_old_formula: Option<ASTNode>,
1567 ) -> VertexId {
1568 self.set_cell_formula_with_old_state_and_plan(
1569 cell_ref,
1570 formula,
1571 fallback_old_value,
1572 fallback_old_formula,
1573 None,
1574 )
1575 }
1576
1577 pub(crate) fn set_cell_formula_with_prepared_plan(
1578 &mut self,
1579 cell_ref: CellRef,
1580 formula: ASTNode,
1581 fallback_old_value: Option<LiteralValue>,
1582 fallback_old_formula: Option<ASTNode>,
1583 ast_id: crate::engine::arena::AstNodeId,
1584 plan: crate::engine::ingest_pipeline::DependencyPlanRow,
1585 ) -> VertexId {
1586 self.set_cell_formula_with_old_state_and_plan(
1587 cell_ref,
1588 formula,
1589 fallback_old_value,
1590 fallback_old_formula,
1591 Some((ast_id, plan)),
1592 )
1593 }
1594
1595 fn set_cell_formula_with_old_state_and_plan(
1596 &mut self,
1597 cell_ref: CellRef,
1598 formula: ASTNode,
1599 fallback_old_value: Option<LiteralValue>,
1600 fallback_old_formula: Option<ASTNode>,
1601 prepared: Option<(
1602 crate::engine::arena::AstNodeId,
1603 crate::engine::ingest_pipeline::DependencyPlanRow,
1604 )>,
1605 ) -> VertexId {
1606 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1607
1608 let old_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1611 let old_value = old_id
1612 .and_then(|id| self.graph.get_value(id))
1613 .or(fallback_old_value);
1614 let old_formula = old_id
1615 .and_then(|id| self.get_formula_ast(id))
1616 .or(fallback_old_formula);
1617
1618 let spill_snapshot =
1620 old_id.and_then(|id| self.snapshot_spill_for_anchor(id).map(|s| (id, s)));
1621 let did_spill_clear = spill_snapshot.is_some();
1622 if let Some((anchor, old_spill)) = spill_snapshot {
1623 if let Some(logger) = &mut self.change_logger {
1624 logger.begin_compound(format!(
1625 "SetFormulaWithSpillClear sheet={} row={} col={}",
1626 cell_ref.sheet_id,
1627 cell_ref.coord.row(),
1628 cell_ref.coord.col()
1629 ));
1630 }
1631 self.graph.clear_spill_region(anchor);
1632 self.log_change(ChangeEvent::SpillCleared {
1633 anchor,
1634 old: old_spill,
1635 });
1636 }
1637
1638 let result = if let Some((ast_id, plan)) = prepared {
1640 self.graph.set_cell_formula_with_plan(
1641 &sheet_name,
1642 cell_ref.coord.row() + 1,
1643 cell_ref.coord.col() + 1,
1644 ast_id,
1645 &plan,
1646 plan.volatile,
1647 plan.dynamic,
1648 )
1649 } else {
1650 self.graph.set_cell_formula(
1651 &sheet_name,
1652 cell_ref.coord.row() + 1,
1653 cell_ref.coord.col() + 1,
1654 formula.clone(),
1655 )
1656 };
1657 match result {
1658 Ok(summary) => {
1659 let change_event = ChangeEvent::SetFormula {
1661 addr: cell_ref,
1662 old_value,
1663 old_formula,
1664 new: formula,
1665 };
1666 self.log_change(change_event);
1667
1668 if did_spill_clear && let Some(logger) = &mut self.change_logger {
1669 logger.end_compound();
1670 }
1671
1672 summary
1673 .affected_vertices
1674 .into_iter()
1675 .next()
1676 .unwrap_or(VertexId::new(0))
1677 }
1678 Err(_) => VertexId::new(0),
1679 }
1680 }
1681
1682 pub fn set_range_values(
1686 &mut self,
1687 sheet_id: SheetId,
1688 start_row: u32,
1689 start_col: u32,
1690 values: &[Vec<LiteralValue>],
1691 ) -> Result<RangeSummary, EditorError> {
1692 let mut summary = RangeSummary::default();
1693
1694 self.begin_batch();
1695 self.graph.begin_deferred_dirty();
1699
1700 for (row_offset, row_values) in values.iter().enumerate() {
1701 for (col_offset, value) in row_values.iter().enumerate() {
1702 let row = start_row + row_offset as u32;
1703 let col = start_col + col_offset as u32;
1704 let cell_ref = self.graph.make_cell_ref_internal(sheet_id, row, col);
1705 let existing_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1706
1707 let id = self.set_cell_value(cell_ref, value.clone());
1708 match existing_id {
1709 Some(existing_id) => summary.vertices_updated.push(existing_id),
1710 None if id.0 != 0 => summary.vertices_created.push(id),
1711 None => {}
1712 }
1713 summary.cells_affected += 1;
1714 }
1715 }
1716
1717 let _ = self.graph.end_deferred_dirty();
1718 self.commit_batch();
1719
1720 Ok(summary)
1721 }
1722
1723 pub fn clear_range(
1725 &mut self,
1726 sheet_id: SheetId,
1727 start_row: u32,
1728 start_col: u32,
1729 end_row: u32,
1730 end_col: u32,
1731 ) -> Result<RangeSummary, EditorError> {
1732 let mut summary = RangeSummary::default();
1733
1734 self.begin_batch();
1735
1736 let vertices_in_range: Vec<_> = self
1738 .graph
1739 .grid_vertices_in_sheet(sheet_id)
1740 .filter(|(_, coord)| {
1741 let row = coord.row();
1742 let col = coord.col();
1743 row >= start_row && row <= end_row && col >= start_col && col <= end_col
1744 })
1745 .map(|(id, _)| id)
1746 .collect();
1747
1748 for id in vertices_in_range {
1749 self.remove_vertex(id)?;
1750 summary.cells_affected += 1;
1751 }
1752
1753 self.commit_batch();
1754
1755 Ok(summary)
1756 }
1757
1758 pub fn copy_range(
1760 &mut self,
1761 sheet_id: SheetId,
1762 from_start_row: u32,
1763 from_start_col: u32,
1764 from_end_row: u32,
1765 from_end_col: u32,
1766 to_sheet_id: SheetId,
1767 to_row: u32,
1768 to_col: u32,
1769 ) -> Result<RangeSummary, EditorError> {
1770 let row_offset = to_row as i32 - from_start_row as i32;
1771 let col_offset = to_col as i32 - from_start_col as i32;
1772
1773 let mut summary = RangeSummary::default();
1774 let mut cell_data = Vec::new();
1775
1776 let vertices_in_range: Vec<_> = self
1778 .graph
1779 .grid_vertices_in_sheet(sheet_id)
1780 .filter(|(_, coord)| {
1781 let row = coord.row();
1782 let col = coord.col();
1783 row >= from_start_row
1784 && row <= from_end_row
1785 && col >= from_start_col
1786 && col <= from_end_col
1787 })
1788 .collect();
1789
1790 for (id, coord) in vertices_in_range {
1791 let row = coord.row();
1792 let col = coord.col();
1793
1794 if let Some(formula) = self.get_formula_ast(id) {
1796 cell_data.push((
1797 row - from_start_row,
1798 col - from_start_col,
1799 CellData::Formula(formula),
1800 ));
1801 } else if let Some(value) = self.graph.get_value(id) {
1802 cell_data.push((
1803 row - from_start_row,
1804 col - from_start_col,
1805 CellData::Value(value),
1806 ));
1807 }
1808 }
1809
1810 self.begin_batch();
1811
1812 for (row_idx, col_idx, data) in cell_data {
1814 let dest_row = (to_row as i32 + row_idx as i32) as u32;
1815 let dest_col = (to_col as i32 + col_idx as i32) as u32;
1816
1817 match data {
1818 CellData::Value(value) => {
1819 let cell_ref =
1820 self.graph
1821 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
1822
1823 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1824 self.graph.update_vertex_value(existing_id, value);
1825 self.graph.mark_vertex_dirty(existing_id);
1826 summary.vertices_updated.push(existing_id);
1827 } else {
1828 let meta =
1829 VertexMeta::new(dest_row, dest_col, to_sheet_id, VertexKind::Cell);
1830 let id = self.try_add_vertex(meta)?;
1831 self.graph.update_vertex_value(id, value);
1832 summary.vertices_created.push(id);
1833 }
1834 }
1835 CellData::Formula(formula) => {
1836 let adjuster = RelativeReferenceAdjuster::new(row_offset, col_offset);
1838 let adjusted = adjuster.adjust_formula(&formula);
1839
1840 let cell_ref =
1841 self.graph
1842 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
1843
1844 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1845 self.graph.update_vertex_formula(existing_id, adjusted)?;
1846 summary.vertices_updated.push(existing_id);
1847 } else {
1848 let meta = VertexMeta::new(
1849 dest_row,
1850 dest_col,
1851 to_sheet_id,
1852 VertexKind::FormulaScalar,
1853 );
1854 let id = self.try_add_vertex(meta)?;
1855 self.graph.update_vertex_formula(id, adjusted)?;
1856 summary.vertices_created.push(id);
1857 }
1858 }
1859 }
1860
1861 summary.cells_affected += 1;
1862 }
1863
1864 self.commit_batch();
1865
1866 Ok(summary)
1867 }
1868
1869 pub fn move_range(
1871 &mut self,
1872 sheet_id: SheetId,
1873 from_start_row: u32,
1874 from_start_col: u32,
1875 from_end_row: u32,
1876 from_end_col: u32,
1877 to_sheet_id: SheetId,
1878 to_row: u32,
1879 to_col: u32,
1880 ) -> Result<RangeSummary, EditorError> {
1881 let mut summary = self.copy_range(
1883 sheet_id,
1884 from_start_row,
1885 from_start_col,
1886 from_end_row,
1887 from_end_col,
1888 to_sheet_id,
1889 to_row,
1890 to_col,
1891 )?;
1892
1893 let clear_summary = self.clear_range(
1895 sheet_id,
1896 from_start_row,
1897 from_start_col,
1898 from_end_row,
1899 from_end_col,
1900 )?;
1901
1902 summary.cells_moved = clear_summary.cells_affected;
1903
1904 let row_offset = to_row as i32 - from_start_row as i32;
1906 let col_offset = to_col as i32 - from_start_col as i32;
1907
1908 let all_formula_vertices: Vec<_> = self.graph.vertices_with_formulas().collect();
1910
1911 let from_sheet_name = self.graph.sheet_name(sheet_id).to_string();
1912 let to_sheet_name = self.graph.sheet_name(to_sheet_id).to_string();
1913 let adjuster = MoveReferenceAdjuster::new(
1914 sheet_id,
1915 from_sheet_name,
1916 from_start_row,
1917 from_start_col,
1918 from_end_row,
1919 from_end_col,
1920 to_sheet_id,
1921 to_sheet_name,
1922 row_offset,
1923 col_offset,
1924 );
1925
1926 for formula_id in all_formula_vertices {
1927 if let Some(formula) = self.get_formula_ast(formula_id) {
1928 let formula_sheet_id = self.graph.get_vertex_sheet_id(formula_id);
1929 if let Some(adjusted) = adjuster.adjust_if_references(&formula, formula_sheet_id) {
1930 self.graph.update_vertex_formula(formula_id, adjusted)?;
1931 }
1932 }
1933 }
1934
1935 Ok(summary)
1936 }
1937
1938 pub fn define_name(
1940 &mut self,
1941 name: &str,
1942 definition: NamedDefinition,
1943 scope: NameScope,
1944 ) -> Result<(), EditorError> {
1945 self.graph.define_name(name, definition.clone(), scope)?;
1946
1947 self.log_change(ChangeEvent::DefineName {
1948 name: name.to_string(),
1949 scope,
1950 definition,
1951 });
1952
1953 Ok(())
1954 }
1955
1956 pub fn define_name_for_cell(
1958 &mut self,
1959 name: &str,
1960 sheet_name: &str,
1961 row: u32,
1962 col: u32,
1963 scope: NameScope,
1964 ) -> Result<(), EditorError> {
1965 let sheet_id = self
1966 .graph
1967 .sheet_id(sheet_name)
1968 .ok_or_else(|| EditorError::InvalidName {
1969 name: sheet_name.to_string(),
1970 reason: "Sheet not found".to_string(),
1971 })?;
1972 let cell_ref = CellRef::new(sheet_id, Coord::from_excel(row, col, true, true));
1973 self.define_name(name, NamedDefinition::Cell(cell_ref), scope)
1974 }
1975
1976 pub fn define_name_for_range(
1978 &mut self,
1979 name: &str,
1980 sheet_name: &str,
1981 start_row: u32,
1982 start_col: u32,
1983 end_row: u32,
1984 end_col: u32,
1985 scope: NameScope,
1986 ) -> Result<(), EditorError> {
1987 let sheet_id = self
1988 .graph
1989 .sheet_id(sheet_name)
1990 .ok_or_else(|| EditorError::InvalidName {
1991 name: sheet_name.to_string(),
1992 reason: "Sheet not found".to_string(),
1993 })?;
1994 let start = CellRef::new(
1995 sheet_id,
1996 Coord::from_excel(start_row, start_col, true, true),
1997 );
1998 let end = CellRef::new(sheet_id, Coord::from_excel(end_row, end_col, true, true));
1999 let range_ref = crate::reference::RangeRef::new(start, end);
2000 self.define_name(name, NamedDefinition::Range(range_ref), scope)
2001 }
2002
2003 pub fn update_name(
2005 &mut self,
2006 name: &str,
2007 new_definition: NamedDefinition,
2008 scope: NameScope,
2009 ) -> Result<(), EditorError> {
2010 let old_definition = self
2012 .graph
2013 .resolve_name(
2014 name,
2015 match scope {
2016 NameScope::Sheet(id) => id,
2017 NameScope::Workbook => 0,
2018 },
2019 )
2020 .cloned();
2021
2022 self.graph
2023 .update_name(name, new_definition.clone(), scope)?;
2024
2025 if let Some(old_def) = old_definition {
2026 self.log_change(ChangeEvent::UpdateName {
2027 name: name.to_string(),
2028 scope,
2029 old_definition: old_def,
2030 new_definition,
2031 });
2032 }
2033
2034 Ok(())
2035 }
2036
2037 pub fn delete_name(&mut self, name: &str, scope: NameScope) -> Result<(), EditorError> {
2039 let old_def = if self.has_logger() {
2041 self.graph
2042 .resolve_name(
2043 name,
2044 match scope {
2045 NameScope::Sheet(id) => id,
2046 NameScope::Workbook => 0,
2047 },
2048 )
2049 .cloned()
2050 } else {
2051 None
2052 };
2053
2054 self.graph.delete_name(name, scope)?;
2055 self.log_change(ChangeEvent::DeleteName {
2056 name: name.to_string(),
2057 scope,
2058 old_definition: old_def,
2059 });
2060
2061 Ok(())
2062 }
2063}
2064
2065enum CellData {
2067 Value(LiteralValue),
2068 Formula(ASTNode),
2069}
2070
2071impl<'g> Drop for VertexEditor<'g> {
2072 fn drop(&mut self) {
2073 if self.batch_mode {
2075 self.commit_batch();
2076 }
2077 }
2078}
2079
2080#[cfg(test)]
2081mod tests {
2082 use super::*;
2083 use crate::engine::graph::editor::change_log::{ChangeEvent, ChangeLog};
2084 use crate::reference::Coord;
2085
2086 fn create_test_graph() -> DependencyGraph {
2087 DependencyGraph::new()
2088 }
2089
2090 #[test]
2091 fn test_vertex_editor_creation() {
2092 let mut graph = create_test_graph();
2093 let editor = VertexEditor::new(&mut graph);
2094 assert!(!editor.has_logger());
2095 assert!(!editor.batch_mode);
2096 }
2097
2098 #[test]
2099 fn test_vertex_editor_with_logger() {
2100 let mut graph = create_test_graph();
2101 let mut log = ChangeLog::new();
2102 let editor = VertexEditor::with_logger(&mut graph, &mut log);
2103 assert!(editor.has_logger());
2104 assert!(!editor.batch_mode);
2105 }
2106
2107 #[test]
2108 fn test_add_vertex() {
2109 let mut graph = create_test_graph();
2110 let mut editor = VertexEditor::new(&mut graph);
2111
2112 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell).dirty();
2113 let vertex_id = editor.add_vertex(meta);
2114
2115 assert!(vertex_id.0 > 0);
2117 }
2118
2119 #[test]
2120 fn test_batch_operations() {
2121 let mut graph = create_test_graph();
2122 let mut editor = VertexEditor::new(&mut graph);
2123
2124 assert!(!editor.batch_mode);
2125 editor.begin_batch();
2126 assert!(editor.batch_mode);
2127
2128 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2130 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::Cell);
2131
2132 let id1 = editor.add_vertex(meta1);
2133 let id2 = editor.add_vertex(meta2);
2134
2135 assert!(editor.add_edge(id1, id2));
2137
2138 editor.commit_batch();
2139 assert!(!editor.batch_mode);
2140 }
2141
2142 #[test]
2143 fn test_remove_vertex() {
2144 let mut graph = create_test_graph();
2145 let mut editor = VertexEditor::new(&mut graph);
2146
2147 let meta = VertexMeta::new(3, 4, 0, VertexKind::Cell).dirty();
2148 let vertex_id = editor.add_vertex(meta);
2149
2150 assert!(editor.remove_vertex(vertex_id).is_ok());
2152 }
2153
2154 #[test]
2155 fn test_remove_vertex_clears_spill_registry_for_anchor() {
2156 let mut graph = create_test_graph();
2157 let sheet_id = graph.sheet_id_mut("Sheet1");
2158
2159 let anchor_cell = CellRef::new(sheet_id, Coord::new(0, 0, true, true));
2161 let anchor_vid = {
2162 let mut editor = VertexEditor::new(&mut graph);
2163 editor.set_cell_value(anchor_cell, LiteralValue::Number(0.0))
2164 };
2165
2166 let target_cells = vec![
2167 CellRef::new(sheet_id, Coord::new(0, 0, true, true)),
2168 CellRef::new(sheet_id, Coord::new(0, 1, true, true)),
2169 CellRef::new(sheet_id, Coord::new(1, 0, true, true)),
2170 CellRef::new(sheet_id, Coord::new(1, 1, true, true)),
2171 ];
2172 let values = vec![
2173 vec![LiteralValue::Number(1.0), LiteralValue::Number(2.0)],
2174 vec![LiteralValue::Number(3.0), LiteralValue::Number(4.0)],
2175 ];
2176
2177 graph
2178 .commit_spill_region_atomic_with_fault(anchor_vid, target_cells.clone(), values, None)
2179 .unwrap();
2180
2181 assert!(graph.spill_registry_has_anchor(anchor_vid));
2182 for cell in &target_cells {
2183 assert_eq!(
2184 graph.spill_registry_anchor_for_cell(*cell),
2185 Some(anchor_vid)
2186 );
2187 }
2188
2189 {
2190 let mut editor = VertexEditor::new(&mut graph);
2191 editor.remove_vertex(anchor_vid).unwrap();
2192 }
2193
2194 assert!(!graph.spill_registry_has_anchor(anchor_vid));
2195 for cell in &target_cells {
2196 assert_eq!(graph.spill_registry_anchor_for_cell(*cell), None);
2197 }
2198 assert_eq!(graph.spill_registry_counts(), (0, 0));
2199 }
2200
2201 #[test]
2202 fn test_edge_operations() {
2203 let mut graph = create_test_graph();
2204 let mut editor = VertexEditor::new(&mut graph);
2205
2206 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2207 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::FormulaScalar);
2208
2209 let id1 = editor.add_vertex(meta1);
2210 let id2 = editor.add_vertex(meta2);
2211
2212 assert!(editor.add_edge(id1, id2));
2214
2215 assert!(!editor.add_edge(id1, id1));
2217
2218 assert!(editor.remove_edge(id1, id2));
2220 }
2221
2222 #[test]
2223 fn test_set_cell_value() {
2224 let mut graph = create_test_graph();
2225 let mut log = ChangeLog::new();
2226
2227 let cell_ref = CellRef {
2228 sheet_id: 0,
2229 coord: Coord::new(2, 3, true, true),
2230 };
2231 let value = LiteralValue::Number(42.0);
2232
2233 let vertex_id = {
2234 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2235 editor.set_cell_value(cell_ref, value.clone())
2236 };
2237
2238 assert!(vertex_id.0 > 0);
2240
2241 assert_eq!(log.len(), 1);
2243 match &log.events()[0] {
2244 ChangeEvent::SetValue { addr, new, .. } => {
2245 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
2246 assert_eq!(addr.coord.row(), cell_ref.coord.row());
2247 assert_eq!(addr.coord.col(), cell_ref.coord.col());
2248 assert_eq!(new, &value);
2249 }
2250 _ => panic!("Expected SetValue event"),
2251 }
2252 }
2253
2254 #[test]
2255 fn test_set_cell_formula() {
2256 let mut graph = create_test_graph();
2257 let mut log = ChangeLog::new();
2258
2259 let cell_ref = CellRef {
2260 sheet_id: 0,
2261 coord: Coord::new(1, 1, true, true),
2262 };
2263
2264 use formualizer_parse::parser::ASTNodeType;
2265 let formula = formualizer_parse::parser::ASTNode {
2266 node_type: ASTNodeType::Literal(LiteralValue::Number(100.0)),
2267 source_token: None,
2268 contains_volatile: false,
2269 };
2270
2271 let vertex_id = {
2272 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2273 editor.set_cell_formula(cell_ref, formula.clone())
2274 };
2275
2276 assert!(vertex_id.0 > 0);
2278
2279 assert_eq!(log.len(), 1);
2281 match &log.events()[0] {
2282 ChangeEvent::SetFormula { addr, .. } => {
2283 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
2284 assert_eq!(addr.coord.row(), cell_ref.coord.row());
2285 assert_eq!(addr.coord.col(), cell_ref.coord.col());
2286 }
2287 _ => panic!("Expected SetFormula event"),
2288 }
2289 }
2290
2291 #[test]
2292 fn test_shift_rows() {
2293 let mut graph = create_test_graph();
2294 let mut log = ChangeLog::new();
2295
2296 {
2297 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2298
2299 let cell1 = CellRef {
2301 sheet_id: 0,
2302 coord: Coord::new(5, 1, true, true),
2303 };
2304 let cell2 = CellRef {
2305 sheet_id: 0,
2306 coord: Coord::new(10, 1, true, true),
2307 };
2308 let cell3 = CellRef {
2309 sheet_id: 0,
2310 coord: Coord::new(15, 1, true, true),
2311 };
2312
2313 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
2314 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
2315 editor.set_cell_value(cell3, LiteralValue::Number(3.0));
2316 }
2317
2318 log.clear();
2320
2321 {
2322 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2323 editor.shift_rows(0, 10, 2);
2325 }
2326
2327 assert_eq!(log.len(), 1);
2329 match &log.events()[0] {
2330 ChangeEvent::SetValue { addr, new, .. } => {
2331 assert_eq!(addr.sheet_id, 0);
2332 assert_eq!(addr.coord.row(), 10);
2333 if let LiteralValue::Text(msg) = new {
2334 assert!(msg.contains("Row shift"));
2335 assert!(msg.contains("start=10"));
2336 assert!(msg.contains("delta=2"));
2337 }
2338 }
2339 _ => panic!("Expected SetValue event for row shift"),
2340 }
2341 }
2342
2343 #[test]
2344 fn test_shift_columns() {
2345 let mut graph = create_test_graph();
2346 let mut log = ChangeLog::new();
2347
2348 {
2349 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2350
2351 let cell1 = CellRef {
2353 sheet_id: 0,
2354 coord: Coord::new(1, 5, true, true),
2355 };
2356 let cell2 = CellRef {
2357 sheet_id: 0,
2358 coord: Coord::new(1, 10, true, true),
2359 };
2360
2361 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
2362 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
2363 }
2364
2365 log.clear();
2367
2368 {
2369 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2370 editor.shift_columns(0, 8, 3);
2372 }
2373
2374 assert_eq!(log.len(), 1);
2376 match &log.events()[0] {
2377 ChangeEvent::SetValue { addr, new, .. } => {
2378 assert_eq!(addr.sheet_id, 0);
2379 assert_eq!(addr.coord.col(), 8);
2380 if let LiteralValue::Text(msg) = new {
2381 assert!(msg.contains("Column shift"));
2382 assert!(msg.contains("start=8"));
2383 assert!(msg.contains("delta=3"));
2384 }
2385 }
2386 _ => panic!("Expected SetValue event for column shift"),
2387 }
2388 }
2389
2390 #[test]
2391 fn test_move_vertex() {
2392 let mut graph = create_test_graph();
2393 let mut editor = VertexEditor::new(&mut graph);
2394
2395 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell);
2396 let vertex_id = editor.add_vertex(meta);
2397
2398 assert!(editor.move_vertex(vertex_id, GridAddr::new(8, 12)).is_ok());
2400
2401 assert!(editor.move_vertex(vertex_id, GridAddr::new(8, 12)).is_ok());
2403 }
2404
2405 #[test]
2406 fn test_vertex_meta_builder() {
2407 let meta = VertexMeta::new(1, 2, 3, VertexKind::FormulaScalar)
2408 .dirty()
2409 .volatile()
2410 .with_flags(0x08);
2411
2412 assert_eq!(meta.coord.row(), 1);
2413 assert_eq!(meta.coord.col(), 2);
2414 assert_eq!(meta.sheet_id, 3);
2415 assert_eq!(meta.kind, VertexKind::FormulaScalar);
2416 assert_eq!(meta.flags, 0x08); }
2418
2419 #[test]
2420 fn test_change_log_management() {
2421 let mut graph = create_test_graph();
2422 let mut log = ChangeLog::new();
2423
2424 {
2425 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2426 let cell_ref = CellRef {
2427 sheet_id: 0,
2428 coord: Coord::new(0, 0, true, true),
2429 };
2430 editor.set_cell_value(cell_ref, LiteralValue::Number(1.0));
2431 editor.set_cell_value(cell_ref, LiteralValue::Number(2.0));
2432 }
2433
2434 assert_eq!(log.len(), 2);
2435
2436 log.clear();
2437 assert_eq!(log.len(), 0);
2438 }
2439
2440 #[test]
2441 fn test_editor_drop_commits_batch() {
2442 let mut graph = create_test_graph();
2443 {
2444 let mut editor = VertexEditor::new(&mut graph);
2445 editor.begin_batch();
2446
2447 let meta = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2448 editor.add_vertex(meta);
2449
2450 }
2452
2453 }
2455}