mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-31 06:06:48 -06:00
Ranges keep their size past the used range
A multi-cell reference was read only up to the sheet's used range, so the array it gave depended on what else was on the sheet: ROWS(A1:A10*1), COUNTBLANK(A1:A10) and SUMPRODUCT(--(A1:A10="")) came out smaller than in Excel, and changed as cells were entered elsewhere without the formula recalculating. References of up to 16,384 cells now keep their size; larger ones (A:A, ranges reserved for data to come such as $A$2:$A$50000) still stop at the used range, so that formulas over them stay fast.
This commit is contained in:
@@ -13,6 +13,10 @@ use gridcraft_model::Workbook;
|
||||
|
||||
/// Largest array a reference may expand to.
|
||||
pub const MAX_CELLS: u64 = 16_000_000;
|
||||
/// References up to this many cells are read at their full size; larger ones (`A:A`, ranges
|
||||
/// reserved for data to come such as `$A$2:$A$50000`) stop at the sheet's used range, so that
|
||||
/// formulas over them stay fast.
|
||||
const FULL_SIZE_CELLS: u64 = 16_384;
|
||||
const MAX_DEPTH: usize = 400;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
@@ -129,7 +133,10 @@ impl<'h> Evaluator<'h> {
|
||||
if a.range.is_single() {
|
||||
return self.host.cell_value(a.sheet, a.range.start);
|
||||
}
|
||||
let range = self.trim(a);
|
||||
// A reference keeps its size, so the array has its shape and its blanks count
|
||||
// (`=A1:A10`, `SUMPRODUCT(--(A1:A10=""))`).
|
||||
let cells = u64::from(a.range.height()) * u64::from(a.range.width());
|
||||
let range = if cells > FULL_SIZE_CELLS { self.trim(a) } else { a.range };
|
||||
let (h, w) = (range.height() as usize, range.width() as usize);
|
||||
if (h as u64) * (w as u64) > MAX_CELLS {
|
||||
return Value::Error(CellError::Num);
|
||||
|
||||
@@ -158,6 +158,24 @@ fn spills() {
|
||||
assert_eq!(t.num("D2"), 40.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_keep_their_size_past_the_used_range() {
|
||||
let mut t = T::new();
|
||||
t.set("A1", "5");
|
||||
t.set("A2", "6");
|
||||
t.set("C1", "=ROWS(A1:A10*1)");
|
||||
t.set("C2", "=SUMPRODUCT(--(A1:A10=\"\"))");
|
||||
t.set("C3", "=COUNTBLANK(A1:A10)");
|
||||
t.set("C4", "=COUNTIF(A1:A10,\"\")");
|
||||
t.set("C5", "=COLUMNS(TRANSPOSE(A1:A10))");
|
||||
for (at, want) in [("C1", 10.0), ("C2", 8.0), ("C3", 8.0), ("C4", 8.0), ("C5", 10.0)] {
|
||||
assert_eq!(t.num(at), want, "{at}");
|
||||
}
|
||||
// Whole columns are still read up to the used range.
|
||||
t.set("D1", "=ROWS(FILTER(A:A,A:A<>\"\"))");
|
||||
assert_eq!(t.num("D1"), 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_lambda() {
|
||||
let mut t = T::new();
|
||||
|
||||
Reference in New Issue
Block a user