1use crate::SheetId;
2use crate::engine::addr::GridAddr;
3use crate::engine::graph::DependencyGraph;
4use crate::engine::graph::editor::change_log::{FormulaRunAdjusted, MutationCapture};
5use crate::engine::graph::editor::reference_adjuster::{
6 MoveReferenceAdjuster, ReferenceAdjuster, ReferenceContext, RelativeReferenceAdjuster,
7 ShiftOperation,
8};
9use crate::engine::named_range::{NameScope, NamedDefinition};
10use crate::engine::{ChangeEvent, ChangeLogger, VertexId, VertexKind};
11use crate::reference::{CellRef, Coord};
12use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
13use formualizer_parse::parser::ASTNode;
14use rustc_hash::FxHashMap;
15use std::sync::atomic::{AtomicU64, Ordering};
16
17#[derive(Debug, Clone)]
19pub struct VertexMeta {
20 pub coord: GridAddr,
21 pub sheet_id: SheetId,
22 pub kind: VertexKind,
23 pub flags: u8,
24}
25
26impl VertexMeta {
27 pub fn new(row: u32, col: u32, sheet_id: SheetId, kind: VertexKind) -> Self {
28 Self {
29 coord: GridAddr::new(row, col),
30 sheet_id,
31 kind,
32 flags: 0,
33 }
34 }
35
36 pub fn with_flags(mut self, flags: u8) -> Self {
37 self.flags = flags;
38 self
39 }
40
41 pub fn dirty(mut self) -> Self {
42 self.flags |= 0x01;
43 self
44 }
45
46 pub fn volatile(mut self) -> Self {
47 self.flags |= 0x02;
48 self
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct VertexMetaPatch {
55 pub kind: Option<VertexKind>,
56 pub coord: Option<GridAddr>,
57 pub dirty: Option<bool>,
58 pub volatile: Option<bool>,
59}
60
61#[derive(Debug, Clone)]
63pub struct VertexDataPatch {
64 pub value: Option<LiteralValue>,
65 pub formula: Option<ASTNode>,
66}
67
68#[derive(Debug, Clone, Default)]
70pub struct MetaUpdateSummary {
71 pub coord_changed: bool,
72 pub kind_changed: bool,
73 pub flags_changed: bool,
74}
75
76#[derive(Debug, Clone, Default)]
78pub struct DataUpdateSummary {
79 pub value_changed: bool,
80 pub formula_changed: bool,
81 pub dependents_marked_dirty: Vec<VertexId>,
82}
83
84#[derive(Debug, Clone, Default)]
86pub struct ShiftSummary {
87 pub vertices_moved: Vec<VertexId>,
88 pub vertices_deleted: Vec<VertexId>,
89 pub references_adjusted: usize,
90 pub formulas_updated: usize,
91 #[cfg(test)]
92 pub(crate) structural_dependents_dirtied: Vec<VertexId>,
93}
94
95#[derive(Debug, Clone, Default)]
97pub struct RangeSummary {
98 pub cells_affected: usize,
99 pub vertices_created: Vec<VertexId>,
100 pub vertices_updated: Vec<VertexId>,
101 pub cells_moved: usize,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub struct TransactionId(u64);
107
108impl TransactionId {
109 fn new() -> Self {
110 static COUNTER: AtomicU64 = AtomicU64::new(0);
111 TransactionId(COUNTER.fetch_add(1, Ordering::Relaxed))
112 }
113}
114
115#[derive(Debug)]
117struct Transaction {
118 id: TransactionId,
119 start_index: usize, }
121
122#[derive(Debug, Clone)]
124pub enum EditorError {
125 TargetOccupied { cell: CellRef },
126 OutOfBounds { row: u32, col: u32 },
127 InvalidName { name: String, reason: String },
128 TransactionFailed { reason: String },
129 TransactionUnsupported { reason: String },
130 NoActiveTransaction,
131 VertexNotFound { id: VertexId },
132 Excel(ExcelError),
133}
134
135impl From<ExcelError> for EditorError {
136 fn from(e: ExcelError) -> Self {
137 EditorError::Excel(e)
138 }
139}
140
141impl std::fmt::Display for EditorError {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 match self {
144 EditorError::TargetOccupied { cell } => {
145 write!(
146 f,
147 "Target cell occupied at row {}, col {}",
148 cell.coord.row(),
149 cell.coord.col()
150 )
151 }
152 EditorError::OutOfBounds { row, col } => {
153 write!(f, "Cell position out of bounds: row {row}, col {col}")
154 }
155 EditorError::InvalidName { name, reason } => {
156 write!(f, "Invalid name '{name}': {reason}")
157 }
158 EditorError::TransactionFailed { reason } => {
159 write!(f, "Transaction failed: {reason}")
160 }
161 EditorError::TransactionUnsupported { reason } => {
162 write!(f, "Transaction unsupported: {reason}")
163 }
164 EditorError::NoActiveTransaction => {
165 write!(f, "No active transaction")
166 }
167 EditorError::VertexNotFound { id } => {
168 write!(f, "Vertex not found: {id:?}")
169 }
170 EditorError::Excel(e) => write!(f, "Excel error: {e:?}"),
171 }
172 }
173}
174
175impl std::error::Error for EditorError {}
176
177pub trait SpillValueReader {
212 fn read_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue>;
213}
214
215fn moved_run_holds(runs: &[crate::engine::graph::ShiftedRun], v: VertexId) -> bool {
217 runs.iter()
218 .any(|r| r.moved && v.0 >= r.first.0 && v.0 - r.first.0 < r.len)
219}
220
221enum EditorLogger<'g> {
224 Dyn(&'g mut dyn ChangeLogger),
225 Capture(&'g mut MutationCapture),
226}
227
228impl EditorLogger<'_> {
229 #[inline]
230 fn record(&mut self, event: ChangeEvent) {
231 match self {
232 EditorLogger::Dyn(l) => l.record(event),
233 EditorLogger::Capture(c) => c.record(event),
234 }
235 }
236
237 #[inline]
238 fn begin_compound(&mut self, description: String) {
239 match self {
240 EditorLogger::Dyn(l) => l.begin_compound(description),
241 EditorLogger::Capture(c) => c.begin_compound(description),
242 }
243 }
244
245 #[inline]
246 fn end_compound(&mut self) {
247 match self {
248 EditorLogger::Dyn(l) => l.end_compound(),
249 EditorLogger::Capture(c) => c.end_compound(),
250 }
251 }
252
253 fn record_formula_run(&mut self, run: FormulaRunAdjusted) {
256 match self {
257 EditorLogger::Dyn(l) => {
258 for event in run.events() {
259 l.record(event);
260 }
261 }
262 EditorLogger::Capture(c) => c.record_lazy(run),
263 }
264 }
265}
266
267pub struct VertexEditor<'g> {
268 graph: &'g mut DependencyGraph,
269 change_logger: Option<EditorLogger<'g>>,
270 spill_value_reader: Option<&'g dyn SpillValueReader>,
271 structural_occupancy: Option<crate::engine::graph::StructuralOccupancy>,
272 batch_mode: bool,
273}
274
275impl<'g> VertexEditor<'g> {
276 pub fn new(graph: &'g mut DependencyGraph) -> Self {
278 Self {
279 graph,
280 change_logger: None,
281 spill_value_reader: None,
282 structural_occupancy: None,
283 batch_mode: false,
284 }
285 }
286
287 pub(crate) fn with_structural_occupancy(
289 mut self,
290 occupancy: crate::engine::graph::StructuralOccupancy,
291 ) -> Self {
292 self.structural_occupancy = Some(occupancy);
293 self
294 }
295
296 pub(crate) fn set_structural_occupancy(
297 &mut self,
298 occupancy: crate::engine::graph::StructuralOccupancy,
299 ) {
300 self.structural_occupancy = Some(occupancy);
301 }
302
303 pub fn with_logger<L: ChangeLogger + 'g>(
305 graph: &'g mut DependencyGraph,
306 logger: &'g mut L,
307 ) -> Self {
308 Self {
309 graph,
310 change_logger: Some(EditorLogger::Dyn(logger as &'g mut dyn ChangeLogger)),
311 spill_value_reader: None,
312 structural_occupancy: None,
313 batch_mode: false,
314 }
315 }
316
317 pub fn with_logger_and_spill_reader<L: ChangeLogger + 'g>(
319 graph: &'g mut DependencyGraph,
320 logger: &'g mut L,
321 spill_value_reader: &'g dyn SpillValueReader,
322 ) -> Self {
323 Self {
324 graph,
325 change_logger: Some(EditorLogger::Dyn(logger as &'g mut dyn ChangeLogger)),
326 spill_value_reader: Some(spill_value_reader),
327 structural_occupancy: None,
328 batch_mode: false,
329 }
330 }
331
332 pub(crate) fn with_capture_and_spill_reader(
335 graph: &'g mut DependencyGraph,
336 capture: &'g mut MutationCapture,
337 spill_value_reader: &'g dyn SpillValueReader,
338 ) -> Self {
339 Self {
340 graph,
341 change_logger: Some(EditorLogger::Capture(capture)),
342 spill_value_reader: Some(spill_value_reader),
343 structural_occupancy: None,
344 batch_mode: false,
345 }
346 }
347
348 pub fn begin_batch(&mut self) {
350 if !self.batch_mode {
351 self.graph.begin_batch();
352 self.batch_mode = true;
353 }
354 }
355
356 pub fn commit_batch(&mut self) {
358 if self.batch_mode {
359 self.graph.end_batch();
360 self.batch_mode = false;
361 }
362 }
363
364 fn log_change(&mut self, event: ChangeEvent) {
366 if let Some(logger) = &mut self.change_logger {
367 logger.record(event);
368 }
369 }
370
371 fn snapshot_spill_for_anchor(
372 &self,
373 anchor: VertexId,
374 ) -> Option<crate::engine::graph::editor::change_log::SpillSnapshot> {
375 let cells = self.graph.spill_cells_for_anchor(anchor)?.to_vec();
376 if cells.is_empty() {
377 return None;
378 }
379
380 let max = self.graph.get_config().spill.max_spill_cells as usize;
382 let mut cells = cells;
383 if cells.len() > max {
384 cells.truncate(max);
385 }
386
387 let first = *cells.first().expect("non-empty spill cells");
388 let sheet_name = self.graph.sheet_name(first.sheet_id).to_string();
389 let row0 = first.coord.row();
390 let col0 = first.coord.col();
391
392 let mut max_row = row0;
393 let mut max_col = col0;
394 let mut by_coord: FxHashMap<(u32, u32), LiteralValue> = FxHashMap::default();
395 for cell in &cells {
396 max_row = max_row.max(cell.coord.row());
397 max_col = max_col.max(cell.coord.col());
398 let v = if let Some(reader) = self.spill_value_reader {
399 reader
400 .read_cell_value(&sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
401 .unwrap_or(LiteralValue::Empty)
402 } else {
403 self.graph
404 .get_cell_value(&sheet_name, cell.coord.row() + 1, cell.coord.col() + 1)
405 .unwrap_or(LiteralValue::Empty)
406 };
407 by_coord.insert((cell.coord.row(), cell.coord.col()), v);
408 }
409
410 let rows = (max_row - row0 + 1) as usize;
411 let cols = (max_col - col0 + 1) as usize;
412 let mut values: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
413 for r in 0..rows {
414 let mut row: Vec<LiteralValue> = Vec::with_capacity(cols);
415 for c in 0..cols {
416 row.push(
417 by_coord
418 .get(&(row0 + r as u32, col0 + c as u32))
419 .cloned()
420 .unwrap_or(LiteralValue::Empty),
421 );
422 }
423 values.push(row);
424 }
425
426 Some(crate::engine::graph::editor::change_log::SpillSnapshot {
427 target_cells: cells,
428 values,
429 })
430 }
431
432 pub fn commit_spill_region(
434 &mut self,
435 anchor: VertexId,
436 target_cells: Vec<CellRef>,
437 values: Vec<Vec<LiteralValue>>,
438 ) -> Result<(), EditorError> {
439 let old = self.snapshot_spill_for_anchor(anchor);
440 self.graph
441 .commit_spill_region_atomic_with_fault(
442 anchor,
443 target_cells.clone(),
444 values.clone(),
445 None,
446 )
447 .map_err(EditorError::Excel)?;
448 self.log_change(ChangeEvent::SpillCommitted {
449 anchor,
450 old,
451 new: crate::engine::graph::editor::change_log::SpillSnapshot {
452 target_cells,
453 values,
454 },
455 });
456 Ok(())
457 }
458
459 pub fn clear_spill_region(&mut self, anchor: VertexId) {
461 let Some(old) = self.snapshot_spill_for_anchor(anchor) else {
462 return;
463 };
464 self.graph.clear_spill_region(anchor);
465 self.log_change(ChangeEvent::SpillCleared { anchor, old });
466 }
467
468 pub fn has_logger(&self) -> bool {
470 self.change_logger.is_some()
471 }
472
473 fn get_formula_ast(&self, id: VertexId) -> Option<ASTNode> {
474 self.graph.get_formula(id)
475 }
476
477 fn snapshot_named_definitions(&self) -> FxHashMap<(NameScope, String), NamedDefinition> {
478 let mut out: FxHashMap<(NameScope, String), NamedDefinition> = FxHashMap::default();
479 for (name, nr) in self.graph.named_ranges_iter() {
480 out.insert((NameScope::Workbook, name.clone()), nr.definition.clone());
481 }
482 for ((sheet_id, name), nr) in self.graph.sheet_named_ranges_iter() {
483 out.insert(
484 (NameScope::Sheet(*sheet_id), name.clone()),
485 nr.definition.clone(),
486 );
487 }
488 out
489 }
490
491 pub(crate) fn inverse_compound_end(&mut self, description: &str) {
501 self.graph.undo_structural_extent(description);
502 }
503
504 pub fn apply_inverse(&mut self, change: ChangeEvent) -> Result<(), EditorError> {
506 match change {
507 ChangeEvent::SetValue {
508 addr,
509 old_value,
510 old_formula,
511 new: _,
512 } => {
513 if let Some(old_formula) = old_formula {
515 self.set_cell_formula(addr, old_formula);
516 } else if let Some(old_value) = old_value {
517 self.set_cell_value(addr, old_value);
518 } else {
519 self.graph.retire_cell_for_replay(addr);
524 }
525 }
526 ChangeEvent::SetFormula {
527 addr,
528 old_value,
529 old_formula,
530 new: _,
531 } => {
532 if let Some(old_formula) = old_formula {
534 self.set_cell_formula(addr, old_formula);
535 } else if let Some(old_value) = old_value {
536 self.set_cell_value(addr, old_value);
537 } else {
538 self.graph.retire_cell_for_replay(addr);
543 }
544 }
545 ChangeEvent::SetRowVisibility { .. } => {
546 }
548 ChangeEvent::AddVertex { id, .. } => {
549 let _ = self.remove_vertex(id); }
552 ChangeEvent::RemoveVertex {
553 id,
554 old_value,
555 old_formula,
556 old_dependencies,
557 old_dependents,
558 coord,
559 sheet_id,
560 kind,
561 ..
562 } => {
563 if let (Some(c), Some(sid)) = (coord, sheet_id) {
564 let meta =
565 VertexMeta::new(c.row(), c.col(), sid, kind.unwrap_or(VertexKind::Cell));
566 let new_id = if self.graph.revive_vertex(id, sid, c) {
571 if self.has_logger() {
572 self.log_change(ChangeEvent::AddVertex {
573 id,
574 coord: meta.coord,
575 sheet_id: meta.sheet_id,
576 value: Some(LiteralValue::Empty),
577 formula: None,
578 kind: Some(meta.kind),
579 flags: Some(meta.flags),
580 });
581 }
582 id
583 } else {
584 self.try_add_vertex(meta)?
585 };
586 if let Some(v) = old_value {
587 let cell_ref = self.graph.make_cell_ref_internal(sid, c.row(), c.col());
588 self.set_cell_value(cell_ref, v);
589 }
590 if let Some(f) = old_formula {
591 let cell_ref = self.graph.make_cell_ref_internal(sid, c.row(), c.col());
592 self.set_cell_formula(cell_ref, f);
593 }
594 #[cfg(any(test, feature = "legacy_oracle"))]
597 {
598 for dep in old_dependencies {
599 self.graph.add_dependency_edge(new_id, dep)?;
600 }
601 for parent in old_dependents {
602 self.graph.add_dependency_edge(parent, new_id)?;
603 }
604 }
605 #[cfg(not(any(test, feature = "legacy_oracle")))]
606 let _ = (old_dependencies, old_dependents, new_id);
607 }
608 }
609 ChangeEvent::DefineName { name, scope, .. } => {
610 self.graph.delete_name(&name, scope)?;
612 }
613 ChangeEvent::UpdateName {
614 name,
615 scope,
616 old_definition,
617 ..
618 } => {
619 self.graph.update_name(&name, old_definition, scope)?;
621 }
622 ChangeEvent::DeleteName {
623 name,
624 scope,
625 old_definition,
626 } => {
627 if let Some(def) = old_definition {
628 self.graph.define_name(&name, def, scope)?;
629 } else {
630 return Err(EditorError::TransactionFailed {
631 reason: "Missing old definition for name deletion rollback".to_string(),
632 });
633 }
634 }
635 ChangeEvent::SpillCommitted { anchor, old, .. } => {
636 if let Some(old) = old {
638 self.graph
639 .commit_spill_region_atomic_with_fault(
640 anchor,
641 old.target_cells,
642 old.values,
643 None,
644 )
645 .map_err(EditorError::Excel)?;
646 } else {
647 self.graph.clear_spill_region(anchor);
648 }
649 }
650 ChangeEvent::SpillCleared { anchor, old } => {
651 self.graph
653 .commit_spill_region_atomic_with_fault(
654 anchor,
655 old.target_cells,
656 old.values,
657 None,
658 )
659 .map_err(EditorError::Excel)?;
660 }
661 ChangeEvent::StagedFormulaCellChanged { .. } => {
662 }
664 ChangeEvent::CompoundStart { description, .. } => {
666 self.graph.replay_structural_marker(&description, false);
669 }
670 ChangeEvent::CompoundEnd { .. } => {
671 }
674 ChangeEvent::VertexMoved {
675 id,
676 sheet_id: _,
677 old_coord,
678 ..
679 } => {
680 self.move_vertex(id, old_coord)?;
682 }
683 ChangeEvent::FormulaAdjusted { id, old_ast, .. } => {
684 self.graph
686 .update_vertex_formula(id, old_ast)
687 .map_err(EditorError::Excel)?;
688 self.graph.mark_vertex_dirty(id);
689 }
690 ChangeEvent::NamedRangeAdjusted {
691 name,
692 scope,
693 old_definition,
694 ..
695 } => {
696 self.graph.update_name(&name, old_definition, scope)?;
698 }
699 ChangeEvent::EdgeAdded { from, to } => {
700 return Err(EditorError::TransactionFailed {
703 reason: "Cannot rollback edge addition yet".to_string(),
704 });
705 }
706 ChangeEvent::EdgeRemoved { from, to } => {
707 return Err(EditorError::TransactionFailed {
710 reason: "Cannot rollback edge removal yet".to_string(),
711 });
712 }
713 }
714 Ok(())
715 }
716
717 pub fn add_vertex(&mut self, meta: VertexMeta) -> VertexId {
722 self.try_add_vertex(meta)
723 .unwrap_or_else(|_| VertexId::new(0))
724 }
725
726 pub fn try_add_vertex(&mut self, meta: VertexMeta) -> Result<VertexId, EditorError> {
727 let id = self
731 .graph
732 .add_empty_vertex(meta.sheet_id, meta.coord.row(), meta.coord.col())
733 .map_err(EditorError::Excel)?;
734
735 if self.has_logger() && id.0 != 0 {
736 self.log_change(ChangeEvent::AddVertex {
737 id,
738 coord: meta.coord,
739 sheet_id: meta.sheet_id,
740 value: Some(LiteralValue::Empty),
741 formula: None,
742 kind: Some(meta.kind),
743 flags: Some(meta.flags),
744 });
745 }
746 Ok(id)
747 }
748
749 pub fn remove_vertex(&mut self, id: VertexId) -> Result<(), EditorError> {
751 if !self.graph.vertex_exists(id) {
753 return Err(EditorError::Excel(
754 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
755 ));
756 }
757 self.graph.materialize_vertex(id);
758
759 let spill_snapshot = self.snapshot_spill_for_anchor(id);
762 let did_spill_clear = spill_snapshot.is_some();
763 if let Some(old_spill) = spill_snapshot {
764 if let Some(logger) = &mut self.change_logger {
765 logger.begin_compound(format!("RemoveVertexWithSpillClear id={}", id.0));
766 }
767 self.graph.clear_spill_region(id);
768 self.log_change(ChangeEvent::SpillCleared {
769 anchor: id,
770 old: old_spill,
771 });
772 }
773
774 let dependents = self.graph.authority_in_edge_readers(id);
776
777 let (
779 old_value,
780 old_formula,
781 old_dependencies,
782 old_dependents,
783 coord,
784 sheet_id_opt,
785 kind,
786 flags,
787 ) = if self.has_logger() {
788 let coord = self.graph.get_grid_addr(id);
789 let sheet_id = self.graph.get_sheet_id(id);
790 let kind = self.graph.get_vertex_kind(id);
791 let flags = 0u8;
793 (
794 self.graph.get_value(id),
795 self.get_formula_ast(id),
796 {
798 #[cfg(any(test, feature = "legacy_oracle"))]
799 let deps = self.graph.get_dependencies(id);
800 #[cfg(not(any(test, feature = "legacy_oracle")))]
801 let deps = Vec::new();
802 deps
803 },
804 dependents.clone(), coord,
806 Some(sheet_id),
807 Some(kind),
808 Some(flags),
809 )
810 } else {
811 (None, None, vec![], vec![], None, None, None, None)
812 };
813
814 self.graph.journal_formula_left(id);
817 if let Some(cell_ref) = self.graph.get_cell_ref_for_vertex(id) {
819 self.graph.remove_cell_mapping(&cell_ref);
820 let (r, c) = (cell_ref.coord.row(), cell_ref.coord.col());
822 self.graph
823 .forget_extent_cells(cell_ref.sheet_id, (r, r), (c, c));
824 }
825
826 self.graph.forget_declared_dynamic_anchor(id);
830 self.graph.vertex_formulas.remove(&id);
831 self.graph.vertex_values.remove(&id);
832 self.graph.clear_formula_vertex_dirty(id);
833 self.graph.mark_volatile(id, false);
834 self.graph.store.set_kind(id, VertexKind::Empty);
835 self.graph.store.set_dynamic(id, false);
836
837 self.graph.remove_all_edges(id);
839
840 for dep_id in &dependents {
842 self.graph.mark_as_ref_error(*dep_id);
843 }
844
845 self.graph.mark_deleted(id, true);
847
848 self.log_change(ChangeEvent::RemoveVertex {
850 id,
851 old_value,
852 old_formula,
853 old_dependencies,
854 old_dependents,
855 coord,
856 sheet_id: sheet_id_opt,
857 kind,
858 flags,
859 });
860
861 if did_spill_clear && let Some(logger) = &mut self.change_logger {
862 logger.end_compound();
863 }
864
865 Ok(())
866 }
867
868 pub fn remove_vertex_at(&mut self, cell: CellRef) -> Result<(), EditorError> {
870 if let Some(id) = self.graph.get_vertex_for_cell(&cell) {
871 self.remove_vertex(id)
872 } else {
873 let (r, c) = (cell.coord.row(), cell.coord.col());
876 self.graph
877 .forget_extent_cells(cell.sheet_id, (r, r), (c, c));
878 Ok(())
879 }
880 }
881
882 pub fn move_vertex(&mut self, id: VertexId, new_coord: GridAddr) -> Result<(), EditorError> {
890 self.move_vertex_inner(id, new_coord, true)
891 }
892
893 fn move_vertex_inner(
896 &mut self,
897 id: VertexId,
898 new_coord: GridAddr,
899 map_cell: bool,
900 ) -> Result<(), EditorError> {
901 self.graph.authority_note_structural(false);
902 self.graph.own_formula_id(id);
905 if !self.graph.vertex_exists(id) {
907 return Err(EditorError::Excel(
908 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
909 ));
910 }
911 if self.graph.get_grid_addr(id).is_none() {
912 return Err(EditorError::Excel(
913 ExcelError::new(ExcelErrorKind::Ref).with_message(
914 "Symbol vertices have no position and cannot be moved onto the grid",
915 ),
916 ));
917 }
918
919 let old_cell_ref = self.graph.get_cell_ref_for_vertex(id);
921
922 let sheet_id = self.graph.get_sheet_id(id);
924 let new_cell_ref = CellRef::new(
925 sheet_id,
926 Coord::new(new_coord.row(), new_coord.col(), true, true),
927 );
928
929 self.graph.set_grid_addr(id, new_coord);
931
932 self.graph.update_edge_grid_addr(id, new_coord);
934
935 if map_cell {
937 self.graph
938 .update_cell_mapping(id, old_cell_ref, new_cell_ref);
939 }
940
941 self.graph.mark_dependents_dirty(id);
943
944 Ok(())
945 }
946
947 pub fn patch_vertex_meta(
949 &mut self,
950 id: VertexId,
951 patch: VertexMetaPatch,
952 ) -> Result<MetaUpdateSummary, EditorError> {
953 if !self.graph.vertex_exists(id) {
954 return Err(EditorError::Excel(
955 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
956 ));
957 }
958
959 let mut summary = MetaUpdateSummary::default();
960
961 if let Some(coord) = patch.coord {
962 if self.graph.get_grid_addr(id).is_none() {
964 return Err(EditorError::Excel(
965 ExcelError::new(ExcelErrorKind::Ref).with_message(
966 "Symbol vertices have no position and cannot be moved onto the grid",
967 ),
968 ));
969 }
970 let _ = self.graph.own_formula_id(id);
972 self.graph.set_grid_addr(id, coord);
973 self.graph.update_edge_grid_addr(id, coord);
974 summary.coord_changed = true;
975 }
976
977 if let Some(kind) = patch.kind {
978 self.graph.set_kind(id, kind);
979 summary.kind_changed = true;
980 }
981
982 if let Some(dirty) = patch.dirty {
983 self.graph.set_dirty(id, dirty);
984 summary.flags_changed = true;
985 }
986
987 if let Some(volatile) = patch.volatile {
988 self.graph.mark_volatile(id, volatile);
989 summary.flags_changed = true;
990 }
991
992 Ok(summary)
993 }
994
995 pub fn patch_vertex_data(
997 &mut self,
998 id: VertexId,
999 patch: VertexDataPatch,
1000 ) -> Result<DataUpdateSummary, EditorError> {
1001 if !self.graph.vertex_exists(id) {
1002 return Err(EditorError::Excel(
1003 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
1004 ));
1005 }
1006
1007 let mut summary = DataUpdateSummary::default();
1008
1009 if let Some(value) = patch.value {
1010 self.graph.update_vertex_value(id, value);
1011 summary.value_changed = true;
1012
1013 let dependents = self.graph.authority_in_edge_readers(id);
1016 for dep in &dependents {
1017 self.graph.set_dirty(*dep, true);
1018 }
1019 summary.dependents_marked_dirty = dependents;
1020 }
1021
1022 if let Some(_formula) = patch.formula {
1023 summary.formula_changed = true;
1026 }
1027
1028 Ok(summary)
1029 }
1030
1031 pub fn add_edge(&mut self, from: VertexId, to: VertexId) -> bool {
1034 if from == to {
1035 return false; }
1037
1038 true
1041 }
1042
1043 pub fn remove_edge(&mut self, _from: VertexId, _to: VertexId) -> bool {
1045 true
1047 }
1048
1049 pub fn insert_rows(
1051 &mut self,
1052 sheet_id: SheetId,
1053 before: u32,
1054 count: u32,
1055 ) -> Result<ShiftSummary, EditorError> {
1056 let result = self.insert_rows_impl(sheet_id, before, count);
1057 self.graph.authority_end_structural();
1058 result
1059 }
1060
1061 fn insert_rows_impl(
1062 &mut self,
1063 sheet_id: SheetId,
1064 before: u32,
1065 count: u32,
1066 ) -> Result<ShiftSummary, EditorError> {
1067 self.graph.authority_note_structural_shift();
1068 if count == 0 {
1069 return Ok(ShiftSummary::default());
1070 }
1071
1072 let mut summary = ShiftSummary::default();
1073
1074 self.begin_batch();
1076 let conservative = crate::engine::graph::StructuralOccupancy::conservative();
1077 let occupancy = self.structural_occupancy.as_ref().unwrap_or(&conservative);
1078 let range_dependents = self.graph.authority_structural_band_readers(
1079 sheet_id,
1080 crate::engine::graph::StructuralEdit::InsertRows { before },
1081 occupancy,
1082 );
1083 #[cfg(test)]
1084 {
1085 summary.structural_dependents_dirtied = range_dependents.clone();
1086 }
1087 self.graph.mark_dirty_many(&range_dependents);
1088
1089 let vertices_to_shift: Vec<(VertexId, GridAddr)> = self
1091 .graph
1092 .grid_vertices_in_sheet(sheet_id)
1093 .filter(|(_, coord)| coord.row() >= before)
1094 .collect();
1095
1096 if let Some(logger) = &mut self.change_logger {
1097 logger.begin_compound(format!(
1098 "InsertRows sheet={sheet_id} before={before} count={count}"
1099 ));
1100 }
1101 let op = ShiftOperation::InsertRows {
1104 sheet_id,
1105 before,
1106 count,
1107 };
1108 self.shift_and_adjust(
1109 sheet_id,
1110 &op,
1111 vertices_to_shift,
1112 |old_coord| GridAddr::new(old_coord.row() + count, old_coord.col()),
1113 &mut summary,
1114 )?;
1115
1116 let old_names = if self.has_logger() {
1118 Some(self.snapshot_named_definitions())
1119 } else {
1120 None
1121 };
1122 self.graph.adjust_named_ranges(&op)?;
1123 if let Some(old_names) = old_names {
1124 let new_names = self.snapshot_named_definitions();
1125 for ((scope, name), old_definition) in old_names {
1126 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1127 && *new_definition != old_definition
1128 {
1129 self.log_change(ChangeEvent::NamedRangeAdjusted {
1130 name,
1131 scope,
1132 old_definition,
1133 new_definition: new_definition.clone(),
1134 });
1135 }
1136 }
1137 }
1138
1139 if let Some(logger) = &mut self.change_logger {
1141 logger.end_compound();
1142 }
1143
1144 self.commit_batch();
1145
1146 Ok(summary)
1147 }
1148
1149 pub fn delete_rows(
1151 &mut self,
1152 sheet_id: SheetId,
1153 start: u32,
1154 count: u32,
1155 ) -> Result<ShiftSummary, EditorError> {
1156 let result = self.delete_rows_impl(sheet_id, start, count);
1157 self.graph.authority_end_structural();
1158 result
1159 }
1160
1161 fn delete_rows_impl(
1162 &mut self,
1163 sheet_id: SheetId,
1164 start: u32,
1165 count: u32,
1166 ) -> Result<ShiftSummary, EditorError> {
1167 if count == 0 {
1168 self.graph.authority_note_structural_shift();
1169 return Ok(ShiftSummary::default());
1170 }
1171 let end = start.saturating_add(count - 1);
1176 let rects: Vec<(SheetId, u32, u32, u32, u32)> = match self
1177 .structural_occupancy
1178 .as_ref()
1179 .and_then(|o| o.occupied_column_runs())
1180 {
1181 Some(runs) => runs
1182 .into_iter()
1183 .map(|(c0, c1)| (sheet_id, start, end, c0, c1))
1184 .collect(),
1185 None => vec![(sheet_id, start, end, 0, 16_383)],
1186 };
1187 let _ = self.graph.mark_dirty_rects(&rects);
1188 self.graph.authority_note_structural_shift();
1189
1190 let mut summary = ShiftSummary::default();
1191
1192 self.begin_batch();
1193
1194 if let Some(logger) = &mut self.change_logger {
1195 logger.begin_compound(format!(
1196 "DeleteRows sheet={sheet_id} start={start} count={count}"
1197 ));
1198 }
1199
1200 let vertices_to_delete: Vec<VertexId> = self
1202 .graph
1203 .grid_vertices_in_sheet(sheet_id)
1204 .filter(|(_, coord)| coord.row() >= start && coord.row() < start + count)
1205 .map(|(id, _)| id)
1206 .collect();
1207 let conservative = crate::engine::graph::StructuralOccupancy::conservative();
1208 let occupancy = self.structural_occupancy.as_ref().unwrap_or(&conservative);
1209 let range_dependents = self.graph.authority_structural_band_readers(
1210 sheet_id,
1211 crate::engine::graph::StructuralEdit::DeleteRows {
1212 start,
1213 end: start.saturating_add(count).saturating_sub(1).max(start),
1214 },
1215 occupancy,
1216 );
1217 #[cfg(test)]
1218 {
1219 summary.structural_dependents_dirtied = range_dependents.clone();
1220 }
1221 self.graph.mark_dirty_many(&range_dependents);
1222
1223 for id in vertices_to_delete {
1224 self.remove_vertex(id)?;
1225 summary.vertices_deleted.push(id);
1226 }
1227 let vertices_to_shift: Vec<(VertexId, GridAddr)> = self
1230 .graph
1231 .grid_vertices_in_sheet(sheet_id)
1232 .filter(|(_, coord)| coord.row() >= start + count)
1233 .collect();
1234 let op = ShiftOperation::DeleteRows {
1235 sheet_id,
1236 start,
1237 count,
1238 };
1239 self.shift_and_adjust(
1240 sheet_id,
1241 &op,
1242 vertices_to_shift,
1243 |old_coord| GridAddr::new(old_coord.row() - count, old_coord.col()),
1244 &mut summary,
1245 )?;
1246
1247 let old_names = if self.has_logger() {
1249 Some(self.snapshot_named_definitions())
1250 } else {
1251 None
1252 };
1253 self.graph.adjust_named_ranges(&op)?;
1254 if let Some(old_names) = old_names {
1255 let new_names = self.snapshot_named_definitions();
1256 for ((scope, name), old_definition) in old_names {
1257 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1258 && *new_definition != old_definition
1259 {
1260 self.log_change(ChangeEvent::NamedRangeAdjusted {
1261 name,
1262 scope,
1263 old_definition,
1264 new_definition: new_definition.clone(),
1265 });
1266 }
1267 }
1268 }
1269
1270 if let Some(logger) = &mut self.change_logger {
1272 logger.end_compound();
1273 }
1274
1275 self.commit_batch();
1276
1277 Ok(summary)
1278 }
1279
1280 pub fn insert_columns(
1282 &mut self,
1283 sheet_id: SheetId,
1284 before: u32,
1285 count: u32,
1286 ) -> Result<ShiftSummary, EditorError> {
1287 let result = self.insert_columns_impl(sheet_id, before, count);
1288 self.graph.authority_end_structural();
1289 result
1290 }
1291
1292 fn insert_columns_impl(
1293 &mut self,
1294 sheet_id: SheetId,
1295 before: u32,
1296 count: u32,
1297 ) -> Result<ShiftSummary, EditorError> {
1298 self.graph.authority_note_structural_shift();
1299 if count == 0 {
1300 return Ok(ShiftSummary::default());
1301 }
1302
1303 let mut summary = ShiftSummary::default();
1304
1305 self.begin_batch();
1307 let conservative = crate::engine::graph::StructuralOccupancy::conservative();
1308 let occupancy = self.structural_occupancy.as_ref().unwrap_or(&conservative);
1309 let range_dependents = self.graph.authority_structural_band_readers(
1310 sheet_id,
1311 crate::engine::graph::StructuralEdit::InsertColumns { before },
1312 occupancy,
1313 );
1314 #[cfg(test)]
1315 {
1316 summary.structural_dependents_dirtied = range_dependents.clone();
1317 }
1318 self.graph.mark_dirty_many(&range_dependents);
1319
1320 let vertices_to_shift: Vec<(VertexId, GridAddr)> = self
1322 .graph
1323 .grid_vertices_in_sheet(sheet_id)
1324 .filter(|(_, coord)| coord.col() >= before)
1325 .collect();
1326
1327 if let Some(logger) = &mut self.change_logger {
1328 logger.begin_compound(format!(
1329 "InsertColumns sheet={sheet_id} before={before} count={count}"
1330 ));
1331 }
1332 let op = ShiftOperation::InsertColumns {
1335 sheet_id,
1336 before,
1337 count,
1338 };
1339 self.shift_and_adjust(
1340 sheet_id,
1341 &op,
1342 vertices_to_shift,
1343 |old_coord| GridAddr::new(old_coord.row(), old_coord.col() + count),
1344 &mut summary,
1345 )?;
1346
1347 let old_names = if self.has_logger() {
1349 Some(self.snapshot_named_definitions())
1350 } else {
1351 None
1352 };
1353 self.graph.adjust_named_ranges(&op)?;
1354 if let Some(old_names) = old_names {
1355 let new_names = self.snapshot_named_definitions();
1356 for ((scope, name), old_definition) in old_names {
1357 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1358 && *new_definition != old_definition
1359 {
1360 self.log_change(ChangeEvent::NamedRangeAdjusted {
1361 name,
1362 scope,
1363 old_definition,
1364 new_definition: new_definition.clone(),
1365 });
1366 }
1367 }
1368 }
1369
1370 if let Some(logger) = &mut self.change_logger {
1372 logger.end_compound();
1373 }
1374
1375 self.commit_batch();
1376
1377 Ok(summary)
1378 }
1379
1380 fn shift_and_adjust(
1393 &mut self,
1394 sheet_id: SheetId,
1395 op: &ShiftOperation,
1396 vertices_to_shift: Vec<(VertexId, GridAddr)>,
1397 shift: impl Fn(GridAddr) -> GridAddr,
1398 summary: &mut ShiftSummary,
1399 ) -> Result<(), EditorError> {
1400 let journal = self.has_logger();
1401 self.graph.shift_retired_ids(op, journal);
1402 let runs = self.graph.shift_virtual_runs(op);
1403
1404 for (id, old_coord) in vertices_to_shift {
1405 let new_coord = shift(old_coord);
1406 if self.has_logger() {
1407 self.log_change(ChangeEvent::VertexMoved {
1408 id,
1409 sheet_id,
1410 old_coord,
1411 new_coord,
1412 });
1413 }
1414 if self.graph.is_virtual_member(id) {
1415 debug_assert_eq!(self.graph.get_grid_addr(id), Some(new_coord));
1417 } else {
1418 let cell = CellRef::new(
1424 sheet_id,
1425 Coord::new(new_coord.row(), new_coord.col(), true, true),
1426 );
1427 let member_moves_later = self
1428 .graph
1429 .virtual_member_at(&cell)
1430 .is_some_and(|m| m.0 > id.0 && moved_run_holds(&runs, m));
1431 self.move_vertex_inner(id, new_coord, !member_moves_later)?;
1432 }
1433 summary.vertices_moved.push(id);
1434 }
1435 self.graph.after_shift_moves(&runs);
1436
1437 let adjuster = ReferenceAdjuster::new();
1438 let mut adjusted_runs: Vec<&crate::engine::graph::ShiftedRun> =
1439 runs.iter().filter(|r| r.adjusted.is_some()).collect();
1440 adjusted_runs.sort_unstable_by_key(|r| r.first.0);
1441 let mut next_run = 0;
1442 let formula_vertices = self.graph.materialized_formula_vertices_sorted();
1443 for id in formula_vertices {
1444 while next_run < adjusted_runs.len() && adjusted_runs[next_run].first.0 < id.0 {
1445 self.log_adjusted_run(adjusted_runs[next_run], summary);
1446 next_run += 1;
1447 }
1448 if let Some(ast) = self.get_formula_ast(id)
1449 && let Some(adjusted) = adjuster.adjust_ast_if_changed_in_context(
1450 &ast,
1451 op,
1452 &ReferenceContext::new(self.graph.get_sheet_id(id), self.graph.sheet_reg()),
1453 )
1454 {
1455 if self.has_logger() {
1456 self.log_change(ChangeEvent::FormulaAdjusted {
1457 id,
1458 addr: self.graph.get_cell_ref_for_vertex(id),
1459 old_ast: ast.clone(),
1460 new_ast: adjusted.clone(),
1461 });
1462 }
1463 self.graph.update_vertex_formula(id, adjusted)?;
1464 self.graph.mark_vertex_dirty(id);
1465 summary.formulas_updated += 1;
1466 }
1467 }
1468 for run in &adjusted_runs[next_run..] {
1469 self.log_adjusted_run(run, summary);
1470 }
1471 Ok(())
1472 }
1473
1474 fn log_adjusted_run(
1478 &mut self,
1479 run: &crate::engine::graph::ShiftedRun,
1480 summary: &mut ShiftSummary,
1481 ) {
1482 summary.formulas_updated += run.len as usize;
1483 let Some((old_first, new_first)) = &run.adjusted else {
1484 return;
1485 };
1486 if let Some(logger) = &mut self.change_logger {
1487 logger.record_formula_run(FormulaRunAdjusted {
1488 first: run.first,
1489 len: run.len,
1490 sheet_id: run.sheet,
1491 col: run.col,
1492 row0: run.row0,
1493 old_first: old_first.clone(),
1494 new_first: new_first.clone(),
1495 });
1496 }
1497 }
1498
1499 pub fn delete_columns(
1501 &mut self,
1502 sheet_id: SheetId,
1503 start: u32,
1504 count: u32,
1505 ) -> Result<ShiftSummary, EditorError> {
1506 let result = self.delete_columns_impl(sheet_id, start, count);
1507 self.graph.authority_end_structural();
1508 result
1509 }
1510
1511 fn delete_columns_impl(
1512 &mut self,
1513 sheet_id: SheetId,
1514 start: u32,
1515 count: u32,
1516 ) -> Result<ShiftSummary, EditorError> {
1517 if count == 0 {
1518 self.graph.authority_note_structural_shift();
1519 return Ok(ShiftSummary::default());
1520 }
1521 let _ = self.graph.mark_dirty_rects(&[(
1525 sheet_id,
1526 0,
1527 1_048_575,
1528 start,
1529 start.saturating_add(count - 1),
1530 )]);
1531 self.graph.authority_note_structural_shift();
1532
1533 let mut summary = ShiftSummary::default();
1534
1535 self.begin_batch();
1536
1537 if let Some(logger) = &mut self.change_logger {
1538 logger.begin_compound(format!(
1539 "DeleteColumns sheet={sheet_id} start={start} count={count}"
1540 ));
1541 }
1542
1543 let vertices_to_delete: Vec<VertexId> = self
1545 .graph
1546 .grid_vertices_in_sheet(sheet_id)
1547 .filter(|(_, coord)| coord.col() >= start && coord.col() < start + count)
1548 .map(|(id, _)| id)
1549 .collect();
1550 let conservative = crate::engine::graph::StructuralOccupancy::conservative();
1551 let occupancy = self.structural_occupancy.as_ref().unwrap_or(&conservative);
1552 let range_dependents = self.graph.authority_structural_band_readers(
1553 sheet_id,
1554 crate::engine::graph::StructuralEdit::DeleteColumns {
1555 start,
1556 end: start.saturating_add(count).saturating_sub(1).max(start),
1557 },
1558 occupancy,
1559 );
1560 #[cfg(test)]
1561 {
1562 summary.structural_dependents_dirtied = range_dependents.clone();
1563 }
1564 self.graph.mark_dirty_many(&range_dependents);
1565
1566 for id in vertices_to_delete {
1567 self.remove_vertex(id)?;
1568 summary.vertices_deleted.push(id);
1569 }
1570 let vertices_to_shift: Vec<(VertexId, GridAddr)> = self
1573 .graph
1574 .grid_vertices_in_sheet(sheet_id)
1575 .filter(|(_, coord)| coord.col() >= start + count)
1576 .collect();
1577 let op = ShiftOperation::DeleteColumns {
1578 sheet_id,
1579 start,
1580 count,
1581 };
1582 self.shift_and_adjust(
1583 sheet_id,
1584 &op,
1585 vertices_to_shift,
1586 |old_coord| GridAddr::new(old_coord.row(), old_coord.col() - count),
1587 &mut summary,
1588 )?;
1589
1590 let old_names = if self.has_logger() {
1592 Some(self.snapshot_named_definitions())
1593 } else {
1594 None
1595 };
1596 self.graph.adjust_named_ranges(&op)?;
1597 if let Some(old_names) = old_names {
1598 let new_names = self.snapshot_named_definitions();
1599 for ((scope, name), old_definition) in old_names {
1600 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1601 && *new_definition != old_definition
1602 {
1603 self.log_change(ChangeEvent::NamedRangeAdjusted {
1604 name,
1605 scope,
1606 old_definition,
1607 new_definition: new_definition.clone(),
1608 });
1609 }
1610 }
1611 }
1612
1613 if let Some(logger) = &mut self.change_logger {
1615 logger.end_compound();
1616 }
1617
1618 self.commit_batch();
1619
1620 Ok(summary)
1621 }
1622
1623 pub fn shift_rows(&mut self, sheet_id: SheetId, start_row: u32, delta: i32) {
1625 if delta == 0 {
1626 return;
1627 }
1628
1629 let change_event = ChangeEvent::SetValue {
1631 addr: CellRef {
1632 sheet_id,
1633 coord: Coord::new(start_row, 0, true, true),
1634 },
1635 old_value: None,
1636 old_formula: None,
1637 new: LiteralValue::Text(format!("Row shift: start={start_row}, delta={delta}")),
1638 };
1639 self.log_change(change_event);
1640
1641 }
1644
1645 pub fn shift_columns(&mut self, sheet_id: SheetId, start_col: u32, delta: i32) {
1647 if delta == 0 {
1648 return;
1649 }
1650
1651 let change_event = ChangeEvent::SetValue {
1653 addr: CellRef {
1654 sheet_id,
1655 coord: Coord::new(0, start_col, true, true),
1656 },
1657 old_value: None,
1658 old_formula: None,
1659 new: LiteralValue::Text(format!("Column shift: start={start_col}, delta={delta}")),
1660 };
1661 self.log_change(change_event);
1662
1663 }
1666
1667 pub fn set_cell_value(&mut self, cell_ref: CellRef, value: LiteralValue) -> VertexId {
1670 self.set_cell_value_with_old_state(cell_ref, value, None, None)
1671 }
1672
1673 pub fn set_cell_value_with_old_state(
1683 &mut self,
1684 cell_ref: CellRef,
1685 value: LiteralValue,
1686 fallback_old_value: Option<LiteralValue>,
1687 fallback_old_formula: Option<ASTNode>,
1688 ) -> VertexId {
1689 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1690
1691 let old_id = self.graph.get_vertex_id_for_address(&cell_ref);
1694 let old_value = old_id
1695 .and_then(|id| self.graph.get_value(id))
1696 .or(fallback_old_value);
1697 let old_formula = old_id
1698 .and_then(|id| self.get_formula_ast(id))
1699 .or(fallback_old_formula);
1700
1701 let spill_snapshot =
1704 old_id.and_then(|id| self.snapshot_spill_for_anchor(id).map(|s| (id, s)));
1705 let did_spill_clear = spill_snapshot.is_some();
1706 if let Some((anchor, old_spill)) = spill_snapshot {
1707 if let Some(logger) = &mut self.change_logger {
1708 logger.begin_compound(format!(
1709 "SetValueWithSpillClear sheet={} row={} col={}",
1710 cell_ref.sheet_id,
1711 cell_ref.coord.row(),
1712 cell_ref.coord.col()
1713 ));
1714 }
1715 self.graph.clear_spill_region(anchor);
1716 self.log_change(ChangeEvent::SpillCleared {
1717 anchor,
1718 old: old_spill,
1719 });
1720 }
1721
1722 match self.graph.set_cell_value(
1725 &sheet_name,
1726 cell_ref.coord.row() + 1,
1727 cell_ref.coord.col() + 1,
1728 value.clone(),
1729 ) {
1730 Ok(_) => {
1731 let change_event = ChangeEvent::SetValue {
1733 addr: cell_ref,
1734 old_value,
1735 old_formula,
1736 new: value,
1737 };
1738 self.log_change(change_event);
1739
1740 if did_spill_clear && let Some(logger) = &mut self.change_logger {
1741 logger.end_compound();
1742 }
1743
1744 VertexId::new(0)
1746 }
1747 Err(_) => VertexId::new(0),
1748 }
1749 }
1750
1751 pub fn set_cell_formula(&mut self, cell_ref: CellRef, formula: ASTNode) -> VertexId {
1756 self.set_cell_formula_with_old_state(cell_ref, formula, None, None)
1757 }
1758
1759 pub fn try_set_cell_formula(
1761 &mut self,
1762 cell_ref: CellRef,
1763 formula: ASTNode,
1764 ) -> Result<VertexId, ExcelError> {
1765 self.try_set_cell_formula_with_old_state(cell_ref, formula, None, None)
1766 }
1767
1768 pub fn set_cell_formula_with_old_state(
1774 &mut self,
1775 cell_ref: CellRef,
1776 formula: ASTNode,
1777 fallback_old_value: Option<LiteralValue>,
1778 fallback_old_formula: Option<ASTNode>,
1779 ) -> VertexId {
1780 self.try_set_cell_formula_with_old_state(
1781 cell_ref,
1782 formula,
1783 fallback_old_value,
1784 fallback_old_formula,
1785 )
1786 .unwrap_or_else(|_| VertexId::new(0))
1787 }
1788
1789 pub fn try_set_cell_formula_with_old_state(
1792 &mut self,
1793 cell_ref: CellRef,
1794 formula: ASTNode,
1795 fallback_old_value: Option<LiteralValue>,
1796 fallback_old_formula: Option<ASTNode>,
1797 ) -> Result<VertexId, ExcelError> {
1798 self.set_cell_formula_with_old_state_and_plan(
1799 cell_ref,
1800 formula,
1801 fallback_old_value,
1802 fallback_old_formula,
1803 None,
1804 )
1805 }
1806
1807 pub(crate) fn set_cell_formula_with_prepared_plan(
1808 &mut self,
1809 cell_ref: CellRef,
1810 formula: ASTNode,
1811 fallback_old_value: Option<LiteralValue>,
1812 fallback_old_formula: Option<ASTNode>,
1813 ast_id: crate::engine::arena::AstNodeId,
1814 plan: crate::engine::ingest_pipeline::DependencyPlanRow,
1815 ) -> VertexId {
1816 self.set_cell_formula_with_old_state_and_plan(
1817 cell_ref,
1818 formula,
1819 fallback_old_value,
1820 fallback_old_formula,
1821 Some((ast_id, plan)),
1822 )
1823 .unwrap_or_else(|_| VertexId::new(0))
1824 }
1825
1826 fn set_cell_formula_with_old_state_and_plan(
1827 &mut self,
1828 cell_ref: CellRef,
1829 formula: ASTNode,
1830 fallback_old_value: Option<LiteralValue>,
1831 fallback_old_formula: Option<ASTNode>,
1832 prepared: Option<(
1833 crate::engine::arena::AstNodeId,
1834 crate::engine::ingest_pipeline::DependencyPlanRow,
1835 )>,
1836 ) -> Result<VertexId, ExcelError> {
1837 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1838
1839 let old_id = self.graph.get_vertex_id_for_address(&cell_ref);
1842 let old_value = old_id
1843 .and_then(|id| self.graph.get_value(id))
1844 .or(fallback_old_value);
1845 let old_formula = old_id
1846 .and_then(|id| self.get_formula_ast(id))
1847 .or(fallback_old_formula);
1848
1849 let spill_snapshot =
1852 old_id.and_then(|id| self.snapshot_spill_for_anchor(id).map(|s| (id, s)));
1853 let did_spill_clear = spill_snapshot.is_some();
1854
1855 let result = if let Some((ast_id, plan)) = prepared {
1857 self.graph.set_cell_formula_with_plan(
1858 &sheet_name,
1859 cell_ref.coord.row() + 1,
1860 cell_ref.coord.col() + 1,
1861 ast_id,
1862 &plan,
1863 plan.volatile,
1864 plan.dynamic,
1865 )
1866 } else {
1867 self.graph.set_cell_formula(
1868 &sheet_name,
1869 cell_ref.coord.row() + 1,
1870 cell_ref.coord.col() + 1,
1871 formula.clone(),
1872 )
1873 };
1874 match result {
1875 Ok(summary) => {
1876 if let Some((anchor, old_spill)) = spill_snapshot {
1877 if let Some(logger) = &mut self.change_logger {
1878 logger.begin_compound(format!(
1879 "SetFormulaWithSpillClear sheet={} row={} col={}",
1880 cell_ref.sheet_id,
1881 cell_ref.coord.row(),
1882 cell_ref.coord.col()
1883 ));
1884 }
1885 self.graph.clear_spill_region(anchor);
1886 self.log_change(ChangeEvent::SpillCleared {
1887 anchor,
1888 old: old_spill,
1889 });
1890 }
1891 let change_event = ChangeEvent::SetFormula {
1893 addr: cell_ref,
1894 old_value,
1895 old_formula,
1896 new: formula,
1897 };
1898 self.log_change(change_event);
1899
1900 if did_spill_clear && let Some(logger) = &mut self.change_logger {
1901 logger.end_compound();
1902 }
1903
1904 Ok(summary
1905 .affected_vertices
1906 .into_iter()
1907 .next()
1908 .unwrap_or(VertexId::new(0)))
1909 }
1910 Err(error) => Err(error),
1911 }
1912 }
1913
1914 pub fn set_range_values(
1918 &mut self,
1919 sheet_id: SheetId,
1920 start_row: u32,
1921 start_col: u32,
1922 values: &[Vec<LiteralValue>],
1923 ) -> Result<RangeSummary, EditorError> {
1924 let mut summary = RangeSummary::default();
1925
1926 self.begin_batch();
1927 self.graph.begin_deferred_dirty();
1931
1932 for (row_offset, row_values) in values.iter().enumerate() {
1933 for (col_offset, value) in row_values.iter().enumerate() {
1934 let row = start_row + row_offset as u32;
1935 let col = start_col + col_offset as u32;
1936 let cell_ref = self.graph.make_cell_ref_internal(sheet_id, row, col);
1937 let existing_id = self.graph.get_vertex_id_for_address(&cell_ref);
1938
1939 let id = self.set_cell_value(cell_ref, value.clone());
1940 match existing_id {
1941 Some(existing_id) => summary.vertices_updated.push(existing_id),
1942 None if id.0 != 0 => summary.vertices_created.push(id),
1943 None => {}
1944 }
1945 summary.cells_affected += 1;
1946 }
1947 }
1948
1949 let _ = self.graph.end_deferred_dirty();
1950 self.commit_batch();
1951
1952 Ok(summary)
1953 }
1954
1955 pub fn clear_range(
1957 &mut self,
1958 sheet_id: SheetId,
1959 start_row: u32,
1960 start_col: u32,
1961 end_row: u32,
1962 end_col: u32,
1963 ) -> Result<RangeSummary, EditorError> {
1964 let mut summary = RangeSummary::default();
1965
1966 self.begin_batch();
1967
1968 let vertices_in_range: Vec<_> = self
1970 .graph
1971 .grid_vertices_in_sheet(sheet_id)
1972 .filter(|(_, coord)| {
1973 let row = coord.row();
1974 let col = coord.col();
1975 row >= start_row && row <= end_row && col >= start_col && col <= end_col
1976 })
1977 .collect();
1978
1979 let mut vertex_cells = rustc_hash::FxHashSet::default();
1980 for (id, coord) in vertices_in_range {
1981 self.remove_vertex(id)?;
1982 vertex_cells.insert((coord.row(), coord.col()));
1983 summary.cells_affected += 1;
1984 }
1985 for (col, r0, r1) in
1988 self.graph
1989 .forget_extent_cells(sheet_id, (start_row, end_row), (start_col, end_col))
1990 {
1991 summary.cells_affected += (r0..=r1)
1992 .filter(|&row| !vertex_cells.contains(&(row, col)))
1993 .count();
1994 }
1995
1996 self.commit_batch();
1997
1998 Ok(summary)
1999 }
2000
2001 pub fn copy_range(
2003 &mut self,
2004 sheet_id: SheetId,
2005 from_start_row: u32,
2006 from_start_col: u32,
2007 from_end_row: u32,
2008 from_end_col: u32,
2009 to_sheet_id: SheetId,
2010 to_row: u32,
2011 to_col: u32,
2012 ) -> Result<RangeSummary, EditorError> {
2013 let row_offset = to_row as i32 - from_start_row as i32;
2014 let col_offset = to_col as i32 - from_start_col as i32;
2015
2016 let mut summary = RangeSummary::default();
2017 let mut cell_data = Vec::new();
2018
2019 let vertices_in_range: Vec<_> = self
2021 .graph
2022 .grid_vertices_in_sheet(sheet_id)
2023 .filter(|(_, coord)| {
2024 let row = coord.row();
2025 let col = coord.col();
2026 row >= from_start_row
2027 && row <= from_end_row
2028 && col >= from_start_col
2029 && col <= from_end_col
2030 })
2031 .collect();
2032
2033 for (id, coord) in vertices_in_range {
2034 let row = coord.row();
2035 let col = coord.col();
2036
2037 if let Some(formula) = self.get_formula_ast(id) {
2039 cell_data.push((
2040 row - from_start_row,
2041 col - from_start_col,
2042 CellData::Formula(formula),
2043 ));
2044 } else if let Some(value) = self.graph.get_value(id) {
2045 cell_data.push((
2046 row - from_start_row,
2047 col - from_start_col,
2048 CellData::Value(value),
2049 ));
2050 }
2051 }
2052
2053 self.begin_batch();
2054
2055 for (row_idx, col_idx, data) in cell_data {
2057 let dest_row = (to_row as i32 + row_idx as i32) as u32;
2058 let dest_col = (to_col as i32 + col_idx as i32) as u32;
2059
2060 match data {
2061 CellData::Value(value) => {
2062 let cell_ref =
2063 self.graph
2064 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
2065
2066 if let Some(existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
2067 self.graph.update_vertex_value(existing_id, value);
2068 self.graph.mark_vertex_dirty(existing_id);
2069 summary.vertices_updated.push(existing_id);
2070 } else {
2071 let meta =
2072 VertexMeta::new(dest_row, dest_col, to_sheet_id, VertexKind::Cell);
2073 let id = self.try_add_vertex(meta)?;
2074 self.graph.update_vertex_value(id, value);
2075 summary.vertices_created.push(id);
2076 }
2077 }
2078 CellData::Formula(formula) => {
2079 let adjuster = RelativeReferenceAdjuster::new(row_offset, col_offset);
2081 let adjusted = adjuster.adjust_formula(&formula);
2082
2083 let cell_ref =
2084 self.graph
2085 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
2086
2087 if let Some(existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
2088 self.graph.update_vertex_formula(existing_id, adjusted)?;
2089 summary.vertices_updated.push(existing_id);
2090 } else {
2091 let meta = VertexMeta::new(
2092 dest_row,
2093 dest_col,
2094 to_sheet_id,
2095 VertexKind::FormulaScalar,
2096 );
2097 let id = self.try_add_vertex(meta)?;
2098 self.graph.update_vertex_formula(id, adjusted)?;
2099 summary.vertices_created.push(id);
2100 }
2101 }
2102 }
2103
2104 summary.cells_affected += 1;
2105 }
2106
2107 self.commit_batch();
2108
2109 Ok(summary)
2110 }
2111
2112 pub fn move_range(
2114 &mut self,
2115 sheet_id: SheetId,
2116 from_start_row: u32,
2117 from_start_col: u32,
2118 from_end_row: u32,
2119 from_end_col: u32,
2120 to_sheet_id: SheetId,
2121 to_row: u32,
2122 to_col: u32,
2123 ) -> Result<RangeSummary, EditorError> {
2124 let result = self.move_range_impl(
2125 sheet_id,
2126 from_start_row,
2127 from_start_col,
2128 from_end_row,
2129 from_end_col,
2130 to_sheet_id,
2131 to_row,
2132 to_col,
2133 );
2134 self.graph.authority_end_structural();
2135 result
2136 }
2137
2138 fn move_range_impl(
2139 &mut self,
2140 sheet_id: SheetId,
2141 from_start_row: u32,
2142 from_start_col: u32,
2143 from_end_row: u32,
2144 from_end_col: u32,
2145 to_sheet_id: SheetId,
2146 to_row: u32,
2147 to_col: u32,
2148 ) -> Result<RangeSummary, EditorError> {
2149 self.graph.authority_note_structural(true);
2150 let mut summary = self.copy_range(
2152 sheet_id,
2153 from_start_row,
2154 from_start_col,
2155 from_end_row,
2156 from_end_col,
2157 to_sheet_id,
2158 to_row,
2159 to_col,
2160 )?;
2161
2162 let clear_summary = self.clear_range(
2164 sheet_id,
2165 from_start_row,
2166 from_start_col,
2167 from_end_row,
2168 from_end_col,
2169 )?;
2170
2171 summary.cells_moved = clear_summary.cells_affected;
2172
2173 let row_offset = to_row as i32 - from_start_row as i32;
2175 let col_offset = to_col as i32 - from_start_col as i32;
2176
2177 let all_formula_vertices: Vec<_> = self.graph.vertices_with_formulas().collect();
2179
2180 let from_sheet_name = self.graph.sheet_name(sheet_id).to_string();
2181 let to_sheet_name = self.graph.sheet_name(to_sheet_id).to_string();
2182 let adjuster = MoveReferenceAdjuster::new(
2183 sheet_id,
2184 from_sheet_name,
2185 from_start_row,
2186 from_start_col,
2187 from_end_row,
2188 from_end_col,
2189 to_sheet_id,
2190 to_sheet_name,
2191 row_offset,
2192 col_offset,
2193 );
2194
2195 for formula_id in all_formula_vertices {
2196 if let Some(formula) = self.get_formula_ast(formula_id) {
2197 let formula_sheet_id = self.graph.get_vertex_sheet_id(formula_id);
2198 if let Some(adjusted) = adjuster.adjust_if_references(&formula, formula_sheet_id) {
2199 self.graph.update_vertex_formula(formula_id, adjusted)?;
2200 }
2201 }
2202 }
2203
2204 Ok(summary)
2205 }
2206
2207 pub fn define_name(
2209 &mut self,
2210 name: &str,
2211 definition: NamedDefinition,
2212 scope: NameScope,
2213 ) -> Result<(), EditorError> {
2214 self.graph.define_name(name, definition.clone(), scope)?;
2215
2216 self.log_change(ChangeEvent::DefineName {
2217 name: name.to_string(),
2218 scope,
2219 definition,
2220 });
2221
2222 Ok(())
2223 }
2224
2225 pub fn define_name_for_cell(
2227 &mut self,
2228 name: &str,
2229 sheet_name: &str,
2230 row: u32,
2231 col: u32,
2232 scope: NameScope,
2233 ) -> Result<(), EditorError> {
2234 let sheet_id = self
2235 .graph
2236 .sheet_id(sheet_name)
2237 .ok_or_else(|| EditorError::InvalidName {
2238 name: sheet_name.to_string(),
2239 reason: "Sheet not found".to_string(),
2240 })?;
2241 let cell_ref = CellRef::new(sheet_id, Coord::from_excel(row, col, true, true));
2242 self.define_name(name, NamedDefinition::Cell(cell_ref), scope)
2243 }
2244
2245 pub fn define_name_for_range(
2247 &mut self,
2248 name: &str,
2249 sheet_name: &str,
2250 start_row: u32,
2251 start_col: u32,
2252 end_row: u32,
2253 end_col: u32,
2254 scope: NameScope,
2255 ) -> Result<(), EditorError> {
2256 let sheet_id = self
2257 .graph
2258 .sheet_id(sheet_name)
2259 .ok_or_else(|| EditorError::InvalidName {
2260 name: sheet_name.to_string(),
2261 reason: "Sheet not found".to_string(),
2262 })?;
2263 let start = CellRef::new(
2264 sheet_id,
2265 Coord::from_excel(start_row, start_col, true, true),
2266 );
2267 let end = CellRef::new(sheet_id, Coord::from_excel(end_row, end_col, true, true));
2268 let range_ref = crate::reference::RangeRef::new(start, end);
2269 self.define_name(name, NamedDefinition::Range(range_ref), scope)
2270 }
2271
2272 pub fn update_name(
2274 &mut self,
2275 name: &str,
2276 new_definition: NamedDefinition,
2277 scope: NameScope,
2278 ) -> Result<(), EditorError> {
2279 let old_definition = self
2281 .graph
2282 .resolve_name(
2283 name,
2284 match scope {
2285 NameScope::Sheet(id) => id,
2286 NameScope::Workbook => 0,
2287 },
2288 )
2289 .cloned();
2290
2291 self.graph
2292 .update_name(name, new_definition.clone(), scope)?;
2293
2294 if let Some(old_def) = old_definition {
2295 self.log_change(ChangeEvent::UpdateName {
2296 name: name.to_string(),
2297 scope,
2298 old_definition: old_def,
2299 new_definition,
2300 });
2301 }
2302
2303 Ok(())
2304 }
2305
2306 pub fn delete_name(&mut self, name: &str, scope: NameScope) -> Result<(), EditorError> {
2308 let old_def = if self.has_logger() {
2310 self.graph
2311 .resolve_name(
2312 name,
2313 match scope {
2314 NameScope::Sheet(id) => id,
2315 NameScope::Workbook => 0,
2316 },
2317 )
2318 .cloned()
2319 } else {
2320 None
2321 };
2322
2323 self.graph.delete_name(name, scope)?;
2324 self.log_change(ChangeEvent::DeleteName {
2325 name: name.to_string(),
2326 scope,
2327 old_definition: old_def,
2328 });
2329
2330 Ok(())
2331 }
2332}
2333
2334enum CellData {
2336 Value(LiteralValue),
2337 Formula(ASTNode),
2338}
2339
2340impl<'g> Drop for VertexEditor<'g> {
2341 fn drop(&mut self) {
2342 if self.batch_mode {
2344 self.commit_batch();
2345 }
2346 }
2347}
2348
2349#[cfg(test)]
2350mod tests {
2351 use super::*;
2352 use crate::engine::graph::editor::change_log::{ChangeEvent, ChangeLog};
2353 use crate::reference::Coord;
2354
2355 fn create_test_graph() -> DependencyGraph {
2356 DependencyGraph::new()
2357 }
2358
2359 #[test]
2360 fn test_vertex_editor_creation() {
2361 let mut graph = create_test_graph();
2362 let editor = VertexEditor::new(&mut graph);
2363 assert!(!editor.has_logger());
2364 assert!(!editor.batch_mode);
2365 }
2366
2367 #[test]
2368 fn test_vertex_editor_with_logger() {
2369 let mut graph = create_test_graph();
2370 let mut log = ChangeLog::new();
2371 let editor = VertexEditor::with_logger(&mut graph, &mut log);
2372 assert!(editor.has_logger());
2373 assert!(!editor.batch_mode);
2374 }
2375
2376 #[test]
2377 fn test_add_vertex() {
2378 let mut graph = create_test_graph();
2379 let mut editor = VertexEditor::new(&mut graph);
2380
2381 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell).dirty();
2382 let vertex_id = editor.add_vertex(meta);
2383
2384 assert!(vertex_id.0 > 0);
2386 }
2387
2388 #[test]
2389 fn test_batch_operations() {
2390 let mut graph = create_test_graph();
2391 let mut editor = VertexEditor::new(&mut graph);
2392
2393 assert!(!editor.batch_mode);
2394 editor.begin_batch();
2395 assert!(editor.batch_mode);
2396
2397 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2399 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::Cell);
2400
2401 let id1 = editor.add_vertex(meta1);
2402 let id2 = editor.add_vertex(meta2);
2403
2404 assert!(editor.add_edge(id1, id2));
2406
2407 editor.commit_batch();
2408 assert!(!editor.batch_mode);
2409 }
2410
2411 #[test]
2412 fn test_remove_vertex() {
2413 let mut graph = create_test_graph();
2414 let mut editor = VertexEditor::new(&mut graph);
2415
2416 let meta = VertexMeta::new(3, 4, 0, VertexKind::Cell).dirty();
2417 let vertex_id = editor.add_vertex(meta);
2418
2419 assert!(editor.remove_vertex(vertex_id).is_ok());
2421 }
2422
2423 #[test]
2424 fn test_remove_vertex_clears_spill_registry_for_anchor() {
2425 let mut graph = create_test_graph();
2426 let sheet_id = graph.sheet_id_mut("Sheet1");
2427
2428 let anchor_cell = CellRef::new(sheet_id, Coord::new(0, 0, true, true));
2430 let anchor_vid = {
2431 let mut editor = VertexEditor::new(&mut graph);
2432 editor.set_cell_formula(anchor_cell, formualizer_parse::parser::parse("=0").unwrap())
2435 };
2436
2437 let target_cells = vec![
2438 CellRef::new(sheet_id, Coord::new(0, 0, true, true)),
2439 CellRef::new(sheet_id, Coord::new(0, 1, true, true)),
2440 CellRef::new(sheet_id, Coord::new(1, 0, true, true)),
2441 CellRef::new(sheet_id, Coord::new(1, 1, true, true)),
2442 ];
2443 let values = vec![
2444 vec![LiteralValue::Number(1.0), LiteralValue::Number(2.0)],
2445 vec![LiteralValue::Number(3.0), LiteralValue::Number(4.0)],
2446 ];
2447
2448 graph
2449 .commit_spill_region_atomic_with_fault(anchor_vid, target_cells.clone(), values, None)
2450 .unwrap();
2451
2452 assert!(graph.spill_registry_has_anchor(anchor_vid));
2453 for cell in &target_cells {
2454 assert_eq!(
2455 graph.spill_registry_anchor_for_cell(*cell),
2456 Some(anchor_vid)
2457 );
2458 }
2459
2460 {
2461 let mut editor = VertexEditor::new(&mut graph);
2462 editor.remove_vertex(anchor_vid).unwrap();
2463 }
2464
2465 assert!(!graph.spill_registry_has_anchor(anchor_vid));
2466 for cell in &target_cells {
2467 assert_eq!(graph.spill_registry_anchor_for_cell(*cell), None);
2468 }
2469 assert_eq!(graph.spill_registry_counts(), (0, 0));
2470 }
2471
2472 #[test]
2473 fn test_edge_operations() {
2474 let mut graph = create_test_graph();
2475 let mut editor = VertexEditor::new(&mut graph);
2476
2477 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2478 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::FormulaScalar);
2479
2480 let id1 = editor.add_vertex(meta1);
2481 let id2 = editor.add_vertex(meta2);
2482
2483 assert!(editor.add_edge(id1, id2));
2485
2486 assert!(!editor.add_edge(id1, id1));
2488
2489 assert!(editor.remove_edge(id1, id2));
2491 }
2492
2493 #[test]
2494 fn test_set_cell_value() {
2495 let mut graph = create_test_graph();
2496 let mut log = ChangeLog::new();
2497
2498 let cell_ref = CellRef {
2499 sheet_id: 0,
2500 coord: Coord::new(2, 3, true, true),
2501 };
2502 let value = LiteralValue::Number(42.0);
2503
2504 let vertex_id = {
2505 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2506 editor.set_cell_value(cell_ref, value.clone())
2507 };
2508
2509 assert_eq!(vertex_id.0, 0);
2511
2512 assert_eq!(log.len(), 1);
2514 match &log.events()[0] {
2515 ChangeEvent::SetValue { addr, new, .. } => {
2516 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
2517 assert_eq!(addr.coord.row(), cell_ref.coord.row());
2518 assert_eq!(addr.coord.col(), cell_ref.coord.col());
2519 assert_eq!(new, &value);
2520 }
2521 _ => panic!("Expected SetValue event"),
2522 }
2523 }
2524
2525 #[test]
2526 fn test_set_cell_formula() {
2527 let mut graph = create_test_graph();
2528 let mut log = ChangeLog::new();
2529
2530 let cell_ref = CellRef {
2531 sheet_id: 0,
2532 coord: Coord::new(1, 1, true, true),
2533 };
2534
2535 use formualizer_parse::parser::ASTNodeType;
2536 let formula = formualizer_parse::parser::ASTNode {
2537 node_type: ASTNodeType::Literal(LiteralValue::Number(100.0)),
2538 source_token: None,
2539 contains_volatile: false,
2540 };
2541
2542 let vertex_id = {
2543 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2544 editor.set_cell_formula(cell_ref, formula.clone())
2545 };
2546
2547 assert!(vertex_id.0 > 0);
2549
2550 assert_eq!(log.len(), 1);
2552 match &log.events()[0] {
2553 ChangeEvent::SetFormula { addr, .. } => {
2554 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
2555 assert_eq!(addr.coord.row(), cell_ref.coord.row());
2556 assert_eq!(addr.coord.col(), cell_ref.coord.col());
2557 }
2558 _ => panic!("Expected SetFormula event"),
2559 }
2560 }
2561
2562 #[test]
2563 fn test_shift_rows() {
2564 let mut graph = create_test_graph();
2565 let mut log = ChangeLog::new();
2566
2567 {
2568 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2569
2570 let cell1 = CellRef {
2572 sheet_id: 0,
2573 coord: Coord::new(5, 1, true, true),
2574 };
2575 let cell2 = CellRef {
2576 sheet_id: 0,
2577 coord: Coord::new(10, 1, true, true),
2578 };
2579 let cell3 = CellRef {
2580 sheet_id: 0,
2581 coord: Coord::new(15, 1, true, true),
2582 };
2583
2584 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
2585 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
2586 editor.set_cell_value(cell3, LiteralValue::Number(3.0));
2587 }
2588
2589 log.clear();
2591
2592 {
2593 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2594 editor.shift_rows(0, 10, 2);
2596 }
2597
2598 assert_eq!(log.len(), 1);
2600 match &log.events()[0] {
2601 ChangeEvent::SetValue { addr, new, .. } => {
2602 assert_eq!(addr.sheet_id, 0);
2603 assert_eq!(addr.coord.row(), 10);
2604 if let LiteralValue::Text(msg) = new {
2605 assert!(msg.contains("Row shift"));
2606 assert!(msg.contains("start=10"));
2607 assert!(msg.contains("delta=2"));
2608 }
2609 }
2610 _ => panic!("Expected SetValue event for row shift"),
2611 }
2612 }
2613
2614 #[test]
2615 fn test_shift_columns() {
2616 let mut graph = create_test_graph();
2617 let mut log = ChangeLog::new();
2618
2619 {
2620 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2621
2622 let cell1 = CellRef {
2624 sheet_id: 0,
2625 coord: Coord::new(1, 5, true, true),
2626 };
2627 let cell2 = CellRef {
2628 sheet_id: 0,
2629 coord: Coord::new(1, 10, true, true),
2630 };
2631
2632 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
2633 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
2634 }
2635
2636 log.clear();
2638
2639 {
2640 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2641 editor.shift_columns(0, 8, 3);
2643 }
2644
2645 assert_eq!(log.len(), 1);
2647 match &log.events()[0] {
2648 ChangeEvent::SetValue { addr, new, .. } => {
2649 assert_eq!(addr.sheet_id, 0);
2650 assert_eq!(addr.coord.col(), 8);
2651 if let LiteralValue::Text(msg) = new {
2652 assert!(msg.contains("Column shift"));
2653 assert!(msg.contains("start=8"));
2654 assert!(msg.contains("delta=3"));
2655 }
2656 }
2657 _ => panic!("Expected SetValue event for column shift"),
2658 }
2659 }
2660
2661 #[test]
2662 fn test_move_vertex() {
2663 let mut graph = create_test_graph();
2664 let mut editor = VertexEditor::new(&mut graph);
2665
2666 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell);
2667 let vertex_id = editor.add_vertex(meta);
2668
2669 assert!(editor.move_vertex(vertex_id, GridAddr::new(8, 12)).is_ok());
2671
2672 assert!(editor.move_vertex(vertex_id, GridAddr::new(8, 12)).is_ok());
2674 }
2675
2676 #[test]
2677 fn test_vertex_meta_builder() {
2678 let meta = VertexMeta::new(1, 2, 3, VertexKind::FormulaScalar)
2679 .dirty()
2680 .volatile()
2681 .with_flags(0x08);
2682
2683 assert_eq!(meta.coord.row(), 1);
2684 assert_eq!(meta.coord.col(), 2);
2685 assert_eq!(meta.sheet_id, 3);
2686 assert_eq!(meta.kind, VertexKind::FormulaScalar);
2687 assert_eq!(meta.flags, 0x08); }
2689
2690 #[test]
2691 fn test_change_log_management() {
2692 let mut graph = create_test_graph();
2693 let mut log = ChangeLog::new();
2694
2695 {
2696 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2697 let cell_ref = CellRef {
2698 sheet_id: 0,
2699 coord: Coord::new(0, 0, true, true),
2700 };
2701 editor.set_cell_value(cell_ref, LiteralValue::Number(1.0));
2702 editor.set_cell_value(cell_ref, LiteralValue::Number(2.0));
2703 }
2704
2705 assert_eq!(log.len(), 2);
2706
2707 log.clear();
2708 assert_eq!(log.len(), 0);
2709 }
2710
2711 #[test]
2712 fn test_editor_drop_commits_batch() {
2713 let mut graph = create_test_graph();
2714 {
2715 let mut editor = VertexEditor::new(&mut graph);
2716 editor.begin_batch();
2717
2718 let meta = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2719 editor.add_vertex(meta);
2720
2721 }
2723
2724 }
2726}