Skip to content

Sheet names: one quoting rule, Excel 365's, for formulas, ADDRESS, pivots and the Name Box - #987

Closed
iluebbe wants to merge 1 commit into
storytold:mainfrom
iluebbe:fix/sheet-name-quoting
Closed

iluebbe wants to merge 1 commit into
storytold:mainfrom
iluebbe:fix/sheet-name-quoting

Conversation

@iluebbe

@iluebbe iluebbe commented Oct 13, 2026

Copy link
Copy Markdown

Summary

Sheet names in references were quoted and unquoted in about eight places, with three different quoting rules. This PR moves the rule into one place, gridcraft_core::addr (sheet_needs_quotes, quote_sheet, unquote_sheet, split_sheet), uses it everywhere, and makes it match Excel 365. It fixes:

  • A file Excel can't open. A formula into a sheet whose name starts with . was saved as .x!A1, and Excel 365 can't open that file, not even with repair.
  • PivotTable source. A PivotTable on a sheet named like a cell reported its source as A1!$A$1:$B$3.
  • ADDRESS. It didn't quote names that read as cells or R1C1 references.
  • Name Box. It never showed a defined name on a sheet with an apostrophe in its name.

Observed

Excel 365's rule, from typing ='name'!A1 and reading .Formula back, and from ADDRESS(1,1,1,TRUE,name):

sheet name Excel 365 formula Excel ADDRESS GridCraft before
A1, XFD1, AB1048576 quoted quoted formulas quoted; ADDRESS and pivot source bare
XFE1, AB1048577 (off the sheet) bare bare bare
R, C, RC, R1, C1, R1C, RC1, R1C1, r2c3 quoted quoted bare everywhere
.x quoted quoted bare everywhere
TRUE, false quoted bare formulas quoted; ADDRESS bare
Ärger, 日本, Σ, Sheet.1, _x, A1A bare bare bare

Effects in GridCraft (main):

  • ='.x'!A1 was saved as <f>.x!A1</f>. Excel 365 refuses to open the file, and the repair option fails too. R1C1!A1 and RC!A1 happen to be accepted by Excel.
  • insert.pivotTable from the current region of a sheet named A1: pivot.list gave the source A1!$A$1:$B$3. The text comes from the engine's own pivot::quote_sheet, which lacks the cell-name check.
  • ADDRESS(1,1,1,TRUE,"A1") gave A1!$A$1; Excel gives 'A1'!$A$1.
  • A name ='Bob''s'!$A$1:$B$2 never showed in the Name Box: it compared Bob''s with Bob's.

Cause

  • Quoting: three implementations. The formula printer's needs_quotes checked cells and booleans but not R1C1 tokens or a leading .. engine::pivot::quote_sheet checked neither cells nor R1C1 tokens. ADDRESS's own copy didn't check cells or R1C1 tokens either.
  • Unquoting: six copies. trim_matches('\'').replace("''","'") appeared in engine/src/cmd/mod.rs, engine/src/pivot.rs (twice), xlsx/src/pivot.rs and xlsx/src/array_formula.rs; strip_prefix/strip_suffix in chart/src/resolve.rs and engine/src/cmd/pivot.rs. The Name Box had a seventh that didn't unescape ''.
  • Go To: it built '{name}'!… by hand, without doubling apostrophes.

Fix

  • gridcraft_core::addr (L0, reachable from every crate): sheet_needs_quotes and quote_sheet follow the table above. That means cell-like names within the sheet's bounds, R1C1 tokens in any case, booleans, a leading digit or ., and any character other than letters, digits, _ and ..
  • unquote_sheet and split_sheet take the sheet part apart (split at the last !, '' read as ').
  • The formula printer re-exports quote_sheet and needs_quotes from core; gridcraft_formula::quote_sheet keeps working.
  • The engine's pivot quote_sheet/split_sheet and cmd::split_sheet now come from core. Solver, Go To, xlsx pivot/array-formula, chart resolve and the Name Box use the same helpers.
  • ADDRESS uses sheet_needs_quotes and leaves TRUE/FALSE bare, as Excel's ADDRESS does.

Testing

  • core: sheet_names_are_quoted_like_excel, every row of the table above.
  • functions: ADDRESS with A1, R1C1, RC, .x, XFE1 and TRUE.
  • engine: sheet_names_are_quoted_by_excels_rule_everywhere. A pivot made from the current region of sheet A1 reports 'A1'!$A$1:$B$3, and ='.x'!A1+'R1C1'!A1 keeps its quotes.
  • ui-egui: tests/name_box.rs, a name on sheet Bob's shows in the Name Box.
  • cargo fmt --all -- --check; cargo clippy for core, formula, functions, xlsx, chart, engine and ui-egui with -D warnings; cargo test for the same crates.

Overlap: #829 also rewrites needs_quotes in crates/formula/src/printer.rs ([1]Sheet / [Book.xlsx]Sheet prefixes, with a local sheet_needs_quotes). Whichever lands second should make #829's wrapper call gridcraft_core::addr::sheet_needs_quotes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SAgnkqJiy3v24Hp25dQLRj

…vots and the Name Box

Sheet names were quoted in three places with three rules (the formula
printer, engine::pivot::quote_sheet, ADDRESS) and unquoted in seven (two
split_sheet copies in the engine, xlsx pivot and array-formula readers,
chart resolve, engine cmd/pivot, the Name Box). gridcraft_core::addr now
holds sheet_needs_quotes, quote_sheet, unquote_sheet and split_sheet, and
every copy uses them.

The rule, observed in Excel 365 (typing ='name'!A1 and reading .Formula
back, and ADDRESS(1,1,1,TRUE,name)): a name is quoted when it reads as an
A1 cell on the sheet (A1, XFD1, AB1048576; not XFE1), an R1C1 reference in
any case (R, C, RC, R1, C1, R1C, RC1, R1C1), a boolean, starts with a digit
or a dot, or holds anything but letters, digits, _ and dot. ADDRESS leaves
TRUE and FALSE bare.

Fixes:
- A formula into a sheet named .x was saved as .x!A1, and Excel 365 can't
  open such a file, not even with repair.
- A PivotTable made from the current region of a sheet named A1 reported
  its source as A1!$A$1:$B$3 (the engine's copy lacked the cell check).
- ADDRESS(1,1,1,TRUE,"A1") gave A1!$A$1; Excel gives 'A1'!$A$1. The same
  for R1C1-like names and a leading dot.
- The Name Box never showed a name on sheet Bob's: it compared Bob''s with
  Bob's.
- Go To built '{name}'! by hand without doubling apostrophes.

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

iluebbe commented Oct 15, 2026

Copy link
Copy Markdown
Author

Closing this pull request. The problem, Excel's measurements and a fix approach are now in #1458.

@iluebbe iluebbe closed this Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant