1use crate::SheetId;
2use crate::engine::graph::DependencyGraph;
3use crate::engine::graph::editor::reference_adjuster::{ReferenceAdjuster, ShiftOperation};
4use crate::engine::named_range::{NameScope, NamedDefinition};
5use crate::engine::packed_coord::PackedCoord;
6use crate::engine::{ChangeEvent, ChangeLogger, VertexId, VertexKind};
7use crate::reference::{CellRef, Coord};
8use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
9use formualizer_parse::parser::ASTNode;
10use std::sync::atomic::{AtomicU64, Ordering};
11
12#[derive(Debug, Clone)]
14pub struct VertexMeta {
15 pub coord: PackedCoord,
16 pub sheet_id: SheetId,
17 pub kind: VertexKind,
18 pub flags: u8,
19}
20
21impl VertexMeta {
22 pub fn new(row: u32, col: u32, sheet_id: SheetId, kind: VertexKind) -> Self {
23 Self {
24 coord: PackedCoord::new(row, col),
25 sheet_id,
26 kind,
27 flags: 0,
28 }
29 }
30
31 pub fn with_flags(mut self, flags: u8) -> Self {
32 self.flags = flags;
33 self
34 }
35
36 pub fn dirty(mut self) -> Self {
37 self.flags |= 0x01;
38 self
39 }
40
41 pub fn volatile(mut self) -> Self {
42 self.flags |= 0x02;
43 self
44 }
45}
46
47#[derive(Debug, Clone)]
49pub struct VertexMetaPatch {
50 pub kind: Option<VertexKind>,
51 pub coord: Option<PackedCoord>,
52 pub dirty: Option<bool>,
53 pub volatile: Option<bool>,
54}
55
56#[derive(Debug, Clone)]
58pub struct VertexDataPatch {
59 pub value: Option<LiteralValue>,
60 pub formula: Option<ASTNode>,
61}
62
63#[derive(Debug, Clone, Default)]
65pub struct MetaUpdateSummary {
66 pub coord_changed: bool,
67 pub kind_changed: bool,
68 pub flags_changed: bool,
69}
70
71#[derive(Debug, Clone, Default)]
73pub struct DataUpdateSummary {
74 pub value_changed: bool,
75 pub formula_changed: bool,
76 pub dependents_marked_dirty: Vec<VertexId>,
77}
78
79#[derive(Debug, Clone, Default)]
81pub struct ShiftSummary {
82 pub vertices_moved: Vec<VertexId>,
83 pub vertices_deleted: Vec<VertexId>,
84 pub references_adjusted: usize,
85 pub formulas_updated: usize,
86}
87
88#[derive(Debug, Clone, Default)]
90pub struct RangeSummary {
91 pub cells_affected: usize,
92 pub vertices_created: Vec<VertexId>,
93 pub vertices_updated: Vec<VertexId>,
94 pub cells_moved: usize,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub struct TransactionId(u64);
100
101impl TransactionId {
102 fn new() -> Self {
103 static COUNTER: AtomicU64 = AtomicU64::new(0);
104 TransactionId(COUNTER.fetch_add(1, Ordering::Relaxed))
105 }
106}
107
108#[derive(Debug)]
110struct Transaction {
111 id: TransactionId,
112 start_index: usize, }
114
115#[derive(Debug, Clone)]
117pub enum EditorError {
118 TargetOccupied { cell: CellRef },
119 OutOfBounds { row: u32, col: u32 },
120 InvalidName { name: String, reason: String },
121 TransactionFailed { reason: String },
122 NoActiveTransaction,
123 VertexNotFound { id: VertexId },
124 Excel(ExcelError),
125}
126
127impl From<ExcelError> for EditorError {
128 fn from(e: ExcelError) -> Self {
129 EditorError::Excel(e)
130 }
131}
132
133impl std::fmt::Display for EditorError {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 match self {
136 EditorError::TargetOccupied { cell } => {
137 write!(
138 f,
139 "Target cell occupied at row {}, col {}",
140 cell.coord.row, cell.coord.col
141 )
142 }
143 EditorError::OutOfBounds { row, col } => {
144 write!(f, "Cell position out of bounds: row {row}, col {col}")
145 }
146 EditorError::InvalidName { name, reason } => {
147 write!(f, "Invalid name '{name}': {reason}")
148 }
149 EditorError::TransactionFailed { reason } => {
150 write!(f, "Transaction failed: {reason}")
151 }
152 EditorError::NoActiveTransaction => {
153 write!(f, "No active transaction")
154 }
155 EditorError::VertexNotFound { id } => {
156 write!(f, "Vertex not found: {id:?}")
157 }
158 EditorError::Excel(e) => write!(f, "Excel error: {e:?}"),
159 }
160 }
161}
162
163impl std::error::Error for EditorError {}
164
165pub struct VertexEditor<'g> {
196 graph: &'g mut DependencyGraph,
197 change_logger: Option<&'g mut dyn ChangeLogger>,
198 batch_mode: bool,
199}
200
201impl<'g> VertexEditor<'g> {
202 pub fn new(graph: &'g mut DependencyGraph) -> Self {
204 Self {
205 graph,
206 change_logger: None,
207 batch_mode: false,
208 }
209 }
210
211 pub fn with_logger<L: ChangeLogger + 'g>(
213 graph: &'g mut DependencyGraph,
214 logger: &'g mut L,
215 ) -> Self {
216 Self {
217 graph,
218 change_logger: Some(logger as &'g mut dyn ChangeLogger),
219 batch_mode: false,
220 }
221 }
222
223 pub fn begin_batch(&mut self) {
225 if !self.batch_mode {
226 self.graph.begin_batch();
227 self.batch_mode = true;
228 }
229 }
230
231 pub fn commit_batch(&mut self) {
233 if self.batch_mode {
234 self.graph.end_batch();
235 self.batch_mode = false;
236 }
237 }
238
239 fn log_change(&mut self, event: ChangeEvent) {
241 if let Some(logger) = &mut self.change_logger {
242 logger.record(event);
243 }
244 }
245
246 pub fn has_logger(&self) -> bool {
248 self.change_logger.is_some()
249 }
250
251 pub fn apply_inverse(&mut self, change: ChangeEvent) -> Result<(), EditorError> {
258 match change {
259 ChangeEvent::SetValue { addr, old, new: _ } => {
260 if let Some(old_value) = old {
262 self.set_cell_value(addr, old_value);
263 } else {
264 if let Some(&id) = self.graph.get_vertex_id_for_address(&addr) {
266 self.remove_vertex(id)?;
267 }
268 }
269 }
270 ChangeEvent::SetFormula { addr, old, new: _ } => {
271 if let Some(old_formula) = old {
273 self.set_cell_formula(addr, old_formula);
274 } else {
275 if let Some(&id) = self.graph.get_vertex_id_for_address(&addr) {
277 self.remove_vertex(id)?;
278 }
279 }
280 }
281 ChangeEvent::AddVertex { id, .. } => {
282 let _ = self.remove_vertex(id); }
285 ChangeEvent::RemoveVertex {
286 id: _,
287 old_value,
288 old_formula,
289 old_dependencies,
290 old_dependents,
291 coord,
292 sheet_id,
293 kind,
294 ..
295 } => {
296 if let (Some(c), Some(sid)) = (coord, sheet_id) {
297 let meta =
298 VertexMeta::new(c.row(), c.col(), sid, kind.unwrap_or(VertexKind::Cell));
299 let new_id = self.add_vertex(meta);
300 if let Some(v) = old_value {
301 let cell_ref = self.graph.make_cell_ref_internal(sid, c.row(), c.col());
302 self.set_cell_value(cell_ref, v);
303 }
304 if let Some(f) = old_formula {
305 let cell_ref = self.graph.make_cell_ref_internal(sid, c.row(), c.col());
306 self.set_cell_formula(cell_ref, f);
307 }
308 for dep in old_dependencies {
309 self.graph.add_dependency_edge(new_id, dep);
310 }
311 for parent in old_dependents {
312 self.graph.add_dependency_edge(parent, new_id);
313 }
314 }
315 }
316 ChangeEvent::DefineName { name, scope, .. } => {
317 self.graph.delete_name(&name, scope)?;
319 }
320 ChangeEvent::UpdateName {
321 name,
322 scope,
323 old_definition,
324 ..
325 } => {
326 self.graph.update_name(&name, old_definition, scope)?;
328 }
329 ChangeEvent::DeleteName {
330 name,
331 scope,
332 old_definition,
333 } => {
334 if let Some(def) = old_definition {
335 self.graph.define_name(&name, def, scope)?;
336 } else {
337 return Err(EditorError::TransactionFailed {
338 reason: "Missing old definition for name deletion rollback".to_string(),
339 });
340 }
341 }
342 ChangeEvent::CompoundStart { .. } | ChangeEvent::CompoundEnd { .. } => {
344 }
346 ChangeEvent::VertexMoved { id, old_coord, .. } => {
347 self.move_vertex(id, old_coord)?;
349 }
350 ChangeEvent::FormulaAdjusted { id, old_ast, .. } => {
351 return Err(EditorError::TransactionFailed {
354 reason: "Cannot rollback formula adjustment yet".to_string(),
355 });
356 }
357 ChangeEvent::NamedRangeAdjusted {
358 name,
359 scope,
360 old_definition,
361 ..
362 } => {
363 self.graph.update_name(&name, old_definition, scope)?;
365 }
366 ChangeEvent::EdgeAdded { from, to } => {
367 return Err(EditorError::TransactionFailed {
370 reason: "Cannot rollback edge addition yet".to_string(),
371 });
372 }
373 ChangeEvent::EdgeRemoved { from, to } => {
374 return Err(EditorError::TransactionFailed {
377 reason: "Cannot rollback edge removal yet".to_string(),
378 });
379 }
380 }
381 Ok(())
382 }
383
384 pub fn add_vertex(&mut self, meta: VertexMeta) -> VertexId {
386 let sheet_name = self.graph.sheet_name(meta.sheet_id).to_string();
389
390 let id = match meta.kind {
391 VertexKind::Cell => {
392 match self.graph.set_cell_value(
394 &sheet_name,
395 meta.coord.row(),
396 meta.coord.col(),
397 LiteralValue::Empty,
398 ) {
399 Ok(summary) => summary
400 .affected_vertices
401 .into_iter()
402 .next()
403 .unwrap_or(VertexId::new(0)),
404 Err(_) => VertexId::new(0),
405 }
406 }
407 _ => {
408 match self.graph.set_cell_value(
411 &sheet_name,
412 meta.coord.row(),
413 meta.coord.col(),
414 LiteralValue::Empty,
415 ) {
416 Ok(summary) => summary
417 .affected_vertices
418 .into_iter()
419 .next()
420 .unwrap_or(VertexId::new(0)),
421 Err(_) => VertexId::new(0),
422 }
423 }
424 };
425
426 if self.has_logger() && id.0 != 0 {
427 self.log_change(ChangeEvent::AddVertex {
428 id,
429 coord: meta.coord,
430 sheet_id: meta.sheet_id,
431 value: Some(LiteralValue::Empty),
432 formula: None,
433 kind: Some(meta.kind),
434 flags: Some(meta.flags),
435 });
436 }
437 id
438 }
439
440 pub fn remove_vertex(&mut self, id: VertexId) -> Result<(), EditorError> {
442 if !self.graph.vertex_exists(id) {
444 return Err(EditorError::Excel(
445 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
446 ));
447 }
448
449 let dependents = self.graph.get_dependents(id);
452
453 let (
455 old_value,
456 old_formula,
457 old_dependencies,
458 old_dependents,
459 coord,
460 sheet_id_opt,
461 kind,
462 flags,
463 ) = if self.has_logger() {
464 let coord = self.graph.get_coord(id);
465 let sheet_id = self.graph.get_sheet_id(id);
466 let kind = self.graph.get_vertex_kind(id);
467 let flags = 0u8;
469 (
470 self.graph.get_value(id),
471 self.graph.get_formula(id),
472 self.graph.get_dependencies(id), dependents.clone(), Some(coord),
475 Some(sheet_id),
476 Some(kind),
477 Some(flags),
478 )
479 } else {
480 (None, None, vec![], vec![], None, None, None, None)
481 };
482
483 if let Some(cell_ref) = self.graph.get_cell_ref_for_vertex(id) {
485 self.graph.remove_cell_mapping(&cell_ref);
486 }
487
488 self.graph.remove_all_edges(id);
490
491 for dep_id in &dependents {
493 self.graph.mark_as_ref_error(*dep_id);
494 }
495
496 self.graph.mark_deleted(id, true);
498
499 self.log_change(ChangeEvent::RemoveVertex {
501 id,
502 old_value,
503 old_formula,
504 old_dependencies,
505 old_dependents,
506 coord,
507 sheet_id: sheet_id_opt,
508 kind,
509 flags,
510 });
511
512 Ok(())
513 }
514
515 pub fn remove_vertex_at(&mut self, cell: CellRef) -> Result<(), EditorError> {
517 if let Some(id) = self.graph.get_vertex_for_cell(&cell) {
518 self.remove_vertex(id)
519 } else {
520 Ok(())
521 }
522 }
523
524 pub fn move_vertex(&mut self, id: VertexId, new_coord: PackedCoord) -> Result<(), EditorError> {
526 if !self.graph.vertex_exists(id) {
528 return Err(EditorError::Excel(
529 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
530 ));
531 }
532
533 let old_cell_ref = self.graph.get_cell_ref_for_vertex(id);
535
536 let sheet_id = self.graph.get_sheet_id(id);
538 let new_cell_ref = CellRef::new(
539 sheet_id,
540 Coord::new(new_coord.row(), new_coord.col(), true, true),
541 );
542
543 self.graph.set_coord(id, new_coord);
545
546 self.graph.update_edge_coord(id, new_coord);
548
549 self.graph
551 .update_cell_mapping(id, old_cell_ref, new_cell_ref);
552
553 self.graph.mark_dependents_dirty(id);
555
556 Ok(())
557 }
558
559 pub fn patch_vertex_meta(
561 &mut self,
562 id: VertexId,
563 patch: VertexMetaPatch,
564 ) -> Result<MetaUpdateSummary, EditorError> {
565 if !self.graph.vertex_exists(id) {
566 return Err(EditorError::Excel(
567 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
568 ));
569 }
570
571 let mut summary = MetaUpdateSummary::default();
572
573 if let Some(coord) = patch.coord {
574 self.graph.set_coord(id, coord);
575 self.graph.update_edge_coord(id, coord);
576 summary.coord_changed = true;
577 }
578
579 if let Some(kind) = patch.kind {
580 self.graph.set_kind(id, kind);
581 summary.kind_changed = true;
582 }
583
584 if let Some(dirty) = patch.dirty {
585 self.graph.set_dirty(id, dirty);
586 summary.flags_changed = true;
587 }
588
589 if let Some(volatile) = patch.volatile {
590 self.graph.mark_volatile(id, volatile);
591 summary.flags_changed = true;
592 }
593
594 Ok(summary)
595 }
596
597 pub fn patch_vertex_data(
599 &mut self,
600 id: VertexId,
601 patch: VertexDataPatch,
602 ) -> Result<DataUpdateSummary, EditorError> {
603 if !self.graph.vertex_exists(id) {
604 return Err(EditorError::Excel(
605 ExcelError::new(ExcelErrorKind::Ref).with_message("Vertex does not exist"),
606 ));
607 }
608
609 let mut summary = DataUpdateSummary::default();
610
611 if let Some(value) = patch.value {
612 self.graph.update_vertex_value(id, value);
613 summary.value_changed = true;
614
615 if self.graph.edges_delta_size() > 0 {
618 self.graph.rebuild_edges();
619 }
620
621 let dependents = self.graph.get_dependents(id);
623 for dep in &dependents {
624 self.graph.set_dirty(*dep, true);
625 }
626 summary.dependents_marked_dirty = dependents;
627 }
628
629 if let Some(_formula) = patch.formula {
630 summary.formula_changed = true;
633 }
634
635 Ok(summary)
636 }
637
638 pub fn add_edge(&mut self, from: VertexId, to: VertexId) -> bool {
640 if from == to {
641 return false; }
643
644 true
647 }
648
649 pub fn remove_edge(&mut self, _from: VertexId, _to: VertexId) -> bool {
651 true
653 }
654
655 pub fn insert_rows(
657 &mut self,
658 sheet_id: SheetId,
659 before: u32,
660 count: u32,
661 ) -> Result<ShiftSummary, EditorError> {
662 if count == 0 {
663 return Ok(ShiftSummary::default());
664 }
665
666 let mut summary = ShiftSummary::default();
667
668 self.begin_batch();
670
671 let vertices_to_shift: Vec<(VertexId, PackedCoord)> = self
673 .graph
674 .vertices_in_sheet(sheet_id)
675 .filter_map(|id| {
676 let coord = self.graph.get_coord(id);
677 if coord.row() >= before {
678 Some((id, coord))
679 } else {
680 None
681 }
682 })
683 .collect();
684
685 if let Some(logger) = &mut self.change_logger {
686 logger.begin_compound(format!(
687 "InsertRows sheet={sheet_id} before={before} count={count}"
688 ));
689 }
690 for (id, old_coord) in vertices_to_shift {
692 let new_coord = PackedCoord::new(old_coord.row() + count, old_coord.col());
693 if self.has_logger() {
694 self.log_change(ChangeEvent::VertexMoved {
695 id,
696 old_coord,
697 new_coord,
698 });
699 }
700 self.move_vertex(id, new_coord)?;
701 summary.vertices_moved.push(id);
702 }
703
704 let op = ShiftOperation::InsertRows {
706 sheet_id,
707 before,
708 count,
709 };
710 let adjuster = ReferenceAdjuster::new();
711
712 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
714
715 for id in formula_vertices {
716 if let Some(ast) = self.graph.get_formula(id) {
717 let adjusted = adjuster.adjust_ast(&ast, &op);
718 if format!("{ast:?}") != format!("{adjusted:?}") {
720 self.graph.update_vertex_formula(id, adjusted)?;
721 self.graph.mark_vertex_dirty(id);
722 summary.formulas_updated += 1;
723 }
724 }
725 }
726
727 self.graph.adjust_named_ranges(&op)?;
729
730 if let Some(logger) = &mut self.change_logger {
732 logger.end_compound();
733 }
734
735 self.commit_batch();
736
737 Ok(summary)
738 }
739
740 pub fn delete_rows(
742 &mut self,
743 sheet_id: SheetId,
744 start: u32,
745 count: u32,
746 ) -> Result<ShiftSummary, EditorError> {
747 if count == 0 {
748 return Ok(ShiftSummary::default());
749 }
750
751 let mut summary = ShiftSummary::default();
752
753 self.begin_batch();
754
755 let vertices_to_delete: Vec<VertexId> = self
757 .graph
758 .vertices_in_sheet(sheet_id)
759 .filter(|&id| {
760 let coord = self.graph.get_coord(id);
761 coord.row() >= start && coord.row() < start + count
762 })
763 .collect();
764
765 for id in vertices_to_delete {
766 self.remove_vertex(id)?;
767 summary.vertices_deleted.push(id);
768 }
769
770 if let Some(logger) = &mut self.change_logger {
771 logger.begin_compound(format!(
772 "DeleteRows sheet={sheet_id} start={start} count={count}"
773 ));
774 }
775 let vertices_to_shift: Vec<(VertexId, PackedCoord)> = self
777 .graph
778 .vertices_in_sheet(sheet_id)
779 .filter_map(|id| {
780 let coord = self.graph.get_coord(id);
781 if coord.row() >= start + count {
782 Some((id, coord))
783 } else {
784 None
785 }
786 })
787 .collect();
788
789 for (id, old_coord) in vertices_to_shift {
790 let new_coord = PackedCoord::new(old_coord.row() - count, old_coord.col());
791 if self.has_logger() {
792 self.log_change(ChangeEvent::VertexMoved {
793 id,
794 old_coord,
795 new_coord,
796 });
797 }
798 self.move_vertex(id, new_coord)?;
799 summary.vertices_moved.push(id);
800 }
801
802 let op = ShiftOperation::DeleteRows {
804 sheet_id,
805 start,
806 count,
807 };
808 let adjuster = ReferenceAdjuster::new();
809
810 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
811
812 for id in formula_vertices {
813 if let Some(ast) = self.graph.get_formula(id) {
814 let adjusted = adjuster.adjust_ast(&ast, &op);
815 if format!("{ast:?}") != format!("{adjusted:?}") {
816 self.graph.update_vertex_formula(id, adjusted)?;
817 self.graph.mark_vertex_dirty(id);
818 summary.formulas_updated += 1;
819 }
820 }
821 }
822
823 self.graph.adjust_named_ranges(&op)?;
825
826 if let Some(logger) = &mut self.change_logger {
828 logger.end_compound();
829 }
830
831 self.commit_batch();
832
833 Ok(summary)
834 }
835
836 pub fn insert_columns(
838 &mut self,
839 sheet_id: SheetId,
840 before: u32,
841 count: u32,
842 ) -> Result<ShiftSummary, EditorError> {
843 if count == 0 {
844 return Ok(ShiftSummary::default());
845 }
846
847 let mut summary = ShiftSummary::default();
848
849 self.begin_batch();
851
852 let vertices_to_shift: Vec<(VertexId, PackedCoord)> = self
854 .graph
855 .vertices_in_sheet(sheet_id)
856 .filter_map(|id| {
857 let coord = self.graph.get_coord(id);
858 if coord.col() >= before {
859 Some((id, coord))
860 } else {
861 None
862 }
863 })
864 .collect();
865
866 if let Some(logger) = &mut self.change_logger {
867 logger.begin_compound(format!(
868 "InsertColumns sheet={sheet_id} before={before} count={count}"
869 ));
870 }
871 for (id, old_coord) in vertices_to_shift {
873 let new_coord = PackedCoord::new(old_coord.row(), old_coord.col() + count);
874 if self.has_logger() {
875 self.log_change(ChangeEvent::VertexMoved {
876 id,
877 old_coord,
878 new_coord,
879 });
880 }
881 self.move_vertex(id, new_coord)?;
882 summary.vertices_moved.push(id);
883 }
884
885 let op = ShiftOperation::InsertColumns {
887 sheet_id,
888 before,
889 count,
890 };
891 let adjuster = ReferenceAdjuster::new();
892
893 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
895
896 for id in formula_vertices {
897 if let Some(ast) = self.graph.get_formula(id) {
898 let adjusted = adjuster.adjust_ast(&ast, &op);
899 if format!("{ast:?}") != format!("{adjusted:?}") {
901 self.graph.update_vertex_formula(id, adjusted)?;
902 self.graph.mark_vertex_dirty(id);
903 summary.formulas_updated += 1;
904 }
905 }
906 }
907
908 self.graph.adjust_named_ranges(&op)?;
910
911 if let Some(logger) = &mut self.change_logger {
913 logger.end_compound();
914 }
915
916 self.commit_batch();
917
918 Ok(summary)
919 }
920
921 pub fn delete_columns(
923 &mut self,
924 sheet_id: SheetId,
925 start: u32,
926 count: u32,
927 ) -> Result<ShiftSummary, EditorError> {
928 if count == 0 {
929 return Ok(ShiftSummary::default());
930 }
931
932 let mut summary = ShiftSummary::default();
933
934 self.begin_batch();
935
936 let vertices_to_delete: Vec<VertexId> = self
938 .graph
939 .vertices_in_sheet(sheet_id)
940 .filter(|&id| {
941 let coord = self.graph.get_coord(id);
942 coord.col() >= start && coord.col() < start + count
943 })
944 .collect();
945
946 for id in vertices_to_delete {
947 self.remove_vertex(id)?;
948 summary.vertices_deleted.push(id);
949 }
950
951 if let Some(logger) = &mut self.change_logger {
952 logger.begin_compound(format!(
953 "DeleteColumns sheet={sheet_id} start={start} count={count}"
954 ));
955 }
956 let vertices_to_shift: Vec<(VertexId, PackedCoord)> = self
958 .graph
959 .vertices_in_sheet(sheet_id)
960 .filter_map(|id| {
961 let coord = self.graph.get_coord(id);
962 if coord.col() >= start + count {
963 Some((id, coord))
964 } else {
965 None
966 }
967 })
968 .collect();
969
970 for (id, old_coord) in vertices_to_shift {
971 let new_coord = PackedCoord::new(old_coord.row(), old_coord.col() - count);
972 if self.has_logger() {
973 self.log_change(ChangeEvent::VertexMoved {
974 id,
975 old_coord,
976 new_coord,
977 });
978 }
979 self.move_vertex(id, new_coord)?;
980 summary.vertices_moved.push(id);
981 }
982
983 let op = ShiftOperation::DeleteColumns {
985 sheet_id,
986 start,
987 count,
988 };
989 let adjuster = ReferenceAdjuster::new();
990
991 let formula_vertices: Vec<VertexId> = self.graph.vertices_with_formulas().collect();
992
993 for id in formula_vertices {
994 if let Some(ast) = self.graph.get_formula(id) {
995 let adjusted = adjuster.adjust_ast(&ast, &op);
996 if format!("{ast:?}") != format!("{adjusted:?}") {
997 self.graph.update_vertex_formula(id, adjusted)?;
998 self.graph.mark_vertex_dirty(id);
999 summary.formulas_updated += 1;
1000 }
1001 }
1002 }
1003
1004 self.graph.adjust_named_ranges(&op)?;
1006
1007 if let Some(logger) = &mut self.change_logger {
1009 logger.end_compound();
1010 }
1011
1012 self.commit_batch();
1013
1014 Ok(summary)
1015 }
1016
1017 pub fn shift_rows(&mut self, sheet_id: SheetId, start_row: u32, delta: i32) {
1019 if delta == 0 {
1020 return;
1021 }
1022
1023 let change_event = ChangeEvent::SetValue {
1025 addr: CellRef {
1026 sheet_id,
1027 coord: Coord::new(start_row, 0, true, true),
1028 },
1029 old: None,
1030 new: LiteralValue::Text(format!("Row shift: start={start_row}, delta={delta}")),
1031 };
1032 self.log_change(change_event);
1033
1034 }
1037
1038 pub fn shift_columns(&mut self, sheet_id: SheetId, start_col: u32, delta: i32) {
1040 if delta == 0 {
1041 return;
1042 }
1043
1044 let change_event = ChangeEvent::SetValue {
1046 addr: CellRef {
1047 sheet_id,
1048 coord: Coord::new(0, start_col, true, true),
1049 },
1050 old: None,
1051 new: LiteralValue::Text(format!("Column shift: start={start_col}, delta={delta}")),
1052 };
1053 self.log_change(change_event);
1054
1055 }
1058
1059 pub fn set_cell_value(&mut self, cell_ref: CellRef, value: LiteralValue) -> VertexId {
1061 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1062
1063 let old_value = self
1065 .graph
1066 .get_vertex_id_for_address(&cell_ref)
1067 .and_then(|&id| self.graph.get_value(id));
1068
1069 match self.graph.set_cell_value(
1071 &sheet_name,
1072 cell_ref.coord.row,
1073 cell_ref.coord.col,
1074 value.clone(),
1075 ) {
1076 Ok(summary) => {
1077 let change_event = ChangeEvent::SetValue {
1079 addr: cell_ref,
1080 old: old_value,
1081 new: value,
1082 };
1083 self.log_change(change_event);
1084
1085 summary
1086 .affected_vertices
1087 .into_iter()
1088 .next()
1089 .unwrap_or(VertexId::new(0))
1090 }
1091 Err(_) => VertexId::new(0),
1092 }
1093 }
1094
1095 pub fn set_cell_formula(&mut self, cell_ref: CellRef, formula: ASTNode) -> VertexId {
1097 let sheet_name = self.graph.sheet_name(cell_ref.sheet_id).to_string();
1098
1099 let old_formula = self
1101 .graph
1102 .get_vertex_id_for_address(&cell_ref)
1103 .and_then(|&id| self.graph.get_formula(id));
1104
1105 match self.graph.set_cell_formula(
1107 &sheet_name,
1108 cell_ref.coord.row,
1109 cell_ref.coord.col,
1110 formula.clone(),
1111 ) {
1112 Ok(summary) => {
1113 let change_event = ChangeEvent::SetFormula {
1115 addr: cell_ref,
1116 old: old_formula,
1117 new: formula,
1118 };
1119 self.log_change(change_event);
1120
1121 summary
1122 .affected_vertices
1123 .into_iter()
1124 .next()
1125 .unwrap_or(VertexId::new(0))
1126 }
1127 Err(_) => VertexId::new(0),
1128 }
1129 }
1130
1131 pub fn set_range_values(
1135 &mut self,
1136 sheet_id: SheetId,
1137 start_row: u32,
1138 start_col: u32,
1139 values: &[Vec<LiteralValue>],
1140 ) -> Result<RangeSummary, EditorError> {
1141 let mut summary = RangeSummary::default();
1142
1143 self.begin_batch();
1144
1145 for (row_offset, row_values) in values.iter().enumerate() {
1146 for (col_offset, value) in row_values.iter().enumerate() {
1147 let row = start_row + row_offset as u32;
1148 let col = start_col + col_offset as u32;
1149
1150 let cell_ref = self.graph.make_cell_ref_internal(sheet_id, row, col);
1152
1153 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1154 self.graph.update_vertex_value(existing_id, value.clone());
1156 self.graph.mark_vertex_dirty(existing_id);
1157 summary.vertices_updated.push(existing_id);
1158 } else {
1159 let meta = VertexMeta::new(row, col, sheet_id, VertexKind::Cell);
1161 let id = self.add_vertex(meta);
1162 self.graph.update_vertex_value(id, value.clone());
1163 summary.vertices_created.push(id);
1164 }
1165
1166 summary.cells_affected += 1;
1167 }
1168 }
1169
1170 self.commit_batch();
1171
1172 Ok(summary)
1173 }
1174
1175 pub fn clear_range(
1177 &mut self,
1178 sheet_id: SheetId,
1179 start_row: u32,
1180 start_col: u32,
1181 end_row: u32,
1182 end_col: u32,
1183 ) -> Result<RangeSummary, EditorError> {
1184 let mut summary = RangeSummary::default();
1185
1186 self.begin_batch();
1187
1188 let vertices_in_range: Vec<_> = self
1190 .graph
1191 .vertices_in_sheet(sheet_id)
1192 .filter(|&id| {
1193 let coord = self.graph.get_coord(id);
1194 let row = coord.row();
1195 let col = coord.col();
1196 row >= start_row && row <= end_row && col >= start_col && col <= end_col
1197 })
1198 .collect();
1199
1200 for id in vertices_in_range {
1201 self.remove_vertex(id)?;
1202 summary.cells_affected += 1;
1203 }
1204
1205 self.commit_batch();
1206
1207 Ok(summary)
1208 }
1209
1210 pub fn copy_range(
1212 &mut self,
1213 sheet_id: SheetId,
1214 from_start_row: u32,
1215 from_start_col: u32,
1216 from_end_row: u32,
1217 from_end_col: u32,
1218 to_sheet_id: SheetId,
1219 to_row: u32,
1220 to_col: u32,
1221 ) -> Result<RangeSummary, EditorError> {
1222 let row_offset = to_row as i32 - from_start_row as i32;
1223 let col_offset = to_col as i32 - from_start_col as i32;
1224
1225 let mut summary = RangeSummary::default();
1226 let mut cell_data = Vec::new();
1227
1228 let vertices_in_range: Vec<_> = self
1230 .graph
1231 .vertices_in_sheet(sheet_id)
1232 .filter(|&id| {
1233 let coord = self.graph.get_coord(id);
1234 let row = coord.row();
1235 let col = coord.col();
1236 row >= from_start_row
1237 && row <= from_end_row
1238 && col >= from_start_col
1239 && col <= from_end_col
1240 })
1241 .collect();
1242
1243 for id in vertices_in_range {
1244 let coord = self.graph.get_coord(id);
1245 let row = coord.row();
1246 let col = coord.col();
1247
1248 if let Some(formula) = self.graph.get_formula(id) {
1250 cell_data.push((
1251 row - from_start_row,
1252 col - from_start_col,
1253 CellData::Formula(formula),
1254 ));
1255 } else if let Some(value) = self.graph.get_value(id) {
1256 cell_data.push((
1257 row - from_start_row,
1258 col - from_start_col,
1259 CellData::Value(value),
1260 ));
1261 }
1262 }
1263
1264 self.begin_batch();
1265
1266 for (row_idx, col_idx, data) in cell_data {
1268 let dest_row = (to_row as i32 + row_idx as i32) as u32;
1269 let dest_col = (to_col as i32 + col_idx as i32) as u32;
1270
1271 match data {
1272 CellData::Value(value) => {
1273 let cell_ref =
1274 self.graph
1275 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
1276
1277 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1278 self.graph.update_vertex_value(existing_id, value);
1279 self.graph.mark_vertex_dirty(existing_id);
1280 summary.vertices_updated.push(existing_id);
1281 } else {
1282 let meta =
1283 VertexMeta::new(dest_row, dest_col, to_sheet_id, VertexKind::Cell);
1284 let id = self.add_vertex(meta);
1285 self.graph.update_vertex_value(id, value);
1286 summary.vertices_created.push(id);
1287 }
1288 }
1289 CellData::Formula(formula) => {
1290 let adjuster = RelativeReferenceAdjuster::new(row_offset, col_offset);
1292 let adjusted = adjuster.adjust_formula(&formula, sheet_id, to_sheet_id);
1293
1294 let cell_ref =
1295 self.graph
1296 .make_cell_ref_internal(to_sheet_id, dest_row, dest_col);
1297
1298 if let Some(&existing_id) = self.graph.get_vertex_id_for_address(&cell_ref) {
1299 self.graph.update_vertex_formula(existing_id, adjusted)?;
1300 summary.vertices_updated.push(existing_id);
1301 } else {
1302 let meta = VertexMeta::new(
1303 dest_row,
1304 dest_col,
1305 to_sheet_id,
1306 VertexKind::FormulaScalar,
1307 );
1308 let id = self.add_vertex(meta);
1309 self.graph.update_vertex_formula(id, adjusted)?;
1310 summary.vertices_created.push(id);
1311 }
1312 }
1313 }
1314
1315 summary.cells_affected += 1;
1316 }
1317
1318 self.commit_batch();
1319
1320 Ok(summary)
1321 }
1322
1323 pub fn move_range(
1325 &mut self,
1326 sheet_id: SheetId,
1327 from_start_row: u32,
1328 from_start_col: u32,
1329 from_end_row: u32,
1330 from_end_col: u32,
1331 to_sheet_id: SheetId,
1332 to_row: u32,
1333 to_col: u32,
1334 ) -> Result<RangeSummary, EditorError> {
1335 let mut summary = self.copy_range(
1337 sheet_id,
1338 from_start_row,
1339 from_start_col,
1340 from_end_row,
1341 from_end_col,
1342 to_sheet_id,
1343 to_row,
1344 to_col,
1345 )?;
1346
1347 let clear_summary = self.clear_range(
1349 sheet_id,
1350 from_start_row,
1351 from_start_col,
1352 from_end_row,
1353 from_end_col,
1354 )?;
1355
1356 summary.cells_moved = clear_summary.cells_affected;
1357
1358 let row_offset = to_row as i32 - from_start_row as i32;
1360 let col_offset = to_col as i32 - from_start_col as i32;
1361
1362 let all_formula_vertices: Vec<_> = self.graph.vertices_with_formulas().collect();
1364
1365 for formula_id in all_formula_vertices {
1366 if let Some(formula) = self.graph.get_formula(formula_id) {
1367 let adjuster = MoveReferenceAdjuster::new(
1368 sheet_id,
1369 from_start_row,
1370 from_start_col,
1371 from_end_row,
1372 from_end_col,
1373 to_sheet_id,
1374 row_offset,
1375 col_offset,
1376 );
1377
1378 if let Some(adjusted) = adjuster.adjust_if_references(&formula) {
1379 self.graph.update_vertex_formula(formula_id, adjusted)?;
1380 }
1381 }
1382 }
1383
1384 Ok(summary)
1385 }
1386
1387 pub fn define_name(
1389 &mut self,
1390 name: &str,
1391 definition: NamedDefinition,
1392 scope: NameScope,
1393 ) -> Result<(), EditorError> {
1394 self.graph.define_name(name, definition.clone(), scope)?;
1395
1396 self.log_change(ChangeEvent::DefineName {
1397 name: name.to_string(),
1398 scope,
1399 definition,
1400 });
1401
1402 Ok(())
1403 }
1404
1405 pub fn define_name_for_cell(
1407 &mut self,
1408 name: &str,
1409 sheet_name: &str,
1410 row: u32,
1411 col: u32,
1412 scope: NameScope,
1413 ) -> Result<(), EditorError> {
1414 let sheet_id = self
1415 .graph
1416 .sheet_id(sheet_name)
1417 .ok_or_else(|| EditorError::InvalidName {
1418 name: sheet_name.to_string(),
1419 reason: "Sheet not found".to_string(),
1420 })?;
1421 let cell_ref = CellRef::new(sheet_id, Coord::new(row, col, true, true));
1422 self.define_name(name, NamedDefinition::Cell(cell_ref), scope)
1423 }
1424
1425 pub fn define_name_for_range(
1427 &mut self,
1428 name: &str,
1429 sheet_name: &str,
1430 start_row: u32,
1431 start_col: u32,
1432 end_row: u32,
1433 end_col: u32,
1434 scope: NameScope,
1435 ) -> Result<(), EditorError> {
1436 let sheet_id = self
1437 .graph
1438 .sheet_id(sheet_name)
1439 .ok_or_else(|| EditorError::InvalidName {
1440 name: sheet_name.to_string(),
1441 reason: "Sheet not found".to_string(),
1442 })?;
1443 let start = CellRef::new(sheet_id, Coord::new(start_row, start_col, true, true));
1444 let end = CellRef::new(sheet_id, Coord::new(end_row, end_col, true, true));
1445 let range_ref = crate::reference::RangeRef::new(start, end);
1446 self.define_name(name, NamedDefinition::Range(range_ref), scope)
1447 }
1448
1449 pub fn update_name(
1451 &mut self,
1452 name: &str,
1453 new_definition: NamedDefinition,
1454 scope: NameScope,
1455 ) -> Result<(), EditorError> {
1456 let old_definition = self
1458 .graph
1459 .resolve_name(
1460 name,
1461 match scope {
1462 NameScope::Sheet(id) => id,
1463 NameScope::Workbook => 0,
1464 },
1465 )
1466 .cloned();
1467
1468 self.graph
1469 .update_name(name, new_definition.clone(), scope)?;
1470
1471 if let Some(old_def) = old_definition {
1472 self.log_change(ChangeEvent::UpdateName {
1473 name: name.to_string(),
1474 scope,
1475 old_definition: old_def,
1476 new_definition,
1477 });
1478 }
1479
1480 Ok(())
1481 }
1482
1483 pub fn delete_name(&mut self, name: &str, scope: NameScope) -> Result<(), EditorError> {
1485 self.graph.delete_name(name, scope)?;
1486
1487 let old_def = if self.has_logger() {
1488 self.graph
1489 .resolve_name(
1490 name,
1491 match scope {
1492 NameScope::Sheet(id) => id,
1493 NameScope::Workbook => 0,
1494 },
1495 )
1496 .cloned()
1497 } else {
1498 None
1499 };
1500 self.log_change(ChangeEvent::DeleteName {
1501 name: name.to_string(),
1502 scope,
1503 old_definition: old_def,
1504 });
1505
1506 Ok(())
1507 }
1508}
1509
1510enum CellData {
1512 Value(LiteralValue),
1513 Formula(ASTNode),
1514}
1515
1516struct RelativeReferenceAdjuster {
1518 row_offset: i32,
1519 col_offset: i32,
1520}
1521
1522impl RelativeReferenceAdjuster {
1523 fn new(row_offset: i32, col_offset: i32) -> Self {
1524 Self {
1525 row_offset,
1526 col_offset,
1527 }
1528 }
1529
1530 fn adjust_formula(
1531 &self,
1532 formula: &ASTNode,
1533 _from_sheet: SheetId,
1534 _to_sheet: SheetId,
1535 ) -> ASTNode {
1536 formula.clone()
1539 }
1540}
1541
1542struct MoveReferenceAdjuster {
1544 from_sheet_id: SheetId,
1545 from_start_row: u32,
1546 from_start_col: u32,
1547 from_end_row: u32,
1548 from_end_col: u32,
1549 to_sheet_id: SheetId,
1550 row_offset: i32,
1551 col_offset: i32,
1552}
1553
1554impl MoveReferenceAdjuster {
1555 fn new(
1556 from_sheet_id: SheetId,
1557 from_start_row: u32,
1558 from_start_col: u32,
1559 from_end_row: u32,
1560 from_end_col: u32,
1561 to_sheet_id: SheetId,
1562 row_offset: i32,
1563 col_offset: i32,
1564 ) -> Self {
1565 Self {
1566 from_sheet_id,
1567 from_start_row,
1568 from_start_col,
1569 from_end_row,
1570 from_end_col,
1571 to_sheet_id,
1572 row_offset,
1573 col_offset,
1574 }
1575 }
1576
1577 fn adjust_if_references(&self, formula: &ASTNode) -> Option<ASTNode> {
1578 None
1582 }
1583}
1584
1585impl<'g> Drop for VertexEditor<'g> {
1586 fn drop(&mut self) {
1587 if self.batch_mode {
1589 self.commit_batch();
1590 }
1591 }
1592}
1593
1594#[cfg(test)]
1595mod tests {
1596 use super::*;
1597 use crate::engine::graph::editor::change_log::{ChangeEvent, ChangeLog};
1598 use crate::reference::Coord;
1599
1600 fn create_test_graph() -> DependencyGraph {
1601 DependencyGraph::new()
1602 }
1603
1604 #[test]
1605 fn test_vertex_editor_creation() {
1606 let mut graph = create_test_graph();
1607 let editor = VertexEditor::new(&mut graph);
1608 assert!(!editor.has_logger());
1609 assert!(!editor.batch_mode);
1610 }
1611
1612 #[test]
1613 fn test_vertex_editor_with_logger() {
1614 let mut graph = create_test_graph();
1615 let mut log = ChangeLog::new();
1616 let editor = VertexEditor::with_logger(&mut graph, &mut log);
1617 assert!(editor.has_logger());
1618 assert!(!editor.batch_mode);
1619 }
1620
1621 #[test]
1622 fn test_add_vertex() {
1623 let mut graph = create_test_graph();
1624 let mut editor = VertexEditor::new(&mut graph);
1625
1626 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell).dirty();
1627 let vertex_id = editor.add_vertex(meta);
1628
1629 assert!(vertex_id.0 > 0);
1631 }
1632
1633 #[test]
1634 fn test_batch_operations() {
1635 let mut graph = create_test_graph();
1636 let mut editor = VertexEditor::new(&mut graph);
1637
1638 assert!(!editor.batch_mode);
1639 editor.begin_batch();
1640 assert!(editor.batch_mode);
1641
1642 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
1644 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::Cell);
1645
1646 let id1 = editor.add_vertex(meta1);
1647 let id2 = editor.add_vertex(meta2);
1648
1649 assert!(editor.add_edge(id1, id2));
1651
1652 editor.commit_batch();
1653 assert!(!editor.batch_mode);
1654 }
1655
1656 #[test]
1657 fn test_remove_vertex() {
1658 let mut graph = create_test_graph();
1659 let mut editor = VertexEditor::new(&mut graph);
1660
1661 let meta = VertexMeta::new(3, 4, 0, VertexKind::Cell).dirty();
1662 let vertex_id = editor.add_vertex(meta);
1663
1664 assert!(editor.remove_vertex(vertex_id).is_ok());
1666 }
1667
1668 #[test]
1669 fn test_edge_operations() {
1670 let mut graph = create_test_graph();
1671 let mut editor = VertexEditor::new(&mut graph);
1672
1673 let meta1 = VertexMeta::new(1, 1, 0, VertexKind::Cell);
1674 let meta2 = VertexMeta::new(2, 2, 0, VertexKind::FormulaScalar);
1675
1676 let id1 = editor.add_vertex(meta1);
1677 let id2 = editor.add_vertex(meta2);
1678
1679 assert!(editor.add_edge(id1, id2));
1681
1682 assert!(!editor.add_edge(id1, id1));
1684
1685 assert!(editor.remove_edge(id1, id2));
1687 }
1688
1689 #[test]
1690 fn test_set_cell_value() {
1691 let mut graph = create_test_graph();
1692 let mut log = ChangeLog::new();
1693
1694 let cell_ref = CellRef {
1695 sheet_id: 0,
1696 coord: Coord::new(2, 3, true, true),
1697 };
1698 let value = LiteralValue::Number(42.0);
1699
1700 let vertex_id = {
1701 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
1702 editor.set_cell_value(cell_ref, value.clone())
1703 };
1704
1705 assert!(vertex_id.0 > 0);
1707
1708 assert_eq!(log.len(), 1);
1710 match &log.events()[0] {
1711 ChangeEvent::SetValue { addr, new, .. } => {
1712 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
1713 assert_eq!(addr.coord.row, cell_ref.coord.row);
1714 assert_eq!(addr.coord.col, cell_ref.coord.col);
1715 assert_eq!(new, &value);
1716 }
1717 _ => panic!("Expected SetValue event"),
1718 }
1719 }
1720
1721 #[test]
1722 fn test_set_cell_formula() {
1723 let mut graph = create_test_graph();
1724 let mut log = ChangeLog::new();
1725
1726 let cell_ref = CellRef {
1727 sheet_id: 0,
1728 coord: Coord::new(1, 1, true, true),
1729 };
1730
1731 use formualizer_parse::parser::ASTNodeType;
1732 let formula = formualizer_parse::parser::ASTNode {
1733 node_type: ASTNodeType::Literal(LiteralValue::Number(100.0)),
1734 source_token: None,
1735 contains_volatile: false,
1736 };
1737
1738 let vertex_id = {
1739 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
1740 editor.set_cell_formula(cell_ref, formula.clone())
1741 };
1742
1743 assert!(vertex_id.0 > 0);
1745
1746 assert_eq!(log.len(), 1);
1748 match &log.events()[0] {
1749 ChangeEvent::SetFormula { addr, .. } => {
1750 assert_eq!(addr.sheet_id, cell_ref.sheet_id);
1751 assert_eq!(addr.coord.row, cell_ref.coord.row);
1752 assert_eq!(addr.coord.col, cell_ref.coord.col);
1753 }
1754 _ => panic!("Expected SetFormula event"),
1755 }
1756 }
1757
1758 #[test]
1759 fn test_shift_rows() {
1760 let mut graph = create_test_graph();
1761 let mut log = ChangeLog::new();
1762
1763 {
1764 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
1765
1766 let cell1 = CellRef {
1768 sheet_id: 0,
1769 coord: Coord::new(5, 1, true, true),
1770 };
1771 let cell2 = CellRef {
1772 sheet_id: 0,
1773 coord: Coord::new(10, 1, true, true),
1774 };
1775 let cell3 = CellRef {
1776 sheet_id: 0,
1777 coord: Coord::new(15, 1, true, true),
1778 };
1779
1780 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
1781 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
1782 editor.set_cell_value(cell3, LiteralValue::Number(3.0));
1783 }
1784
1785 log.clear();
1787
1788 {
1789 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
1790 editor.shift_rows(0, 10, 2);
1792 }
1793
1794 assert_eq!(log.len(), 1);
1796 match &log.events()[0] {
1797 ChangeEvent::SetValue { addr, new, .. } => {
1798 assert_eq!(addr.sheet_id, 0);
1799 assert_eq!(addr.coord.row, 10);
1800 if let LiteralValue::Text(msg) = new {
1801 assert!(msg.contains("Row shift"));
1802 assert!(msg.contains("start=10"));
1803 assert!(msg.contains("delta=2"));
1804 }
1805 }
1806 _ => panic!("Expected SetValue event for row shift"),
1807 }
1808 }
1809
1810 #[test]
1811 fn test_shift_columns() {
1812 let mut graph = create_test_graph();
1813 let mut log = ChangeLog::new();
1814
1815 {
1816 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
1817
1818 let cell1 = CellRef {
1820 sheet_id: 0,
1821 coord: Coord::new(1, 5, true, true),
1822 };
1823 let cell2 = CellRef {
1824 sheet_id: 0,
1825 coord: Coord::new(1, 10, true, true),
1826 };
1827
1828 editor.set_cell_value(cell1, LiteralValue::Number(1.0));
1829 editor.set_cell_value(cell2, LiteralValue::Number(2.0));
1830 }
1831
1832 log.clear();
1834
1835 {
1836 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
1837 editor.shift_columns(0, 8, 3);
1839 }
1840
1841 assert_eq!(log.len(), 1);
1843 match &log.events()[0] {
1844 ChangeEvent::SetValue { addr, new, .. } => {
1845 assert_eq!(addr.sheet_id, 0);
1846 assert_eq!(addr.coord.col, 8);
1847 if let LiteralValue::Text(msg) = new {
1848 assert!(msg.contains("Column shift"));
1849 assert!(msg.contains("start=8"));
1850 assert!(msg.contains("delta=3"));
1851 }
1852 }
1853 _ => panic!("Expected SetValue event for column shift"),
1854 }
1855 }
1856
1857 #[test]
1858 fn test_move_vertex() {
1859 let mut graph = create_test_graph();
1860 let mut editor = VertexEditor::new(&mut graph);
1861
1862 let meta = VertexMeta::new(5, 10, 0, VertexKind::Cell);
1863 let vertex_id = editor.add_vertex(meta);
1864
1865 assert!(
1867 editor
1868 .move_vertex(vertex_id, PackedCoord::new(8, 12))
1869 .is_ok()
1870 );
1871
1872 assert!(
1874 editor
1875 .move_vertex(vertex_id, PackedCoord::new(8, 12))
1876 .is_ok()
1877 );
1878 }
1879
1880 #[test]
1881 fn test_vertex_meta_builder() {
1882 let meta = VertexMeta::new(1, 2, 3, VertexKind::FormulaScalar)
1883 .dirty()
1884 .volatile()
1885 .with_flags(0x08);
1886
1887 assert_eq!(meta.coord.row(), 1);
1888 assert_eq!(meta.coord.col(), 2);
1889 assert_eq!(meta.sheet_id, 3);
1890 assert_eq!(meta.kind, VertexKind::FormulaScalar);
1891 assert_eq!(meta.flags, 0x08); }
1893
1894 #[test]
1895 fn test_change_log_management() {
1896 let mut graph = create_test_graph();
1897 let mut log = ChangeLog::new();
1898
1899 {
1900 let mut editor = VertexEditor::with_logger(&mut graph, &mut log);
1901 let cell_ref = CellRef {
1902 sheet_id: 0,
1903 coord: Coord::new(0, 0, true, true),
1904 };
1905 editor.set_cell_value(cell_ref, LiteralValue::Number(1.0));
1906 editor.set_cell_value(cell_ref, LiteralValue::Number(2.0));
1907 }
1908
1909 assert_eq!(log.len(), 2);
1910
1911 log.clear();
1912 assert_eq!(log.len(), 0);
1913 }
1914
1915 #[test]
1916 fn test_editor_drop_commits_batch() {
1917 let mut graph = create_test_graph();
1918 {
1919 let mut editor = VertexEditor::new(&mut graph);
1920 editor.begin_batch();
1921
1922 let meta = VertexMeta::new(1, 1, 0, VertexKind::Cell);
1923 editor.add_vertex(meta);
1924
1925 }
1927
1928 }
1930}