Skip to content

feat(spreadsheet): read the formula of every cell of a shared group - #885

Merged
andiwand merged 1 commit into
mainfrom
feat/shared-formulas
Sep 12, 2026
Merged

feat(spreadsheet): read the formula of every cell of a shared group#885
andiwand merged 1 commit into
mainfrom
feat/shared-formulas

Conversation

@andiwand

@andiwand andiwand commented Sep 11, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Step 3.1 of docs/design/spreadsheet-editing.md, finishing what the parser was for.

The problem

[ECMA-376] 18.3.1.40 spells a shared formula on the group's master alone:

<c r="C1"><f t="shared" ref="C1:C3" si="0">A1+$B$1</f><v>3</v></c>
<c r="C2"><f t="shared" si="0"/><v>7</v></c>
<c r="C3"><f t="shared" si="0"/><v>9</v></c>

Excel writes this whenever you fill a column down, so most formula cells in a real workbook are members. A member reported an empty formula, which leaves a formula bar with nothing to show and the dependency graph (next PR) with nothing to read.

What it does

The parser collects the masters per sheet, and sheet_cell_value reads a member through the one its si names: parse the master's expression, move every relative reference by the offset between the two cells, write it again. So C2 above answers A2+$B$1 and C3 A3+$B$1 — the $B$1 does not move, because a $ axis never does.

That needs two pieces beside the parser, both in internal/formula:

  • shift over the tree. A reference moved off the grid becomes #REF!, as a sheet makes it.
  • a writer, to_string(node, syntax), which spells a tree back in either syntax. It drops a parenthesis the precedence already states, so 1+(2*3) comes back as 1+2*3 while 1-(2-3) keeps its own.

One fix fell out of having a writer: the parser accepted 1e999 as infinity, which the writer would then spell inf. It refuses the number instead, so the master's own text is what a member gets.

Two cells that keep what they had

  • a master the parser cannot read is handed out as it stands rather than dropped;
  • a member whose si names no master stays set and empty — it computes, and nothing here can spell what.

An array formula (t="array") writes no <f> at all on its members, so those still report none. Noted in the module's AGENTS.md.

Test

test/src/internal/formula/formula_writer_test.cpp round-trips both syntaxes and pins the shift; the shared-formula cases are in ooxml_spreadsheet_value_test.cpp, from inline fixtures. test/data/input/odr-public/xlsx/sample.xlsx is a real file of the shape — 32 members of one RAND() group.

[ECMA-376] 18.3.1.40 spells a shared formula on the group's master alone, and
a member states its `si` and nothing else. A member reported an empty formula,
so a formula bar had nothing to show and a dependency has nothing to read.

The parser collects the masters per sheet, and `sheet_cell_value` reads a
member through the one its `si` names: parse the master's expression, move
every relative reference by the offset between the two cells, and write it
again. An absolute axis does not move, and a reference moved off the grid
becomes `#REF!`, as a sheet makes it.

That takes two pieces beside the parser, both in `internal/formula`: `shift`
over the tree, and a writer that spells a tree back in either syntax. The
writer drops a parenthesis the precedence already states, so what it writes
is the tree rather than the producer's own text. A number that overflows to
infinity no longer parses, because no formula spells one.

A master the parser cannot read is handed out as it stands, and a member
whose `si` names no master stays set and empty: it computes, and nothing here
can spell what.

Step 3.1 of `docs/design/spreadsheet-editing.md`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVmjmddv2Ui17Nptc1ggue
@andiwand
andiwand force-pushed the feat/shared-formulas branch from 5eebf32 to f2855af Compare September 12, 2026 08:03
@andiwand
andiwand merged commit a26cce7 into main Sep 12, 2026
34 checks passed
@andiwand
andiwand deleted the feat/shared-formulas branch September 12, 2026 08:33
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