From 4548bab2e38ad8f382c159f1e625fabe0e6a8ae4 Mon Sep 17 00:00:00 2001 From: Fubeo <83821420+Fubeo@users.noreply.github.com> Date: Sun, 25 Oct 2026 01:31:30 +0200 Subject: [PATCH] feat(engine): open a saved workbook without recalculating it DocState::new recalculates the whole workbook, so values saved in the file change where nothing was edited (volatile functions, functions GridCraft lacks, cycles). DocState::from_saved builds the dependency graph without evaluating, so every value stays the file's until an edit reaches it. --- crates/engine/src/lib.rs | 11 +++++++++++ crates/engine/src/tests.rs | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index b4ab60f3a..3f3c6f3ea 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -266,6 +266,17 @@ impl DocState { pub fn new(mut wb: Workbook, path: Option, title: String, trust: Trust) -> DocState { let mut calc = Calc::new(); calc.recalc_all(&mut wb); + Self::with_calc(wb, calc, path, title, trust) + } + /// A workbook as its file saved it: the dependency graph is built but nothing is + /// evaluated, so every value is the one the file holds until an edit reaches it. `trust` + /// is as for [`DocState::new`]. + pub fn from_saved(wb: Workbook, path: Option, title: String, trust: Trust) -> DocState { + let mut calc = Calc::new(); + calc.rebuild(&wb); + Self::with_calc(wb, calc, path, title, trust) + } + fn with_calc(wb: Workbook, calc: Calc, path: Option, title: String, trust: Trust) -> DocState { let scripts_trusted = !cmd::extra::has_scripts(&wb); let wb = Arc::new(wb); let sel = Selection::at(wb.active().map(|s| s.view_active).unwrap_or_default()); diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index f4c47bb62..6c91da435 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -4661,3 +4661,26 @@ fn a_script_cannot_switch_windows() { } mod seams; + +#[test] +fn a_saved_workbook_keeps_its_values_until_an_edit_reaches_them() { + let mut s = s(); + s.execute("cell.set", json!({"cell": "A1", "input": "2"})).unwrap(); + s.execute("cell.set", json!({"cell": "B1", "input": "=A1*2"})).unwrap(); + s.execute("cell.set", json!({"cell": "C1", "input": "=10"})).unwrap(); + let mut wb = (*s.doc().unwrap().wb).clone(); + // The values a file would have saved, not the ones the formulas give. + for (a, n) in [("B1", 99.0), ("C1", 7.0)] { + let sheet = std::sync::Arc::make_mut(&mut wb.sheets[0]); + let c = CellRef::parse(a).unwrap(); + let mut cell = sheet.cells.get(c).unwrap().clone(); + cell.value = Value::Number(n); + sheet.set_cell(c, cell); + } + let mut saved = Session::new(); + saved.add_document(crate::DocState::from_saved(wb, None, "saved".into(), crate::Trust::FromFile)); + assert_eq!(v(&saved, "B1"), Value::Number(99.0)); + saved.execute("cell.set", json!({"cell": "A1", "input": "3"})).unwrap(); + assert_eq!(v(&saved, "B1"), Value::Number(6.0)); + assert_eq!(v(&saved, "C1"), Value::Number(7.0)); +}