diff --git a/crates/calc/src/recalc.rs b/crates/calc/src/recalc.rs index e537ccf0b..2aa23645c 100644 --- a/crates/calc/src/recalc.rs +++ b/crates/calc/src/recalc.rs @@ -524,6 +524,7 @@ impl Calc { self.circular = cycles; // Write back. let mut spill_changes: Vec = Vec::new(); + let mut freed: Vec<(usize, RangeRef)> = Vec::new(); for (k, v) in results { let Some(sheet) = wb.sheet_mut(k.0) else { continue }; if let Some(cell) = sheet.cells.get_mut(k.1) @@ -572,6 +573,23 @@ impl Calc { self.graph.dependents_of_range(k.0, r, &mut deps); spill_changes.extend(deps); } + if let Some(o) = old + && !sheet.spill_ranges.get(&k.1).is_some_and(|n| n.contains(o.start) && n.contains(o.end)) + { + freed.push((k.0, o)); + } + } + // An area a formula no longer spills over may unblock another formula's array. + for (si, o) in freed { + for &(s, c) in self.graph.nodes.keys() { + if s == si + && c.row <= o.end.row + && c.col <= o.end.col + && wb.sheet(s).is_some_and(|sh| sh.value(c) == Value::Error(CellError::Spill)) + { + spill_changes.push((s, c)); + } + } } if full { // Everything was already evaluated once; spill dependents need another pass. diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index b7f7323fd..c90802128 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -194,6 +194,18 @@ fn clearing_a_distant_blocker_unblocks_the_spill() { assert_eq!((v(&s, "A1"), v(&s, "A100")), (Value::Number(1.0), Value::Number(100.0))); } +#[test] +fn a_spill_that_goes_away_unblocks_another() { + let mut s = s(); + s.execute("cell.set", json!({"cell": "F8", "input": "=SEQUENCE(5)"})).unwrap(); + s.execute("cell.set", json!({"cell": "E10", "input": "=SEQUENCE(1,3)"})).unwrap(); + assert_eq!(v(&s, "E10"), Value::Error(gridcraft_core::CellError::Spill)); + // Blocking F8's array frees F10 for E10's. + s.execute("cell.set", json!({"cell": "F9", "input": "0"})).unwrap(); + assert_eq!(v(&s, "F8"), Value::Error(gridcraft_core::CellError::Spill)); + assert_eq!((v(&s, "E10"), v(&s, "G10")), (Value::Number(1.0), Value::Number(3.0))); +} + #[test] fn every_command_survives_empty_params() { let mut s = Session::new();