From a0ec1fb785ab17d084ac76d5ad41024169d32214 Mon Sep 17 00:00:00 2001 From: DCM4711 <107349915+DCM4711@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:24:56 +0200 Subject: [PATCH] Selection: whole columns and rows don't widen to merged cells Clicking column B's header on a sheet with A2:C2 merged selected A:C, because selection.set grows every area until merged cells are whole. Excel leaves header selections alone: column B stays B:B and row 3 stays 3:3. Cell ranges still take merged cells whole (B1:B3 -> A1:C3). Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/selection.rs | 6 +++++- crates/engine/src/tests.rs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/selection.rs b/crates/engine/src/selection.rs index 27418a21a..4df6fbe4a 100644 --- a/crates/engine/src/selection.rs +++ b/crates/engine/src/selection.rs @@ -63,9 +63,13 @@ impl Selection { pub fn a1(&self) -> String { self.ranges.iter().map(|r| r.a1()).collect::>().join(",") } - /// Expands areas so merged cells are included whole. + /// Expands areas so merged cells are included whole. Whole columns and whole rows stay as + /// they are: clicking column B's header selects just B even when B1:C1 is merged, as in Excel. pub fn expand_merges(&mut self, sheet: &Sheet) { for r in self.ranges.iter_mut() { + if r.is_full_cols() || r.is_full_rows() { + continue; + } let mut cur = *r; loop { let mut grown = cur; diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index ae499a189..9051d29e4 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -2267,6 +2267,21 @@ fn whole_sheet_selection_stays_within_the_data() { assert!(s.execute("data.removeDuplicates", json!({"range": "A1:XFD1048576", "header": false})).is_ok()); } +#[test] +fn whole_columns_and_rows_ignore_merges_but_cell_ranges_take_them_whole() { + // As in Excel: a merged A2:C2 doesn't widen a click on column B's header to A:C, nor a + // merged B2:B4 a click on row 3's header to rows 2:4; a cell range still takes them whole. + let mut s = s(); + s.execute("home.mergeCenter", json!({"range": "A2:C2"})).unwrap(); + s.execute("home.mergeCenter", json!({"range": "E2:E4"})).unwrap(); + let sel = |s: &mut Session, range: &str| s.execute("selection.set", json!({"range": range})).unwrap()["selection"].clone(); + assert_eq!(sel(&mut s, "B:B"), "B:B"); + assert_eq!(sel(&mut s, "A:B"), "A:B"); + assert_eq!(sel(&mut s, "3:3"), "3:3"); + assert_eq!(sel(&mut s, "B1:B3"), "A1:C3"); + assert_eq!(sel(&mut s, "E3"), "E2:E4"); +} + #[test] fn parity_counts() { let (done, total) = crate::catalog::parity();