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