Skip to main content

formualizer_eval/engine/graph/
names.rs

1use super::*;
2use formualizer_common::parse_a1_1based;
3
4#[inline]
5fn normalize_name_key(name: &str) -> String {
6    name.to_lowercase()
7}
8
9/// Validate that a name conforms to Excel naming rules.
10fn is_valid_excel_name(name: &str) -> bool {
11    // Excel name rules:
12    // 1. Must start with a letter, underscore, or backslash
13    // 2. Can contain letters, numbers, periods, and underscores
14    // 3. Cannot be a cell reference (like A1, B2, etc.)
15    // 4. Cannot exceed 255 characters
16    // 5. Cannot contain spaces
17
18    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    // First character must be letter, underscore, or backslash
29    if let Some(first) = chars.next()
30        && !first.is_alphabetic()
31        && first != '_'
32        && first != '\\'
33    {
34        return false;
35    }
36
37    // Remaining characters must be letters, digits, periods, or underscores
38    for c in chars {
39        if !c.is_alphanumeric() && c != '.' && c != '_' {
40            return false;
41        }
42    }
43
44    true
45}
46
47/// Helper function to adjust a named definition during structural operations.
48fn 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            // Constant names are not affected by structural shifts.
74        }
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    // Named Range Methods
134
135    /// Define a new named range
136    pub fn define_name(
137        &mut self,
138        name: &str,
139        definition: NamedDefinition,
140        scope: NameScope,
141    ) -> Result<(), ExcelError> {
142        // Validate name
143        if !is_valid_excel_name(name) {
144            return Err(
145                ExcelError::new(ExcelErrorKind::Name).with_message(format!("Invalid name: {name}"))
146            );
147        }
148
149        // Check for duplicates / collisions (respect case-sensitivity config)
150        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        // Extract dependencies if formula
173        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        // Allocate vertex only after dependency extraction succeeds
189        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    /// Iterate workbook-scoped named ranges (for bindings/testing)
233    pub fn named_ranges_iter(&self) -> impl Iterator<Item = (&String, &NamedRange)> {
234        self.named_ranges.iter()
235    }
236
237    /// Iterate sheet-scoped named ranges (for bindings/testing)
238    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    /// Resolve a named range to its definition
263    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    /// The folded lookup key (see [`Self::name_lookup_key`]) of the name
269    /// represented by `vertex`, if it is a name vertex. Used by SCC tasks for
270    /// deterministic member ordering and live name-read matching (RFC #112).
271    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    /// Update an existing named range definition
289    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        // First collect dependents to avoid borrow checker issues
301        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            // Mark all dependents as dirty
314            for vertex_id in dependents {
315                self.mark_vertex_dirty(vertex_id);
316            }
317
318            // Now update the definition
319            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    /// Delete a named range
362    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                // No dependencies.
653            }
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        // Adjust workbook-scoped names
696        for named_range in self.named_ranges.values_mut() {
697            adjust_named_definition(&mut named_range.definition, &adjuster, operation)?;
698        }
699
700        // Adjust sheet-scoped names
701        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    /// Mark a vertex as having a #NAME! error
709    pub fn mark_as_name_error(&mut self, vertex_id: VertexId) {
710        // Mark the vertex as dirty
711        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}