fix(odf): address a repeated sheet cell by position, not by index - #857
Merged
Conversation
andiwand
force-pushed
the
feat/sheet-repeat-identity
branch
2 times, most recently
from
September 7, 2026 19:24
a5ef1c7 to
4c37fed
Compare
One element stands for every position a repeat covers, so an index cannot say which of them a handle means: `SheetCell::position()` answered with the anchor of the range, `Sheet::cell()` handed back the same handle throughout it, and `DocumentPath` named a cell the caller had not asked for. `sheet_cell(sheet, column, row)` now hands out `tag | ordinal | column | row` for a repeat, and the registry decodes it against the sheet's cell index. A cell standing for one position alone keeps its index, so nothing but a repeat pays for the indirection and the store stays a flat array. The decode is the shared `ElementRegistry::resolve_id`, identity for every engine that does not shadow it. `RegistryElementAdapter` navigates through `element_at`, so nothing else has to know, and `element_parent` drops the position on its own - a sheet is stored as a plain index. Because the decode goes through the index rather than the element it found, a handle follows the index. `extract_path` already spells a cell by position and `navigate_path` already goes back through `sheet_cell`, so a path names the right cell untouched. The position stops at the cell: one paragraph and one run stand for every position, so a path into a repeated cell still names the anchor. Cell style now resolves at the position the id carries rather than the payload's, since the anchor sits in another column and `table:default-cell-style-name` is a column's to state. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J325TWocZ4iXBjvKv2ZVZi
andiwand
force-pushed
the
feat/sheet-repeat-identity
branch
from
September 7, 2026 19:37
4c37fed to
6a041fa
Compare
andiwand
enabled auto-merge (squash)
September 7, 2026 19:38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Generated with Claude Code
The bug
One element stands for every position a repeat covers, so an index cannot say which of them a handle means:
DocumentPathinherits it, sinceextract_pathspells a cell bysheet_cell_position. This is the half of the repeated-cell change in v6.12.0 (#776) that a consumer could observe — it was marked Breaking at the time and justified with "spreadsheets are not editable", which #856 has now retired.The fix
sheet_cell(sheet, column, row)hands out a positional id for a repeat:the ordinal counting sheets in document order. A cell standing for one position alone keeps its index, so only a repeat pays for the indirection and the element store stays a flat array.
The decode is one hook —
ElementRegistry::resolve_id, identity for every engine that does not shadow it. odf's shadow looks the position up in the sheet's existing run index (a binary search that was already there), and every payload accessor goes through it.Two properties fall out:
RegistryElementAdapternavigates entirely throughelement_at, so all navigation works untouched, andelement_parentdrops the position for free — a sheet's storedparent_idis a plain index.extract_path/navigate_pathneeded no change: they already go out by position and back throughsheet_cell.The position stops at the cell — one paragraph and one run stand for every position — so a path into a repeated cell still names the anchor, and round-trips. A test pins that boundary.
One line goes with it: a cell's style resolved at the payload's position, which for a repeat is the anchor's column, and
table:default-cell-style-nameis a column's to state. Not reachable today (SheetCellexposes nostyle(), and children reach it through the plain anchor id), so this is consistency rather than a fix.Not xlsx
Sheet.cellsthere is one entry per real<c>, each with its own element and its own true position. Neither bug exists, so nothing is added to it.Checks
-Wall -Wextra -Werrorover seven touched TUs.Next
Splitting a repeated run on write, which makes
Sheet::set_cellaccept a repeated cell instead of refusing it. It needs the DOM row/cell split plus a parser entry point for re-registering the copies, and it is safe only because a handle re-resolves — hence this first.