From d19627fc269ec982badaebb1493fb9bfe4a4bf3e Mon Sep 17 00:00:00 2001 From: bfenty <1398236+bfenty@users.noreply.github.com> Date: Mon, 19 Oct 2026 16:54:45 +0000 Subject: [PATCH] fix(engine): walk stored cells in transitive precedent selection Bound the all-level walk to the sheet sparse cell store instead of iterating every position of each precedent area: formulas late in a wide area are no longer skipped, blank positions are never selected, and only formula cells extend the frontier. Adds a regression test (=SUM(A1:Z1) with a formula at Z1) and silences two clippy lints in the same function. Co-Authored-By: Claude --- crates/engine/src/cmd/formulas.rs | 20 +++++++++++++------- crates/ui-egui/tests/trace_navigation.rs | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/crates/engine/src/cmd/formulas.rs b/crates/engine/src/cmd/formulas.rs index 0607901ff..c1287f988 100644 --- a/crates/engine/src/cmd/formulas.rs +++ b/crates/engine/src/cmd/formulas.rs @@ -1,6 +1,6 @@ //! Formulas tab: AutoSum, Insert Function, names, auditing, calculation. -use gridcraft_core::{CellRef, RangeRef}; +use gridcraft_core::{CellRef, RangeRef, Value}; use gridcraft_model::{CalcMode, DefinedName, Formula}; use serde_json::{Value as Json, json}; @@ -485,7 +485,6 @@ fn trace_dependents(s: &mut Session, p: &Json) -> Result { /// another sheet, jump there. `all` walks every level (Ctrl+Shift+[ / Ctrl+Shift+]). fn select_trace_cells(s: &mut Session, p: &Json, precedents: bool) -> Result { use std::collections::BTreeSet; - const CAP: usize = 4096; let d = s.doc()?; let at = cell_param(s, p, "cell")?.unwrap_or(d.selection.active); let sheet = d.wb.active_sheet; @@ -495,15 +494,23 @@ fn select_trace_cells(s: &mut Session, p: &Json, precedents: bool) -> Result = BTreeSet::new(); let mut frontier = vec![(sheet, at)]; seen.insert((sheet, at)); - loop { - let Some((ks, kc)) = frontier.pop() else { break }; + while let Some((ks, kc)) = frontier.pop() { if precedents { for a in d.calc.graph.precedents_of((ks, kc)) { if all { - for c in a.range.iter().take(CAP) { + // Walk the sheet's stored cells inside the precedent area: blanks are + // never selected and only formula cells extend the frontier, so a + // formula late in a large range is not skipped. + let Some(sh) = d.wb.sheet(a.sheet) else { continue }; + for (c, cell) in sh.cells.iter() { + if !a.range.contains(c) || matches!(cell.value, Value::Empty) { + continue; + } if seen.insert((a.sheet, c)) { found.insert((a.sheet, c.a1())); - frontier.push((a.sheet, c)); + if cell.formula.is_some() { + frontier.push((a.sheet, c)); + } } } } else { @@ -538,7 +545,6 @@ fn select_trace_cells(s: &mut Session, p: &Json, precedents: bool) -> Result