Commit Graph
438 Commits
Author SHA1 Message Date
aiongg 512b2aa9fd Integration: track t04
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/edit.rs
#	crates/engine/src/cmd/formulas.rs
#	crates/engine/src/tests.rs
#	crates/formula/src/lib.rs
#	crates/formula/src/parser.rs
#	crates/formula/src/tests.rs
#	crates/ui-egui/src/control.rs
#	crates/ui-egui/src/formula_bar.rs
#	crates/ui-egui/src/grid.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/ribbon.rs
#	crates/ui-egui/tests/selection.rs
#	docs/function-parity.md
#	docs/ui-parity.md
2026-10-22 03:01:31 +00:00
aiongg b0b6409112 Integration: track t03
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/tests/keyboard_shortcuts.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 03:00:13 +00:00
aiongg 54d0f7cfc0 Integration: track t02
# Conflicts:
#	ROADMAP.md
#	crates/core/src/lib.rs
#	crates/ui-egui/src/editor.rs
#	crates/ui-egui/src/grid.rs
#	crates/ui-egui/src/tabs.rs
#	docs/chart-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 02:59:55 +00:00
aiongg 1316d39865 Merge PR #1151: Charts: a huge trendline forecast no longer collapses the chart (#1148)
ROADMAP.md: all rows kept.
crates/chart/src/tests.rs: both tests kept.
2026-10-22 02:37:37 +00:00
aiongg 169c78dda8 Merge PR #1111: Respect workbook structure protection in sheet rename, move, copy, hide and unhide (#1049) 2026-10-22 02:37:22 +00:00
aiongg 470a2ae2c1 Merge PR #1765: Sheet protection: obey the Sort and Use AutoFilter boxes
Conflicts:
- crates/engine/src/cmd/data.rs: do_sort keeps #1284's sheet parameter and checks Sorting on it; toggle_filter checks Filtering after #1285's data_target
- crates/engine/src/tests.rs: adjacent tests, both kept
2026-10-22 02:37:16 +00:00
aiongg 8ec29a8b3b Merge PR #1652: Fill handle: right-drag opens the fill menu (Copy Cells, Fill Series, Fill Formatting Only, Fill Without Formatting, Series…)
crates/ui-egui/src/grid.rs: kept both GridState fields (context_menu_frame, fill_menu); previous_popups tuple has four elements inside the touch_pan branch; release match has Drag::Rows|Cols, then the two Drag::Fill arms
docs/ui-parity.md: kept HEAD header line, both change rows
2026-10-22 02:36:24 +00:00
aiongg 8e2974cc65 Merge PR #1629: Memory: undo steps share picture and macro bytes instead of copying them
Conflicts:
- ROADMAP.md: both progress-log and revision rows kept
- crates/xlsx/src/sheet_write.rs: keeps #995's image_part call; drops #1629's to_vec line (image_part copies from &[u8])
2026-10-22 02:36:22 +00:00
aiongg cba89cfda8 Merge PR #1066: Engine: formulas.evaluate refuses what cell entry refuses, saying why
ROADMAP.md: both progress-log rows kept.
2026-10-22 02:36:20 +00:00
aiongg 7fd1553a8b Merge PR #1626: Keyboard: Ctrl+6 hides and shows objects, saved as workbookPr showObjects
Conflicts:
- docs/file-format-parity.md: header and the three .xlsx rows keep main's text plus the show-objects entry; both log rows kept
- docs/ui-parity.md: header keeps main's text plus Ctrl+6; both log rows kept
2026-10-22 02:36:15 +00:00
aiongg 635056bd23 Merge PR #2131: Print: JIS B4/B5 and Japanese paper sizes (はがき・封筒・B6)
ROADMAP.md: one status line naming both changes; both feature-table and progress-log rows, newest first.
2026-10-22 02:36:10 +00:00
aiongg c03d4a881b Merge PR #1570: XLSX: keep dynamic-array cell metadata (cm) on save
ROADMAP.md: kept both progress-log rows
2026-10-22 02:36:04 +00:00
aiongg 7e74b35a1b Merge PR #2095: Number Stored as Text: green indicator, warning button and Convert to Number (#1994)
crates/ui-egui/src/grid.rs: kept object_menu (2084) and error_check_tag (2095), both fns and both calls.
crates/ui-egui/src/i18n/de.rs, pt.rs: union of the entries.
docs/ui-parity.md: one status line naming both changes, both history rows.
2026-10-22 02:36:03 +00:00
aiongg 7768f56603 Merge PR #1580: Calc: CELL("filename") names the workbook's file 2026-10-22 02:35:56 +00:00
aiongg 6b0411091a Merge PR #1559: Sort and de-duplicate data.textToColumns fixed-width breaks
Conflicts:
- crates/engine/src/cmd/data.rs: adjacent test insertions, both kept
2026-10-22 02:35:56 +00:00
aiongg 524ab89a1a Merge PR #1379: engine: subtotal honours header:false instead of dropping the first data row
Conflicts:
- crates/engine/src/cmd/data.rs: subtotal keeps #1285's data_target and sheet-qualified calls with #1379's header/first logic; grand-total range starts at first; both tests kept (#1299 filter blanks, #1379 subtotal)
2026-10-22 02:35:51 +00:00
aiongg 3ed16929d9 Merge PR #1320: AutoFit Column Width measures text in the font the grid draws it with
Conflicts:
- crates/engine/src/cmd/format.rs: autofit_cols keeps #885's (si, lines) and adds #1320's measure
2026-10-22 02:35:39 +00:00
aiongg 6d4e62d12b Merge PR #2032: Formula entry: an incomplete formula's error describes what was typed (fixes #2030)
crates/formula/src/tests.rs: kept both tests.
2026-10-22 02:35:39 +00:00
aiongg 4fd4d39aa0 Merge PR #1308: engine: refuse removeDuplicates with no valid column instead of deleting all rows
Conflicts:
- crates/engine/src/cmd/data.rs: keeps #1308's column guard after cols is built; drops its later 'let sheet' (#1285 supplies sheet)
2026-10-22 02:35:34 +00:00
aiongg 66c64a9331 Merge PR #2019: Formulas: the reference app's longest legal operator chain (4,096 operands) no longer crashes entry or save
docs/function-parity.md: union of the status line and both history rows.
2026-10-22 02:35:30 +00:00
aiongg 77b861392a Merge PR #1299: Filter: count empty text and ="" results as (Blanks) 2026-10-22 02:35:26 +00:00
aiongg d691e95f6d Merge PR #1285: Remove Duplicates, Filter and Subtotal: a Sheet! prefix on the range works on that sheet 2026-10-22 02:35:24 +00:00
aiongg 87df1947f9 Merge PR #1284: Sort: a Sheet! prefix on the range sorts that sheet, not the active one 2026-10-22 02:35:21 +00:00
aiongg d26bbe3432 Merge PR #1085: Format: Row Height and Column Width say pixels, and take points like the reference app
Conflicts:
- crates/engine/src/cmd/format.rs: spec strings keep #885's sheet?/Sheet! forms with #1085's pixels/points wording; row_height and col_width keep #1085's unit logic with #885's (si, lines) = line_targets
2026-10-22 02:35:21 +00:00
aiongg 69f6ea363d Merge PR #1529: fix #1458: one reference-app sheet-name quoting rule for formulas, pivot sources and ADDRESS 2026-10-22 02:35:16 +00:00
aiongg 2c10a8da46 Merge PR #759: Names: a name over several ranges reads its top-level commas as unions
crates/formula/src/lib.rs: union pub use list (MAX_FORMULA_LEN and parse_name).
crates/engine/src/cmd/formulas.rs: keep both new tests.
2026-10-22 02:35:11 +00:00
aiongg 708c82cf2c Integration: #1059 delete_areas accepts a single row or column
home.deleteRows {rows:"2"} and home.deleteColumns {cols:"B"} failed with
"not a range"; delete_areas now parses with parse_lines. The PR's test
covers both.
2026-10-22 02:35:09 +00:00
aiongg c8e0323cf3 Merge PR #1975: Select precedent/dependent cells with Ctrl+[ / Ctrl+] (#1865 part 1) 2026-10-22 02:35:09 +00:00
aiongg 93517ccf51 Merge PR #1500: Fix number-format palette colours in grid and PDF output
crates/engine/src/cmd/print.rs: kept both appended tests
docs/ui-parity.md: kept HEAD header line, both change rows
2026-10-22 02:35:06 +00:00
aiongg 675d96ef17 Merge PR #1373: Protection: Protect Sheet entries unprotect, selectLockedCells enforced (#1346) 2026-10-22 02:34:56 +00:00
aiongg 93de710d28 Merge PR #1306: Show dynamic array source formulas read-only in spill cells
docs/ui-parity.md: kept HEAD's header line, both change rows (newest first)
2026-10-22 02:34:52 +00:00
aiongg 0b573362bc Merge PR #1059: rows/cols parameters: accept a single row or column 2026-10-22 02:34:51 +00:00
aiongg 2c73c2b18f Merge PR #1214: Grid: an armed Format Painter pastes onto the next cells clicked or dragged 2026-10-22 02:34:39 +00:00
aiongg ccc0e8cfe4 Merge PR #885: Row, column and freeze commands act on the sheet they are given 2026-10-22 02:34:36 +00:00
midasdf b092633561 Print: Japanese Envelope #4 is 90×205 mm and add B6 (ISO) to the Size menu
長形4号 is a 90×205 mm envelope (90×225 mm is 長形40号, a different one), so
the paper table gave it a height 20 mm too tall — and XLSX paper id 74, the PDF
MediaBox and the print preview all followed it. Use 205 mm (581.10 pt) and pin
the corrected size with a dedicated assertion; the ROADMAP progress row is
corrected too. 長形40号 is not added: it is a different envelope.

