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::StagedFormulaStateChanged { .. } => {
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);
667
668 let (
670 old_value,
671 old_formula,
672 old_dependencies,
673 old_dependents,
674 coord,
675 sheet_id_opt,
676 kind,
677 flags,
678 ) = if self.has_logger() {
679 let coord = self.graph.get_coord(id);
680 let sheet_id = self.graph.get_sheet_id(id);
681 let kind = self.graph.get_vertex_kind(id);
682 let flags = 0u8;
684 (
685 self.graph.get_value(id),
686 self.get_formula_ast(id),
687 self.graph.get_dependencies(id), dependents.clone(), Some(coord),
690 Some(sheet_id),
691 Some(kind),
692 Some(flags),
693 )
694 } else {
695 (None, None, vec![], vec![], None, None, None, None)
696 };
697
698 if let Some(cell_ref) = self.graph.get_cell_ref_for_vertex(id) {
700 self.graph.remove_cell_mapping(&cell_ref);
701 }
702
703 self.graph.vertex_formulas.remove(&id);
707 self.graph.vertex_values.remove(&id);
708 self.graph.dirty_vertices.remove(&id);
709 self.graph.mark_volatile(id, false);
710 self.graph.store.set_kind(id, VertexKind::Empty);
711 self.graph.store.set_dynamic(id, false);
712
713 self.graph.remove_all_edges(id);
715
716 for dep_id in &dependents {
718 self.graph.mark_as_ref_error(*dep_id);
719 }
720
721 self.graph.mark_deleted(id, true);
723
724 self.log_change(ChangeEvent::RemoveVertex {
726 id,
727 old_value,
728 old_formula,
729 old_dependencies,
730 old_dependents,
731 coord,
732 sheet_id: sheet_id_opt,
733 kind,
734 flags,
735 });
736
737 if did_spill_clear && let Some(logger) = &mut self.change_logger {
738 logger.end_compound();
739 }
740
741 Ok(())
742 }
743
744 pub fn remove_vertex_at(&mut self, cell: CellRef) -> Result<(), EditorError> {
746 if let Some(id) = self.graph.get_vertex_for_cell(&cell) {
747 self.remove_vertex(id)
748 } else {
749 Ok(())
750 }
751 }
752
753 pub fn move_vertex(&mut self, id: VertexId, new_coord: AbsCoord) -> Result<(), EditorError> {
755 if !self.graph.vertex_exists(id) {
757 return Err(EditorError::Excel(
758 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
759 ));
760 }
761
762 let old_cell_ref = self.graph.get_cell_ref_for_vertex(id);
764
765 let sheet_id = self.graph.get_sheet_id(id);
767 let new_cell_ref = CellRef::new(
768 sheet_id,
769 Coord::new(new_coord.row(), new_coord.col(), true, true),
770 );
771
772 self.graph.set_coord(id, new_coord);
774
775 self.graph.update_edge_coord(id, new_coord);
777
778 self.graph
780 .update_cell_mapping(id, old_cell_ref, new_cell_ref);
781
782 self.graph.mark_dependents_dirty(id);
784
785 Ok(())
786 }
787
788 pub fn patch_vertex_meta(
790 &mut self,
791 id: VertexId,
792 patch: VertexMetaPatch,
793 ) -> Result<MetaUpdateSummary, EditorError> {
794 if !self.graph.vertex_exists(id) {
795 return Err(EditorError::Excel(
796 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
797 ));
798 }
799
800 let mut summary = MetaUpdateSummary::default();
801
802 if let Some(coord) = patch.coord {
803 self.graph.set_coord(id, coord);
804 self.graph.update_edge_coord(id, coord);
805 summary.coord_changed = true;
806 }
807
808 if let Some(kind) = patch.kind {
809 self.graph.set_kind(id, kind);
810 summary.kind_changed = true;
811 }
812
813 if let Some(dirty) = patch.dirty {
814 self.graph.set_dirty(id, dirty);
815 summary.flags_changed = true;
816 }
817
818 if let Some(volatile) = patch.volatile {
819 self.graph.mark_volatile(id, volatile);
820 summary.flags_changed = true;
821 }
822
823 Ok(summary)
824 }
825
826 pub fn patch_vertex_data(
828 &mut self,
829 id: VertexId,
830 patch: VertexDataPatch,
831 ) -> Result<DataUpdateSummary, EditorError> {
832 if !self.graph.vertex_exists(id) {
833 return Err(EditorError::Excel(
834 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
835 ));
836 }
837
838 let mut summary = DataUpdateSummary::default();
839
840 if let Some(value) = patch.value {
841 self.graph.update_vertex_value(id, value);
842 summary.value_changed = true;
843
844 if self.graph.edges_delta_size() > 0 {
847 self.graph.rebuild_edges();
848 }
849
850 let dependents = self.graph.get_dependents(id);
852 for dep in &dependents {
853 self.graph.set_dirty(*dep, true);
854 }
855 summary.dependents_marked_dirty = dependents;
856 }
857
858 if let Some(_formula) = patch.formula {
859 summary.formula_changed = true;
862 }
863
864 Ok(summary)
865 }
866
867 pub fn add_edge(&mut self, from: VertexId, to: VertexId) -> bool {
869 if from == to {
870 return false; }
872
873 true
876 }
877
878 pub fn remove_edge(&mut self, _from: VertexId, _to: VertexId) -> bool {
880 true
882 }
883
884 pub fn insert_rows(
886 &mut self,
887 sheet_id: SheetId,
888 before: u32,
889 count: u32,
890 ) -> Result<ShiftSummary, EditorError> {
891 if count == 0 {
892 return Ok(ShiftSummary::default());
893 }
894
895 let mut summary = ShiftSummary::default();
896
897 self.begin_batch();
899
900 let vertices_to_shift: Vec<(VertexId, AbsCoord)> = self
902 .graph
903 .vertices_in_sheet(sheet_id)
904 .filter_map(|id| {
905 let coord = self.graph.get_coord(id);
906 if coord.row() >= before {
907 Some((id, coord))
908 } else {
909 None
910 }
911 })
912 .collect();
913
914 if let Some(logger) = &mut self.change_logger {
915 logger.begin_compound(format!(
916 "InsertRows sheet={sheet_id} before={before} count={count}"
917 ));
918 }
919 for (id, old_coord) in vertices_to_shift {
921 let new_coord = AbsCoord::new(old_coord.row() + count, old_coord.col());
922 if self.has_logger() {
923 self.log_change(ChangeEvent::VertexMoved {
924 id,
925 sheet_id,
926 old_coord,
927 new_coord,
928 });
929 }
930 self.move_vertex(id, new_coord)?;
931 summary.vertices_moved.push(id);
932 }
933
934 let op = ShiftOperation::InsertRows {
936 sheet_id,
937 before,
938 count,
939 };
940 let adjuster = ReferenceAdjuster::new();
941
942 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
944
945 for id in formula_vertices {
946 if let Some(ast) = self.get_formula_ast(id) {
947 let adjusted = adjuster.adjust_ast(&ast, &op);
948 if format!("{ast:?}") != format!("{adjusted:?}") {
950 if self.has_logger() {
951 self.log_change(ChangeEvent::FormulaAdjusted {
952 id,
953 addr: self.graph.get_cell_ref_for_vertex(id),
954 old_ast: ast.clone(),
955 new_ast: adjusted.clone(),
956 });
957 }
958 self.graph.update_vertex_formula(id, adjusted)?;
959 self.graph.mark_vertex_dirty(id);
960 summary.formulas_updated += 1;
961 }
962 }
963 }
964
965 let old_names = if self.has_logger() {
967 Some(self.snapshot_named_definitions())
968 } else {
969 None
970 };
971 self.graph.adjust_named_ranges(&op)?;
972 if let Some(old_names) = old_names {
973 let new_names = self.snapshot_named_definitions();
974 for ((scope, name), old_definition) in old_names {
975 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
976 && *new_definition != old_definition
977 {
978 self.log_change(ChangeEvent::NamedRangeAdjusted {
979 name,
980 scope,
981 old_definition,
982 new_definition: new_definition.clone(),
983 });
984 }
985 }
986 }
987
988 if let Some(logger) = &mut self.change_logger {
990 logger.end_compound();
991 }
992
993 self.commit_batch();
994
995 Ok(summary)
996 }
997
998 pub fn delete_rows(
1000 &mut self,
1001 sheet_id: SheetId,
1002 start: u32,
1003 count: u32,
1004 ) -> Result<ShiftSummary, EditorError> {
1005 if count == 0 {
1006 return Ok(ShiftSummary::default());
1007 }
1008
1009 let mut summary = ShiftSummary::default();
1010
1011 self.begin_batch();
1012
1013 if let Some(logger) = &mut self.change_logger {
1014 logger.begin_compound(format!(
1015 "DeleteRows sheet={sheet_id} start={start} count={count}"
1016 ));
1017 }
1018
1019 let vertices_to_delete: Vec<VertexId> = self
1021 .graph
1022 .vertices_in_sheet(sheet_id)
1023 .filter(|&id| {
1024 let coord = self.graph.get_coord(id);
1025 coord.row() >= start && coord.row() < start + count
1026 })
1027 .collect();
1028
1029 for id in vertices_to_delete {
1030 self.remove_vertex(id)?;
1031 summary.vertices_deleted.push(id);
1032 }
1033 let vertices_to_shift: Vec<(VertexId, AbsCoord)> = self
1035 .graph
1036 .vertices_in_sheet(sheet_id)
1037 .filter_map(|id| {
1038 let coord = self.graph.get_coord(id);
1039 if coord.row() >= start + count {
1040 Some((id, coord))
1041 } else {
1042 None
1043 }
1044 })
1045 .collect();
1046
1047 for (id, old_coord) in vertices_to_shift {
1048 let new_coord = AbsCoord::new(old_coord.row() - count, old_coord.col());
1049 if self.has_logger() {
1050 self.log_change(ChangeEvent::VertexMoved {
1051 id,
1052 sheet_id,
1053 old_coord,
1054 new_coord,
1055 });
1056 }
1057 self.move_vertex(id, new_coord)?;
1058 summary.vertices_moved.push(id);
1059 }
1060
1061 let op = ShiftOperation::DeleteRows {
1063 sheet_id,
1064 start,
1065 count,
1066 };
1067 let adjuster = ReferenceAdjuster::new();
1068
1069 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1070
1071 for id in formula_vertices {
1072 if let Some(ast) = self.get_formula_ast(id) {
1073 let adjusted = adjuster.adjust_ast(&ast, &op);
1074 if format!("{ast:?}") != format!("{adjusted:?}") {
1075 if self.has_logger() {
1076 self.log_change(ChangeEvent::FormulaAdjusted {
1077 id,
1078 addr: self.graph.get_cell_ref_for_vertex(id),
1079 old_ast: ast.clone(),
1080 new_ast: adjusted.clone(),
1081 });
1082 }
1083 self.graph.update_vertex_formula(id, adjusted)?;
1084 self.graph.mark_vertex_dirty(id);
1085 summary.formulas_updated += 1;
1086 }
1087 }
1088 }
1089
1090 let old_names = if self.has_logger() {
1092 Some(self.snapshot_named_definitions())
1093 } else {
1094 None
1095 };
1096 self.graph.adjust_named_ranges(&op)?;
1097 if let Some(old_names) = old_names {
1098 let new_names = self.snapshot_named_definitions();
1099 for ((scope, name), old_definition) in old_names {
1100 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1101 && *new_definition != old_definition
1102 {
1103 self.log_change(ChangeEvent::NamedRangeAdjusted {
1104 name,
1105 scope,
1106 old_definition,
1107 new_definition: new_definition.clone(),
1108 });
1109 }
1110 }
1111 }
1112
1113 if let Some(logger) = &mut self.change_logger {
1115 logger.end_compound();
1116 }
1117
1118 self.commit_batch();
1119
1120 Ok(summary)
1121 }
1122
1123 pub fn insert_columns(
1125 &mut self,
1126 sheet_id: SheetId,
1127 before: u32,
1128 count: u32,
1129 ) -> Result<ShiftSummary, EditorError> {
1130 if count == 0 {
1131 return Ok(ShiftSummary::default());
1132 }
1133
1134 let mut summary = ShiftSummary::default();
1135
1136 self.begin_batch();
1138
1139 let vertices_to_shift: Vec<(VertexId, AbsCoord)> = self
1141 .graph
1142 .vertices_in_sheet(sheet_id)
1143 .filter_map(|id| {
1144 let coord = self.graph.get_coord(id);
1145 if coord.col() >= before {
1146 Some((id, coord))
1147 } else {
1148 None
1149 }
1150 })
1151 .collect();
1152
1153 if let Some(logger) = &mut self.change_logger {
1154 logger.begin_compound(format!(
1155 "InsertColumns sheet={sheet_id} before={before} count={count}"
1156 ));
1157 }
1158 for (id, old_coord) in vertices_to_shift {
1160 let new_coord = AbsCoord::new(old_coord.row(), old_coord.col() + count);
1161 if self.has_logger() {
1162 self.log_change(ChangeEvent::VertexMoved {
1163 id,
1164 sheet_id,
1165 old_coord,
1166 new_coord,
1167 });
1168 }
1169 self.move_vertex(id, new_coord)?;
1170 summary.vertices_moved.push(id);
1171 }
1172
1173 let op = ShiftOperation::InsertColumns {
1175 sheet_id,
1176 before,
1177 count,
1178 };
1179 let adjuster = ReferenceAdjuster::new();
1180
1181 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1183
1184 for id in formula_vertices {
1185 if let Some(ast) = self.get_formula_ast(id)
1186 && let Some(adjusted) = adjuster.adjust_ast_if_changed(&ast, &op)
1187 {
1188 if self.has_logger() {
1189 self.log_change(ChangeEvent::FormulaAdjusted {
1190 id,
1191 addr: self.graph.get_cell_ref_for_vertex(id),
1192 old_ast: ast.clone(),
1193 new_ast: adjusted.clone(),
1194 });
1195 }
1196 self.graph.update_vertex_formula(id, adjusted)?;
1197 self.graph.mark_vertex_dirty(id);
1198 summary.formulas_updated += 1;
1199 }
1200 }
1201
1202 let old_names = if self.has_logger() {
1204 Some(self.snapshot_named_definitions())
1205 } else {
1206 None
1207 };
1208 self.graph.adjust_named_ranges(&op)?;
1209 if let Some(old_names) = old_names {
1210 let new_names = self.snapshot_named_definitions();
1211 for ((scope, name), old_definition) in old_names {
1212 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1213 && *new_definition != old_definition
1214 {
1215 self.log_change(ChangeEvent::NamedRangeAdjusted {
1216 name,
1217 scope,
1218 old_definition,
1219 new_definition: new_definition.clone(),
1220 });
1221 }
1222 }
1223 }
1224
1225 if let Some(logger) = &mut self.change_logger {
1227 logger.end_compound();
1228 }
1229
1230 self.commit_batch();
1231
1232 Ok(summary)
1233 }
1234
1235 pub fn delete_columns(
1237 &mut self,
1238 sheet_id: SheetId,
1239 start: u32,
1240 count: u32,
1241 ) -> Result<ShiftSummary, EditorError> {
1242 if count == 0 {
1243 return Ok(ShiftSummary::default());
1244 }
1245
1246 let mut summary = ShiftSummary::default();
1247
1248 self.begin_batch();
1249
1250 if let Some(logger) = &mut self.change_logger {
1251 logger.begin_compound(format!(
1252 "DeleteColumns sheet={sheet_id} start={start} count={count}"
1253 ));
1254 }
1255
1256 let vertices_to_delete: Vec<VertexId> = self
1258 .graph
1259 .vertices_in_sheet(sheet_id)
1260 .filter(|&id| {
1261 let coord = self.graph.get_coord(id);
1262 coord.col() >= start && coord.col() < start + count
1263 })
1264 .collect();
1265
1266 for id in vertices_to_delete {
1267 self.remove_vertex(id)?;
1268 summary.vertices_deleted.push(id);
1269 }
1270 let vertices_to_shift: Vec<(VertexId, AbsCoord)> = self
1272 .graph
1273 .vertices_in_sheet(sheet_id)
1274 .filter_map(|id| {
1275 let coord = self.graph.get_coord(id);
1276 if coord.col() >= start + count {
1277 Some((id, coord))
1278 } else {
1279 None
1280 }
1281 })
1282 .collect();
1283
1284 for (id, old_coord) in vertices_to_shift {
1285 let new_coord = AbsCoord::new(old_coord.row(), old_coord.col() - count);
1286 if self.has_logger() {
1287 self.log_change(ChangeEvent::VertexMoved {
1288 id,
1289 sheet_id,
1290 old_coord,
1291 new_coord,
1292 });
1293 }
1294 self.move_vertex(id, new_coord)?;
1295 summary.vertices_moved.push(id);
1296 }
1297
1298 let op = ShiftOperation::DeleteColumns {
1300 sheet_id,
1301 start,
1302 count,
1303 };
1304 let adjuster = ReferenceAdjuster::new();
1305
1306 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
1307
1308 for id in formula_vertices {
1309 if let Some(ast) = self.get_formula_ast(id)
1310 && let Some(adjusted) = adjuster.adjust_ast_if_changed(&ast, &op)
1311 {
1312 if self.has_logger() {
1313 self.log_change(ChangeEvent::FormulaAdjusted {
1314 id,
1315 addr: self.graph.get_cell_ref_for_vertex(id),
1316 old_ast: ast.clone(),
1317 new_ast: adjusted.clone(),
1318 });
1319 }
1320 self.graph.update_vertex_formula(id, adjusted)?;
1321 self.graph.mark_vertex_dirty(id);
1322 summary.formulas_updated += 1;
1323 }
1324 }
1325
1326 let old_names = if self.has_logger() {
1328 Some(self.snapshot_named_definitions())
1329 } else {
1330 None
1331 };
1332 self.graph.adjust_named_ranges(&op)?;
1333 if let Some(old_names) = old_names {
1334 let new_names = self.snapshot_named_definitions();
1335 for ((scope, name), old_definition) in old_names {
1336 if let Some(new_definition) = new_names.get(&(scope, name.clone()))
1337 && *new_definition != old_definition
1338 {
1339 self.log_change(ChangeEvent::NamedRangeAdjusted {
1340 name,
1341 scope,
1342 old_definition,
1343 new_definition: new_definition.clone(),
1344 });
1345 }
1346 }
1347 }
1348
1349 if let Some(logger) = &mut self.change_logger {
1351 logger.end_compound();
1352 }
1353
1354 self.commit_batch();
1355
1356 Ok(summary)
1357 }
1358
1359 pub fn shift_rows(&mut self, sheet_id: SheetId, start_row: u32, delta: i32) {
1361 if delta == 0 {
1362 return;
1363 }
1364
1365 let change_event = ChangeEvent::SetValue {
1367 addr: CellRef {
1368 sheet_id,
1369 coord: Coord::new(start_row, 0, true, true),
1370 },
1371 old_value: None,
1372 old_formula: None,
1373 new: LiteralValue::Text(format!("Row shift: start={start_row}, delta={delta}")),
1374 };
1375 self.log_change(change_event);
1376
1377 }
1380
1381 pub fn shift_columns(&mut self, sheet_id: SheetId, start_col: u32, delta: i32) {
1383 if delta == 0 {
1384 return;
1385 }
1386
1387 let change_event = ChangeEvent::SetValue {
1389 addr: CellRef {
1390 sheet_id,
1391 coord: Coord::new(0, start_col, true, true),
1392 },
1393 old_value: None,
1394 old_formula: None,
1395 new: LiteralValue::Text(format!("Column shift: start={start_col}, delta={delta}")),
1396 };
1397 self.log_change(change_event);
1398
1399 }
1402
1403 pub fn set_cell_value(&mut self, cell_ref: CellRef, value: LiteralValue) -> VertexId {
1405 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1406
1407 let old_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1409 let old_value = old_id.and_then(|id| self.graph.get_value(id));
1410 let old_formula = old_id.and_then(|id| self.get_formula_ast(id));
1411
1412 let spill_snapshot =
1415 old_id.and_then(|id| self.snapshot_spill_for_anchor(id).map(|s| (id, s)));
1416 let did_spill_clear = spill_snapshot.is_some();
1417 if let Some((anchor, old_spill)) = spill_snapshot {
1418 if let Some(logger) = &mut self.change_logger {
1419 logger.begin_compound(format!(
1420 "SetValueWithSpillClear sheet={} row={} col={}",
1421 cell_ref.sheet_id,
1422 cell_ref.coord.row(),
1423 cell_ref.coord.col()
1424 ));
1425 }
1426 self.graph.clear_spill_region(anchor);
1427 self.log_change(ChangeEvent::SpillCleared {
1428 anchor,
1429 old: old_spill,
1430 });
1431 }
1432
1433 match self.graph.set_cell_value(
1436 &sheet_name,
1437 cell_ref.coord.row() + 1,
1438 cell_ref.coord.col() + 1,
1439 value.clone(),
1440 ) {
1441 Ok(summary) => {
1442 let change_event = ChangeEvent::SetValue {
1444 addr: cell_ref,
1445 old_value,
1446 old_formula,
1447 new: value,
1448 };
1449 self.log_change(change_event);
1450
1451 if did_spill_clear && let Some(logger) = &mut self.change_logger {
1452 logger.end_compound();
1453 }
1454
1455 summary
1456 .affected_vertices
1457 .into_iter()
1458 .next()
1459 .unwrap_or(VertexId::new(0))
1460 }
1461 Err(_) => VertexId::new(0),
1462 }
1463 }
1464
1465 pub fn set_cell_formula(&mut self, cell_ref: CellRef, formula: ASTNode) -> VertexId {
1467 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1468
1469 let old_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1471 let old_value = old_id.and_then(|id| self.graph.get_value(id));
1472 let old_formula = old_id.and_then(|id| self.get_formula_ast(id));
1473
1474 let spill_snapshot =
1476 old_id.and_then(|id| self.snapshot_spill_for_anchor(id).map(|s| (id, s)));
1477 let did_spill_clear = spill_snapshot.is_some();
1478 if let Some((anchor, old_spill)) = spill_snapshot {
1479 if let Some(logger) = &mut self.change_logger {
1480 logger.begin_compound(format!(
1481 "SetFormulaWithSpillClear sheet={} row={} col={}",
1482 cell_ref.sheet_id,
1483 cell_ref.coord.row(),
1484 cell_ref.coord.col()
1485 ));
1486 }
1487 self.graph.clear_spill_region(anchor);
1488 self.log_change(ChangeEvent::SpillCleared {
1489 anchor,
1490 old: old_spill,
1491 });
1492 }
1493
1494 match self.graph.set_cell_formula(
1497 &sheet_name,
1498 cell_ref.coord.row() + 1,
1499 cell_ref.coord.col() + 1,
1500 formula.clone(),
1501 ) {
1502 Ok(summary) => {
1503 let change_event = ChangeEvent::SetFormula {
1505 addr: cell_ref,
1506 old_value,
1507 old_formula,
1508 new: formula,
1509 };
1510 self.log_change(change_event);
1511
1512 if did_spill_clear && let Some(logger) = &mut self.change_logger {
1513 logger.end_compound();
1514 }
1515
1516 summary
1517 .affected_vertices
1518 .into_iter()
1519 .next()
1520 .unwrap_or(VertexId::new(0))
1521 }
1522 Err(_) => VertexId::new(0),
1523 }
1524 }
1525
1526 pub fn set_range_values(
1530 &mut self,
1531 sheet_id: SheetId,
1532 start_row: u32,
1533 start_col: u32,
1534 values: &[Vec<LiteralValue>],
1535 ) -> Result<RangeSummary, EditorError> {
1536 let mut summary = RangeSummary::default();
1537
1538 self.begin_batch();
1539
1540 for (row_offset, row_values) in values.iter().enumerate() {
1541 for (col_offset, value) in row_values.iter().enumerate() {
1542 let row = start_row + row_offset as u32;
1543 let col = start_col + col_offset as u32;
1544 let cell_ref = self.graph.make_cell_ref_internal(sheet_id, row, col);
1545 let existing_id = self.graph.get_vertex_id_for_address(&cell_ref).copied();
1546
1547 let id = self.set_cell_value(cell_ref, value.clone());
1548 match existing_id {
1549 Some(existing_id) => summary.vertices_updated.push(existing_id),
1550 None if id.0 != 0 => summary.vertices_created.push(id),
1551 None => {}
1552 }
1553 summary.cells_affected += 1;
1554 }
1555 }
1556
1557 self.commit_batch();
1558
1559 Ok(summary)
1560 }
1561
1562 pub fn clear_range(
1564 &mut self,
1565 sheet_id: SheetId,
1566 start_row: u32,
1567 start_col: u32,
1568 end_row: u32,
1569 end_col: u32,
1570 ) -> Result<RangeSummary, EditorError> {
1571 let mut summary = RangeSummary::default();
1572
1573 self.begin_batch();
1574
1575 let vertices_in_range: Vec<_> = self
1577 .graph
1578 .vertices_in_sheet(sheet_id)
1579 .filter(|&id| {
1580 let coord = self.graph.get_coord(id);
1581 let row = coord.row();
1582 let col = coord.col();
1583 row >= start_row && row <= end_row && col >= start_col && col <= end_col
1584 })
1585 .collect();
1586
1587 for id in vertices_in_range {
1588 self.remove_vertex(id)?;
1589 summary.cells_affected += 1;
1590 }
1591
1592 self.commit_batch();
1593
1594 Ok(summary)
1595 }
1596
1597 pub fn copy_range(
1599 &mut self,
1600 sheet_id: SheetId,
1601 from_start_row: u32,
1602 from_start_col: u32,
1603 from_end_row: u32,
1604 from_end_col: u32,
1605 to_sheet_id: SheetId,
1606 to_row: u32,
1607 to_col: u32,
1608 ) -> Result<RangeSummary, EditorError> {
1609 let row_offset = to_row as i32 - from_start_row as i32;
1610 let col_offset = to_col as i32 - from_start_col as i32;
1611
1612 let mut summary = RangeSummary::default();
1613 let mut cell_data = Vec::new();
1614
1615 let vertices_in_range: Vec<_> = self
1617 .graph
1618 .vertices_in_sheet(sheet_id)
1619 .filter(|&id| {
1620 let coord = self.graph.get_coord(id);
1621 let row = coord.row();
1622 let col = coord.col();
1623 row >= from_start_row
1624 && row <= from_end_row
1625 && col >= from_start_col
1626 && col <= from_end_col
1627 })
1628 .collect();
1629
1630 for id in vertices_in_range {
1631 let coord = self.graph.get_coord(id);
1632 let row = coord.row();
1633 let col = coord.col();
1634
1635 if let Some(formula) = self.get_formula_ast(id) {
1637 cell_data.push((
1638 row - from_start_row,
1639 col - from_start_col,
1640 CellData::Formula(formula),
1641 ));
1642 } else if let Some(value) = self.graph.get_value(id) {
1643 cell_data.push((
1644 row - from_start_row,
1645 col - from_start_col,
1646 CellData::Value(value),
1647 ));
1648 }
1649 }
1650
1651 self.begin_batch();
1652
1653 for (row_idx, col_idx, data) in cell_data {
1655 let dest_row = (to_row as i32 + row_idx as i32) as u32;
1656 let dest_col = (to_col as i32 + col_idx as i32) as u32;
1657
1658 match data {
1659 CellData::Value(value) => {
1660 let cell_ref =
1661 self.graph
1662 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
1663
1664 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1665 self.graph.update_vertex_value(existing_id, value);
1666 self.graph.mark_vertex_dirty(existing_id);
1667 summary.vertices_updated.push(existing_id);
1668 } else {
1669 let meta =
1670 VertexMeta::new(dest_row, dest_col, to_sheet_id, VertexKind::Cell);
1671 let id = self.add_vertex(meta);
1672 self.graph.update_vertex_value(id, value);
1673 summary.vertices_created.push(id);
1674 }
1675 }
1676 CellData::Formula(formula) => {
1677 let adjuster = RelativeReferenceAdjuster::new(row_offset, col_offset);
1679 let adjusted = adjuster.adjust_formula(&formula);
1680
1681 let cell_ref =
1682 self.graph
1683 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
1684
1685 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1686 self.graph.update_vertex_formula(existing_id, adjusted)?;
1687 summary.vertices_updated.push(existing_id);
1688 } else {
1689 let meta = VertexMeta::new(
1690 dest_row,
1691 dest_col,
1692 to_sheet_id,
1693 VertexKind::FormulaScalar,
1694 );
1695 let id = self.add_vertex(meta);
1696 self.graph.update_vertex_formula(id, adjusted)?;
1697 summary.vertices_created.push(id);
1698 }
1699 }
1700 }
1701
1702 summary.cells_affected += 1;
1703 }
1704
1705 self.commit_batch();
1706
1707 Ok(summary)
1708 }
1709
1710 pub fn move_range(
1712 &mut self,
1713 sheet_id: SheetId,
1714 from_start_row: u32,
1715 from_start_col: u32,
1716 from_end_row: u32,
1717 from_end_col: u32,
1718 to_sheet_id: SheetId,
1719 to_row: u32,
1720 to_col: u32,
1721 ) -> Result<RangeSummary, EditorError> {
1722 let mut summary = self.copy_range(
1724 sheet_id,
1725 from_start_row,
1726 from_start_col,
1727 from_end_row,
1728 from_end_col,
1729 to_sheet_id,
1730 to_row,
1731 to_col,
1732 )?;
1733
1734 let clear_summary = self.clear_range(
1736 sheet_id,
1737 from_start_row,
1738 from_start_col,
1739 from_end_row,
1740 from_end_col,
1741 )?;
1742
1743 summary.cells_moved = clear_summary.cells_affected;
1744
1745 let row_offset = to_row as i32 - from_start_row as i32;
1747 let col_offset = to_col as i32 - from_start_col as i32;
1748
1749 let all_formula_vertices: Vec<_> = self.graph.vertices_with_formulas().collect();
1751
1752 let from_sheet_name = self.graph.sheet_name(sheet_id).to_string();
1753 let to_sheet_name = self.graph.sheet_name(to_sheet_id).to_string();
1754 let adjuster = MoveReferenceAdjuster::new(
1755 sheet_id,
1756 from_sheet_name,
1757 from_start_row,
1758 from_start_col,
1759 from_end_row,
1760 from_end_col,
1761 to_sheet_id,
1762 to_sheet_name,
1763 row_offset,
1764 col_offset,
1765 );
1766
1767 for formula_id in all_formula_vertices {
1768 if let Some(formula) = self.get_formula_ast(formula_id) {
1769 let formula_sheet_id = self.graph.get_vertex_sheet_id(formula_id);
1770 if let Some(adjusted) = adjuster.adjust_if_references(&formula, formula_sheet_id) {
1771 self.graph.update_vertex_formula(formula_id, adjusted)?;
1772 }
1773 }
1774 }
1775
1776 Ok(summary)
1777 }
1778
1779 pub fn define_name(
1781 &mut self,
1782 name: &str,
1783 definition: NamedDefinition,
1784 scope: NameScope,
1785 ) -> Result<(), EditorError> {
1786 self.graph.define_name(name, definition.clone(), scope)?;
1787
1788 self.log_change(ChangeEvent::DefineName {
1789 name: name.to_string(),
1790 scope,
1791 definition,
1792 });
1793
1794 Ok(())
1795 }
1796
1797 pub fn define_name_for_cell(
1799 &mut self,
1800 name: &str,
1801 sheet_name: &str,
1802 row: u32,
1803 col: u32,
1804 scope: NameScope,
1805 ) -> Result<(), EditorError> {
1806 let sheet_id = self
1807 .graph
1808 .sheet_id(sheet_name)
1809 .ok_or_else(|| EditorError::InvalidName {
1810 name: sheet_name.to_string(),
1811 reason: "Sheet not found".to_string(),
1812 })?;
1813 let cell_ref = CellRef::new(sheet_id, Coord::from_excel(row, col, true, true));
1814 self.define_name(name, NamedDefinition::Cell(cell_ref), scope)
1815 }
1816
1817 pub fn define_name_for_range(
1819 &mut self,
1820 name: &str,
1821 sheet_name: &str,
1822 start_row: u32,
1823 start_col: u32,
1824 end_row: u32,
1825 end_col: u32,
1826 scope: NameScope,
1827 ) -> Result<(), EditorError> {
1828 let sheet_id = self
1829 .graph
1830 .sheet_id(sheet_name)
1831 .ok_or_else(|| EditorError::InvalidName {
1832 name: sheet_name.to_string(),
1833 reason: "Sheet not found".to_string(),
1834 })?;
1835 let start = CellRef::new(
1836 sheet_id,
1837 Coord::from_excel(start_row, start_col, true, true),
1838 );
1839 let end = CellRef::new(sheet_id, Coord::from_excel(end_row, end_col, true, true));
1840 let range_ref = crate::reference::RangeRef::new(start, end);
1841 self.define_name(name, NamedDefinition::Range(range_ref), scope)
1842 }
1843
1844 pub fn update_name(
1846 &mut self,
1847 name: &str,
1848 new_definition: NamedDefinition,
1849 scope: NameScope,
1850 ) -> Result<(), EditorError> {
1851 let old_definition = self
1853 .graph
1854 .resolve_name(
1855 name,
1856 match scope {
1857 NameScope::Sheet(id) => id,
1858 NameScope::Workbook => 0,
1859 },
1860 )
1861 .cloned();
1862
1863 self.graph
1864 .update_name(name, new_definition.clone(), scope)?;
1865
1866 if let Some(old_def) = old_definition {
1867 self.log_change(ChangeEvent::UpdateName {
1868 name: name.to_string(),
1869 scope,
1870 old_definition: old_def,
1871 new_definition,
1872 });
1873 }
1874
1875 Ok(())
1876 }
1877
1878 pub fn delete_name(&mut self, name: &str, scope: NameScope) -> Result<(), EditorError> {
1880 let old_def = if self.has_logger() {
1882 self.graph
1883 .resolve_name(
1884 name,
1885 match scope {
1886 NameScope::Sheet(id) => id,
1887 NameScope::Workbook => 0,
1888 },
1889 )
1890 .cloned()
1891 } else {
1892 None
1893 };
1894
1895 self.graph.delete_name(name, scope)?;
1896 self.log_change(ChangeEvent::DeleteName {
1897 name: name.to_string(),
1898 scope,
1899 old_definition: old_def,
1900 });
1901
1902 Ok(())
1903 }
1904}
1905
1906enum CellData {
1908 Value(LiteralValue),
1909 Formula(ASTNode),
1910}
1911
1912impl<'g> Drop for VertexEditor<'g> {
1913 fn drop(&mut self) {
1914 if self.batch_mode {
1916 self.commit_batch();
1917 }
1918 }
1919}
1920
1921#[cfg(test)]
1922mod tests {
1923 use super::*;
1924 use crate::engine::graph::editor::change_log::{ChangeEvent, ChangeLog};
1925 use crate::reference::Coord;
1926
1927 fn create_test_graph() -> DependencyGraph {
1928 DependencyGraph::new()
1929 }
1930
1931 #[test]
1932 fn test_vertex_editor_creation() {
1933 let mut graph = create_test_graph();
1934 let editor = VertexEditor::new(&mut graph);
1935 assert!(!editor.has_logger());
1936 assert!(!editor.batch_mode);
1937 }
1938
1939 #[test]
1940 fn test_vertex_editor_with_logger() {
1941 let mut graph = create_test_graph();
1942 let mut log = ChangeLog::new();
1943 let editor = VertexEditor::with_logger(&mut graph, &mut log);
1944 assert!(editor.has_logger());
1945 assert!(!editor.batch_mode);
1946 }
1947
1948 #[test]
1949 fn test_add_vertex() {
1950 let mut graph = create_test_graph();
1951 let mut editor = VertexEditor::new(&mut graph);
1952
1953 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell).dirty();
1954 let vertex_id = editor.add_vertex(meta);
1955
1956 assert!(vertex_id.0 > 0);
1958 }
1959
1960 #[test]
1961 fn test_batch_operations() {
1962 let mut graph = create_test_graph();
1963 let mut editor = VertexEditor::new(&mut graph);
1964
1965 assert!(!editor.batch_mode);
1966 editor.begin_batch();
1967 assert!(editor.batch_mode);
1968
1969 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
1971 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::Cell);
1972
1973 let id1 = editor.add_vertex(meta1);
1974 let id2 = editor.add_vertex(meta2);
1975
1976 assert!(editor.add_edge(id1, id2));
1978
1979 editor.commit_batch();
1980 assert!(!editor.batch_mode);
1981 }
1982
1983 #[test]
1984 fn test_remove_vertex() {
1985 let mut graph = create_test_graph();
1986 let mut editor = VertexEditor::new(&mut graph);
1987
1988 let meta = VertexMeta::new(3, 4, 0, VertexKind::Cell).dirty();
1989 let vertex_id = editor.add_vertex(meta);
1990
1991 assert!(editor.remove_vertex(vertex_id).is_ok());
1993 }
1994
1995 #[test]
1996 fn test_remove_vertex_clears_spill_registry_for_anchor() {
1997 let mut graph = create_test_graph();
1998 let sheet_id = graph.sheet_id_mut("Sheet1");
1999
2000 let anchor_cell = CellRef::new(sheet_id, Coord::new(0, 0, true, true));
2002 let anchor_vid = {
2003 let mut editor = VertexEditor::new(&mut graph);
2004 editor.set_cell_value(anchor_cell, LiteralValue::Number(0.0))
2005 };
2006
2007 let target_cells = vec![
2008 CellRef::new(sheet_id, Coord::new(0, 0, true, true)),
2009 CellRef::new(sheet_id, Coord::new(0, 1, true, true)),
2010 CellRef::new(sheet_id, Coord::new(1, 0, true, true)),
2011 CellRef::new(sheet_id, Coord::new(1, 1, true, true)),
2012 ];
2013 let values = vec![
2014 vec![LiteralValue::Number(1.0), LiteralValue::Number(2.0)],
2015 vec![LiteralValue::Number(3.0), LiteralValue::Number(4.0)],
2016 ];
2017
2018 graph
2019 .commit_spill_region_atomic_with_fault(anchor_vid, target_cells.clone(), values, None)
2020 .unwrap();
2021
2022 assert!(graph.spill_registry_has_anchor(anchor_vid));
2023 for cell in &target_cells {
2024 assert_eq!(
2025 graph.spill_registry_anchor_for_cell(*cell),
2026 Some(anchor_vid)
2027 );
2028 }
2029
2030 {
2031 let mut editor = VertexEditor::new(&mut graph);
2032 editor.remove_vertex(anchor_vid).unwrap();
2033 }
2034
2035 assert!(!graph.spill_registry_has_anchor(anchor_vid));
2036 for cell in &target_cells {
2037 assert_eq!(graph.spill_registry_anchor_for_cell(*cell), None);
2038 }
2039 assert_eq!(graph.spill_registry_counts(), (0, 0));
2040 }
2041
2042 #[test]
2043 fn test_edge_operations() {
2044 let mut graph = create_test_graph();
2045 let mut editor = VertexEditor::new(&mut graph);
2046
2047 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2048 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::FormulaScalar);
2049
2050 let id1 = editor.add_vertex(meta1);
2051 let id2 = editor.add_vertex(meta2);
2052
2053 assert!(editor.add_edge(id1, id2));
2055
2056 assert!(!editor.add_edge(id1, id1));
2058
2059 assert!(editor.remove_edge(id1, id2));
2061 }
2062
2063 #[test]
2064 fn test_set_cell_value() {
2065 let mut graph = create_test_graph();
2066 let mut log = ChangeLog::new();
2067
2068 let cell_ref = CellRef {
2069 sheet_id: 0,
2070 coord: Coord::new(2, 3, true, true),
2071 };
2072 let value = LiteralValue::Number(42.0);
2073
2074 let vertex_id = {
2075 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2076 editor.set_cell_value(cell_ref, value.clone())
2077 };
2078
2079 assert!(vertex_id.0 > 0);
2081
2082 assert_eq!(log.len(), 1);
2084 match &log.events()[0] {
2085 ChangeEvent::SetValue { addr, new, .. } => {
2086 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
2087 assert_eq!(addr.coord.row(), cell_ref.coord.row());
2088 assert_eq!(addr.coord.col(), cell_ref.coord.col());
2089 assert_eq!(new, &value);
2090 }
2091 _ => panic!("Expected SetValue event"),
2092 }
2093 }
2094
2095 #[test]
2096 fn test_set_cell_formula() {
2097 let mut graph = create_test_graph();
2098 let mut log = ChangeLog::new();
2099
2100 let cell_ref = CellRef {
2101 sheet_id: 0,
2102 coord: Coord::new(1, 1, true, true),
2103 };
2104
2105 use formualizer_parse::parser::ASTNodeType;
2106 let formula = formualizer_parse::parser::ASTNode {
2107 node_type: ASTNodeType::Literal(LiteralValue::Number(100.0)),
2108 source_token: None,
2109 contains_volatile: false,
2110 };
2111
2112 let vertex_id = {
2113 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2114 editor.set_cell_formula(cell_ref, formula.clone())
2115 };
2116
2117 assert!(vertex_id.0 > 0);
2119
2120 assert_eq!(log.len(), 1);
2122 match &log.events()[0] {
2123 ChangeEvent::SetFormula { addr, .. } => {
2124 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
2125 assert_eq!(addr.coord.row(), cell_ref.coord.row());
2126 assert_eq!(addr.coord.col(), cell_ref.coord.col());
2127 }
2128 _ => panic!("Expected SetFormula event"),
2129 }
2130 }
2131
2132 #[test]
2133 fn test_shift_rows() {
2134 let mut graph = create_test_graph();
2135 let mut log = ChangeLog::new();
2136
2137 {
2138 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2139
2140 let cell1 = CellRef {
2142 sheet_id: 0,
2143 coord: Coord::new(5, 1, true, true),
2144 };
2145 let cell2 = CellRef {
2146 sheet_id: 0,
2147 coord: Coord::new(10, 1, true, true),
2148 };
2149 let cell3 = CellRef {
2150 sheet_id: 0,
2151 coord: Coord::new(15, 1, true, true),
2152 };
2153
2154 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
2155 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
2156 editor.set_cell_value(cell3, LiteralValue::Number(3.0));
2157 }
2158
2159 log.clear();
2161
2162 {
2163 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2164 editor.shift_rows(0, 10, 2);
2166 }
2167
2168 assert_eq!(log.len(), 1);
2170 match &log.events()[0] {
2171 ChangeEvent::SetValue { addr, new, .. } => {
2172 assert_eq!(addr.sheet_id, 0);
2173 assert_eq!(addr.coord.row(), 10);
2174 if let LiteralValue::Text(msg) = new {
2175 assert!(msg.contains("Row shift"));
2176 assert!(msg.contains("start=10"));
2177 assert!(msg.contains("delta=2"));
2178 }
2179 }
2180 _ => panic!("Expected SetValue event for row shift"),
2181 }
2182 }
2183
2184 #[test]
2185 fn test_shift_columns() {
2186 let mut graph = create_test_graph();
2187 let mut log = ChangeLog::new();
2188
2189 {
2190 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2191
2192 let cell1 = CellRef {
2194 sheet_id: 0,
2195 coord: Coord::new(1, 5, true, true),
2196 };
2197 let cell2 = CellRef {
2198 sheet_id: 0,
2199 coord: Coord::new(1, 10, true, true),
2200 };
2201
2202 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
2203 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
2204 }
2205
2206 log.clear();
2208
2209 {
2210 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2211 editor.shift_columns(0, 8, 3);
2213 }
2214
2215 assert_eq!(log.len(), 1);
2217 match &log.events()[0] {
2218 ChangeEvent::SetValue { addr, new, .. } => {
2219 assert_eq!(addr.sheet_id, 0);
2220 assert_eq!(addr.coord.col(), 8);
2221 if let LiteralValue::Text(msg) = new {
2222 assert!(msg.contains("Column shift"));
2223 assert!(msg.contains("start=8"));
2224 assert!(msg.contains("delta=3"));
2225 }
2226 }
2227 _ => panic!("Expected SetValue event for column shift"),
2228 }
2229 }
2230
2231 #[test]
2232 fn test_move_vertex() {
2233 let mut graph = create_test_graph();
2234 let mut editor = VertexEditor::new(&mut graph);
2235
2236 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell);
2237 let vertex_id = editor.add_vertex(meta);
2238
2239 assert!(editor.move_vertex(vertex_id, AbsCoord::new(8, 12)).is_ok());
2241
2242 assert!(editor.move_vertex(vertex_id, AbsCoord::new(8, 12)).is_ok());
2244 }
2245
2246 #[test]
2247 fn test_vertex_meta_builder() {
2248 let meta = VertexMeta::new(1, 2, 3, VertexKind::FormulaScalar)
2249 .dirty()
2250 .volatile()
2251 .with_flags(0x08);
2252
2253 assert_eq!(meta.coord.row(), 1);
2254 assert_eq!(meta.coord.col(), 2);
2255 assert_eq!(meta.sheet_id, 3);
2256 assert_eq!(meta.kind, VertexKind::FormulaScalar);
2257 assert_eq!(meta.flags, 0x08); }
2259
2260 #[test]
2261 fn test_change_log_management() {
2262 let mut graph = create_test_graph();
2263 let mut log = ChangeLog::new();
2264
2265 {
2266 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
2267 let cell_ref = CellRef {
2268 sheet_id: 0,
2269 coord: Coord::new(0, 0, true, true),
2270 };
2271 editor.set_cell_value(cell_ref, LiteralValue::Number(1.0));
2272 editor.set_cell_value(cell_ref, LiteralValue::Number(2.0));
2273 }
2274
2275 assert_eq!(log.len(), 2);
2276
2277 log.clear();
2278 assert_eq!(log.len(), 0);
2279 }
2280
2281 #[test]
2282 fn test_editor_drop_commits_batch() {
2283 let mut graph = create_test_graph();
2284 {
2285 let mut editor = VertexEditor::new(&mut graph);
2286 editor.begin_batch();
2287
2288 let meta = VertexMeta::new(1, 1, 0, VertexKind::Cell);
2289 editor.add_vertex(meta);
2290
2291 }
2293
2294 }
2296}