1use super::*;
2use formualizer_common::parse_a1_1based;
3
4#[inline]
5fn normalize_name_key(name: &str) -> String {
6 name.to_lowercase()
7}
8
9fn is_valid_excel_name(name: &str) -> bool {
11 if name.is_empty() || name.len() > 255 {
19 return false;
20 }
21
22 if parse_a1_1based(name).is_ok() {
23 return false;
24 }
25
26 let mut chars = name.chars();
27
28 if let Some(first) = chars.next()
30 && !first.is_alphabetic()
31 && first != '_'
32 && first != '\\'
33 {
34 return false;
35 }
36
37 for c in chars {
39 if !c.is_alphanumeric() && c != '.' && c != '_' {
40 return false;
41 }
42 }
43
44 true
45}
46
47fn adjust_named_definition(
52 definition: &mut NamedDefinition,
53 adjuster: &crate::engine::graph::editor::reference_adjuster::ReferenceAdjuster,
54 operation: &crate::engine::graph::editor::reference_adjuster::ShiftOperation,
55 context: &crate::engine::graph::editor::reference_adjuster::ReferenceContext<'_>,
56) -> Result<(), ExcelError> {
57 use crate::engine::graph::editor::reference_adjuster::AbsShiftPolicy;
58 let mut invalidated = false;
59 match definition {
60 NamedDefinition::Cell(cell_ref) => {
61 if let Some(adjusted) =
62 adjuster.adjust_cell_ref_with_policy(cell_ref, operation, AbsShiftPolicy::Track)
63 {
64 *cell_ref = adjusted;
65 } else {
66 invalidated = true;
67 }
68 }
69 NamedDefinition::Range(range_ref) => {
70 let adjusted_start = adjuster.adjust_cell_ref_with_policy(
71 &range_ref.start,
72 operation,
73 AbsShiftPolicy::Track,
74 );
75 let adjusted_end = adjuster.adjust_cell_ref_with_policy(
76 &range_ref.end,
77 operation,
78 AbsShiftPolicy::Track,
79 );
80
81 if let (Some(start), Some(end)) = (adjusted_start, adjusted_end) {
82 range_ref.start = start;
83 range_ref.end = end;
84 } else {
85 invalidated = true;
86 }
87 }
88 NamedDefinition::Literal(_) => {
89 }
91 NamedDefinition::Formula {
92 ast,
93 dependencies,
94 range_deps,
95 } => {
96 let adjusted_ast = adjuster.adjust_ast_with_policy_in_context(
97 ast,
98 operation,
99 AbsShiftPolicy::Track,
100 context,
101 );
102 *ast = adjusted_ast;
103
104 dependencies.clear();
105 range_deps.clear();
106 }
107 }
108 if invalidated {
109 *definition = NamedDefinition::Formula {
110 ast: formualizer_parse::parser::ASTNode::new(
111 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Error(
112 ExcelError::new(ExcelErrorKind::Ref),
113 )),
114 None,
115 ),
116 dependencies: Vec::new(),
117 range_deps: Vec::new(),
118 };
119 }
120 Ok(())
121}
122
123impl DependencyGraph {
124 #[inline]
125 pub(crate) fn name_lookup_key(&self, name: &str) -> String {
126 if self.config.case_sensitive_names {
127 name.to_string()
128 } else {
129 normalize_name_key(name)
130 }
131 }
132
133 fn canonical_name_in_scope(&self, scope: NameScope, name: &str) -> Option<String> {
134 let key = self.name_lookup_key(name);
135 match scope {
136 NameScope::Workbook => self.named_ranges_lookup.get(&key).cloned(),
137 NameScope::Sheet(sheet_id) => self
138 .sheet_named_ranges_lookup
139 .get(&(sheet_id, key))
140 .cloned(),
141 }
142 }
143
144 pub(super) fn next_symbol_addr(&mut self) -> VertexAddr {
150 let seq = self.symbol_vertex_seq;
151 self.symbol_vertex_seq = self.symbol_vertex_seq.wrapping_add(1);
152 VertexAddr::symbol(SymbolAddr::new(seq))
153 }
154
155 pub(super) fn allocate_symbol_vertex(
162 &mut self,
163 kind: VertexKind,
164 scope_sheet_id: SheetId,
165 ) -> VertexId {
166 let addr = self.next_symbol_addr();
167 let vertex_id = self.store.allocate(addr, scope_sheet_id, 0x01);
168 self.store.set_kind(vertex_id, kind);
169 #[cfg(any(test, feature = "legacy_oracle"))]
170 self.edges.add_vertex(addr, vertex_id.0);
171 vertex_id
172 }
173
174 pub(super) fn allocate_name_vertex(&mut self, scope: NameScope) -> VertexId {
175 let scope_sheet_id = match scope {
178 NameScope::Sheet(id) => id,
179 NameScope::Workbook => self.default_sheet_id,
180 };
181 let vertex_id = self.allocate_symbol_vertex(VertexKind::NamedScalar, scope_sheet_id);
182 self.mark_vertex_dirty(vertex_id);
183 vertex_id
184 }
185
186 pub(crate) fn validate_define_name(
189 &self,
190 name: &str,
191 scope: NameScope,
192 ) -> Result<(), ExcelError> {
193 if !is_valid_excel_name(name) {
194 return Err(
195 ExcelError::new(ExcelErrorKind::Name).with_message(format!("Invalid name: {name}"))
196 );
197 }
198
199 let lookup_key = self.name_lookup_key(name);
200 match scope {
201 NameScope::Workbook => {
202 if let Some(existing) = self.named_ranges_lookup.get(&lookup_key) {
203 return Err(ExcelError::new(ExcelErrorKind::Name).with_message(format!(
204 "Name collision under normalization: '{name}' conflicts with '{existing}'"
205 )));
206 }
207 }
208 NameScope::Sheet(sheet_id) => {
209 if let Some(existing) = self.sheet_named_ranges_lookup.get(&(sheet_id, lookup_key))
210 {
211 return Err(ExcelError::new(ExcelErrorKind::Name).with_message(format!(
212 "Name collision under normalization in sheet: '{name}' conflicts with '{existing}'"
213 )));
214 }
215 }
216 }
217 Ok(())
218 }
219
220 pub(crate) fn validate_existing_name(
221 &self,
222 name: &str,
223 scope: NameScope,
224 ) -> Result<(), ExcelError> {
225 self.canonical_name_in_scope(scope, name)
226 .map(|_| ())
227 .ok_or_else(|| {
228 ExcelError::new(ExcelErrorKind::Name)
229 .with_message(format!("Name not found: {name}"))
230 })
231 }
232
233 pub fn define_name(
235 &mut self,
236 name: &str,
237 definition: NamedDefinition,
238 scope: NameScope,
239 ) -> Result<(), ExcelError> {
240 self.validate_define_name(name, scope)?;
241
242 let mut final_definition = definition;
243 if let NamedDefinition::Formula { ref ast, .. } = final_definition {
245 let (deps, range_deps, _, _) = self.extract_dependencies(
246 ast,
247 match scope {
248 NameScope::Sheet(id) => id,
249 NameScope::Workbook => self.default_sheet_id,
250 },
251 )?;
252 final_definition = NamedDefinition::Formula {
253 ast: ast.clone(),
254 dependencies: deps,
255 range_deps,
256 };
257 }
258
259 let vertex_id = self.allocate_name_vertex(scope);
261
262 let named_range = NamedRange {
263 definition: final_definition,
264 scope,
265 #[cfg(any(test, feature = "legacy_oracle"))]
266 dependents: FxHashSet::default(),
267 vertex: vertex_id,
268 };
269
270 if matches!(named_range.definition, NamedDefinition::Range(_)) {
271 self.store.set_kind(vertex_id, VertexKind::NamedArray);
272 } else {
273 self.store.set_kind(vertex_id, VertexKind::NamedScalar);
274 }
275
276 let referenced_names =
278 self.rebuild_name_dependencies(vertex_id, &named_range.definition, scope)?;
279 if !referenced_names.is_empty() {
280 self.attach_vertex_to_names(vertex_id, &referenced_names);
281 }
282
283 let key = name.to_string();
284
285 match scope {
286 NameScope::Workbook => {
287 self.named_ranges.insert(key.clone(), named_range);
288 self.named_ranges_lookup
289 .insert(self.name_lookup_key(&key), key.clone());
290 }
291 NameScope::Sheet(id) => {
292 self.sheet_named_ranges
293 .insert((id, key.clone()), named_range);
294 self.sheet_named_ranges_lookup
295 .insert((id, self.name_lookup_key(&key)), key.clone());
296 }
297 }
298
299 self.name_vertex_lookup.insert(vertex_id, (scope, key));
300 self.authority_note_symbol(Some(vertex_id));
303 self.resolve_pending_name_references(scope, name);
304 self.bump_symbol_revision();
305
306 Ok(())
307 }
308
309 pub fn named_ranges_iter(&self) -> impl Iterator<Item = (&String, &NamedRange)> {
311 self.named_ranges.iter()
312 }
313
314 pub fn sheet_named_ranges_iter(
316 &self,
317 ) -> impl Iterator<Item = (&(SheetId, String), &NamedRange)> {
318 self.sheet_named_ranges.iter()
319 }
320
321 pub fn resolve_name_entry_in_scope(&self, name: &str, scope: NameScope) -> Option<&NamedRange> {
333 let workbook_entry = || {
334 if self.config.case_sensitive_names {
335 self.named_ranges.get(name)
336 } else {
337 self.named_ranges_lookup
338 .get(&self.name_lookup_key(name))
339 .and_then(|canon| self.named_ranges.get(canon))
340 }
341 };
342
343 match scope {
344 NameScope::Workbook => workbook_entry(),
345 NameScope::Sheet(current_sheet) => {
346 if self.config.case_sensitive_names {
347 self.sheet_named_ranges
348 .get(&(current_sheet, name.to_string()))
349 .or_else(workbook_entry)
350 } else {
351 let key = self.name_lookup_key(name);
352 self.sheet_named_ranges_lookup
353 .get(&(current_sheet, key))
354 .and_then(|canon| {
355 self.sheet_named_ranges.get(&(current_sheet, canon.clone()))
356 })
357 .or_else(workbook_entry)
358 }
359 }
360 }
361 }
362
363 pub fn resolve_name_entry(&self, name: &str, current_sheet: SheetId) -> Option<&NamedRange> {
367 self.resolve_name_entry_in_scope(name, NameScope::Sheet(current_sheet))
368 }
369
370 pub fn resolve_name(&self, name: &str, current_sheet: SheetId) -> Option<&NamedDefinition> {
372 self.resolve_name_entry(name, current_sheet)
373 .map(|nr| &nr.definition)
374 }
375
376 pub(crate) fn name_key_for_vertex(&self, vertex: VertexId) -> Option<String> {
380 self.name_vertex_lookup
381 .get(&vertex)
382 .map(|(_, name)| self.name_lookup_key(name))
383 }
384
385 pub fn named_range_by_vertex(&self, vertex: VertexId) -> Option<&NamedRange> {
386 self.name_vertex_lookup
387 .get(&vertex)
388 .and_then(|(scope, name)| match scope {
389 NameScope::Workbook => self.named_ranges.get(name),
390 NameScope::Sheet(sheet_id) => {
391 self.sheet_named_ranges.get(&(*sheet_id, name.clone()))
392 }
393 })
394 }
395
396 pub fn update_name(
398 &mut self,
399 name: &str,
400 new_definition: NamedDefinition,
401 scope: NameScope,
402 ) -> Result<(), ExcelError> {
403 let Some(canon_name) = self.canonical_name_in_scope(scope, name) else {
404 return Err(ExcelError::new(ExcelErrorKind::Name)
405 .with_message(format!("Name not found: {name}")));
406 };
407
408 let name_vertex = match scope {
410 NameScope::Workbook => self.named_ranges.get(&canon_name).map(|nr| nr.vertex),
411 NameScope::Sheet(id) => self
412 .sheet_named_ranges
413 .get(&(id, canon_name.clone()))
414 .map(|nr| nr.vertex),
415 };
416 let dependents_to_dirty = name_vertex.map(|v| self.authority_symbol_readers(v));
418
419 if let Some(dependents) = dependents_to_dirty {
420 self.mark_dirty_many(&dependents);
426
427 let named_range = match scope {
429 NameScope::Workbook => self.named_ranges.get_mut(&canon_name),
430 NameScope::Sheet(id) => self.sheet_named_ranges.get_mut(&(id, canon_name.clone())),
431 };
432
433 let mut update_data: Option<(VertexId, NameScope, NamedDefinition, bool)> = None;
434 if let Some(named_range) = named_range {
435 named_range.definition = new_definition;
436 let is_range = matches!(named_range.definition, NamedDefinition::Range(_));
437 update_data = Some((
438 named_range.vertex,
439 named_range.scope,
440 named_range.definition.clone(),
441 is_range,
442 ));
443 }
444
445 if let Some((vertex, scope_value, definition_snapshot, is_range)) = update_data {
446 self.detach_vertex_from_names(vertex);
447
448 if is_range {
449 self.store.set_kind(vertex, VertexKind::NamedArray);
450 } else {
451 self.store.set_kind(vertex, VertexKind::NamedScalar);
452 }
453
454 let referenced_names =
455 self.rebuild_name_dependencies(vertex, &definition_snapshot, scope_value)?;
456 if !referenced_names.is_empty() {
457 self.attach_vertex_to_names(vertex, &referenced_names);
458 }
459 self.authority_note_symbol(Some(vertex));
462 self.mark_dirty_many(&[vertex]);
463 }
464
465 self.bump_symbol_revision();
466 Ok(())
467 } else {
468 Err(ExcelError::new(ExcelErrorKind::Name)
469 .with_message(format!("Name not found: {name}")))
470 }
471 }
472
473 pub fn delete_name(&mut self, name: &str, scope: NameScope) -> Result<(), ExcelError> {
475 let Some(canon_name) = self.canonical_name_in_scope(scope, name) else {
476 return Err(ExcelError::new(ExcelErrorKind::Name)
477 .with_message(format!("Name not found: {name}")));
478 };
479
480 let named_range = match scope {
481 NameScope::Workbook => {
482 let removed = self.named_ranges.remove(&canon_name);
483 let key = self.name_lookup_key(&canon_name);
484 self.named_ranges_lookup.remove(&key);
485 removed
486 }
487 NameScope::Sheet(id) => {
488 let removed = self.sheet_named_ranges.remove(&(id, canon_name.clone()));
489 let key = self.name_lookup_key(&canon_name);
490 self.sheet_named_ranges_lookup.remove(&(id, key));
491 removed
492 }
493 };
494
495 if let Some(named_range) = named_range {
496 let affected: FxHashSet<VertexId> = self
499 .authority_symbol_readers(named_range.vertex)
500 .into_iter()
501 .collect();
502 let formulas_to_rebuild = affected
503 .iter()
504 .filter(|&&vertex_id| self.get_cell_ref_for_vertex(vertex_id).is_some())
505 .filter_map(|&vertex_id| self.get_formula(vertex_id).map(|ast| (vertex_id, ast)))
506 .collect::<Vec<_>>();
507 let names_to_rebuild = affected
513 .iter()
514 .filter(|&&vertex_id| vertex_id != named_range.vertex)
515 .filter_map(|&vertex_id| {
516 self.named_range_by_vertex(vertex_id)
517 .map(|nr| (vertex_id, nr.definition.clone(), nr.scope))
518 })
519 .collect::<Vec<_>>();
520 let dirty_sources = affected
521 .iter()
522 .copied()
523 .filter(|&vertex_id| vertex_id != named_range.vertex)
524 .collect::<Vec<_>>();
525 #[cfg(any(test, feature = "legacy_oracle"))]
526 for vertex_id in affected {
527 if let Some(names) = self.vertex_to_names.get_mut(&vertex_id) {
528 names.retain(|vid| *vid != named_range.vertex);
529 if names.is_empty() {
530 self.vertex_to_names.remove(&vertex_id);
531 }
532 }
533 }
534 self.mark_named_vertex_deleted(&named_range);
535 for (vertex_id, ast) in formulas_to_rebuild {
540 self.rebuild_formula_dependencies(vertex_id, &ast);
541 }
542 for (vertex_id, definition, name_scope) in names_to_rebuild {
543 let referenced_names =
544 self.rebuild_name_dependencies(vertex_id, &definition, name_scope)?;
545 if !referenced_names.is_empty() {
546 self.attach_vertex_to_names(vertex_id, &referenced_names);
547 }
548 }
549 self.authority_note_symbol(Some(named_range.vertex));
553 self.mark_dirty_many(&dirty_sources);
554 self.bump_symbol_revision();
555 Ok(())
556 } else {
557 Err(ExcelError::new(ExcelErrorKind::Name)
558 .with_message(format!("Name not found: {name}")))
559 }
560 }
561
562 pub(super) fn detach_vertex_from_names(&mut self, vertex: VertexId) {
563 #[cfg(not(any(test, feature = "legacy_oracle")))]
564 let _ = (&vertex,);
565 #[cfg(any(test, feature = "legacy_oracle"))]
566 {
567 if let Some(prior) = self.vertex_to_names.remove(&vertex) {
568 for name_vertex in prior {
569 if let Some((scope, name)) = self.name_vertex_lookup.get(&name_vertex).cloned()
570 {
571 match scope {
572 NameScope::Workbook => {
573 if let Some(entry) = self.named_ranges.get_mut(&name) {
574 entry.dependents.remove(&vertex);
575 }
576 }
577 NameScope::Sheet(sheet_id) => {
578 if let Some(entry) =
579 self.sheet_named_ranges.get_mut(&(sheet_id, name.clone()))
580 {
581 entry.dependents.remove(&vertex);
582 }
583 }
584 }
585 }
586 }
587 }
588 }
589 }
590
591 pub(crate) fn attach_vertex_to_names(&mut self, vertex: VertexId, names: &[VertexId]) {
592 #[cfg(not(any(test, feature = "legacy_oracle")))]
593 let _ = (&vertex, &names);
594 #[cfg(any(test, feature = "legacy_oracle"))]
595 {
596 if names.is_empty() {
597 return;
598 }
599 let mut unique = FxHashSet::default();
600 let mut recorded = Vec::new();
601 for &name_vertex in names {
602 if !unique.insert(name_vertex) {
603 continue;
604 }
605 if let Some((scope, name)) = self.name_vertex_lookup.get(&name_vertex).cloned() {
606 match scope {
607 NameScope::Workbook => {
608 if let Some(entry) = self.named_ranges.get_mut(&name) {
609 entry.dependents.insert(vertex);
610 }
611 }
612 NameScope::Sheet(sheet_id) => {
613 if let Some(entry) =
614 self.sheet_named_ranges.get_mut(&(sheet_id, name.clone()))
615 {
616 entry.dependents.insert(vertex);
617 }
618 }
619 }
620 recorded.push(name_vertex);
621 }
622 }
623 if !recorded.is_empty() {
624 self.vertex_to_names.insert(vertex, recorded);
625 }
626 }
627 }
628
629 pub(super) fn unregister_name_cell_dependencies(&mut self, name_vertex: VertexId) {
630 #[cfg(not(any(test, feature = "legacy_oracle")))]
631 let _ = (&name_vertex,);
632 #[cfg(any(test, feature = "legacy_oracle"))]
633 {
634 if let Some(prev) = self.name_to_cell_dependencies.remove(&name_vertex) {
635 for dep in prev {
636 if let Some(set) = self.cell_to_name_dependents.get_mut(&dep) {
637 set.remove(&name_vertex);
638 if set.is_empty() {
639 self.cell_to_name_dependents.remove(&dep);
640 }
641 }
642 }
643 }
644 }
645 }
646
647 pub(super) fn register_name_cell_dependencies(
648 &mut self,
649 name_vertex: VertexId,
650 dependencies: &[VertexId],
651 ) {
652 #[cfg(not(any(test, feature = "legacy_oracle")))]
653 let _ = (&name_vertex, &dependencies);
654 #[cfg(any(test, feature = "legacy_oracle"))]
655 {
656 self.unregister_name_cell_dependencies(name_vertex);
657 if dependencies.is_empty() {
658 return;
659 }
660 for dep in dependencies {
661 self.cell_to_name_dependents
662 .entry(*dep)
663 .or_default()
664 .insert(name_vertex);
665 }
666 self.name_to_cell_dependencies
667 .insert(name_vertex, dependencies.to_vec());
668 }
669 }
670
671 pub(crate) fn record_pending_name_reference(
672 &mut self,
673 sheet_id: SheetId,
674 name: &str,
675 formula_vertex: VertexId,
676 ) {
677 self.materialize_vertex(formula_vertex);
678 let key = self.name_lookup_key(name);
679 self.pending_name_links
680 .entry(key.clone())
681 .or_default()
682 .insert((sheet_id, formula_vertex));
683 self.vertex_to_pending_names
684 .entry(formula_vertex)
685 .or_default()
686 .insert(key);
687 }
688
689 pub(crate) fn clear_pending_name_references(&mut self, formula_vertex: VertexId) {
690 let Some(keys) = self.vertex_to_pending_names.remove(&formula_vertex) else {
691 return;
692 };
693
694 for key in keys {
695 let mut remove_key = false;
696 if let Some(entries) = self.pending_name_links.get_mut(&key) {
697 entries.retain(|(_, vertex_id)| *vertex_id != formula_vertex);
698 remove_key = entries.is_empty();
699 }
700 if remove_key {
701 self.pending_name_links.remove(&key);
702 }
703 }
704 }
705
706 pub(crate) fn unbound_symbol_key(kind: &str, name: &str) -> String {
711 format!("\u{0}{kind}:{}", name.to_lowercase())
712 }
713
714 pub(crate) fn resolve_pending_symbol(&mut self, kind: &str, name: &str) {
717 if self.pending_name_links.is_empty() {
718 return;
719 }
720 let key = Self::unbound_symbol_key(kind, name);
721 self.resolve_pending_name_references(NameScope::Workbook, &key);
722 }
723
724 pub(super) fn resolve_pending_name_references(&mut self, scope: NameScope, name: &str) {
725 let key = self.name_lookup_key(name);
726 if let Some(entries) = self.pending_name_links.remove(&key) {
727 for (sheet_id, formula_vertex) in entries {
728 let attach = match scope {
729 NameScope::Workbook => true,
730 NameScope::Sheet(expected) => expected == sheet_id,
731 };
732 if attach {
733 if let Some(ast) = self.get_formula(formula_vertex) {
734 self.rebuild_formula_dependencies(formula_vertex, &ast);
735 } else {
736 self.clear_pending_name_references(formula_vertex);
737 }
738 } else {
739 self.record_pending_name_reference(sheet_id, name, formula_vertex);
740 }
741 }
742 }
743 }
744
745 pub(super) fn name_depends_on_vertex(
751 &mut self,
752 name_vertex: VertexId,
753 target: VertexId,
754 visited: &mut FxHashSet<VertexId>,
755 ) -> bool {
756 if !visited.insert(name_vertex) {
757 return false;
758 }
759 let Some(entry) = self.named_range_by_vertex(name_vertex) else {
760 return false;
761 };
762 let (definition, scope) = (entry.definition.clone(), entry.scope);
763 let target_cell = self.get_cell_ref(target);
764 match definition {
765 NamedDefinition::Cell(cell_ref) => target_cell
766 .is_some_and(|t| t.sheet_id == cell_ref.sheet_id && t.coord == cell_ref.coord),
767 NamedDefinition::Range(range_ref) => {
768 let height = range_ref
769 .end
770 .coord
771 .row()
772 .saturating_sub(range_ref.start.coord.row())
773 + 1;
774 let width = range_ref
775 .end
776 .coord
777 .col()
778 .saturating_sub(range_ref.start.coord.col())
779 + 1;
780 let size = u64::from(width) * u64::from(height);
781 let limit = u64::try_from(self.config.range_expansion_limit).unwrap_or(u64::MAX);
782 size <= limit
783 && target_cell.is_some_and(|t| {
784 t.sheet_id == range_ref.start.sheet_id
785 && (range_ref.start.coord.row()..=range_ref.end.coord.row())
786 .contains(&t.coord.row())
787 && (range_ref.start.coord.col()..=range_ref.end.coord.col())
788 .contains(&t.coord.col())
789 })
790 }
791 NamedDefinition::Literal(_) => false,
792 NamedDefinition::Formula { ast, .. } => {
793 let sheet = match scope {
794 NameScope::Sheet(id) => id,
795 NameScope::Workbook => self.default_sheet_id,
796 };
797 let Ok((dependencies, _, _, named, _)) =
798 self.extract_dependencies_with_pending_names(&ast, sheet)
799 else {
800 return false;
801 };
802 if dependencies.contains(&target) || named.contains(&target) {
803 return true;
804 }
805 let mut nested: Vec<VertexId> = named;
806 nested.extend(dependencies.into_iter().filter(|&d| {
807 matches!(
808 self.store.kind(d),
809 VertexKind::NamedScalar | VertexKind::NamedArray
810 )
811 }));
812 nested
813 .into_iter()
814 .any(|n| self.name_depends_on_vertex(n, target, visited))
815 }
816 }
817 }
818
819 pub(super) fn rebuild_name_dependencies(
820 &mut self,
821 vertex: VertexId,
822 definition: &NamedDefinition,
823 scope: NameScope,
824 ) -> Result<Vec<VertexId>, ExcelError> {
825 let formula_dependencies = if let NamedDefinition::Formula { ast, .. } = definition {
826 let current_sheet_id = match scope {
827 NameScope::Sheet(id) => id,
828 NameScope::Workbook => self.default_sheet_id,
829 };
830 let (dependencies, range_dependencies, formula_vertexless, _, _pending_names) =
831 self.extract_dependencies_with_pending_names(ast, current_sheet_id)?;
832 Some((dependencies, range_dependencies, formula_vertexless))
833 } else {
834 None
835 };
836
837 self.remove_dependent_edges(vertex);
838 self.unregister_name_cell_dependencies(vertex);
839
840 let mut dependencies: Vec<VertexId> = Vec::new();
841 let mut range_dependencies: Vec<SharedRangeRef<'static>> = Vec::new();
842 let mut vertexless: Vec<CellRef> = Vec::new();
845
846 match definition {
847 NamedDefinition::Cell(cell_ref) => match self.dep_vertex(cell_ref) {
848 Some(vertex_id) => dependencies.push(vertex_id),
849 None => vertexless.push(*cell_ref),
850 },
851 NamedDefinition::Range(range_ref) => {
852 let height = range_ref
853 .end
854 .coord
855 .row()
856 .saturating_sub(range_ref.start.coord.row())
857 + 1;
858 let width = range_ref
859 .end
860 .coord
861 .col()
862 .saturating_sub(range_ref.start.coord.col())
863 + 1;
864 let size = u64::from(width) * u64::from(height);
868 let limit = u64::try_from(self.config.range_expansion_limit).unwrap_or(u64::MAX);
869
870 if size <= limit {
871 for row in range_ref.start.coord.row()..=range_ref.end.coord.row() {
872 for col in range_ref.start.coord.col()..=range_ref.end.coord.col() {
873 let coord = Coord::new(row, col, true, true);
874 let addr = CellRef::new(range_ref.start.sheet_id, coord);
875 match self.dep_vertex(&addr) {
876 Some(vertex_id) => dependencies.push(vertex_id),
877 None => vertexless.push(addr),
878 }
879 }
880 }
881 } else {
882 let sheet_loc = SharedSheetLocator::Id(range_ref.start.sheet_id);
883 let sr = formualizer_common::AxisBound::new(
884 range_ref.start.coord.row(),
885 range_ref.start.coord.row_abs(),
886 );
887 let sc = formualizer_common::AxisBound::new(
888 range_ref.start.coord.col(),
889 range_ref.start.coord.col_abs(),
890 );
891 let er = formualizer_common::AxisBound::new(
892 range_ref.end.coord.row(),
893 range_ref.end.coord.row_abs(),
894 );
895 let ec = formualizer_common::AxisBound::new(
896 range_ref.end.coord.col(),
897 range_ref.end.coord.col_abs(),
898 );
899 if let Ok(r) = SharedRangeRef::from_parts(
900 sheet_loc,
901 Some(sr),
902 Some(sc),
903 Some(er),
904 Some(ec),
905 ) {
906 range_dependencies.push(r.into_owned());
907 }
908 }
909 }
910 NamedDefinition::Literal(_) => {
911 }
913 NamedDefinition::Formula { .. } => {
914 let Some((formula_deps, range_deps, formula_vertexless)) = formula_dependencies
915 else {
916 return Err(ExcelError::new(ExcelErrorKind::Error)
917 .with_message("Internal error: formula dependencies were not extracted"));
918 };
919 dependencies.extend(formula_deps);
920 range_dependencies.extend(range_deps);
921 vertexless.extend(formula_vertexless);
922 }
923 }
924
925 if !dependencies.is_empty() {
926 self.add_dependent_edges(vertex, &dependencies);
927 }
928 self.note_vertexless_deps(
929 vertex,
930 vertexless
931 .iter()
932 .map(|c| (c.sheet_id, c.coord.row(), c.coord.col())),
933 );
934 self.register_name_cell_dependencies(vertex, &dependencies);
935
936 if !range_dependencies.is_empty() {
937 let sheet_id = match scope {
938 NameScope::Sheet(id) => id,
939 NameScope::Workbook => self.default_sheet_id,
940 };
941 self.add_range_dependent_edges(vertex, &range_dependencies, sheet_id);
942 }
943
944 Ok(dependencies
945 .iter()
946 .filter(|vid| {
947 matches!(
948 self.store.kind(**vid),
949 VertexKind::NamedScalar | VertexKind::NamedArray
950 )
951 })
952 .copied()
953 .collect())
954 }
955
956 pub fn adjust_named_ranges(
957 &mut self,
958 operation: &crate::engine::graph::editor::reference_adjuster::ShiftOperation,
959 ) -> Result<(), ExcelError> {
960 let adjuster = crate::engine::graph::editor::reference_adjuster::ReferenceAdjuster::new();
961
962 let changed = !self.named_ranges.is_empty() || !self.sheet_named_ranges.is_empty();
963 let workbook_context =
965 crate::engine::graph::editor::reference_adjuster::ReferenceContext::new(
966 self.default_sheet_id,
967 &self.sheet_reg,
968 );
969 let mut adjusted_named_ranges = self.named_ranges.clone();
972 let mut adjusted_sheet_named_ranges = self.sheet_named_ranges.clone();
973 for named_range in adjusted_named_ranges.values_mut() {
974 adjust_named_definition(
975 &mut named_range.definition,
976 &adjuster,
977 operation,
978 &workbook_context,
979 )?;
980 }
981
982 for ((scope_sheet_id, _), named_range) in adjusted_sheet_named_ranges.iter_mut() {
984 let context = crate::engine::graph::editor::reference_adjuster::ReferenceContext::new(
985 *scope_sheet_id,
986 &self.sheet_reg,
987 );
988 adjust_named_definition(&mut named_range.definition, &adjuster, operation, &context)?;
989 }
990 let changed_names: Vec<_> = adjusted_named_ranges
991 .iter()
992 .filter_map(|(key, adjusted)| {
993 self.named_ranges
994 .get(key)
995 .is_some_and(|current| current.definition != adjusted.definition)
996 .then_some((adjusted.vertex, adjusted.scope, adjusted.definition.clone()))
997 })
998 .chain(
999 adjusted_sheet_named_ranges
1000 .iter()
1001 .filter_map(|(key, adjusted)| {
1002 self.sheet_named_ranges
1003 .get(key)
1004 .is_some_and(|current| current.definition != adjusted.definition)
1005 .then_some((
1006 adjusted.vertex,
1007 adjusted.scope,
1008 adjusted.definition.clone(),
1009 ))
1010 }),
1011 )
1012 .collect();
1013 self.named_ranges = adjusted_named_ranges;
1014 self.sheet_named_ranges = adjusted_sheet_named_ranges;
1015 for &(vertex, scope, ref definition) in &changed_names {
1016 self.detach_vertex_from_names(vertex);
1017 self.store.set_kind(
1018 vertex,
1019 if matches!(definition, NamedDefinition::Range(_)) {
1020 VertexKind::NamedArray
1021 } else {
1022 VertexKind::NamedScalar
1023 },
1024 );
1025 let referenced_names = self.rebuild_name_dependencies(vertex, definition, scope)?;
1026 if !referenced_names.is_empty() {
1027 self.attach_vertex_to_names(vertex, &referenced_names);
1028 }
1029 }
1030 self.mark_dirty_many(
1031 &changed_names
1032 .iter()
1033 .map(|(vertex, _, _)| *vertex)
1034 .collect::<Vec<_>>(),
1035 );
1036 if changed {
1037 for &(vertex, _, _) in &changed_names {
1038 self.authority_note_symbol(Some(vertex));
1039 }
1040 self.bump_symbol_revision();
1041 }
1042
1043 Ok(())
1044 }
1045
1046 pub fn mark_as_name_error(&mut self, vertex_id: VertexId) {
1048 self.mark_vertex_dirty(vertex_id);
1050 }
1051
1052 pub(super) fn mark_named_vertex_deleted(&mut self, named_range: &NamedRange) {
1053 self.detach_vertex_from_names(named_range.vertex);
1054 self.remove_dependent_edges(named_range.vertex);
1055 self.unregister_name_cell_dependencies(named_range.vertex);
1056 self.store.mark_deleted(named_range.vertex, true);
1057 self.vertex_values.remove(&named_range.vertex);
1058 self.vertex_formulas.remove(&named_range.vertex);
1059 self.clear_formula_vertex_dirty(named_range.vertex);
1060 self.volatile_vertices.remove(&named_range.vertex);
1061 #[cfg(any(test, feature = "legacy_oracle"))]
1062 self.vertex_to_names.remove(&named_range.vertex);
1063 self.name_vertex_lookup.remove(&named_range.vertex);
1064 }
1065}