Commit Graph
1983 Commits
Author SHA1 Message Date
Mario f065f9168a Clean up Command Palette code 2026-10-15 05:18:08 +02:00
skylar taylor-barrickandskylartaylor e6b6b34970 ci: add comfortable runtime limits to unbounded jobs (#1418)
Co-authored-by: skylartaylor <26588250+skylartaylor@users.noreply.github.com>
2026-10-15 05:16:03 +02:00
derechopiano be20ca163d UI: keep PivotTable field-list scrollbar at pane edge 2026-10-15 04:13:02 +01:00
David ArdilaandClaude Opus 5.5 cd94a6d7b4 Recalculation: a spill anchor given a formula that reads its old spill area sees it empty
When a dynamic array's anchor gets a new formula, the formula is evaluated
while the old array is still laid out, so it can read the old spilled
values. The anchor was then never evaluated again: it is left out of its
own spill area's dependents, so as not to read its own array.

- `A1` was `=SEQUENCE(3)` and becomes `=SUM(A2:A3)+1`: it showed 6 (2 + 3
  + 1) with A2:A3 empty; recalculating from scratch gives 1.
- `A1` was `=SEQUENCE(3)` and becomes `=SEQUENCE(1,A2+1)`: it read A2 as 2
  and spilled over A1:C1 instead of showing 1.

The anchor is now evaluated again when its old array is gone or partly
freed. It is still left out when its new array covers the old one, where
it would read its own array.

Regression test in calc
(`an_anchor_given_a_formula_that_reads_its_old_spill_area_sees_it_empty`:
the new formula stops spilling, spills elsewhere, or spills over part of
the old area); it fails without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:05:37 -05:00
David ArdilaandClaude Opus 5.5 ac7595975a Recalculation: formulas reading an array that loses a spill conflict see #SPILL!
When two dynamic arrays of one recalculation want the same empty cells,
both look free while the pass runs, so formulas evaluated in that pass
read both arrays. The one laid out second becomes #SPILL! at write-back,
but nothing evaluated its readers again, and they kept its values.

With B1 `=SEQUENCE(3)` and A2 `=SEQUENCE(1,3)` (both want B2), a full
recalculation (opening a file, Calculate Now) leaves A2 at #SPILL!, yet
`=A2*10` showed 10, `=SUM(A2#)` 6, and `=C2` and `=SUM(C2:C3)` 3, though C2
is empty. Entering the same formulas one at a time gave the right values.

An array blocked at write-back now sends the formulas that read its anchor
or the area it would have spilled into to the next round, where they read
#SPILL! and empty cells.

Regression test in calc
(`formulas_reading_an_array_that_loses_a_spill_conflict_read_spill_errors`);
it fails without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:05:35 -05:00
Mario 4bfd0bc5a1 Improve Command Palette keyboard navigation and layout 2026-10-15 03:23:16 +02:00
Harrison BoothandClaude Opus 5.5 ea190adad6 functions: Analysis ToolPak value arguments reject multi-cell references
EOMONTH, EDATE, WORKDAY(.INTL), NETWORKDAYS(.INTL), YEARFRAC, WEEKNUM, the
coupon, bond and T-bill functions, DISC, BIN2DEC, IM* and the other Analysis
ToolPak functions give #VALUE! in Excel 365 for a reference to more than one
cell in a value argument, in dynamic-array and legacy formulas alike, where
GridCraft lifted the range and spilled. Array constants and computed arrays
still lift, and range arguments (holidays, cash flows, schedules) and the
.INTL weekend still take ranges.

FnSpec gains a value_args mask, filled at registration from one table in
lift.rs; call_lifted turns a multi-cell reference at a value position into a
scalar #VALUE! before lifting, so EOMONTH(A1:A3,{1;2}) is a 2x1 array of
#VALUE! as in Excel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:55:46 -05:00
Harrison BoothandClaude Opus 5.5 01eb237209 Docs: corpus count for dropped dynamic-array metadata
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:29:44 -05:00
Harrison BoothandClaude Opus 5.5 7ee11fe137 XLSX: keep dynamic-array cell metadata (cm) on save
A formula read with `cm` (Excel's XLDAPR dynamic-array record in
xl/metadata.xml) now carries `Formula::dynamic`, and the writer keeps the
`cm` and the metadata part for it even when the formula returns one value
and the export shape analysis would not need array evaluation. Before, such
formulas were saved as legacy formulas, so Excel recalculated some of them
with implicit intersection (SUMPRODUCT(IFERROR(range/range,0)) changed
value), and workbooks whose every dynamic formula was scalar lost
xl/metadata.xml altogether.

The mark survives moves, row and column insertion or deletion, sheet and
name renames and LAMBDA call-case fixes; retyping a formula drops it and
the writer decides from the formula's shape, as before. It is serialized
only when set, so saved documents without it are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:05:03 -05:00
Harrison BoothandClaude Opus 5.5 5c20f16434 core: read accounting negatives like $(1,234) in number text
Excel 365 reads a dollar sign before accounting parentheses ($(1,234),
$ (5)) and a percent sign after them ((5)%) as negative numbers when the
text is typed, coerced by arithmetic or passed to VALUE. GridCraft gave
#VALUE! for these.

The parentheses branch no longer recurses: Excel reads only an unsigned
plain number or fraction inside, so (-5), ((5)), ($-5), and dates or
times in parentheses are now #VALUE! as in Excel (they used to give 5
or a negative serial). $5% is #VALUE! too: Excel never combines the
currency and percent signs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:46 -05:00
Harrison BoothandClaude Opus 5.5 ab4d58ca6f Lookups compare numbers exactly, like Excel
MATCH, XMATCH, VLOOKUP, HLOOKUP, XLOOKUP and LOOKUP compared numbers at
15 significant digits (through compare / compare_numbers), so a computed
key such as =0.1+0.2 (0.30000000000000004) found 0.3. Excel 365 compares
lookup keys exactly in exact, approximate and binary search modes, while
=, SWITCH and the criteria functions (COUNTIF(S), SUMIF(S), AVERAGEIF,
MAXIFS) keep the 15-digit comparison. Observed in Excel via COM with
synthetic values.

