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 let range_dependents = self
1013 .graph
1014 .compressed_range_dependents_intersecting_deleted_rows(
1015 sheet_id,
1016 start,
1017 start.saturating_add(count).saturating_sub(1).max(start),
1018 );
1019 self.graph.mark_dirty_many(&range_dependents);
1020
1021 for id in vertices_to_delete {
1022 self.remove_vertex(id)?;
1023 summary.vertices_deleted.push(id);
1024 }
1025 let vertices_to_shift: Vec<(VertexId, AbsCoord)> = self
1027 .graph
1028 .vertices_in_sheet(sheet_id)
1029 .filter_map(|id| {
1030 let coord = self.graph.get_coord(id);
1031 if coord.row() >= start + count {
1032 Some((id, coord))
1033 } else {
1034 None
1035 }
1036 })
1037 .collect();
1038
1039 for (id, old_coord) in vertices_to_shift {
1040 let new_coord = AbsCoord::new(old_coord.row() - count, old_coord.col());
1041 if self.has_logger() {
1042 self.log_change(ChangeEvent::VertexMoved {
1043 id,
1044 sheet_id,
1045 old_coord,
1046 new_coord,
1047 });
1048 }
1049 self.move_vertex(id, new_coord)?;
1050 summary.vertices_moved.push(id);
1051 }
1052
1053 let op = ShiftOperation::DeleteRows {
1055 sheet_id,
1056 start,
1057 count,
1058 };
1059 let adjuster = ReferenceAdjuster::new();
1060
1061 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1062
1063 for id in formula_vertices {
1064 if let Some(ast) = self.get_formula_ast(id)
1065 && let Some(adjusted) = adjuster.adjust_ast_if_changed_in_context(
1066 &ast,
1067 &op,
1068 &ReferenceContext::new(self.graph.get_sheet_id(id), self.graph.sheet_reg()),
1069 )
1070 {
1071 if self.has_logger() {
1072 self.log_change(ChangeEvent::FormulaAdjusted {
1073 id,
1074 addr: self.graph.get_cell_ref_for_vertex(id),
1075 old_ast: ast.clone(),
1076 new_ast: adjusted.clone(),
1077 });
1078 }
1079 self.graph.update_vertex_formula(id, adjusted)?;
1080 self.graph.mark_vertex_dirty(id);
1081 summary.formulas_updated += 1;
1082 }
1083 }
1084
1085 let old_names = if self.has_logger() {
1087 Some(self.snapshot_named_definitions())
1088 } else {
1089 None
1090 };
1091 self.graph.adjust_named_ranges(&op)?;
1092 if let Some(old_names) = old_names {
1093 let new_names = self.snapshot_named_definitions();
1094 for ((scope, name), old_definition) in old_names {
1095 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1096 && *new_definition != old_definition
1097 {
1098 self.log_change(ChangeEvent::NamedRangeAdjusted {
1099 name,
1100 scope,
1101 old_definition,
1102 new_definition: new_definition.clone(),
1103 });
1104 }
1105 }
1106 }
1107
1108 if let Some(logger) = &mut self.change_logger {
1110 logger.end_compound();
1111 }
1112
1113 self.commit_batch();
1114
1115 Ok(summary)
1116 }
1117
1118 pub fn insert_columns(
1120 &mut self,
1121 sheet_id: SheetId,
1122 before: u32,
1123 count: u32,
1124 ) -> Result<ShiftSummary, EditorError> {
1125 if count == 0 {
1126 return Ok(ShiftSummary::default());
1127 }
1128
1129 let mut summary = ShiftSummary::default();
1130
1131 self.begin_batch();
1133
1134 let vertices_to_shift: Vec<(VertexId, AbsCoord)> = self
1136 .graph
1137 .vertices_in_sheet(sheet_id)
1138 .filter_map(|id| {
1139 let coord = self.graph.get_coord(id);
1140 if coord.col() >= before {
1141 Some((id, coord))
1142 } else {
1143 None
1144 }
1145 })
1146 .collect();
1147
1148 if let Some(logger) = &mut self.change_logger {
1149 logger.begin_compound(format!(
1150 "InsertColumns sheet={sheet_id} before={before} count={count}"
1151 ));
1152 }
1153 for (id, old_coord) in vertices_to_shift {
1155 let new_coord = AbsCoord::new(old_coord.row(), old_coord.col() + count);
1156 if self.has_logger() {
1157 self.log_change(ChangeEvent::VertexMoved {
1158 id,
1159 sheet_id,
1160 old_coord,
1161 new_coord,
1162 });
1163 }
1164 self.move_vertex(id, new_coord)?;
1165 summary.vertices_moved.push(id);
1166 }
1167
1168 let op = ShiftOperation::InsertColumns {
1170 sheet_id,
1171 before,
1172 count,
1173 };
1174 let adjuster = ReferenceAdjuster::new();
1175
1176 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1178
1179 for id in formula_vertices {
1180 if let Some(ast) = self.get_formula_ast(id)
1181 && let Some(adjusted) = adjuster.adjust_ast_if_changed_in_context(
1182 &ast,
1183 &op,
1184 &ReferenceContext::new(self.graph.get_sheet_id(id), self.graph.sheet_reg()),
1185 )
1186 {
1187 if self.has_logger() {
1188 self.log_change(ChangeEvent::FormulaAdjusted {
1189 id,
1190 addr: self.graph.get_cell_ref_for_vertex(id),
1191 old_ast: ast.clone(),
1192 new_ast: adjusted.clone(),
1193 });
1194 }
1195 self.graph.update_vertex_formula(id, adjusted)?;
1196 self.graph.mark_vertex_dirty(id);
1197 summary.formulas_updated += 1;
1198 }
1199 }
1200
1201 let old_names = if self.has_logger() {
1203 Some(self.snapshot_named_definitions())
1204 } else {
1205 None
1206 };
1207 self.graph.adjust_named_ranges(&op)?;
1208 if let Some(old_names) = old_names {
1209 let new_names = self.snapshot_named_definitions();
1210 for ((scope, name), old_definition) in old_names {
1211 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1212 && *new_definition != old_definition
1213 {
1214 self.log_change(ChangeEvent::NamedRangeAdjusted {
1215 name,
1216 scope,
1217 old_definition,
1218 new_definition: new_definition.clone(),
1219 });
1220 }
1221 }
1222 }
1223
1224 if let Some(logger) = &mut self.change_logger {
1226 logger.end_compound();
1227 }
1228
1229 self.commit_batch();
1230
1231 Ok(summary)
1232 }
1233
1234 pub fn delete_columns(
1236 &mut self,
1237 sheet_id: SheetId,
1238 start: u32,
1239 count: u32,
1240 ) -> Result<ShiftSummary, EditorError> {
1241 if count == 0 {
1242 return Ok(ShiftSummary::default());
1243 }
1244
1245 let mut summary = ShiftSummary::default();
1246
1247 self.begin_batch();
1248
1249 if let Some(logger) = &mut self.change_logger {
1250 logger.begin_compound(format!(
1251 "DeleteColumns sheet={sheet_id} start={start} count={count}"
1252 ));
1253 }
1254
1255 let vertices_to_delete: Vec<VertexId> = self
1257 .graph
1258 .vertices_in_sheet(sheet_id)
1259 .filter(|&id| {
1260 let coord = self.graph.get_coord(id);
1261 coord.col() >= start && coord.col() < start + count
1262 })
1263 .collect();
1264 let range_dependents = self
1265 .graph
1266 .compressed_range_dependents_intersecting_deleted_columns(
1267 sheet_id,
1268 start,
1269 start.saturating_add(count).saturating_sub(1).max(start),
1270 );
1271 self.graph.mark_dirty_many(&range_dependents);
1272
1273 for id in vertices_to_delete {
1274 self.remove_vertex(id)?;
1275 summary.vertices_deleted.push(id);
1276 }
1277 let vertices_to_shift: Vec<(VertexId, AbsCoord)> = self
1279 .graph
1280 .vertices_in_sheet(sheet_id)
1281 .filter_map(|id| {
1282 let coord = self.graph.get_coord(id);
1283 if coord.col() >= start + count {
1284 Some((id, coord))
1285 } else {
1286 None
1287 }
1288 })
1289 .collect();
1290
1291 for (id, old_coord) in vertices_to_shift {
1292 let new_coord = AbsCoord::new(old_coord.row(), old_coord.col() - count);
1293 if self.has_logger() {
1294 self.log_change(ChangeEvent::VertexMoved {
1295 id,
1296 sheet_id,
1297 old_coord,
1298 new_coord,
1299 });
1300 }
1301 self.move_vertex(id, new_coord)?;
1302 summary.vertices_moved.push(id);
1303 }
1304
1305 let op = ShiftOperation::DeleteColumns {
1307 sheet_id,
1308 start,
1309 count,
1310 };
1311 let adjuster = ReferenceAdjuster::new();
1312
1313 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1314
1315 for id in formula_vertices {
1316 if let Some(ast) = self.get_formula_ast(id)
1317 && let Some(adjusted) = adjuster.adjust_ast_if_changed_in_context(
1318 &ast,
1319 &op,
1320 &ReferenceContext::new(self.graph.get_sheet_id(id), self.graph.sheet_reg()),
1321 )
1322 {
1323 if self.has_logger() {
1324 self.log_change(ChangeEvent::FormulaAdjusted {
1325 id,
1326 addr: self.graph.get_cell_ref_for_vertex(id),
1327 old_ast: ast.clone(),
1328 new_ast: adjusted.clone(),
1329 });
1330 }
1331 self.graph.update_vertex_formula(id, adjusted)?;
1332 self.graph.mark_vertex_dirty(id);
1333 summary.formulas_updated += 1;
1334 }
1335 }
1336
1337 let old_names = if self.has_logger() {
1339 Some(self.snapshot_named_definitions())
1340 } else {
1341 None
1342 };
1343 self.graph.adjust_named_ranges(&op)?;
1344 if let Some(old_names) = old_names {
1345 let new_names = self.snapshot_named_definitions();
1346 for ((scope, name), old_definition) in old_names {
1347 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1348 && *new_definition != old_definition
1349 {
1350 self.log_change(ChangeEvent::NamedRangeAdjusted {
1351 name,
1352 scope,
1353 old_definition,
1354 new_definition: new_definition.clone(),
1355 });
1356 }
1357 }
1358 }
1359
1360 if let Some(logger) = &mut self.change_logger {
1362 logger.end_compound();
1363 }
1364
1365 self.commit_batch();
1366
1367 Ok(summary)
1368 }
1369
1370 pub fn shift_rows(&mut self, sheet_id: SheetId, start_row: u32, delta: i32) {
1372 if delta == 0 {
1373 return;
1374 }
1375
1376 let change_event = ChangeEvent::SetValue {
1378 addr: CellRef {
1379 sheet_id,
1380 coord: Coord::new(start_row, 0, true, true),
1381 },
1382 old_value: None,
1383 old_formula: None,
1384 new: LiteralValue::Text(format!("Row shift: start={start_row}, delta={delta}")),
1385 };
1386 self.log_change(change_event);
1387
1388 }
1391
1392 pub fn shift_columns(&mut self, sheet_id: SheetId, start_col: u32, delta: i32) {
1394 if delta == 0 {
1395 return;
1396 }
1397
1398 let change_event = ChangeEvent::SetValue {
1400 addr: CellRef {
1401 sheet_id,
1402 coord: Coord::new(0, start_col, true, true),
1403 },
1404 old_value: None,
1405 old_formula: None,
1406 new: LiteralValue::Text(format!("Column shift: start={start_col}, delta={delta}")),
1407 };
1408 self.log_change(change_event);
1409
1410 }
1413
1414 pub fn set_cell_value(&mut self, cell_ref: CellRef, value: LiteralValue) -> VertexId {
1416 self.set_cell_value_with_old_state(cell_ref, value, None, None)
1417 }
1418
1419 pub fn set_cell_value_with_old_state(
1429 &mut self,
1430 cell_ref: CellRef,
1431 value: LiteralValue,
1432 fallback_old_value: Option<LiteralValue>,
1433 fallback_old_formula: Option<ASTNode>,
1434 ) -> VertexId {
1435 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1436
1437 let old_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1440 let old_value = old_id
1441 .and_then(|id| self.graph.get_value(id))
1442 .or(fallback_old_value);
1443 let old_formula = old_id
1444 .and_then(|id| self.get_formula_ast(id))
1445 .or(fallback_old_formula);
1446
1447 let spill_snapshot =
1450 old_id.and_then(|id| self.snapshot_spill_for_anchor(id).map(|s| (id, s)));
1451 let did_spill_clear = spill_snapshot.is_some();
1452 if let Some((anchor, old_spill)) = spill_snapshot {
1453 if let Some(logger) = &mut self.change_logger {
1454 logger.begin_compound(format!(
1455 "SetValueWithSpillClear sheet={} row={} col={}",
1456 cell_ref.sheet_id,
1457 cell_ref.coord.row(),
1458 cell_ref.coord.col()
1459 ));
1460 }
1461 self.graph.clear_spill_region(anchor);
1462 self.log_change(ChangeEvent::SpillCleared {
1463 anchor,
1464 old: old_spill,
1465 });
1466 }
1467
1468 match self.graph.set_cell_value(
1471 &sheet_name,
1472 cell_ref.coord.row() + 1,
1473 cell_ref.coord.col() + 1,
1474 value.clone(),
1475 ) {
1476 Ok(summary) => {
1477 let change_event = ChangeEvent::SetValue {
1479 addr: cell_ref,
1480 old_value,
1481 old_formula,
1482 new: value,
1483 };
1484 self.log_change(change_event);
1485
1486 if did_spill_clear && let Some(logger) = &mut self.change_logger {
1487 logger.end_compound();
1488 }
1489
1490 summary
1491 .affected_vertices
1492 .into_iter()
1493 .next()
1494 .unwrap_or(VertexId::new(0))
1495 }
1496 Err(_) => VertexId::new(0),
1497 }
1498 }
1499
1500 pub fn set_cell_formula(&mut self, cell_ref: CellRef, formula: ASTNode) -> VertexId {
1502 self.set_cell_formula_with_old_state(cell_ref, formula, None, None)
1503 }
1504
1505 pub fn set_cell_formula_with_old_state(
1511 &mut self,
1512 cell_ref: CellRef,
1513 formula: ASTNode,
1514 fallback_old_value: Option<LiteralValue>,
1515 fallback_old_formula: Option<ASTNode>,
1516 ) -> VertexId {
1517 self.set_cell_formula_with_old_state_and_plan(
1518 cell_ref,
1519 formula,
1520 fallback_old_value,
1521 fallback_old_formula,
1522 None,
1523 )
1524 }
1525
1526 pub(crate) fn set_cell_formula_with_prepared_plan(
1527 &mut self,
1528 cell_ref: CellRef,
1529 formula: ASTNode,
1530 fallback_old_value: Option<LiteralValue>,
1531 fallback_old_formula: Option<ASTNode>,
1532 ast_id: crate::engine::arena::AstNodeId,
1533 plan: crate::engine::ingest_pipeline::DependencyPlanRow,
1534 ) -> VertexId {
1535 self.set_cell_formula_with_old_state_and_plan(
1536 cell_ref,
1537 formula,
1538 fallback_old_value,
1539 fallback_old_formula,
1540 Some((ast_id, plan)),
1541 )
1542 }
1543
1544 fn set_cell_formula_with_old_state_and_plan(
1545 &mut self,
1546 cell_ref: CellRef,
1547 formula: ASTNode,
1548 fallback_old_value: Option<LiteralValue>,
1549 fallback_old_formula: Option<ASTNode>,
1550 prepared: Option<(
1551 crate::engine::arena::AstNodeId,
1552 crate::engine::ingest_pipeline::DependencyPlanRow,
1553 )>,
1554 ) -> VertexId {
1555 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1556
1557 let old_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1560 let old_value = old_id
1561 .and_then(|id| self.graph.get_value(id))
1562 .or(fallback_old_value);
1563 let old_formula = old_id
1564 .and_then(|id| self.get_formula_ast(id))
1565 .or(fallback_old_formula);
1566
1567 let spill_snapshot =
1569 old_id.and_then(|id| self.snapshot_spill_for_anchor(id).map(|s| (id, s)));
1570 let did_spill_clear = spill_snapshot.is_some();
1571 if let Some((anchor, old_spill)) = spill_snapshot {
1572 if let Some(logger) = &mut self.change_logger {
1573 logger.begin_compound(format!(
1574 "SetFormulaWithSpillClear sheet={} row={} col={}",
1575 cell_ref.sheet_id,
1576 cell_ref.coord.row(),
1577 cell_ref.coord.col()
1578 ));
1579 }
1580 self.graph.clear_spill_region(anchor);
1581 self.log_change(ChangeEvent::SpillCleared {
1582 anchor,
1583 old: old_spill,
1584 });
1585 }
1586
1587 let result = if let Some((ast_id, plan)) = prepared {
1589 self.graph.set_cell_formula_with_plan(
1590 &sheet_name,
1591 cell_ref.coord.row() + 1,
1592 cell_ref.coord.col() + 1,
1593 ast_id,
1594 &plan,
1595 plan.volatile,
1596 plan.dynamic,
1597 )
1598 } else {
1599 self.graph.set_cell_formula(
1600 &sheet_name,
1601 cell_ref.coord.row() + 1,
1602 cell_ref.coord.col() + 1,
1603 formula.clone(),
1604 )
1605 };
1606 match result {
1607 Ok(summary) => {
1608 let change_event = ChangeEvent::SetFormula {
1610 addr: cell_ref,
1611 old_value,
1612 old_formula,
1613 new: formula,
1614 };
1615 self.log_change(change_event);
1616
1617 if did_spill_clear && let Some(logger) = &mut self.change_logger {
1618 logger.end_compound();
1619 }
1620
1621 summary
1622 .affected_vertices
1623 .into_iter()
1624 .next()
1625 .unwrap_or(VertexId::new(0))
1626 }
1627 Err(_) => VertexId::new(0),
1628 }
1629 }
1630
1631 pub fn set_range_values(
1635 &mut self,
1636 sheet_id: SheetId,
1637 start_row: u32,
1638 start_col: u32,
1639 values: &[Vec<LiteralValue>],
1640 ) -> Result<RangeSummary, EditorError> {
1641 let mut summary = RangeSummary::default();
1642
1643 self.begin_batch();
1644 self.graph.begin_deferred_dirty();
1648
1649 for (row_offset, row_values) in values.iter().enumerate() {
1650 for (col_offset, value) in row_values.iter().enumerate() {
1651 let row = start_row + row_offset as u32;
1652 let col = start_col + col_offset as u32;
1653 let cell_ref = self.graph.make_cell_ref_internal(sheet_id, row, col);
1654 let existing_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1655
1656 let id = self.set_cell_value(cell_ref, value.clone());
1657 match existing_id {
1658 Some(existing_id) => summary.vertices_updated.push(existing_id),
1659 None if id.0 != 0 => summary.vertices_created.push(id),
1660 None => {}
1661 }
1662 summary.cells_affected += 1;
1663 }
1664 }
1665
1666 let _ = self.graph.end_deferred_dirty();
1667 self.commit_batch();
1668
1669 Ok(summary)
1670 }
1671
1672 pub fn clear_range(
1674 &mut self,
1675 sheet_id: SheetId,
1676 start_row: u32,
1677 start_col: u32,
1678 end_row: u32,
1679 end_col: u32,
1680 ) -> Result<RangeSummary, EditorError> {
1681 let mut summary = RangeSummary::default();
1682
1683 self.begin_batch();
1684
1685 let vertices_in_range: Vec<_> = self
1687 .graph
1688 .vertices_in_sheet(sheet_id)
1689 .filter(|&id| {
1690 let coord = self.graph.get_coord(id);
1691 let row = coord.row();
1692 let col = coord.col();
1693 row >= start_row && row <= end_row && col >= start_col && col <= end_col
1694 })
1695 .collect();
1696
1697 for id in vertices_in_range {
1698 self.remove_vertex(id)?;
1699 summary.cells_affected += 1;
1700 }
1701
1702 self.commit_batch();
1703
1704 Ok(summary)
1705 }
1706
1707 pub fn copy_range(
1709 &mut self,
1710 sheet_id: SheetId,
1711 from_start_row: u32,
1712 from_start_col: u32,
1713 from_end_row: u32,
1714 from_end_col: u32,
1715 to_sheet_id: SheetId,
1716 to_row: u32,
1717 to_col: u32,
1718 ) -> Result<RangeSummary, EditorError> {
1719 let row_offset = to_row as i32 - from_start_row as i32;
1720 let col_offset = to_col as i32 - from_start_col as i32;
1721
1722 let mut summary = RangeSummary::default();
1723 let mut cell_data = Vec::new();
1724
1725 let vertices_in_range: Vec<_> = self
1727 .graph
1728 .vertices_in_sheet(sheet_id)
1729 .filter(|&id| {
1730 let coord = self.graph.get_coord(id);
1731 let row = coord.row();
1732 let col = coord.col();
1733 row >= from_start_row
1734 && row <= from_end_row
1735 && col >= from_start_col
1736 && col <= from_end_col
1737 })
1738 .collect();
1739
1740 for id in vertices_in_range {
1741 let coord = self.graph.get_coord(id);
1742 let row = coord.row();
1743 let col = coord.col();
1744
1745 if let Some(formula) = self.get_formula_ast(id) {
1747 cell_data.push((
1748 row - from_start_row,
1749 col - from_start_col,
1750 CellData::Formula(formula),
1751 ));
1752 } else if let Some(value) = self.graph.get_value(id) {
1753 cell_data.push((
1754 row - from_start_row,
1755 col - from_start_col,
1756 CellData::Value(value),
1757 ));
1758 }
1759 }
1760
1761 self.begin_batch();
1762
1763 for (row_idx, col_idx, data) in cell_data {
1765 let dest_row = (to_row as i32 + row_idx as i32) as u32;
1766 let dest_col = (to_col as i32 + col_idx as i32) as u32;
1767
1768 match data {
1769 CellData::Value(value) => {
1770 let cell_ref =
1771 self.graph
1772 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
1773
1774 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1775 self.graph.update_vertex_value(existing_id, value);
1776 self.graph.mark_vertex_dirty(existing_id);
1777 summary.vertices_updated.push(existing_id);
1778 } else {
1779 let meta =
1780 VertexMeta::new(dest_row, dest_col, to_sheet_id, VertexKind::Cell);
1781 let id = self.try_add_vertex(meta)?;
1782 self.graph.update_vertex_value(id, value);
1783 summary.vertices_created.push(id);
1784 }
1785 }
1786 CellData::Formula(formula) => {
1787 let adjuster = RelativeReferenceAdjuster::new(row_offset, col_offset);
1789 let adjusted = adjuster.adjust_formula(&formula);
1790
1791 let cell_ref =
1792 self.graph
1793 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
1794
1795 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1796 self.graph.update_vertex_formula(existing_id, adjusted)?;
1797 summary.vertices_updated.push(existing_id);
1798 } else {
1799 let meta = VertexMeta::new(
1800 dest_row,
1801 dest_col,
1802 to_sheet_id,
1803 VertexKind::FormulaScalar,
1804 );
1805 let id = self.try_add_vertex(meta)?;
1806 self.graph.update_vertex_formula(id, adjusted)?;
1807 summary.vertices_created.push(id);
1808 }
1809 }
1810 }
1811
1812 summary.cells_affected += 1;
1813 }
1814
1815 self.commit_batch();
1816
1817 Ok(summary)
1818 }
1819
1820 pub fn move_range(
1822 &mut self,
1823 sheet_id: SheetId,
1824 from_start_row: u32,
1825 from_start_col: u32,
1826 from_end_row: u32,
1827 from_end_col: u32,
1828 to_sheet_id: SheetId,
1829 to_row: u32,
1830 to_col: u32,
1831 ) -> Result<RangeSummary, EditorError> {
1832 let mut summary = self.copy_range(
1834 sheet_id,
1835 from_start_row,
1836 from_start_col,
1837 from_end_row,
1838 from_end_col,
1839 to_sheet_id,
1840 to_row,
1841 to_col,
1842 )?;
1843
1844 let clear_summary = self.clear_range(
1846 sheet_id,
1847 from_start_row,
1848 from_start_col,
1849 from_end_row,
1850 from_end_col,
1851 )?;
1852
1853 summary.cells_moved = clear_summary.cells_affected;
1854
1855 let row_offset = to_row as i32 - from_start_row as i32;
1857 let col_offset = to_col as i32 - from_start_col as i32;
1858
1859 let all_formula_vertices: Vec<_> = self.graph.vertices_with_formulas().collect();
1861
1862 let from_sheet_name = self.graph.sheet_name(sheet_id).to_string();
1863 let to_sheet_name = self.graph.sheet_name(to_sheet_id).to_string();
1864 let adjuster = MoveReferenceAdjuster::new(
1865 sheet_id,
1866 from_sheet_name,
1867 from_start_row,
1868 from_start_col,
1869 from_end_row,
1870 from_end_col,
1871 to_sheet_id,
1872 to_sheet_name,
1873 row_offset,
1874 col_offset,
1875 );
1876
1877 for formula_id in all_formula_vertices {
1878 if let Some(formula) = self.get_formula_ast(formula_id) {
1879 let formula_sheet_id = self.graph.get_vertex_sheet_id(formula_id);
1880 if let Some(adjusted) = adjuster.adjust_if_references(&formula, formula_sheet_id) {
1881 self.graph.update_vertex_formula(formula_id, adjusted)?;
1882 }
1883 }
1884 }
1885
1886 Ok(summary)
1887 }
1888
1889 pub fn define_name(
1891 &mut self,
1892 name: &str,
1893 definition: NamedDefinition,
1894 scope: NameScope,
1895 ) -> Result<(), EditorError> {
1896 self.graph.define_name(name, definition.clone(), scope)?;
1897
1898 self.log_change(ChangeEvent::DefineName {
1899 name: name.to_string(),
1900 scope,
1901 definition,
1902 });
1903
1904 Ok(())
1905 }
1906
1907 pub fn define_name_for_cell(
1909 &mut self,
1910 name: &str,
1911 sheet_name: &str,
1912 row: u32,
1913 col: u32,
1914 scope: NameScope,
1915 ) -> Result<(), EditorError> {
1916 let sheet_id = self
1917 .graph
1918 .sheet_id(sheet_name)
1919 .ok_or_else(|| EditorError::InvalidName {
1920 name: sheet_name.to_string(),
1921 reason: "Sheet not found".to_string(),
1922 })?;
1923 let cell_ref = CellRef::new(sheet_id, Coord::from_excel(row, col, true, true));
1924 self.define_name(name, NamedDefinition::Cell(cell_ref), scope)
1925 }
1926
1927 pub fn define_name_for_range(
1929 &mut self,
1930 name: &str,
1931 sheet_name: &str,
1932 start_row: u32,
1933 start_col: u32,
1934 end_row: u32,
1935 end_col: u32,
1936 scope: NameScope,
1937 ) -> Result<(), EditorError> {
1938 let sheet_id = self
1939 .graph
1940 .sheet_id(sheet_name)
1941 .ok_or_else(|| EditorError::InvalidName {
1942 name: sheet_name.to_string(),
1943 reason: "Sheet not found".to_string(),
1944 })?;
1945 let start = CellRef::new(
1946 sheet_id,
1947 Coord::from_excel(start_row, start_col, true, true),
1948 );
1949 let end = CellRef::new(sheet_id, Coord::from_excel(end_row, end_col, true, true));
1950 let range_ref = crate::reference::RangeRef::new(start, end);
1951 self.define_name(name, NamedDefinition::Range(range_ref), scope)
1952 }
1953
1954 pub fn update_name(
1956 &mut self,
1957 name: &str,
1958 new_definition: NamedDefinition,
1959 scope: NameScope,
1960 ) -> Result<(), EditorError> {
1961 let old_definition = self
1963 .graph
1964 .resolve_name(
1965 name,
1966 match scope {
1967 NameScope::Sheet(id) => id,
1968 NameScope::Workbook => 0,
1969 },
1970 )
1971 .cloned();
1972
1973 self.graph
1974 .update_name(name, new_definition.clone(), scope)?;
1975
1976 if let Some(old_def) = old_definition {
1977 self.log_change(ChangeEvent::UpdateName {
1978 name: name.to_string(),
1979 scope,
1980 old_definition: old_def,
1981 new_definition,
1982 });
1983 }
1984
1985 Ok(())
1986 }
1987
1988 pub fn delete_name(&mut self, name: &str, scope: NameScope) -> Result<(), EditorError> {
1990 let old_def = if self.has_logger() {
1992 self.graph
1993 .resolve_name(
1994 name,
1995 match scope {
1996 NameScope::Sheet(id) => id,
1997 NameScope::Workbook => 0,
1998 },
1999 )
2000 .cloned()
2001 } else {
2002 None
2003 };
2004
2005 self.graph.delete_name(name, scope)?;
2006 self.log_change(ChangeEvent::DeleteName {
2007 name: name.to_string(),
2008 scope,
2009 old_definition: old_def,
2010 });
2011
2012 Ok(())
2013 }
2014}
2015
2016enum CellData {
2018 Value(LiteralValue),
2019 Formula(ASTNode),
2020}
2021
2022impl<'g> Drop for VertexEditor<'g> {
2023 fn drop(&mut self) {
2024 if self.batch_mode {
2026 self.commit_batch();
2027 }
2028 }
2029}
2030
2031#[cfg(test)]
2032mod tests {
2033 use super::*;
2034 use crate::engine::graph::editor::change_log::{ChangeEvent, ChangeLog};
2035 use crate::reference::Coord;
2036
2037 fn create_test_graph() -> DependencyGraph {
2038 DependencyGraph::new()
2039 }
2040
2041 #[test]
2042 fn test_vertex_editor_creation() {
2043 let mut graph = create_test_graph();
2044 let editor = VertexEditor::new(&mut graph);
2045 assert!(!editor.has_logger());
2046 assert!(!editor.batch_mode);
2047 }
2048
2049 #[test]
2050 fn test_vertex_editor_with_logger() {
2051 let mut graph = create_test_graph();
2052 let mut log = ChangeLog::new();
2053 let editor = VertexEditor::with_logger(&mut graph, &mut log);
2054 assert!(editor.has_logger());
2055 assert!(!editor.batch_mode);
2056 }
2057
2058 #[test]
2059 fn test_add_vertex() {
2060 let mut graph = create_test_graph();
2061 let mut editor = VertexEditor::new(&mut graph);
2062
2063 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell).dirty();
2064 let vertex_id = editor.add_vertex(meta);
2065
2066 assert!(vertex_id.0 > 0);
2068 }
2069
2070 #[test]
2071 fn test_batch_operations() {
2072 let mut graph = create_test_graph();
2073 let mut editor = VertexEditor::new(&mut graph);
2074
2075 assert!(!editor.batch_mode);
2076 editor.begin_batch();
2077 assert!(editor.batch_mode);
2078
2079 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2081 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::Cell);
2082
2083 let id1 = editor.add_vertex(meta1);
2084 let id2 = editor.add_vertex(meta2);
2085
2086 assert!(editor.add_edge(id1, id2));
2088
2089 editor.commit_batch();
2090 assert!(!editor.batch_mode);
2091 }
2092
2093 #[test]
2094 fn test_remove_vertex() {
2095 let mut graph = create_test_graph();
2096 let mut editor = VertexEditor::new(&mut graph);
2097
2098 let meta = VertexMeta::new(3, 4, 0, VertexKind::Cell).dirty();
2099 let vertex_id = editor.add_vertex(meta);
2100
2101 assert!(editor.remove_vertex(vertex_id).is_ok());
2103 }
2104
2105 #[test]
2106 fn test_remove_vertex_clears_spill_registry_for_anchor() {
2107 let mut graph = create_test_graph();
2108 let sheet_id = graph.sheet_id_mut("Sheet1");
2109
2110 let anchor_cell = CellRef::new(sheet_id, Coord::new(0, 0, true, true));
2112 let anchor_vid = {
2113 let mut editor = VertexEditor::new(&mut graph);
2114 editor.set_cell_value(anchor_cell, LiteralValue::Number(0.0))
2115 };
2116
2117 let target_cells = vec![
2118 CellRef::new(sheet_id, Coord::new(0, 0, true, true)),
2119 CellRef::new(sheet_id, Coord::new(0, 1, true, true)),
2120 CellRef::new(sheet_id, Coord::new(1, 0, true, true)),
2121 CellRef::new(sheet_id, Coord::new(1, 1, true, true)),
2122 ];
2123 let values = vec![
2124 vec![LiteralValue::Number(1.0), LiteralValue::Number(2.0)],
2125 vec![LiteralValue::Number(3.0), LiteralValue::Number(4.0)],
2126 ];
2127
2128 graph
2129 .commit_spill_region_atomic_with_fault(anchor_vid, target_cells.clone(), values, None)
2130 .unwrap();
2131
2132 assert!(graph.spill_registry_has_anchor(anchor_vid));
2133 for cell in &target_cells {
2134 assert_eq!(
2135 graph.spill_registry_anchor_for_cell(*cell),
2136 Some(anchor_vid)
2137 );
2138 }
2139
2140 {
2141 let mut editor = VertexEditor::new(&mut graph);
2142 editor.remove_vertex(anchor_vid).unwrap();
2143 }
2144
2145 assert!(!graph.spill_registry_has_anchor(anchor_vid));
2146 for cell in &target_cells {
2147 assert_eq!(graph.spill_registry_anchor_for_cell(*cell), None);
2148 }
2149 assert_eq!(graph.spill_registry_counts(), (0, 0));
2150 }
2151
2152 #[test]
2153 fn test_edge_operations() {
2154 let mut graph = create_test_graph();
2155 let mut editor = VertexEditor::new(&mut graph);
2156
2157 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2158 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::FormulaScalar);
2159
2160 let id1 = editor.add_vertex(meta1);
2161 let id2 = editor.add_vertex(meta2);
2162
2163 assert!(editor.add_edge(id1, id2));
2165
2166 assert!(!editor.add_edge(id1, id1));
2168
2169 assert!(editor.remove_edge(id1, id2));
2171 }
2172
2173 #[test]
2174 fn test_set_cell_value() {
2175 let mut graph = create_test_graph();
2176 let mut log = ChangeLog::new();
2177
2178 let cell_ref = CellRef {
2179 sheet_id: 0,
2180 coord: Coord::new(2, 3, true, true),
2181 };
2182 let value = LiteralValue::Number(42.0);
2183
2184 let vertex_id = {
2185 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2186 editor.set_cell_value(cell_ref, value.clone())
2187 };
2188
2189 assert!(vertex_id.0 > 0);
2191
2192 assert_eq!(log.len(), 1);
2194 match &log.events()[0] {
2195 ChangeEvent::SetValue { addr, new, .. } => {
2196 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
2197 assert_eq!(addr.coord.row(), cell_ref.coord.row());
2198 assert_eq!(addr.coord.col(), cell_ref.coord.col());
2199 assert_eq!(new, &value);
2200 }
2201 _ => panic!("Expected SetValue event"),
2202 }
2203 }
2204
2205 #[test]
2206 fn test_set_cell_formula() {
2207 let mut graph = create_test_graph();
2208 let mut log = ChangeLog::new();
2209
2210 let cell_ref = CellRef {
2211 sheet_id: 0,
2212 coord: Coord::new(1, 1, true, true),
2213 };
2214
2215 use formualizer_parse::parser::ASTNodeType;
2216 let formula = formualizer_parse::parser::ASTNode {
2217 node_type: ASTNodeType::Literal(LiteralValue::Number(100.0)),
2218 source_token: None,
2219 contains_volatile: false,
2220 };
2221
2222 let vertex_id = {
2223 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2224 editor.set_cell_formula(cell_ref, formula.clone())
2225 };
2226
2227 assert!(vertex_id.0 > 0);
2229
2230 assert_eq!(log.len(), 1);
2232 match &log.events()[0] {
2233 ChangeEvent::SetFormula { addr, .. } => {
2234 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
2235 assert_eq!(addr.coord.row(), cell_ref.coord.row());
2236 assert_eq!(addr.coord.col(), cell_ref.coord.col());
2237 }
2238 _ => panic!("Expected SetFormula event"),
2239 }
2240 }
2241
2242 #[test]
2243 fn test_shift_rows() {
2244 let mut graph = create_test_graph();
2245 let mut log = ChangeLog::new();
2246
2247 {
2248 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2249
2250 let cell1 = CellRef {
2252 sheet_id: 0,
2253 coord: Coord::new(5, 1, true, true),
2254 };
2255 let cell2 = CellRef {
2256 sheet_id: 0,
2257 coord: Coord::new(10, 1, true, true),
2258 };
2259 let cell3 = CellRef {
2260 sheet_id: 0,
2261 coord: Coord::new(15, 1, true, true),
2262 };
2263
2264 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
2265 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
2266 editor.set_cell_value(cell3, LiteralValue::Number(3.0));
2267 }
2268
2269 log.clear();
2271
2272 {
2273 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2274 editor.shift_rows(0, 10, 2);
2276 }
2277
2278 assert_eq!(log.len(), 1);
2280 match &log.events()[0] {
2281 ChangeEvent::SetValue { addr, new, .. } => {
2282 assert_eq!(addr.sheet_id, 0);
2283 assert_eq!(addr.coord.row(), 10);
2284 if let LiteralValue::Text(msg) = new {
2285 assert!(msg.contains("Row shift"));
2286 assert!(msg.contains("start=10"));
2287 assert!(msg.contains("delta=2"));
2288 }
2289 }
2290 _ => panic!("Expected SetValue event for row shift"),
2291 }
2292 }
2293
2294 #[test]
2295 fn test_shift_columns() {
2296 let mut graph = create_test_graph();
2297 let mut log = ChangeLog::new();
2298
2299 {
2300 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2301
2302 let cell1 = CellRef {
2304 sheet_id: 0,
2305 coord: Coord::new(1, 5, true, true),
2306 };
2307 let cell2 = CellRef {
2308 sheet_id: 0,
2309 coord: Coord::new(1, 10, true, true),
2310 };
2311
2312 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
2313 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
2314 }
2315
2316 log.clear();
2318
2319 {
2320 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2321 editor.shift_columns(0, 8, 3);
2323 }
2324
2325 assert_eq!(log.len(), 1);
2327 match &log.events()[0] {
2328 ChangeEvent::SetValue { addr, new, .. } => {
2329 assert_eq!(addr.sheet_id, 0);
2330 assert_eq!(addr.coord.col(), 8);
2331 if let LiteralValue::Text(msg) = new {
2332 assert!(msg.contains("Column shift"));
2333 assert!(msg.contains("start=8"));
2334 assert!(msg.contains("delta=3"));
2335 }
2336 }
2337 _ => panic!("Expected SetValue event for column shift"),
2338 }
2339 }
2340
2341 #[test]
2342 fn test_move_vertex() {
2343 let mut graph = create_test_graph();
2344 let mut editor = VertexEditor::new(&mut graph);
2345
2346 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell);
2347 let vertex_id = editor.add_vertex(meta);
2348
2349 assert!(editor.move_vertex(vertex_id, AbsCoord::new(8, 12)).is_ok());
2351
2352 assert!(editor.move_vertex(vertex_id, AbsCoord::new(8, 12)).is_ok());
2354 }
2355
2356 #[test]
2357 fn test_vertex_meta_builder() {
2358 let meta = VertexMeta::new(1, 2, 3, VertexKind::FormulaScalar)
2359 .dirty()
2360 .volatile()
2361 .with_flags(0x08);
2362
2363 assert_eq!(meta.coord.row(), 1);
2364 assert_eq!(meta.coord.col(), 2);
2365 assert_eq!(meta.sheet_id, 3);
2366 assert_eq!(meta.kind, VertexKind::FormulaScalar);
2367 assert_eq!(meta.flags, 0x08); }
2369
2370 #[test]
2371 fn test_change_log_management() {
2372 let mut graph = create_test_graph();
2373 let mut log = ChangeLog::new();
2374
2375 {
2376 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2377 let cell_ref = CellRef {
2378 sheet_id: 0,
2379 coord: Coord::new(0, 0, true, true),
2380 };
2381 editor.set_cell_value(cell_ref, LiteralValue::Number(1.0));
2382 editor.set_cell_value(cell_ref, LiteralValue::Number(2.0));
2383 }
2384
2385 assert_eq!(log.len(), 2);
2386
2387 log.clear();
2388 assert_eq!(log.len(), 0);
2389 }
2390
2391 #[test]
2392 fn test_editor_drop_commits_batch() {
2393 let mut graph = create_test_graph();
2394 {
2395 let mut editor = VertexEditor::new(&mut graph);
2396 editor.begin_batch();
2397
2398 let meta = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2399 editor.add_vertex(meta);
2400
2401 }
2403
2404 }
2406}