Commit Graph
130 Commits
Author SHA1 Message Date
3b65b9af58 Sonar maintainability: explicit imports, method paths, smaller functions (#2412)
* Sonar: explicit imports, fn paths for closures, extract helpers for cognitive complexity

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Fix imports

* Fix serde imports

* Fix engine imports

* Fix clippy gate and a type error from the explicit-import change

Remove the unused imports the review flagged (cf.rs, pivot.rs, tables.rs, cmd/sheet.rs) so clippy -D warnings passes, and point the backstage parent-folder lookup at Path::to_str, which the fn-path rewrite had broken.

Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Apply rustfmt to the explicit-import cleanup so the fmt gate passes

* Share the per-directory font path expansion in theme.rs

The candidate font lists repeated the same loop over the Linux font
directories in every role. A single helper expands them in the same order,
so the lookup order is unchanged and the duplicated blocks go away.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Read the Windows fonts folder in one place in theme.rs

Every script-font list repeated the same WINDIR lookup. A single helper
returns the same path, so the duplicated lines go away without changing
which font files are tried.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Build the light and dark UI palettes from one table in theme.rs

Tokens::light() and Tokens::dark() listed the same 40 colour fields in the
same order, so every change had to be made twice. A single palette
constructor takes the mode and picks each colour's value, so each field is
written once and both modes keep their exact colours.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Share the Noto CJK font list between the CJK script lists in theme.rs

The Japanese, Simplified and Korean lists each repeated the same loop over
the Linux CJK directories with a different locale file name. One helper
builds that loop, and each list keeps its own face index, so the lookup
order is unchanged.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Share Linux Noto directory list across Thai and Hebrew font lookups

Removes the duplicated directory arrays flagged by SonarCloud new-code duplication.

* Reuse in_dirs for Thai and Hebrew Noto font candidates

Removes the remaining duplicated per-directory loops flagged by SonarCloud.

* Share one font-list builder for the Thai and Hebrew faces

The two per-script functions repeated the same Windows, macOS, Linux and
variable-font setup. A single script_fonts helper now builds both lists,
keeping the candidate order unchanged, which clears the Sonar duplication gate.

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-25 12:32:02 -04:00
Brandon Thomas 0f5227ddde Merge pull request #2513 from Fubeo:fub/keep-sheet-id 2026-10-25 09:36:35 -04:00
NetterSilverandClaude Opus 5.5 85b6f564f9 Keep <scheme val="minor"/> on every body-font font on save
Fixes #2517

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:20:17 +01:00
aiongg 6194f8241d Merge storytold/gridcraft#2522: Charts: 100% Stacked Area draws each value as a share of its category's total
# Conflicts:
#	ROADMAP.md
2026-10-25 02:54:23 +00:00
aiongg b58846a549 Merge storytold/gridcraft#2482: Outline: Settings puts summary rows above and summary columns left of their detail
# Conflicts:
#	docs/ui-parity.md
2026-10-25 02:54:23 +00:00
dotCooCoo 0eb2f1f6f0 Charts: 100% Stacked Area draws each value as a share of its category's total
Insert > Line or Area and the All Charts list offer 100% Stacked Area,
and insert.chart and chart.set take it as type area with subtype
stacked100. Each series' area stacks on the ones before it as its
share of the category's total. When every series on the value axis is
100% stacked and no value is negative, the axis runs from 0% to 100%.
A blank cell counts as zero, and a category whose total is zero stays
on the baseline.

An XLSX area chart with percentStacked grouping now opens as 100%
stacked and is saved back as percentStacked. It used to open as a
stacked area and was saved as stacked.
2026-10-24 16:42:59 -07:00
Fubeo 85e8f7909e fix(xlsx): keep each sheet's sheetId across a rewrite
The writer numbers sheetIds from 1 in sheet order, so a rewritten workbook
changes the id of any sheet that is not part of a gapless series from 1.
Anything that recognises a sheet by its id (parts carried through as they
are, external tools) loses it. The model keeps the sheetId it read
(Sheet::file_id, PreservedSheet::sheet_id, not serialised to JSON) and the
writer writes it back; a new sheet, or a copy with a taken id, gets the first
free one.
2026-10-25 01:31:19 +02:00
dotCooCoo 118cd15d93 Outline: Settings puts summary rows above and summary columns left of their detail
Data > Outline > Settings now has Summary rows below detail and Summary
columns to right of detail. With one cleared, each group's - / + button
sits on the line before the group, its bracket runs down to the last
detail line, and Hide Detail, Show Detail, the level buttons and
Ungroup mark or clear the collapsed flag on that line. Changing a
direction moves the flags of collapsed groups to their new summary
lines.

- model: Sheet::summary_below and summary_right (on by default, also
  for older JSON).
- xlsx: read and written as sheetPr/outlinePr summaryBelow and
  summaryRight; files that set them no longer lose them on save.
- engine: data.outlineSettings {sheet?, summaryBelow?, summaryRight?},
  undoable; outline::groups_with places the summary line.
- ui: a Settings button in the Data tab's Outline group opens the
  dialog; the outline band follows the setting.
2026-10-24 11:34:35 -07:00
dotCooCoo 30126a709a PivotTables: Enable show details turns Show Details off for a PivotTable
A PivotTable has an Enable show details option, set with the new
pivot.options command ({pivot?, enableShowDetails?: bool}). While it is
off, pivot.showDetails and double-clicking a value cell list no records:
the double-click shows a message and does not open the cell for editing.
Labels and headers still edit. Changing the option is one undo step and
does not refresh the report. pivot.fields reports the setting.

XLSX files keep it: an off option is written as enableDrill="0" on the
pivotTableDefinition, and enableDrill is read on open (on when absent).
2026-10-24 07:46:29 -07:00
Dominik StrebelandClaude Opus 5.5 840ba5c3a5 view: Show Outline Symbols (Ctrl+8), read and written
A sheet can hide its outline's level buttons and brackets while keeping
the outline (Excel's Ctrl+8, saved as showOutlineSymbols="0"). It wasn't
modelled, so such sheets showed a wide outline band Excel doesn't.

- model: Sheet::show_outline_symbols (default on, also for older JSON).
- xlsx: read and written on the sheet view.
- engine: view.showOutlineSymbols {on?} (Ctrl+8), undoable.
- ui: no outline bands while it's off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:54:06 +02:00
aiongg d5cdd2ea2b Integration: track t15
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
2026-10-22 03:05:39 +00:00
aiongg 561382cc6d Integration: track t14
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/formula/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 03:05:30 +00:00
aiongg ca1cdd4229 Integration: track t13
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/engine/src/cmd/data.rs
#	crates/xlsx/src/tests/mod.rs
#	docs/chart-parity.md
#	docs/file-format-parity.md
#	docs/function-parity.md
#	docs/gaps.md
#	docs/localization-parity.md
#	docs/target-app-parity.md
2026-10-22 03:05:18 +00:00
aiongg c7fd66d807 Integration: track t12
# Conflicts:
#	ROADMAP.md
#	crates/calc/src/tests.rs
#	crates/chart/src/tests.rs
#	crates/engine/src/tests.rs
#	crates/xlsx/src/tests/mod.rs
#	docs/chart-parity.md
2026-10-22 03:05:03 +00:00
aiongg 9350ab1def Integration: track t11
# Conflicts:
#	ROADMAP.md
#	apps/gridcraft-web/Cargo.toml
#	crates/ui-egui/src/i18n.rs
#	docs/control-protocol.md
#	docs/gaps.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-22 03:04:52 +00:00
aiongg d3201e7cea Integration: track t08
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
2026-10-22 03:03:55 +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 857bdae023 Merge PR #1692: XLSX: keep Japanese phonetic runs on open and save
Conflicts:
- crates/model/src/lib.rs, crates/xlsx/src/read.rs, crates/xlsx/src/write.rs: use lists unioned (ShowObjects and the phonetic types)
- docs/file-format-parity.md: main's header and rows kept (#1692 collapsed the three duplicate .xlsx rows; they stay), phonetic-runs text added to each; Last updated 2026-10-20
2026-10-22 02:36:58 +00:00
aiongg d97f61748e Merge PR #1946: XLSX: keep cell fonts' charset (Arabic 178) and family on save
crates/xlsx/src/styles.rs: family/charset block ported into the dxf.is_none() branch of font_xml (PR #1225 made it take Option<&DxfStated>)
docs/file-format-parity.md: header Change line keeps both sides' clauses
2026-10-22 02:36:23 +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 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 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 7768f56603 Merge PR #1580: Calc: CELL("filename") names the workbook's file 2026-10-22 02:35:56 +00:00
aiongg d83dc11ff2 Merge PR #1695: Charts: box & whisker charts honor their quartile method
crates/engine/src/tests.rs: kept both new tests (csv_as_text_keeps_identifier_columns_as_written and box_whisker_quartile_methods_survive_xlsx).
2026-10-22 02:35:39 +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 43cd35d4bd Merge PR #1263: Bound Undo history growth after bulk edits
crates/engine/src/lib.rs: kept both the journal caps (#1230) and the undo byte budget; separate constants and call sites.
crates/engine/src/cmd/edit.rs: undo pushes through push_history; the redo entry keeps the undone edit's selection (#1855 behaviour).
crates/engine/src/tests.rs: kept both tests (redo_selects_the_redone_cell and the two history-bound tests).
2026-10-22 02:35:17 +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 6735e9e7c0 Integration: #451 qualify the run-stop row through value_ref
A stored cell with no value and no formula falls through to spill in
value_ref; the scan now asks value_ref for the row where the stored run
stops. Docs corrected; tests cover a style-only stored cell over a spill
value.
2026-10-22 02:34:58 +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 49328f80c0 Merge PR #1381: engine: rebuild cell-store bands and counts when importing a JSON workbook 2026-10-22 02:34:49 +00:00
aiongg 522b8a1f42 Merge PR #1225: XLSX: keep a dxf's protection, font size and face, and the font parts it turns off 2026-10-22 02:34:44 +00:00
aiongg 8041fbfc22 Merge PR #451: perf: fill-handle double-click scans a dense neighbour column ~2.4x faster 2026-10-22 02:34:35 +00:00
Gert Pellin a35d241caf XLSX: a note keeps its box's place and size through open and save (#1608)
The reader took only <x:Visible/> from a note's VML shape and vml_xml
wrote every note with a fixed anchor (two columns right of the cell,
four rows tall) and a 108 x 59.25 pt style, so a plain open and save
moved and resized every note box Excel had stored.

Comment gets an optional NoteBox: the <x:Anchor> corners relative to the
note's cell (so the box follows the note when rows or columns move) and
the shape's width and height. The writer puts it back, clamped to the
sheet; notes made here, and boxes equal to the default, keep the
default box.
2026-10-21 09:20:06 +02:00
Mouhand-Kaddo 189eb7ae52 M12.AR14: keep the theme's per-script typefaces (<a:font script="Arab">) on save 2026-10-20 09:21:42 +04:00
midasdf 40803e6aca XLSX: keep Japanese phonetic runs (ふりがな) on open and save
xl/sharedStrings.xml can carry phonetic runs (<rPh sb="0" eb="2"><t>ふりがな</t></rPh>)
and a <phoneticPr> beside a string item. GridCraft read only the text and wrote every
string back as <si><t>…</t></si>, so a plain open + save deleted a Japanese workbook's
furigana.

Both are now read — offsets validated against the base text, runs with bad or missing
offsets, sb >= eb or no reading dropped with a warning, and never used to index — and
kept on the workbook as Workbook::phonetics, one entry per string text. The writer
emits them next to the <si> of the same text. The saved table holds each text once, so
two identical texts cannot keep two different readings: the reader keeps the first and
warns. Strings without phonetic data are written byte for byte as before.

Behaviour reference: ISO/IEC 29500-1 §18.4.6 rPh and §18.4.7 phoneticPr (Microsoft
Learn PhoneticRun/PhoneticProperties). Tests: crates/xlsx/src/tests/phonetic_tests.rs
(round trip, plain string, hostile offsets, shared text, unused data, comments and
inline strings). Docs: file-format-parity.md, localization-parity.md.
2026-10-20 13:39:48 +09:00
midasdf 2c6ffe489c XLSX theme: keep the major/minor ea and cs typefaces through open + save
A theme's <a:fontScheme> carries latin, ea (East Asian) and cs (complex
script) typefaces for each of the major and minor fonts. The model held
only the latin ones and the writer emitted <a:ea typeface=""/> and
<a:cs typeface=""/> unconditionally, so a Japanese workbook's theme fonts
(e.g. 游ゴシック, MS Pゴシック) were silently deleted on open + save.

Theme gains major_font_ea, minor_font_ea, major_font_cs and
minor_font_cs (#[serde(default)], so older GridCraft JSON still loads;
empty means the file did not set one). The XLSX reader and writer
round-trip them escaped, and a theme without them writes exactly the
empty attributes it wrote before.

Rendering and font resolution are unchanged: the UI does not consume ea
yet, and wiring it in is a follow-up.
2026-10-20 13:35:52 +09: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
Mouhand-Kaddo 24b0b5d167 M12.AR15: keep cell fonts' charset and family on save 2026-10-19 21:18:06 +04:00
Mouhand-Kaddo 90344445d7 M12.AR10: keep right-to-left paragraph direction in text boxes and shapes 2026-10-19 15:46:21 +04:00
webdevtodayjason ea3ef8faf7 Charts: honor box and whisker quartile methods
Keep exclusive and inclusive quartiles per series when drawing and saving XLSX, and expose the setting through chart.set.

Fixes #1453
2026-10-16 12:37:27 -05:00
Bryan MarkandClaude Opus 5.5 a09d3f6934 Memory: undo steps share picture and macro bytes instead of copying them
Every edit copies the workbook for its undo step (copy-on-write: sheets
and 64-row bands of cells are shared). A floating picture's bytes
(Image.data) and the VBA project (Workbook.vba_project) were plain
Vec<u8>, so each edit on a sheet with a picture, and every edit in a
workbook with macros, kept another full copy of them, up to the 100
steps of the history. They are Arc<Vec<u8>> now, as cell pictures
already are, with base64 helpers for serde (b64::shared, b64::opt). JSON
and XLSX are unchanged.

Measured on macOS 27 (Apple M3 Max), gridcraft-cli, kernel peak
footprint: a 19 MB picture and 50 cell edits on its sheet, 992 -> 22 MB;
10 edits, 215 -> 21 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 14:26:53 +09:00
Marco Arnulfo efc8171609 Keyboard: Ctrl+6 hides and shows objects, saved as workbookPr showObjects 2026-10-16 07:03:58 +02: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
midasmatterhorn 34a4ed3ead engine: rebuild cell-store bands and counts when importing a JSON workbook 2026-10-15 00:09:25 +03:00
Pranav Chandra 3177fbe03f ui: show spill-cell formula as greyed out 2026-10-14 20:23:44 +05:30
derechopiano 72db972de3 fix(engine): bound Undo history by retained cell storage (#1227) 2026-10-14 08:23:57 +01:00
Marco Arnulfo f519fd67f6 XLSX: keep a dxf's protection, font size and face, and the font parts it turns off (part of #1209) 2026-10-14 04:49:33 +02:00
vangel76andClaude Sonnet 5.5 568dd58264 Charts: a huge trendline forecast no longer collapses the chart (#1148)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 21:13:05 +02:00