lookup_equal now compares numbers with ==, and the lookup searches order
values with lookup_order (exact for numbers, compare otherwise).
compare_numbers and the criteria path are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:26:06 -05:00
Mario f9802ecc9d Improved font dropdown row sizing and scroll alignment 2026-10-15 01:15:15 +02:00
Harrison BoothandClaude Opus 5.5 74de1b32eb Keytips: Alt, H, B border letters apply their own preset
Every Alt, H, B letter ran home.borders with no params, which defaults to
a bottom border, so H,B,N (No Border) and H,B,A (All Borders) both drew a
bottom border. keytips.json entries now take optional fixed `params`
(Action::CommandWith / DialogWith), and each Borders menu letter passes the
preset Excel's menu gives it. H,B,M opens Format Cells on the Border tab.
Erase Border, Line Color and Line Style are reserved (pending) instead of
drawing a bottom border.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:14:07 -05:00
Harrison BoothandClaude Opus 5.5 ced7243234 calc: bounded references up to 1,048,576 cells keep their full shape (#1010)
A reference over 16,384 cells was cut to the sheet's used range when it was
read into an array. Next to a full-size operand the two no longer lined up:
SUMPRODUCT(D3:AZ400*B3:B400,(A3:A400=1)*(D1:AZ1=2)) gave #VALUE!,
D3:AZ400*B3:B400 filled with #N/A, and ROWS, COUNTBLANK and --(r="") lost
the blanks past the data. Excel keeps the reference's size.

Bounded references now keep their full shape up to one column's worth of
cells (1,048,576). References that run to the edge of the sheet (A:A,
A2:A1048576, several whole rows) and larger blocks still stop at the used
range, so whole-column formulas stay fast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:36:36 -05:00
midasmatterhorn 34a4ed3ead engine: rebuild cell-store bands and counts when importing a JSON workbook 2026-10-15 00:09:25 +03:00
vangel76andClaude Sonnet 5.5 0d18137114 Protection: enforce selectLockedCells/selectUnlockedCells and offer them in Protect Sheet (#1346)
selection.set refuses a cell the protection forbids selecting, selection.move steps on to the
next selectable cell, and review.protectSheet keeps the sheet's current selection rights unless
told otherwise. The dialog gains the two select options and insert/delete columns.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 23:00:50 +02:00
midasmatterhorn d66ed11253 xlsx: keep every property of the default cell format when importing a file 2026-10-14 23:58:57 +03:00
vangel76andClaude Sonnet 5.5 33abe3a9f7 UI: Protect Sheet entries become Unprotect Sheet on a protected sheet (#1346)
Home > Format, HOP and RPS always opened the Protect Sheet dialog, which re-protected
the sheet. open_dialog now turns protectSheet into unprotectSheet for a protected sheet,
and unprotects at once when there is no password.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 22:57:29 +02:00
vangel76andClaude Sonnet 5.5 833fe0da1f Edit: text that would parse as a number or formula keeps its quote prefix in the editor (#1362)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 22:56:36 +02:00
NetterSilverandClaude Haiku 5.5 ac8f6f6ce4 Keep Middle/Bottom vertical alignment for wrapped text taller than its row
Fixes #1347

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 22:56:35 +02:00
midasmatterhorn 2c23b61908 engine: subtotal honours header:false instead of dropping the first data row 2026-10-14 23:51:59 +03:00
DCM4711andClaude Opus 5.5 7b10212b5a Header menus: Format Cells… for the selected rows or columns
As in Excel, right-clicking a row or column heading offers Format
Cells…, between Clear Contents and Row Height… / Column Width….

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:43:51 +02:00
midasmatterhorn 686c13c8b7 engine: keep case-insensitive replace from dropping text when folding shifts byte boundaries 2026-10-14 23:22:05 +03:00
Mario 50f8c0d42b Fix font dropdown resizing and keyboard navigation 2026-10-14 22:16:10 +02:00
midasmatterhorn 938762d796 engine: keep AutoFill text series from panicking or wrapping on i64 overflow 2026-10-14 22:56:07 +03:00
DCM4711andClaude Opus 5.5 d48bcaf043 Context menu: the right-click that opens it doesn't close it
A quick right-click delivers press and release in one frame. The menu
opens at the pointer, and its corner snaps to the nearest physical pixel
(252.46 -> 252.5 on a Retina display), which can leave the pointer just
outside it. The close check then saw that frame's press as a click
outside the menu and closed it right away, so the menu often flashed and
vanished on macOS.

The menu remembers the frame it opened in, and that frame's press no
longer counts as a click elsewhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:55:20 +02:00
DCM4711andClaude Opus 5.5 a0ec1fb785 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 <noreply@anthropic.com>
2026-10-14 20:24:56 +02:00
Harrison BoothandClaude Opus 5.5 1241fbf260 calc: calculate long dependency chains with bounded stack
A formula that reads one later in row-major order (further down, or on a
later sheet) is calculated on demand, inside the formula that reads it.
Each level costs stack, so real models with chains of about 1,000 such
formulas overflowed the 1 MB main-thread stack on Windows and crashed on
open. Past 2,000 levels the chain gave #CIRC! instead.

On-demand evaluation now goes at most 64 formulas deep. A formula needed
deeper suspends the evaluations in progress: it is calculated from the
top, then the suspended ones again, reading the results kept so far.
Chains of any length calculate, with the same results. A suspended
formula read again is a circular reference, as a formula in progress is,
so loops are found at the same cell as before.

Regression tests build the chains in code: 20,000 formulas each reading
the row below, the same hopping between two sheets, a 5,000-formula loop,
and a loop closing into the middle of a deep chain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:08:42 -05:00
Marco Arnulfo 62729e3a99 Keyboard: Alt+= no longer starts an edit with the = it types (fixes #1327) 2026-10-14 19:52:36 +02:00
MarekSpodBiedry ef894c20e5 Reject a list data validation that has no formula1 (#1303) 2026-10-14 19:31:01 +02:00
Marco Arnulfo f36bc100d4 AutoFit measures each distinct text once, and quickly on large columns (#1316) 2026-10-14 18:14:09 +02:00
Marco Arnulfo f92b4d30cb AutoFit Column Width measures text in the font the grid draws it with (fixes #1316) 2026-10-14 18:01:11 +02:00
midasmatterhorn 450ef1aea0 engine: refuse removeDuplicates with no valid column instead of deleting all rows 2026-10-14 18:01:42 +03:00
dev-exeandClaude Opus 5.5 2cae3a22b1 Slicers: a table named in table is found on any sheet
insert.slicer and table.insertSlicer looked for the table named by `table` only on the active
sheet. With another sheet active, the call failed with "Select a cell inside a table first, or
pass `table`" although `table` was passed. Other table commands find a table by name in the
whole workbook (Workbook::table).

The named table is now found on any sheet, and the slicer goes on that table's sheet. `at` is
read on that sheet, so "Sheet1!G2" names the table's own sheet. A name that matches no table
gives "no table named `X`".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:30:46 +05:30
Pranav Chandra 3177fbe03f ui: show spill-cell formula as greyed out 2026-10-14 20:23:44 +05:30
MarekSpodBiedry 139098e174 Filter: count empty text and ="" results as (Blanks) (#1294) 2026-10-14 15:44:31 +02:00
Brandon ThomasandClaude Opus 5.5 d9d09f0baa Release 0.7.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.7.0
2026-10-14 09:33:30 -04:00
Harrison BoothandClaude Opus 5.5 efd081acc0 UI parity: form dialogs open ready to type
Column Width, Row Height and the other form dialogs now open with the cursor
in their first field and its value selected, as in Excel: Alt+H, O, W, then
12 and Enter sets the width without clicking into the field first.

- The first text or number field takes focus once it can (a new window's
  first frame is a sizing pass that ignores focus requests), and its value
  is selected so typing replaces it.
- Number fields commit while typing, so Enter or OK in the same frame no
  longer confirms the old value.

Tests: tests/dialog_focus.rs (Column Width and Row Height: type, Enter,
value applied).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:07:59 -05:00
derechopiano 78ffd3b4c3 fix(pdf): preserve exported links and allow CLI PDF conversion (#954) 2026-10-14 13:40:16 +01:00
Marco Arnulfo 39602e6ce1 Filter and Subtotal: a Sheet! prefix on the range works on that sheet, not the active one (refs #1283) 2026-10-14 13:00:17 +02:00
Marco Arnulfo 13691e95e5 Remove Duplicates: a Sheet! prefix on the range works on that sheet, not the active one (refs #1283) 2026-10-14 12:51:05 +02:00
Marco Arnulfo be1af2d6ac Sort: a Sheet! prefix on the range sorts that sheet, not the active one (fixes #1283) 2026-10-14 12:08:41 +02:00
Marco Arnulfo 531d292930 N() converts a computed array element by element, so it works inside SUMPRODUCT (fixes #1279) 2026-10-14 11:46:48 +02:00
RichardandClaude Fable 5.1 34aa98870d Zoom dialog: Excel's layout, current zoom pre-selected, Fit selection (#930)
View > Zoom is now one Magnification column as in Excel: 200%, 100%, 75%,
50%, 25%, Fit selection and Custom with its percentage box. The current zoom
is pre-selected (Custom when it matches no preset), the box shows whole
percentages clamped to 10..400 and typing in it selects Custom. Fit
selection runs view.zoomToSelection with the grid's size, the same answer
as the ribbon's Zoom to Selection. The window is 220 px wide instead of
380; its height is left to the shared dialog sizing work (#978).

Magnification and Fit selection are translated in every catalog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2bJ3LxrZ6YnmppgrfwHwr
2026-10-14 20:22:00 +11:00
derechopiano 72db972de3 fix(engine): bound Undo history by retained cell storage (#1227) 2026-10-14 08:23:57 +01:00
Marco Arnulfo 89439636d8 Dialogs move by their title bar instead of staying pinned to the centre (fixes #1250) 2026-10-14 08:08:41 +02:00
RichardandClaude Opus 5.5 07f6fe0b0b Headers: mark hidden columns and rows, split cursor and double-click to unhide (#1247)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2bJ3LxrZ6YnmppgrfwHwr
2026-10-14 16:52:24 +11:00
Marco Arnulfo 736cc89b2b Opening an XLSX with a part over 512 MB says how to get the data in (fixes #1242) 2026-10-14 07:44:41 +02:00
Marco Arnulfo d1075c067b BAHTTEXT: a final one after the millions reads เอ็ด (fixes #1234) 2026-10-14 05:46:44 +02:00
allenyllee 938097f5b6 I1231: load Flatpak host fonts for native CJK fallback 2026-10-14 11:44:01 +08:00