feat(odf): write a repeated sheet cell by cutting the run - #858
Merged
Conversation
`Sheet::set_cell` refused a repeated cell: one element stands for every position the repeat covers, so a write would have hit all of them. `split_repeat` cuts the run instead. The `table:table-row` and the `table:table-cell` are copied around the position written, the original node staying as the one written so its element and children survive, and `reindex_sheet` rebuilds the sheet's position index off the dom - a cell node keeping the element it already carries, a copied one getting a fresh one. The positions around it read as they did. The cost follows the row the write touched, not the grid the repeat claims: a write into a `1048576 x 1024` repeat leaves the registry under 32 elements, which is the expansion #776 removed and a test pins. The formula and markup refusals now run against the cell before the split, so a refused write leaves the run uncut. `only_text_run` splits in two: `holds_one_run` decides the refusal without writing anything, `text_run_of` creates the run an empty paragraph needs. 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-split-on-write
branch
from
September 7, 2026 20:05
2c5a194 to
8f951d4
Compare
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
What it does
Sheet::set_cellrefused a repeated.odscell — one element stands for every position the repeat covers, so a write would have hit all of them. It now cuts the run:split_repeatcopies thetable:table-rowand thetable:table-cellaround the position, and leaves the original node as the one written — so its element, its paragraph and its run all survive, and the write goes through them unchanged. Only the copies need parsing.reindex_sheetthen rebuilds the sheet's position index off the dom. A cell node keeps the element it already carries (matched on the node), a copied one gets a fresh one. That replaces surgical insertion into the flatcellsvector with its per-rowfirst_celloffsets, which is the part that would have been easy to get subtly wrong.It does not reintroduce the expansion
The cost follows the row the write touched, not the grid the repeat claims. A write into a
1048576 × 1024repeat — #776's bomb — leaves the registry under 32 elements, with the neighbouring positions still reading their old value. A test pins exactly that.Two costs worth stating, and they are in
odf/AGENTS.md: cutting a repeated row copies every cell in it, so elements grow with the row rather than with the repeat; and the reindex walks the row nodes, which a repeat collapses, so it is bounded by the dom rather than the grid.Refusals stay clean
The formula and markup checks now run before the split, so a refused write leaves the run uncut — the property #856 established, which the obvious ordering would have broken (splitting, then throwing, rewrites the file for nothing).
only_text_runsplits in two for this:holds_one_rundecides without writing,text_run_ofcreates the run an empty paragraph needs.A cell the file states no element for still refuses; that is materialising a missing
<table:table-cell>, a separate thing.Why it needed #857 first
A handle taken before the write keeps naming its position and re-resolves to the new element, so it shows the write:
Without positional ids that handle would silently have come to mean the left part of the cut run.
Checks
-Wall -Wextra -Werrorover five TUs..ods: three repeated cells written, saved, and both the original and the result converted withsoffice --convert-to csv. 121 lines each, and the only line that differs is the one holding the three cells written — nothing else moved. Not committed,sofficeis not in CI.