Skip to content

feat(spreadsheet): state the cell an xlsx file spells none of - #869

Open
andiwand wants to merge 1 commit into
mainfrom
feat/sheet-grow-xlsx
Open

feat(spreadsheet): state the cell an xlsx file spells none of#869
andiwand wants to merge 1 commit into
mainfrom
feat/sheet-grow-xlsx

Conversation

@andiwand

@andiwand andiwand commented Sep 9, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Rebased on main now that #868 has landed. Step 2.2 of docs/design/spreadsheet-editing.md.

What it does

An .xlsx file spells a c only for a cell that holds something, so a write into an empty position refused. The page carries no lock on such a cell, so the user typed and the host got the refusal back. This was the last such hole.

insert_cell states the c in its row in column order, and the row in sheetData in row order where the file states none, then widens dimension around the new cell. Nothing is reindexed — the cell map is keyed by position, so an insert touches one entry.

A position a merge covers refuses first, because Excel ignores what a covered c holds. That check reads mergeCells again, since the parsed is_covered flags only exist for a cell the file states.

The fix that came with it

TableRange::contains was half open — it left the last row and column of the range out. The parser marks the covered cells of a big merge with contains and those of a small one with an inclusive loop, so the two disagreed: a merge too big to walk cell by cell marked one row and one column too few, and SheetCell::is_covered was wrong for those cells.

to() is read as the last position of the range everywhere else — the parser's own loop, the dimension handling — so contains is closed now too. It had exactly one caller before this pull request. TableRange.contains pins it, and the_last_cell_of_a_wide_merge_refuses_too pins what it meant for the parser; both fail against the old comparison.

Checks

  • soffice --headless --convert-to csv on a grown Ordnerruecken.xlsx reads 7.5 at the new column of row 1 and grown at the new bottom-right corner.
  • The reference output is byte-identical to the pins, so the contains fix moves no rendered cell. It reaches the renderer only through a backstop the cursor normally makes unreachable.

Test

OoxmlSpreadsheetWrite gains seven cases: an absent cell written, column order, row order, the widened dimension, the absent covered cell that still refuses, the last cell of a wide merge that now refuses too, and a save and reopen. The test workbook gained a sheet_prefix argument so a <dimension> can sit where the schema orders it.

https://claude.ai/code/session_01KKFKbUVCYF2VhujdmjhhPW

@andiwand
andiwand force-pushed the feat/sheet-grow-ods branch from b1a34f0 to 0e4adc5 Compare September 9, 2026 18:36
@andiwand
andiwand force-pushed the feat/sheet-grow-xlsx branch from fdc77f2 to 64210bb Compare September 9, 2026 18:36
@andiwand
andiwand force-pushed the feat/sheet-grow-ods branch from 0e4adc5 to 9e6f87f Compare September 9, 2026 19:41
@andiwand
andiwand force-pushed the feat/sheet-grow-xlsx branch from 64210bb to 1b8e95e Compare September 9, 2026 19:42
@andiwand
andiwand force-pushed the feat/sheet-grow-ods branch from 9e6f87f to 4e73db9 Compare September 9, 2026 20:11
Base automatically changed from feat/sheet-grow-ods to main September 9, 2026 20:12
An `.xlsx` file spells a `c` only for a cell that holds something, so a
write into an empty position refused. The page carries no lock on such a
cell, so the host got the refusal back after the user had typed.

`insert_cell` states the `c` in its row in column order, and the `row` in
`sheetData` in row order where the file states none, then widens
`dimension` around the new cell. Nothing is reindexed: the cell map is
keyed by position, so an insert touches one entry.

A position a merge covers refuses first, because Excel ignores what a
covered `c` holds. The check reads `mergeCells` again, since the parsed
flags only exist for a cell the file states.

`TableRange::contains` left the last row and column of the range out,
which made the parser's two covered-cell branches disagree: a merge too
big to walk cell by cell marked one row and one column too few. The range
is closed everywhere else it is read, so `contains` is closed now too.

LibreOffice opens a grown workbook and reads the new cells, checked with
`soffice --convert-to`. The reference output is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKFKbUVCYF2VhujdmjhhPW
@andiwand
andiwand force-pushed the feat/sheet-grow-xlsx branch from 1b8e95e to 51aec1f Compare September 9, 2026 20:22
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