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