The Page Layout ▸ Size menu stopped at `B5 (ISO)` and never offered
`B6 (ISO)`, which the engine and the command docs support; add it.
2026-10-22 00:24:30 +09:00
midasdf f91f7c05d4 Print: JIS B4/B5/B6 and Japanese paper sizes
Japanese Excel's B4/B5/B6 are the JIS sizes (257×364, 182×257 and 128×182 mm),
but Page Setup's `b4`/`b5` were the ISO ones (250×353, 176×250): a Japanese B5
worksheet printed 8 mm narrow. `known_paper` now reads `B4`/`B5`/`B6` as JIS and
keeps the ISO sizes reachable as `B4 (ISO)`/`B5 (ISO)`/`B6 (ISO)`. This also
fixes the XLSX round-trip, whose `paperSize` ids 12/13 are the JIS sizes.

Adds the Japanese papers Page Setup lacked: Japanese Postcard (はがき,
100×148 mm), Japanese Double Postcard (往復はがき, 148×200 mm), Japanese
Envelope #3/#4 (長形3号/4号, 120×235 and 90×225 mm), Japanese Envelope Kaku
#2/#3 (角形2号/3号, 240×332 and 216×277 mm), A6, and aliases such as
`postcard`, `chou #3`, `kaku #2`; the xlsx paper table gains ids 42 (ISO B4) and
43/69–74/88 so these names survive save and reopen (the ISO B5/B6 names have no
ECMA-376 paper id and stay engine-side).

