Repository navigation
fix: formatting, hyperlinks and undo in and around tables; text before images - #41
Conversation
…e images - Deleting a selection from a paragraph before a table to one after it deletes the table with the selected text (unchanged), but undo lost the table and put the last paragraph back in the wrong place, because DeleteRangeAction only snapshotted paragraphs. Undo now records every block in the range (SnapshotHelper.CaptureBlocks/RestoreBlocks, keeping object identity) and reinserts the table at its index. The same snapshot covers table-cell paragraphs, so undoing a deletion inside a cell (e.g. Backspace) now restores the cell text instead of doing nothing. - Inserting a hyperlink with the caret or selection in a table cell threw (HyperlinkEngine looked up the paragraph by block index only). It now uses the cell's paragraph, undo restores it, and the caret lands after the link in the cell. A selection that runs out of the start cell links only its part in that cell. - Character formatting skipped table cells but still recorded an undo step. DocumentEditor.ApplyTextStyle, FormattingEngine.IsAppliedToEntireRange, FontFormattingEngine.GetTextStyleForRange, ApplyCharacterFormatting and ApplyStyleAction now walk the paragraphs of the cells in the range (reading order from the start cell to the end cell, as FormattingStateTracker already did), so bold etc. apply in cells and across several cells, toggles turn off again, and undo/redo restore the exact runs. GetSelectedText returns cell text too (it returned ""). - DocumentEditor.InsertText at offset 0 of an image (or other non-text inline) put the text after it; it now goes before it, appended to the preceding run when there is one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
awk compared the hunk line number (a string from substr) with the section bounds as strings, so an entry on line 40 was not counted as after line 9. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
|
Changelog entry check failed on 85bc0ab: the bug was in the check, not this PR's entry. The PR does add a Generated by Claude Code |
There was a problem hiding this comment.
🟡 Changes recommended
Clipboard extraction loses linked text, merged-cell traversal includes hidden content, and redo can change hyperlink membership.
3 open issues
What changed in this PR
Improves table editing in the core editor, including formatting, hyperlink insertion, undo restoration, and text insertion before images.
Changes:
- Adds table-aware range traversal and block snapshots.
- Fixes hyperlink insertion in cells and text placement before non-text inlines.
- Adds regression tests and documents supported behavior.
| File | Description |
|---|---|
| tests/MintedTextEditor.Core.Tests/TableEditingFixesTests.cs | Adds 15 editing regression tests. |
| src/MintedTextEditor.Core/Formatting/HyperlinkEngine.cs | Supports insertion in cells and clamps cross-cell selections. |
| src/MintedTextEditor.Core/Formatting/FormattingEngine.cs | Checks formatting across selected cells. |
| src/MintedTextEditor.Core/Formatting/FontFormattingEngine.cs | Reads styles across selected cells. |
| src/MintedTextEditor.Core/Editing/UndoableActions.cs | Captures table content and restores block identity. |
| src/MintedTextEditor.Core/Document/DocumentEditor.cs | Adds cell traversal and insertion-before-image handling. |
| docs/tables.md | Documents cell editing behavior. |
| CHANGELOG.md | Records editing fixes. |
Review effort: Balanced
GetSelectedText walked only the TextRun inlines of each paragraph, so in a cell holding "A", a link showing "B" and "C" copy returned "AC", cutting the linked text deleted it without copying it, and linking already-linked text with no display text produced an empty link. It now extracts text by character offsets, recursing into hyperlink children and adding a newline for each line break, so a partly selected link contributes only its selected characters. This applies to top-level paragraphs too, which had the same gap. HyperlinkEngine.InsertHyperlink also re-resolves its insertion point by character offset after deleting the selection: deleting a whole link between two runs merges them, which left the old inline index pointing past the merged run and put the new link after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
GetParagraphsInRange walked every cell between the start and end cell, including slots covered by another cell's RowSpan/ColumnSpan. MergeCells keeps those cells' blocks but layout hides them, so after merging the top row of a 2x2 table, a selection from (0,0) into (1,0) copied the hidden text of (0,1) and formatting changed it. The traversal now works out span ownership once per table with TableBlock.BuildSpanOwners(), as layout does, and skips covered slots (unless the range starts or ends in one). FormattingStateTracker had its own copy of the traversal with the same problem (hidden text could make the toolbar state look mixed); it now uses GetParagraphsInRange. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
…e a preceding link
With [link("site"), image], an InsertTextAction at the image's offset 0
gives [link, "X", image]. Its start anchor is a paragraph character
offset, which after undo resolves to the end of the link, and because
EscapeHyperlinkBoundary is false the redo put "X" inside the link.
ParagraphAnchor's keepHyperlinkStart option becomes keepHyperlinkSide: it
still keeps a position recorded at the start of a link there, and now
also resolves a position recorded outside any link to the start of the
inline after a link's end (when that inline is not another link), via the
new DocumentEditor.PreferAfterHyperlinkEnd. Only InsertTextAction's start
anchor uses the option, so insertions before images, line breaks or runs
replay on the same side of the link; a position recorded at a link's end
still extends the link on redo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa

Summary
SnapshotHelper.CaptureBlocks/RestoreBlocks, keeping object identity).DocumentEditor.ApplyTextStyle,FormattingEngine.IsAppliedToEntireRange,FontFormattingEngine.GetTextStyleForRange,DocumentEditor.ApplyCharacterFormattingandApplyStyleAction. Cells are taken in reading order from the start cell to the end cell, the same ruleFormattingStateTrackeruses.GetSelectedText, which copy and cut use, now returns the selected cell text instead of an empty string.DocumentEditor.InsertTextat offset 0 of an image or line break now inserts the text before it instead of after it. If there is a text run before the image, the text is added to that run.Not in this PR
DeleteRangetreats it as a range inside the start cell.HyperlinkEngine.GetHyperlinkAtPositiononly checks top-level paragraphs.Tests
TableEditingFixesTestswith 15 tests, each written to fail before its fix.Generated by Claude Code