Skip to content

Make character formatting and link removal undoable - #39

Merged
michaelstonis merged 4 commits into
mainfrom
housekeeping/character-format-undo
Oct 21, 2026
Merged

michaelstonis merged 4 commits into
mainfrom
housekeeping/character-format-undo

Conversation

@michaelstonis

Copy link
Copy Markdown
Contributor

Summary

Character formatting (bold, italic, underline, strikethrough, sub/superscript, font family/size, text/highlight colour) and hyperlink removal can now each be undone as one step.

  • ApplyStyleAction gains a constructor that wraps any formatting operation. It saves each affected paragraph with its index and restores only the inline runs, so a table inside the range and later paragraph formatting are left alone. Runs it split are merged on undo.
  • New EditorContext.ApplyCharacterFormatting: with a selection, it records one undo step and keeps the same text selected. With no selection, it only sets the pending style and records nothing. All formatting commands, Ctrl+B/I/U, and the MAUI font/colour pickers go through it.
  • HyperlinkEngine.RemoveHyperlink keeps the link text's styled runs. The new RemoveHyperlinkAction restores the URL, title and formatted text on undo.
  • Docs: formatting.md, images-hyperlinks.md, architecture.md. CHANGELOG entry under Unreleased → Fixed.

Tests

31 new cases in CharacterFormatUndoTests.cs:

  • Every command over runs with mixed styles, with and without a selection.
  • Ranges spanning several paragraphs with a table in between.
  • Repeated toggle/undo without fragmenting runs.
  • Link removal, plus its undo and redo.

Results: Core 825 passed, SkiaSharp 29 passed, Release build with 0 warnings, and the MAUI compile check is clean.

Not in this PR

  • Character formatting inside table cells does nothing yet: ApplyTextStyle skips tables. This is unchanged from before.
  • The heading dropdown is paragraph formatting and is covered by the paragraph undo PR.

Generated by Claude Code

Bold/italic/underline/strikethrough/sub/superscript/clear-formatting commands,
Ctrl+B/I/U, the font family/size dropdowns and the text/highlight colour
pickers called FormattingEngine/FontFormattingEngine directly, so none of them
could be undone. ApplyStyleAction existed but only took a fixed transform (the
toggles decide on/off from the current runs), restored the paragraph style on
undo, and paired snapshots with block indices by position, restoring the wrong
paragraph when a table sat inside the range.

ApplyStyleAction now also wraps any formatting operation, snapshots each
paragraph with its block index, restores only the inlines on undo, and merges
split runs after applying. EditorContext.ApplyCharacterFormatting pushes one
ApplyStyleAction for a non-empty selection (keeping the selection on the same
text) and leaves a collapsed selection to the engines' pending style, which is
not recorded. The commands, keyboard shortcuts and MAUI dropdowns/pickers all
go through it.

HyperlinkEngine.RemoveHyperlink now keeps the link's runs and their styles
instead of one plain run, and RemoveHyperlinkCommand pushes a new
RemoveHyperlinkAction that snapshots the paragraph so undo restores the link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
Copilot AI balanced review requested due to automatic review settings October 21, 2026 16:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Snapshot restoration breaks earlier insertion undo actions and loses supported inline content.

2 open issues
What changed in this PR

Adds undo support for character formatting and hyperlink removal across editor commands, keyboard shortcuts, and MAUI controls.

Changes:

  • Records selected-text formatting as one undo step while preserving selection.
  • Makes hyperlink removal undoable and preserves styled text.
  • Adds regression tests and updates documentation.
File Description
tests/​MintedTextEditor.Core.Tests/​CharacterFormatUndoTests.cs Tests formatting and hyperlink undo behavior.
src/​MintedTextEditor.Maui/​MintedEditorView.cs Routes font and colour pickers through undo.
src/​MintedTextEditor.Core/​Input/​EditorInputController.cs Records keyboard formatting actions.
src/​MintedTextEditor.Core/​Formatting/​HyperlinkEngine.cs Preserves formatting when removing links.
src/​MintedTextEditor.Core/​Editing/​UndoableActions.cs Extends formatting snapshots and adds link-removal undo.
src/​MintedTextEditor.Core/​Commands/​HyperlinkCommands.cs Makes link removal undoable.
src/​MintedTextEditor.Core/​Commands/​FormattingCommands.cs Uses undo-aware formatting.
src/​MintedTextEditor.Core/​Commands/​FontCommands.cs Adds undo-aware font and colour commands.
src/​MintedTextEditor.Core/​Commands/​EditorContext.cs Centralizes undo-aware character formatting.
docs/​images-hyperlinks.md Documents undoable link removal.
docs/​formatting.md Explains formatting undo integration.
docs/​architecture.md Clarifies snapshot-based restoration.
CHANGELOG.md Records the undo improvements.

Review effort: Balanced

…napshots

SnapshotHelper rebuilt every inline from copied data on restore, so undo steps
recorded earlier that hold the original objects (InsertHyperlinkAction,
InsertImageAction) could no longer find them: insert a link, remove or format
it, undo twice, and the link stayed. Snapshots also turned LineBreak into a
"\n" text run and kept only TextRun children of hyperlinks.

Adopt the identity-preserving SnapshotHelper from PR #28
(housekeeping/hyperlink-display-text) verbatim so the two PRs merge cleanly:
snapshots keep each original inline (and paragraph) with its state, restore
puts the same objects back, line breaks and other inline types are kept as
themselves, and hyperlink children are captured recursively. The inline-only
restore used by ApplyStyleAction and RemoveHyperlinkAction is now a small
ApplyStyleAction.RestoreInlines wrapper outside SnapshotHelper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197tHRWhTiagB1MTsDuPkoa
# Conflicts:
#	CHANGELOG.md
#	docs/architecture.md
#	docs/formatting.md
#	src/MintedTextEditor.Core/Editing/UndoableActions.cs
#	src/MintedTextEditor.Maui/MintedEditorView.cs
@michaelstonis
michaelstonis merged commit da55878 into main Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants