diff --git a/crates/calc/src/recalc.rs b/crates/calc/src/recalc.rs index 584339534..32d588705 100644 --- a/crates/calc/src/recalc.rs +++ b/crates/calc/src/recalc.rs @@ -345,6 +345,16 @@ impl Calc { /// Recalculates every formula (F9 / Ctrl+Alt+F9, and after loading). pub fn recalc_all(&mut self, wb: &mut Workbook) { self.rebuild(wb); + // Spills are laid out again from the formulas where they are now (a structural edit moves + // or deletes the formulas, not the values they spilled). + for i in 0..wb.sheets.len() { + if wb.sheet(i).is_some_and(|s| !s.spill_ranges.is_empty() || !s.spill.is_empty()) + && let Some(sh) = wb.sheet_mut(i) + { + sh.spill.clear(); + sh.spill_ranges.clear(); + } + } let all: Vec = self.graph.nodes.keys().copied().collect(); self.run(wb, all, true); } diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 9fe3fa8fa..9343faeda 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -173,6 +173,17 @@ fn range_ending_in_index_recalculates() { assert_eq!(v(&s, "B6"), Value::Number(2.0)); } +#[test] +fn structural_edits_move_spills_with_their_formula() { + let mut s = s(); + s.execute("cell.set", json!({"cell": "A5", "input": "=SEQUENCE(3)"})).unwrap(); + s.execute("home.deleteRows", json!({"rows": "1:1"})).unwrap(); + let col = |s: &Session| (4..=7).map(|r| v(s, &format!("A{r}"))).collect::>(); + assert_eq!(col(&s), [Value::Number(1.0), Value::Number(2.0), Value::Number(3.0), Value::Empty]); + s.execute("home.deleteRows", json!({"rows": "4:4"})).unwrap(); + assert_eq!(col(&s), [Value::Empty, Value::Empty, Value::Empty, Value::Empty]); +} + #[test] fn every_command_survives_empty_params() { let mut s = Session::new();