chore: sync upstream (97 Commits) #19

Merged
alf merged 97 commits from chore/sync-upstream-20261108 into main 2026-11-08 11:54:31 +00:00
Owner

Upstream-Sync von upstream/main (97 Commits). Fast-Forward.

  • 21920d3d Merge pull request #3297: complete equation input caps with #3328
  • a2509bdf Merge newline matrix-cap fix into prerequisite (#3297, #3328)
  • 94a8fb08 Merge pull request #1152 from vangel76/perf/chart-measure-allocs
  • fca22f6e Merge pull request #1155 from vangel76/fix/chart-work-budgets
  • 300b0748 Merge pull request #3325 from vangel76/fix/binom-poisson-accuracy
  • 9dd3b51b Merge pull request #3383 from vangel76/fix/3350-ctrl-tab-arrows
  • 4a93722e Merge pull request #2691 from vangel76/perf/calc-range-memo-2681
  • 6338cef4 Merge pull request #3578 from vangel76/fix/3577-json-formula-trees
  • 24119e49 Merge pull request #3581 from vangel76/perf/3575-store-shifts
  • cbd0879c Merge pull request #3591 from vangel76/perf/engine-adjust-if-changed
  • 0057fc00 Merge pull request #3579 from vangel76/perf/adjust-unchanged-check
  • 9ab297ce perf(engine): rewrite only the formulas a structural edit changes
  • 2ee261c0 perf(model): move whole rows when inserting or deleting rows and columns (#3575)
  • 7cb00883 perf(formula): adjust_if_changed skips formulas a structural edit leaves alone
  • 4bba98fd perf(model): cache formula trees when reading a workbook from JSON
Upstream-Sync von `upstream/main` (97 Commits). Fast-Forward. - 21920d3d Merge pull request #3297: complete equation input caps with #3328 - a2509bdf Merge newline matrix-cap fix into prerequisite (#3297, #3328) - 94a8fb08 Merge pull request #1152 from vangel76/perf/chart-measure-allocs - fca22f6e Merge pull request #1155 from vangel76/fix/chart-work-budgets - 300b0748 Merge pull request #3325 from vangel76/fix/binom-poisson-accuracy - 9dd3b51b Merge pull request #3383 from vangel76/fix/3350-ctrl-tab-arrows - 4a93722e Merge pull request #2691 from vangel76/perf/calc-range-memo-2681 - 6338cef4 Merge pull request #3578 from vangel76/fix/3577-json-formula-trees - 24119e49 Merge pull request #3581 from vangel76/perf/3575-store-shifts - cbd0879c Merge pull request #3591 from vangel76/perf/engine-adjust-if-changed - 0057fc00 Merge pull request #3579 from vangel76/perf/adjust-unchanged-check - 9ab297ce perf(engine): rewrite only the formulas a structural edit changes - 2ee261c0 perf(model): move whole rows when inserting or deleting rows and columns (#3575) - 7cb00883 perf(formula): adjust_if_changed skips formulas a structural edit leaves alone - 4bba98fd perf(model): cache formula trees when reading a workbook from JSON
alf added 97 commits 2026-11-08 11:54:30 +00:00
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Binary search in Ctx::fit, early exit of the side-legend column measure,
no per-point Vecs in value_range/squarify, treemap carries the value.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A chart keeps at most 2,000,000 series x points (every series and the
categories keep their first points), 16 trendlines per chart; columns and
bars narrower than 0.25 px are merged per pixel column; series references
are cut after the rows that can matter before calc evaluates them; the
moving average is linear; fit_trendline allocates no Vec per point;
render borrows clean data instead of cloning it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts:
#	ROADMAP.md
#	crates/chart/src/tests.rs
# Conflicts:
#	ROADMAP.md
# Conflicts:
#	ROADMAP.md
#	crates/chart/src/resolve.rs
Also removes conflict markers committed to ROADMAP.md by the previous merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Read the needed order statistics with select_nth_unstable_by(total_cmp)
instead of a full sort. Results are bit-identical; a test compares them
against the previous full-sort code on randomised data.

Fixes #2679

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A block that holds only constants can't change during a pass, so its sum
and count are worked out once and reused by every formula that reads the
same block. A block that is the same rows from the top but longer (a
running total) continues the shorter block's sum, which is the same
additions in the same order as one walk from the start.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Calc::rebuild and Calc::cells_changed called Formula::expr(), which deep-copies each formula's
syntax tree only to read its references. Use expr_arc() (already used when formulas are
calculated), which hands out the cached Arc without copying.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
render.rs was split into render/ modules on main; the budget and hairline-bar changes are re-applied in render.rs and render/cartesian.rs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
render.rs was split into render/ modules on main; the label-fitting, legend and treemap changes are re-applied in the new modules.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
render.rs was split into render/ modules on main; the plot-kind and fixed-scale cleanups are re-applied in the new modules.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
stat.rs was split into stat/; re-applied the selection changes in stat/descriptive.rs and moved the oracle test to stat/tests.rs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
eval.rs and recalc.rs were split into submodules; re-applied the range_sum hunks in eval.rs, eval/call.rs and recalc/pass_host.rs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A full sort recognises ascending and descending runs in O(n), which a
selection cannot beat, so sorted, reverse-sorted and tie-block input was
up to 1.8x slower than before. Check for a monotone run first (one pass
that stops at the first out-of-order pair) and index into it. Extend the
bit-for-bit oracle test with ascending, descending and nearly ascending
data.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Formula::with_expr runs a closure on the cached tree, or on a freshly parsed
one that is dropped afterwards, so rebuild and cells_changed need neither a
tree copy nor an Arc allocation / reference count.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A Python cell is not a constant for the range_sum block memo.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
RaTeX counts macro expansions (1,000) but not how large they grow, so a
definition whose argument doubles on every call keeps the expander busy
without bound. User macros have no use in an equation, so check_caps now
refuses the definers (def, gdef, edef, xdef, let, futurelet, global, long,
newcommand, renewcommand, providecommand) before RaTeX sees the source. The
scan reads control words, verbatim text and comments the way RaTeX's lexer
does.

A matrix is laid out and written as rows times the widest row, so one long row
in a tall matrix multiplied the output. check_caps now counts the padded cells
of every environment from the source and refuses more than 2,500 in total;
to_omml checks the parsed grid as well.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The single-cell AutoSum guess stepped over one blank cell when a number sat
above it, so a second block of numbers below a gap was summed together with
the first block. It now stops at the first non-number, as Excel does.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The mass came from ln_gamma differences (relative error 1.1e-13 at n = 100,
1.5e-11 at n = 10,000) and the cumulative forms summed up to 10,000 of them.
The mass now comes from Loader's saddle-point formula, the cumulative sum
walks away from the mode by the ratio of neighbouring terms and stops when
terms stop mattering, and the incomplete beta / gamma branches take the same
mass as their prefactor.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
CodeRabbit found that \begin{alignedat}%<newline>{99999999} and {9 9} made the
count parser return None, which skipped the 50-pair cap. The lookahead now drops
comments and spaces as RaTeX's lexer does, and a matched alignat name with a
count that is not a plain digit string is refused.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
CodeRabbit pointed out that an error or NaN result passed the accuracy test and
that a zero reference allowed any finite value (the bound was infinite). Errors
now count as infinite error and a zero reference allows 1e-300.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
RaTeX reads \newline as \\, but the source scan only counted \\ and \cr, so
a matrix built with \newline skipped the 2,500 cell cap.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Sort dialog's closed "Sort by" box showed only the stored column
letter ("B") while its open list showed "B: Revenue". It now looks the
letter up in the dialog's column labels and shows the same label, falling
back to the bare letter for a column outside the data block.
Scroll positions were stored by (window, sheet index), and nothing re-keyed
them when sheets were inserted, deleted or moved. A new PivotTable sheet,
which goes in front of its source, opened at the source's position, and the
source took over whatever was stored at its new index. Positions are now
keyed by (window, sheet uid), so they follow the sheet through inserts,
deletes, moves and undo. A closed window's positions are dropped. Saving the
active cell still happens when no window is active.

Co-Authored-By: Olivier <5681030+tutis@users.noreply.github.com>
Ctrl+Shift+Arrow searched from the selection's moving corner, so after
Ctrl+Shift+Right from A2 the next Ctrl+Shift+Down searched the far column
and ran to the last row. As in Excel, it now searches the active cell's
column (Up/Down) or row (Left/Right) from the edge being moved, and moves
only that edge. A diagonal move from command params keeps the old jump.

Co-Authored-By: Olivier <5681030+tutis@users.noreply.github.com>
The web shell queues the page's calls as control requests (the channel the desktop control server uses) and answers them on the next frame. A call the page gave up on is released, so the app skips it instead of running it late. The API is installed in every mode; opening, saving, the file name and the unsaved flag stay with the host messages.
Sort: the closed Sort by box names the column, as its list does (#3446)
View: a sheet keeps its own scroll position when sheets move (#3485)
Selection: Ctrl+Shift+Arrow searches the active cell's line (#3484)
gridcraft-web: window.gridcraft, the page command API from craft-web-api
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
contributors: set names for vangel76
fix(autosum): stop the guessed range at a blank cell
perf: select instead of sort in MEDIAN, PERCENTILE, QUARTILE, LARGE, SMALL
calc: rebuild the dependency graph from shared syntax trees
Charts: one combo plot-kind function, no dead scale helpers (#1146)
COUNT never reads the running sum, so the zero-sum gate that keeps SUM
and AVERAGE additions in order does not apply to it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A name such as align\@char{97}t reads as alignat to the parser but split at
the first } looked like another environment, so the pair limit was skipped.
Names that contain a backslash or brace, or do not close in the window, are
now refused like an unreadable count.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The small-k product also needs (1 - p)^(n - k) to stay a normal number;
otherwise it returned 0 for BINOM.DIST(15,1100,0.5,FALSE) (exact
2.1367646606291324e-298) and the cumulative form built on it. Those cases
now use the logarithmic formula. Reference grid gets k = 10..25 at n = 1100.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
When the edge sheet was already active nothing changed, so the strip was not
scrolled back to it. A Ctrl-click on the arrow now asks for the reveal itself.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Read only the cells the category levels use and cut the category dimension
at the per-series cap, so a multi-level axis keeps all its levels.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The width of a prefix plus an ellipsis is not monotone when the text has a
line break, so the binary search could drop a label that fits. Also pass the
series index to plot_kind in the data table (upstream/main does not compile
without it).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fix(chart): pass series index to data-table keys
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Paste, Ctrl+Enter and Fill skip rows hidden by a filter (#1101)

Repeating one cell or row down a filtered selection (paste of a single
cell or row, Ctrl+Enter, Fill Down/Up/Right/Left) now leaves the rows an
applied filter hides alone, as Excel does. A taller pasted block still
goes straight down, and rows hidden by hand are still written, also as
in Excel. Each case was measured in Excel for Microsoft 365.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2bJ3LxrZ6YnmppgrfwHwr

* Copying a filtered range takes only the rows showing (#1101)

A copied range with rows hidden by an applied filter is split into its
runs of showing rows and packed like a multi-area copy (formulas as
values), so pasting it inside GridCraft no longer brings the hidden rows
back. One run is copied as a plain range, keeping formulas. Cut still
moves every row. All as measured in Excel for Microsoft 365.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2bJ3LxrZ6YnmppgrfwHwr

* Find the rows a filter hides without rescanning every table per row

filtered_rows asks the sheet for its filter row spans once instead of
checking every table for each hidden row.

* Paste over filtered rows leaves their validation rules alone

When one row is tiled over filtered rows, paste_validations no longer
clears or copies rules on the rows the filter hides.

* Filtered copy packs links and validation with the rows showing

slide_together now moves hyperlinks and validation rules with their rows, and a
filtered whole-row copy records the packed rows as its selection so Insert
Copied Cells inserts only the rows copied.

* Group validation paste options and format the new tests

* Pack a filtered copy in one pass over notes, merges, links and spills

slide_together finds each entry's area by search instead of scanning every map
once per area, so a filter leaving thousands of separate runs no longer costs
runs times map size on every copy.

---------

Co-authored-by: Richard <richard1912@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: skylar <hi@skylar.cc>
A Formula read from JSON had no cached tree (`expr` is not saved), so
every graph rebuild and every recalculation parsed each formula again.
Reading one now parses its text once and keeps the tree, as
Formula::new does. The saved text is kept as written.

Fixes #3577

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adjust had no way to report that an edit does not affect a formula, so
callers copied, rebuilt and printed every formula in the workbook.
adjust_if_changed borrows the formula and returns None when nothing
moves. Its check reuses adjust_ref on each reference into the edited
sheet, on a copy without the sheet name, so it cannot disagree with
adjust. adjust_ref and shift_ref take the reference by value and no
longer clone sheet names.

An integration test keeps the previous implementation as an oracle
over every edit kind and a randomized property test.

Fixes #3576

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CellStore::shift_rows, shift_cols, shift_cols_in_rows and take_range removed
and re-set every affected cell one by one. They now take whole row maps (or
the split-off part of one) out of the bands and put them back at their new
position, so a row shift costs a few map operations per row instead of a
band lookup, a remove and a set per cell. take_range does this when the
block reaches the last column.

Behaviour is unchanged, including blank cells left behind by get_mut, cells off the grid (they
are still dropped, and still clear the cell they land on), cells shifted off
the sheet, and copy-on-write: bands without moved cells stay shared with undo
snapshots. A differential test runs 12,000 random operations against the old
per-cell code and compares contents, length, iteration order and band sharing.

shift_rows_in_cols, and take_range on a block with columns to its right, keep
moving cell by cell: the row-map version measured no faster there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rewrite_all_formulas borrows each tree with Formula::with_expr and calls
adjust_if_changed, so a formula the edit leaves alone is neither copied
nor printed, and a changed one is printed once. A legacy array whose
range moves while its formula stays the same still gets the new range.

Sheet rename and delete compare the adjusted tree with the old one
instead of printed text, and replace only formulas that changed. Delete
no longer rebuilds every formula in the workbook, so unrelated formulas
keep their array range and dynamic-array mark.

A stored non-canonical formula text is no longer re-printed by an edit
that does not change the formula; a test pins this.

Part of #3576

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Formulas: adjust_if_changed reports when a structural edit leaves a formula alone
Structural edits rewrite only the formulas they change
Move whole rows when inserting or deleting rows and columns
Cache formula trees when a workbook is read from JSON
perf(calc): SUM/COUNT/AVERAGE over big ranges reuse constant-only results
Tabs: Ctrl-click on the sheet arrows jumps to the first or last sheet
BINOM.DIST, POISSON.DIST: Loader mass and recurrence sums, error 1e-11 -> 1e-14 (#3324)
Charts: work and memory are bounded by series x points; ranges are clipped before evaluation (#1147)
Charts: label fitting and legend measuring avoid hundreds of text layouts per render (#1149)
Keep the complete reviewed matrix bound together so main never receives the known newline-row bypass. This merge tree is identical to the validated #3328 head 83670dfe3a.
Merge pull request #3297: complete equation input caps with #3328
Some checks failed
ci.yml / Merge pull request #3297: complete equation input caps with #3328 (push) Failing after 0s
nix.yml / Merge pull request #3297: complete equation input caps with #3328 (push) Failing after 0s
packaging-lint.yml / Merge pull request #3297: complete equation input caps with #3328 (push) Failing after 0s
sonarcloud.yml / Merge pull request #3297: complete equation input caps with #3328 (push) Failing after 0s
ci.yml / Merge pull request #3297: complete equation input caps with #3328 (pull_request) Failing after 0s
nix.yml / Merge pull request #3297: complete equation input caps with #3328 (pull_request) Failing after 0s
packaging-lint.yml / Merge pull request #3297: complete equation input caps with #3328 (pull_request) Failing after 0s
sonarcloud.yml / Merge pull request #3297: complete equation input caps with #3328 (pull_request) Failing after 0s
21920d3d61
Equations: refuse macro definitions and cap matrix cells
alf merged commit 21920d3d61 into main 2026-11-08 11:54:31 +00:00
alf deleted branch chore/sync-upstream-20261108 2026-11-08 11:54:31 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
iris/gridcraft!19
No description provided.