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(
49 definition: &mut NamedDefinition,
50 adjuster: &crate::engine::graph::editor::reference_adjuster::ReferenceAdjuster,
51 operation: &crate::engine::graph::editor::reference_adjuster::ShiftOperation,
52) -> Result<(), ExcelError> {
53 match definition {
54 NamedDefinition::Cell(cell_ref) => {
55 if let Some(adjusted) = adjuster.adjust_cell_ref(cell_ref, operation) {
56 *cell_ref = adjusted;
57 } else {
58 return Err(ExcelError::new(ExcelErrorKind::Ref));
59 }
60 }
61 NamedDefinition::Range(range_ref) => {
62 let adjusted_start = adjuster.adjust_cell_ref(&range_ref.start, operation);
63 let adjusted_end = adjuster.adjust_cell_ref(&range_ref.end, operation);
64
65 if let (Some(start), Some(end)) = (adjusted_start, adjusted_end) {
66 range_ref.start = start;
67 range_ref.end = end;
68 } else {
69 return Err(ExcelError::new(ExcelErrorKind::Ref));
70 }
71 }
72 NamedDefinition::Literal(_) => {
73 }
75 NamedDefinition::Formula {
76 ast,
77 dependencies,
78 range_deps,
79 } => {
80 let adjusted_ast = adjuster.adjust_ast(ast, operation);
81 *ast = adjusted_ast;
82
83 dependencies.clear();
84 range_deps.clear();
85 }
86 }
87 Ok(())
88}
89
90impl DependencyGraph {
91 #[inline]
92 pub(crate) fn name_lookup_key(&self, name: &str) -> String {
93 if self.config.case_sensitive_names {
94 name.to_string()
95 } else {
96 normalize_name_key(name)
97 }
98 }
99
100 fn canonical_name_in_scope(&self, scope: NameScope, name: &str) -> Option<String> {
101 let key = self.name_lookup_key(name);
102 match scope {
103 NameScope::Workbook => self.named_ranges_lookup.get(&key).cloned(),
104 NameScope::Sheet(sheet_id) => self
105 .sheet_named_ranges_lookup
106 .get(&(sheet_id, key))
107 .cloned(),
108 }
109 }
110
111 fn next_name_coord(&mut self) -> AbsCoord {
112 let seq = self.name_vertex_seq;
113 self.name_vertex_seq = self.name_vertex_seq.wrapping_add(1);
114 let row = (seq / 16_384).min(0x000F_FFFF);
115 let col = seq % 16_384;
116 AbsCoord::new(row, col)
117 }
118
119 pub(super) fn allocate_name_vertex(&mut self, scope: NameScope) -> VertexId {
120 let coord = self.next_name_coord();
121 let sheet_id = match scope {
122 NameScope::Sheet(id) => id,
123 NameScope::Workbook => self.default_sheet_id,
124 };
125 let vertex_id = self.store.allocate(coord, sheet_id, 0x01);
126 self.store.set_kind(vertex_id, VertexKind::NamedScalar);
127 self.store.set_dirty(vertex_id, true);
128 self.edges.add_vertex(coord, vertex_id.0);
129 self.dirty_vertices.insert(vertex_id);
130 vertex_id
131 }
132
133 pub fn define_name(
137 &mut self,
138 name: &str,
139 definition: NamedDefinition,
140 scope: NameScope,
141 ) -> Result<(), ExcelError> {
142 if !is_valid_excel_name(name) {
144 return Err(
145 ExcelError::new(ExcelErrorKind::Name).with_message(format!("Invalid name: {name}"))
146 );
147 }
148
149 let lookup_key = self.name_lookup_key(name);
151 match scope {
152 NameScope::Workbook => {
153 if let Some(existing) = self.named_ranges_lookup.get(&lookup_key) {
154 return Err(ExcelError::new(ExcelErrorKind::Name).with_message(format!(
155 "Name collision under normalization: '{name}' conflicts with '{existing}'"
156 )));
157 }
158 }
159 NameScope::Sheet(sheet_id) => {
160 if let Some(existing) = self
161 .sheet_named_ranges_lookup
162 .get(&(sheet_id, lookup_key.clone()))
163 {
164 return Err(ExcelError::new(ExcelErrorKind::Name).with_message(format!(
165 "Name collision under normalization in sheet: '{name}' conflicts with '{existing}'"
166 )));
167 }
168 }
169 }
170
171 let mut final_definition = definition;
172 if let NamedDefinition::Formula { ref ast, .. } = final_definition {
174 let (deps, range_deps, _, _) = self.extract_dependencies(
175 ast,
176 match scope {
177 NameScope::Sheet(id) => id,
178 NameScope::Workbook => self.default_sheet_id,
179 },
180 )?;
181 final_definition = NamedDefinition::Formula {
182 ast: ast.clone(),
183 dependencies: deps,
184 range_deps,
185 };
186 }
187
188 let vertex_id = self.allocate_name_vertex(scope);
190
191 let named_range = NamedRange {
192 definition: final_definition,
193 scope,
194 dependents: FxHashSet::default(),
195 vertex: vertex_id,
196 };
197
198 if matches!(named_range.definition, NamedDefinition::Range(_)) {
199 self.store.set_kind(vertex_id, VertexKind::NamedArray);
200 } else {
201 self.store.set_kind(vertex_id, VertexKind::NamedScalar);
202 }
203
204 let referenced_names =
205 self.rebuild_name_dependencies(vertex_id, &named_range.definition, scope);
206 if !referenced_names.is_empty() {
207 self.attach_vertex_to_names(vertex_id, &referenced_names);
208 }
209
210 let key = name.to_string();
211
212 match scope {
213 NameScope::Workbook => {
214 self.named_ranges.insert(key.clone(), named_range);
215 self.named_ranges_lookup
216 .insert(self.name_lookup_key(&key), key.clone());
217 }
218 NameScope::Sheet(id) => {
219 self.sheet_named_ranges
220 .insert((id, key.clone()), named_range);
221 self.sheet_named_ranges_lookup
222 .insert((id, self.name_lookup_key(&key)), key.clone());
223 }
224 }
225
226 self.name_vertex_lookup.insert(vertex_id, (scope, key));
227 self.resolve_pending_name_references(scope, name);
228
229 Ok(())
230 }
231
232 pub fn named_ranges_iter(&self) -> impl Iterator<Item = (&String, &NamedRange)> {
234 self.named_ranges.iter()
235 }
236
237 pub fn sheet_named_ranges_iter(
239 &self,
240 ) -> impl Iterator<Item = (&(SheetId, String), &NamedRange)> {
241 self.sheet_named_ranges.iter()
242 }
243
244 pub fn resolve_name_entry(&self, name: &str, current_sheet: SheetId) -> Option<&NamedRange> {
245 if self.config.case_sensitive_names {
246 self.sheet_named_ranges
247 .get(&(current_sheet, name.to_string()))
248 .or_else(|| self.named_ranges.get(name))
249 } else {
250 let key = self.name_lookup_key(name);
251 self.sheet_named_ranges_lookup
252 .get(&(current_sheet, key.clone()))
253 .and_then(|canon| self.sheet_named_ranges.get(&(current_sheet, canon.clone())))
254 .or_else(|| {
255 self.named_ranges_lookup
256 .get(&key)
257 .and_then(|canon| self.named_ranges.get(canon))
258 })
259 }
260 }
261
262 pub fn resolve_name(&self, name: &str, current_sheet: SheetId) -> Option<&NamedDefinition> {
264 self.resolve_name_entry(name, current_sheet)
265 .map(|nr| &nr.definition)
266 }
267
268 pub(crate) fn name_key_for_vertex(&self, vertex: VertexId) -> Option<String> {
272 self.name_vertex_lookup
273 .get(&vertex)
274 .map(|(_, name)| self.name_lookup_key(name))
275 }
276
277 pub fn named_range_by_vertex(&self, vertex: VertexId) -> Option<&NamedRange> {
278 self.name_vertex_lookup
279 .get(&vertex)
280 .and_then(|(scope, name)| match scope {
281 NameScope::Workbook => self.named_ranges.get(name),
282 NameScope::Sheet(sheet_id) => {
283 self.sheet_named_ranges.get(&(*sheet_id, name.clone()))
284 }
285 })
286 }
287
288 pub fn update_name(
290 &mut self,
291 name: &str,
292 new_definition: NamedDefinition,
293 scope: NameScope,
294 ) -> Result<(), ExcelError> {
295 let Some(canon_name) = self.canonical_name_in_scope(scope, name) else {
296 return Err(ExcelError::new(ExcelErrorKind::Name)
297 .with_message(format!("Name not found: {name}")));
298 };
299
300 let dependents_to_dirty = match scope {
302 NameScope::Workbook => self
303 .named_ranges
304 .get(&canon_name)
305 .map(|nr| nr.dependents.iter().copied().collect::<Vec<_>>()),
306 NameScope::Sheet(id) => self
307 .sheet_named_ranges
308 .get(&(id, canon_name.clone()))
309 .map(|nr| nr.dependents.iter().copied().collect::<Vec<_>>()),
310 };
311
312 if let Some(dependents) = dependents_to_dirty {
313 for vertex_id in dependents {
315 self.mark_vertex_dirty(vertex_id);
316 }
317
318 let named_range = match scope {
320 NameScope::Workbook => self.named_ranges.get_mut(&canon_name),
321 NameScope::Sheet(id) => self.sheet_named_ranges.get_mut(&(id, canon_name.clone())),
322 };
323
324 let mut update_data: Option<(VertexId, NameScope, NamedDefinition, bool)> = None;
325 if let Some(named_range) = named_range {
326 named_range.definition = new_definition;
327 let is_range = matches!(named_range.definition, NamedDefinition::Range(_));
328 update_data = Some((
329 named_range.vertex,
330 named_range.scope,
331 named_range.definition.clone(),
332 is_range,
333 ));
334 }
335
336 if let Some((vertex, scope_value, definition_snapshot, is_range)) = update_data {
337 self.detach_vertex_from_names(vertex);
338
339 if is_range {
340 self.store.set_kind(vertex, VertexKind::NamedArray);
341 } else {
342 self.store.set_kind(vertex, VertexKind::NamedScalar);
343 }
344 self.store.set_dirty(vertex, true);
345 self.dirty_vertices.insert(vertex);
346
347 let referenced_names =
348 self.rebuild_name_dependencies(vertex, &definition_snapshot, scope_value);
349 if !referenced_names.is_empty() {
350 self.attach_vertex_to_names(vertex, &referenced_names);
351 }
352 }
353
354 Ok(())
355 } else {
356 Err(ExcelError::new(ExcelErrorKind::Name)
357 .with_message(format!("Name not found: {name}")))
358 }
359 }
360
361 pub fn delete_name(&mut self, name: &str, scope: NameScope) -> Result<(), ExcelError> {
363 let Some(canon_name) = self.canonical_name_in_scope(scope, name) else {
364 return Err(ExcelError::new(ExcelErrorKind::Name)
365 .with_message(format!("Name not found: {name}")));
366 };
367
368 let named_range = match scope {
369 NameScope::Workbook => {
370 let removed = self.named_ranges.remove(&canon_name);
371 let key = self.name_lookup_key(&canon_name);
372 self.named_ranges_lookup.remove(&key);
373 removed
374 }
375 NameScope::Sheet(id) => {
376 let removed = self.sheet_named_ranges.remove(&(id, canon_name.clone()));
377 let key = self.name_lookup_key(&canon_name);
378 self.sheet_named_ranges_lookup.remove(&(id, key));
379 removed
380 }
381 };
382
383 if let Some(named_range) = named_range {
384 let mut affected: FxHashSet<VertexId> = FxHashSet::default();
385 for &vertex_id in &named_range.dependents {
386 affected.insert(vertex_id);
387 }
388 for (vertex_id, names) in self.vertex_to_names.iter() {
389 if names.contains(&named_range.vertex) {
390 affected.insert(*vertex_id);
391 }
392 }
393 for vertex_id in affected {
394 self.mark_vertex_dirty(vertex_id);
395 if let Some(names) = self.vertex_to_names.get_mut(&vertex_id) {
396 names.retain(|vid| *vid != named_range.vertex);
397 if names.is_empty() {
398 self.vertex_to_names.remove(&vertex_id);
399 }
400 }
401 }
402 self.mark_named_vertex_deleted(&named_range);
403 Ok(())
404 } else {
405 Err(ExcelError::new(ExcelErrorKind::Name)
406 .with_message(format!("Name not found: {name}")))
407 }
408 }
409
410 pub(super) fn detach_vertex_from_names(&mut self, vertex: VertexId) {
411 if let Some(prior) = self.vertex_to_names.remove(&vertex) {
412 for name_vertex in prior {
413 if let Some((scope, name)) = self.name_vertex_lookup.get(&name_vertex).cloned() {
414 match scope {
415 NameScope::Workbook => {
416 if let Some(entry) = self.named_ranges.get_mut(&name) {
417 entry.dependents.remove(&vertex);
418 }
419 }
420 NameScope::Sheet(sheet_id) => {
421 if let Some(entry) =
422 self.sheet_named_ranges.get_mut(&(sheet_id, name.clone()))
423 {
424 entry.dependents.remove(&vertex);
425 }
426 }
427 }
428 }
429 }
430 }
431 }
432
433 pub(crate) fn attach_vertex_to_names(&mut self, vertex: VertexId, names: &[VertexId]) {
434 if names.is_empty() {
435 return;
436 }
437 let mut unique = FxHashSet::default();
438 let mut recorded = Vec::new();
439 for &name_vertex in names {
440 if !unique.insert(name_vertex) {
441 continue;
442 }
443 if let Some((scope, name)) = self.name_vertex_lookup.get(&name_vertex).cloned() {
444 match scope {
445 NameScope::Workbook => {
446 if let Some(entry) = self.named_ranges.get_mut(&name) {
447 entry.dependents.insert(vertex);
448 }
449 }
450 NameScope::Sheet(sheet_id) => {
451 if let Some(entry) =
452 self.sheet_named_ranges.get_mut(&(sheet_id, name.clone()))
453 {
454 entry.dependents.insert(vertex);
455 }
456 }
457 }
458 recorded.push(name_vertex);
459 }
460 }
461 if !recorded.is_empty() {
462 self.vertex_to_names.insert(vertex, recorded);
463 }
464 }
465
466 pub(super) fn unregister_name_cell_dependencies(&mut self, name_vertex: VertexId) {
467 if let Some(prev) = self.name_to_cell_dependencies.remove(&name_vertex) {
468 for dep in prev {
469 if let Some(set) = self.cell_to_name_dependents.get_mut(&dep) {
470 set.remove(&name_vertex);
471 if set.is_empty() {
472 self.cell_to_name_dependents.remove(&dep);
473 }
474 }
475 }
476 }
477 }
478
479 pub(super) fn register_name_cell_dependencies(
480 &mut self,
481 name_vertex: VertexId,
482 dependencies: &[VertexId],
483 ) {
484 self.unregister_name_cell_dependencies(name_vertex);
485 if dependencies.is_empty() {
486 return;
487 }
488 for dep in dependencies {
489 self.cell_to_name_dependents
490 .entry(*dep)
491 .or_default()
492 .insert(name_vertex);
493 }
494 self.name_to_cell_dependencies
495 .insert(name_vertex, dependencies.to_vec());
496 }
497
498 pub(crate) fn record_pending_name_reference(
499 &mut self,
500 sheet_id: SheetId,
501 name: &str,
502 formula_vertex: VertexId,
503 ) {
504 let key = self.name_lookup_key(name);
505 self.pending_name_links
506 .entry(key.clone())
507 .or_default()
508 .insert((sheet_id, formula_vertex));
509 self.vertex_to_pending_names
510 .entry(formula_vertex)
511 .or_default()
512 .insert(key);
513 }
514
515 pub(crate) fn clear_pending_name_references(&mut self, formula_vertex: VertexId) {
516 let Some(keys) = self.vertex_to_pending_names.remove(&formula_vertex) else {
517 return;
518 };
519
520 for key in keys {
521 let mut remove_key = false;
522 if let Some(entries) = self.pending_name_links.get_mut(&key) {
523 entries.retain(|(_, vertex_id)| *vertex_id != formula_vertex);
524 remove_key = entries.is_empty();
525 }
526 if remove_key {
527 self.pending_name_links.remove(&key);
528 }
529 }
530 }
531
532 pub(super) fn resolve_pending_name_references(&mut self, scope: NameScope, name: &str) {
533 let key = self.name_lookup_key(name);
534 if let Some(entries) = self.pending_name_links.remove(&key) {
535 for (sheet_id, formula_vertex) in entries {
536 let attach = match scope {
537 NameScope::Workbook => true,
538 NameScope::Sheet(expected) => expected == sheet_id,
539 };
540 if attach {
541 if let Some(ast) = self.get_formula(formula_vertex) {
542 self.rebuild_formula_dependencies(formula_vertex, &ast);
543 } else {
544 self.clear_pending_name_references(formula_vertex);
545 }
546 } else {
547 self.record_pending_name_reference(sheet_id, name, formula_vertex);
548 }
549 }
550 }
551 }
552
553 pub(super) fn name_depends_on_vertex(
554 &self,
555 name_vertex: VertexId,
556 target: VertexId,
557 visited: &mut FxHashSet<VertexId>,
558 ) -> bool {
559 if !visited.insert(name_vertex) {
560 return false;
561 }
562
563 for dependency in self.edges.out_edges(name_vertex).iter().copied() {
564 if dependency == target {
565 return true;
566 }
567
568 if matches!(
569 self.store.kind(dependency),
570 VertexKind::NamedScalar | VertexKind::NamedArray
571 ) && self.name_depends_on_vertex(dependency, target, visited)
572 {
573 return true;
574 }
575 }
576
577 false
578 }
579
580 pub(super) fn rebuild_name_dependencies(
581 &mut self,
582 vertex: VertexId,
583 definition: &NamedDefinition,
584 scope: NameScope,
585 ) -> Vec<VertexId> {
586 self.remove_dependent_edges(vertex);
587 self.unregister_name_cell_dependencies(vertex);
588
589 let mut dependencies: Vec<VertexId> = Vec::new();
590 let mut range_dependencies: Vec<SharedRangeRef<'static>> = Vec::new();
591 let mut placeholders = Vec::new();
592
593 match definition {
594 NamedDefinition::Cell(cell_ref) => {
595 let vertex_id = self.get_or_create_vertex(cell_ref, &mut placeholders);
596 dependencies.push(vertex_id);
597 }
598 NamedDefinition::Range(range_ref) => {
599 let height = range_ref
600 .end
601 .coord
602 .row()
603 .saturating_sub(range_ref.start.coord.row())
604 + 1;
605 let width = range_ref
606 .end
607 .coord
608 .col()
609 .saturating_sub(range_ref.start.coord.col())
610 + 1;
611 let size = (width * height) as usize;
612
613 if size <= self.config.range_expansion_limit {
614 for row in range_ref.start.coord.row()..=range_ref.end.coord.row() {
615 for col in range_ref.start.coord.col()..=range_ref.end.coord.col() {
616 let coord = Coord::new(row, col, true, true);
617 let addr = CellRef::new(range_ref.start.sheet_id, coord);
618 let vertex_id = self.get_or_create_vertex(&addr, &mut placeholders);
619 dependencies.push(vertex_id);
620 }
621 }
622 } else {
623 let sheet_loc = SharedSheetLocator::Id(range_ref.start.sheet_id);
624 let sr = formualizer_common::AxisBound::new(
625 range_ref.start.coord.row(),
626 range_ref.start.coord.row_abs(),
627 );
628 let sc = formualizer_common::AxisBound::new(
629 range_ref.start.coord.col(),
630 range_ref.start.coord.col_abs(),
631 );
632 let er = formualizer_common::AxisBound::new(
633 range_ref.end.coord.row(),
634 range_ref.end.coord.row_abs(),
635 );
636 let ec = formualizer_common::AxisBound::new(
637 range_ref.end.coord.col(),
638 range_ref.end.coord.col_abs(),
639 );
640 if let Ok(r) = SharedRangeRef::from_parts(
641 sheet_loc,
642 Some(sr),
643 Some(sc),
644 Some(er),
645 Some(ec),
646 ) {
647 range_dependencies.push(r.into_owned());
648 }
649 }
650 }
651 NamedDefinition::Literal(_) => {
652 }
654 NamedDefinition::Formula {
655 dependencies: formula_deps,
656 range_deps,
657 ..
658 } => {
659 dependencies.extend(formula_deps.iter().copied());
660 range_dependencies.extend(range_deps.iter().cloned());
661 }
662 }
663
664 if !dependencies.is_empty() {
665 self.add_dependent_edges(vertex, &dependencies);
666 }
667 self.register_name_cell_dependencies(vertex, &dependencies);
668
669 if !range_dependencies.is_empty() {
670 let sheet_id = match scope {
671 NameScope::Sheet(id) => id,
672 NameScope::Workbook => self.default_sheet_id,
673 };
674 self.add_range_dependent_edges(vertex, &range_dependencies, sheet_id);
675 }
676
677 dependencies
678 .iter()
679 .filter(|vid| {
680 matches!(
681 self.store.kind(**vid),
682 VertexKind::NamedScalar | VertexKind::NamedArray
683 )
684 })
685 .copied()
686 .collect()
687 }
688
689 pub fn adjust_named_ranges(
690 &mut self,
691 operation: &crate::engine::graph::editor::reference_adjuster::ShiftOperation,
692 ) -> Result<(), ExcelError> {
693 let adjuster = crate::engine::graph::editor::reference_adjuster::ReferenceAdjuster::new();
694
695 for named_range in self.named_ranges.values_mut() {
697 adjust_named_definition(&mut named_range.definition, &adjuster, operation)?;
698 }
699
700 for named_range in self.sheet_named_ranges.values_mut() {
702 adjust_named_definition(&mut named_range.definition, &adjuster, operation)?;
703 }
704
705 Ok(())
706 }
707
708 pub fn mark_as_name_error(&mut self, vertex_id: VertexId) {
710 self.mark_vertex_dirty(vertex_id);
712 }
713
714 pub(super) fn mark_named_vertex_deleted(&mut self, named_range: &NamedRange) {
715 self.detach_vertex_from_names(named_range.vertex);
716 self.remove_dependent_edges(named_range.vertex);
717 self.unregister_name_cell_dependencies(named_range.vertex);
718 self.store.mark_deleted(named_range.vertex, true);
719 self.vertex_values.remove(&named_range.vertex);
720 self.vertex_formulas.remove(&named_range.vertex);
721 self.dirty_vertices.remove(&named_range.vertex);
722 self.volatile_vertices.remove(&named_range.vertex);
723 self.vertex_to_names.remove(&named_range.vertex);
724 self.name_vertex_lookup.remove(&named_range.vertex);
725 }
726}