The pageSetup/pageLayout.size parameter docs list the names, the Page Layout
Size menu lists them, and tests pin the point sizes, the PDF MediaBox for A4
(unchanged), B5, postcard, double postcard and kaku #2, landscape and
fit-to-page, the unknown-name Letter fallback, and the XLSX round-trip.

Docs: docs/target-app-parity.md print row + revision history, and ROADMAP.md
progress log, dated 2026-10-21.
2026-10-21 23:38:26 +09:00
dantebertuzziandClaude Opus 5.5 68f956a410 Number Stored as Text: green indicator, warning button and Convert to Number (#1994)
Numbers stored as text are skipped by SUM and friends, and GridCraft gave
no hint. Like Excel's background error checking, a constant whose text
would be a number if typed now gets a green triangle in its top-left
corner; with such a cell active, a warning button beside it opens a menu
naming the problem with Convert to Number, which converts every flagged
cell in the selection as one undo step.

- core: `parse::number_stored_as_text` (signs, thousands separators,
  currency, percent, exponent, one parenthesised negative). Dates, times
  and fractions kept as text are not flagged, as in Excel.
- engine: `cmd::formulas::number_stored_as_text(cell)` (constants only,
  not formula results) and `formulas.convertToNumber {range?}`, which
  keeps each cell's format.
- ui: the triangle in the grid and the warning button with its menu;
  pt-BR and German strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:39:30 -03:00
kezmdorandClaude Opus 5.5 cfb0e186ed Formula entry: an incomplete formula's error describes what was typed (fixes #2030)
`=1+` reported "unexpected `)`", although no `)` was typed: entry closes
missing parentheses by appending up to eight `)`, and when every retry
failed it reported the last retry's error. It now reports the error of
the formula as typed ("unexpected end of formula").

`gridcraft_formula::parse_closing_parens` holds the convenience and takes
the parse function, so cell entry and the UI's Spanish formula path share
it instead of each having its own loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:44:09 +02:00
Harrison BoothandClaude Opus 5.5 2aeb21d4d3 Formulas: flat operator chains as long as Excel allows (#916)
along: =1+1+... with 4,096 operands (8,192 characters, exactly Excel's
limit, which Excel accepts and calculates) overflowed the stack when
entered, because the derived Clone of Expr recursed once per operand
down the chain's left spine. Saving hit the same in the XLSX writer.

- Expr: Clone, PartialEq, walk and map follow the left spine in a loop
  and recurse only into right operands and real nesting.
- XLSX: _xlpm. prefixing and the implicit-intersection scan follow the
  spine too. The scan's depth limit of 64 wrote any chain of more than
  about 64 operands as a dynamic-array formula; such sums are plain
  formulas again (ranges in the chain still get array evaluation).
- Evaluate Formula borrows sub-expressions instead of cloning each
  operator's subtree (quadratic memory on a long chain).
- Spanish formula entry and display no longer refuse chains of more
  than 256 operands; the left spine does not count as nesting.

Tests run on a 1 MiB stack (the Windows main thread) with Excel's
observed results: formula (clone/eq/walk/map/adjust at 4,096), calc
(401 and 4,096 operands, -, *, /, ^, &, %, comparisons, 1,500 refs),
xlsx (long chains written as scalar formulas), engine (cell.set, fill,
copy/paste, insert rows/columns, undo/redo, Evaluate Formula, XLSX save
and reopen), Spanish entry. Each fails without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:54:30 -05:00
Eduardo GarciaandClaude Sonnet 5.5 0b4cc5618f fix #1458: one Excel sheet-name quoting rule for formulas, pivot sources and ADDRESS
Sheet names starting with '.' or reading as R1C1 (R, C, RC, R1C1...) were saved
unquoted, so Excel could not open the file; PivotTable sources and ADDRESS left
cell-like names (A1) bare. The quoting rule, unquoting and sheet splitting now
live once in gridcraft_core::addr and replace the copies in the formula printer,
pivot, ADDRESS, chart, xlsx, Solver, Go To and the Name Box (which now reads ''
as ').

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 17:36:40 -03:00
bfentyandClaude 871c4e6c8f perf(engine): scan only the precedent area's stored cells
iter_range walks the sparse store's bands for the area's row range
only, so a chain of N formulas no longer rescans every stored cell of
the sheet per area.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-19 19:47:07 +00:00
Hamza e5aeea7a94 Sheet protection: obey the Sort and Use AutoFilter boxes
`SheetProtection` carries eleven permissions. `format_cells` is checked in
`apply_style`, and an edit to a locked cell is caught after the fact by
`check_protection`, but `sort` and `autofilter` had no reader anywhere outside
`sheet_read.rs`, `sheet_write.rs` and a round-trip test: they only ever
travelled through the file. So a protected sheet could still be sorted, and
Ctrl+Shift+L still put an AutoFilter on it.

`refuse_unless_allowed` sits beside `locked_cell_error`, with three call sites:
`do_sort` (which both Sort… and the A-to-Z buttons come through), `toggle_filter`
and `filter_by`.

Where the box decides is worth knowing. Sorting a range of locked cells was
already refused, by the locked-cell guard rather than the permission, because a
sort moves values between locked cells — which is Excel's rule too ("users
cannot sort ranges that contain locked cells on a protected worksheet,
regardless of this setting"). So the Sort box decides for a range that has been
unlocked, while an AutoFilter changes no cell's contents and the box is the only
thing that ever decided it. The test asserts both, and that an unprotected sheet
still sorts and filters.

Not fixed here: Delete Rows and Delete Columns are refused even when their boxes
are ticked, because deleting removes locked cells and trips the same post-hoc
guard, while Insert Rows and Insert Columns work. Letting the guard know which
operation ran is a change to code every command passes through and wants its own
PR.
2026-10-19 22:30:36 +05:00
Hamza aff9153dac Calc: CELL("filename") names the workbook's file (#1489)
It returned an empty string for every workbook, saved or not, so the classic
sheet-name formula `MID(CELL("filename",A1),FIND("]",…)+1,31)` gave #VALUE!:
with no `]` in the string there is nothing for FIND to find. Excel returns
`<folder>\[<file>]<sheet>`, and empty only before the first save.

The model had no file to report. `source_dir` could not serve: it names the
folder relative links to other workbooks are relative to, and must keep naming
it after a Save As into another folder, because the links held in memory are
still written for the old one — only the saved copy is rebased. `source_path`
is therefore its own field, with that distinction written down.

It is set when a workbook is opened, from the path made absolute so the folder
is the one the user sees, and when a full-fidelity save makes a file the
document's own — before the `saved` snapshot, or the workbook would read as
dirty the moment it was saved, and only when it changes, so an ordinary Save
does not clone the workbook for nothing. A workbook that arrived as bytes knows
its name but never a folder and reports `[name]Sheet`, which still splits on the
`]`; an import reports the file it came from, as Excel does. The sheet is the one
the reference is on, not the active one.

CELL is volatile and already in the volatile sets, so a cell holding it answers
from the next recalculation; after a Save As that means F9 rather than the save
itself, which the engine test asserts rather than papers over. Making a save
recalculate is a larger decision than this issue.

`check` still skips CELL("filename") as volatile, as docs/cli.md says: the
cached value in a file is the folder of the machine that saved it.
2026-10-19 22:29:37 +05:00
bfentyandClaude d19627fc26 fix(engine): walk stored cells in transitive precedent selection
Bound the all-level walk to the sheet sparse cell store instead of
iterating every position of each precedent area: formulas late in a
wide area are no longer skipped, blank positions are never selected,
and only formula cells extend the frontier. Adds a regression test
(=SUM(A1:Z1) with a formula at Z1) and silences two clippy lints in
the same function.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-19 16:54:45 +00:00
Brandon Thomas b402359fa4 Merge pull request #1938 from Mouhand-Kaddo:fix/html-export-rtl
# Conflicts:
#	docs/file-format-parity.md
#	docs/localization-parity.md
2026-10-19 12:32:51 -04:00
Brandon Thomas ab6fd48856 Merge pull request #1867 from HyakoV3:fix/1808-csv-as-text
# Conflicts:
#	docs/file-format-parity.md
2026-10-19 12:22:05 -04:00
Brandon Thomas 12ee27a177 Merge pull request #1943 from Mouhand-Kaddo:fix/table-name-length
# Conflicts:
#	docs/localization-parity.md
2026-10-19 12:22:01 -04:00
Brandon Thomas 8d562e9b56 Merge pull request #1879 from Silvertree2010:fix/h2804-custom-autofilter-is-less-than-l 2026-10-19 12:21:34 -04:00
Brandon Thomas 937cfd1e91 Merge pull request #1903 from justinjohn0306:fix/1855-backspace-undo 2026-10-19 12:21:29 -04:00
Brandon Thomas 8e8e2ba73c Merge pull request #1920 from vedran-dobos:range-fill-formats 2026-10-19 12:19:51 -04:00