diff --git a/crates/engine/src/cmd/insert.rs b/crates/engine/src/cmd/insert.rs index 9f044613d..036e18a58 100644 --- a/crates/engine/src/cmd/insert.rs +++ b/crates/engine/src/cmd/insert.rs @@ -2,9 +2,11 @@ //! conditional formatting (Home › Styles) and symbols. use gridcraft_core::{CellRef, MAX_ROWS, RangeRef}; +use gridcraft_formula::adjust::{Axis, Edit}; use gridcraft_model::*; use serde_json::{Value as Json, json}; +use super::sheet::{rewrite, shift_cell_features}; use super::*; use crate::selection::current_region; @@ -276,13 +278,13 @@ fn insert_table(s: &mut Session, p: &Json) -> Result { return Err(EngineError::Other("To prevent possible loss of data, cells can't be pushed off the end of the worksheet.".into())); } sh.cells.shift_rows_in_cols(r.start.col, r.end.col, r.start.row, 1); - sh.cell_pictures = std::mem::take(&mut sh.cell_pictures) - .into_iter() - .filter_map(|(c, v)| { - let dest = if c.col >= r.start.col && c.col <= r.end.col && c.row >= r.start.row { c.offset(1, 0) } else { Some(c) }; - dest.map(|c| (c, v)) - }) - .collect(); + // Like Insert Cells > Shift down: move merges, conditional formats, notes, links and pictures, and re-point every formula + // reference (on any sheet) at the cells that moved. + let e = Edit::InsertCells { axis: Axis::Rows, range: RangeRef::new(r.start, CellRef::new(r.start.row, r.end.col)) }; + shift_cell_features(sh, &e); + let name = sh.name.clone(); + rewrite(&mut cx.wb, &name, &e); + let sh = cx.sheet_mut(sheet)?; for (i, c) in (r.start.col..=r.end.col).enumerate() { sh.set_value(CellRef::new(r.start.row, c), gridcraft_core::Value::text(format!("Column{}", i + 1))); } @@ -1554,4 +1556,29 @@ mod tests { let rules = s.execute("home.manageRules", json!({})).unwrap(); assert_eq!(rules["rules"].as_array().map(Vec::len), Some(0)); } + + #[test] + fn table_without_header_keeps_outside_references() { + // #1030: the data moved down for the new header row, but formulas outside kept the old addresses. + let mut s = Session::new(); + s.new_workbook(); + s.execute("home.insertSheet", json!({"name": "Two"})).unwrap(); + s.execute("sheet.activate", json!({"sheet": 0})).unwrap(); + s.execute("cell.set", json!({"cell": "A1", "input": "10"})).unwrap(); + s.execute("cell.set", json!({"cell": "A2", "input": "20"})).unwrap(); + s.execute("cell.set", json!({"cell": "B1", "input": "=SUM(A1:A2)"})).unwrap(); + s.execute("cell.set", json!({"cell": "C1", "input": "=A2"})).unwrap(); + s.execute("cell.set", json!({"sheet": "Two", "cell": "A1", "input": "=Sheet1!A2"})).unwrap(); + s.execute("insert.table", json!({"range": "A1:A2", "header": false})).unwrap(); + let get = |s: &mut Session, a: &str| s.execute("cell.get", json!({"cell": a})).unwrap(); + assert_eq!(get(&mut s, "A1")["value"], "Column1"); + assert_eq!(get(&mut s, "A2")["value"], json!(10.0)); + assert_eq!(get(&mut s, "B1")["formula"], "=SUM(A2:A3)"); + assert_eq!(get(&mut s, "B1")["value"], json!(30.0)); + assert_eq!(get(&mut s, "C1")["formula"], "=A3"); + assert_eq!(get(&mut s, "C1")["value"], json!(20.0)); + let other = s.execute("cell.get", json!({"sheet": "Two", "cell": "A1"})).unwrap(); + assert_eq!(other["formula"], "=Sheet1!A3"); + assert_eq!(other["value"], json!(20.0)); + } } diff --git a/crates/engine/src/cmd/sheet.rs b/crates/engine/src/cmd/sheet.rs index 163773399..2e257e3af 100644 --- a/crates/engine/src/cmd/sheet.rs +++ b/crates/engine/src/cmd/sheet.rs @@ -214,7 +214,7 @@ fn shift_sheet_features(sh: &mut Sheet, axis: Axis, at: u32, count: u32, insert: } /// Rewrites every formula reference (and chart series) for a structural edit on `target`. -fn rewrite(wb: &mut gridcraft_model::Workbook, target: &str, e: &Edit) { +pub(super) fn rewrite(wb: &mut gridcraft_model::Workbook, target: &str, e: &Edit) { super::edit::rewrite_all_formulas(wb, target, e); for si in 0..wb.sheets.len() { let host = wb.sheets.get(si).map(|s| s.name.clone()).unwrap_or_default(); @@ -973,7 +973,7 @@ pub(crate) fn map_range(r: RangeRef, e: &Edit) -> Option { /// Shifts what Insert/Delete Cells moves along with the cells: merged areas, conditional /// format, validation, table, filter and print-area ranges, notes, links and sparklines. -fn shift_cell_features(sh: &mut Sheet, e: &Edit) { +pub(super) fn shift_cell_features(sh: &mut Sheet, e: &Edit) { let map_cell = |c: CellRef| map_range(RangeRef::cell(c), e).map(|r| r.start); sh.merges = sh.merges.iter().filter_map(|m| map_range(*m, e)).filter(|m| !m.is_single()).collect(); for cf in sh.cond_formats.iter_mut() {