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