Preserve caller attributes on code block content - #83
Open
cjs226 wants to merge 1 commit into
Open
Conversation
`HighlightedTextFragment` rebuilt every syntax token from its plain `String`, which discarded all attributes the caller's `MarkupParser` had applied to the code block. Any decoration a client added — search highlighting, custom inline styling — was silently dropped between parsing and rendering, with no seam to intervene: `CodeBlockStyle` only receives a type-erased label, so a custom style cannot recover the text either. Walk the original attributed content in parallel with the tokens and slice it instead. Theme attributes are still merged on top, so syntax colouring wins for the keys it sets while caller attributes survive for the rest. If the tokenizer's total length ever disagrees with the source, fall back to the previous plain-token behaviour so text is never lost. Found while adding find-in-text search highlighting to an app that renders notes with Textual: matches inside fenced code blocks were located and highlighted correctly by the parser, then vanished before reaching the screen.
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.
HighlightedTextFragment rebuilds every syntax token from its plain String:
That discards every attribute the caller's
MarkupParserapplied to the code block. Any decoration a client adds, such as search highlighting and custom inline styling, is applied correctly at parse time, survives into the AttributedSubstring the code block receives, and is then silently dropped before it reaches the screen.There is no way to work around it from outside the library.
CodeBlockStyleConfigurationexposes only a type-erasedlabel, anindentationLevel, alanguageHint, an opaqueCodeBlockProxy, and the theme. So a customCodeBlockStylecannot recover the text to re-render it, and cannot even detect that a block contains anything of interest.The change
Walk the original attributed content in parallel with the tokens and slice it, rather than rebuilding each token from its plain string. Theme attributes are still merged on top, so syntax colouring continues to win for the keys it sets (
foregroundColor,presentationIntent) while caller attributes survive for the rest.If the tokenizer's total length ever disagrees with the source, that token falls back to the previous plain-string behaviour, so text can never be lost — only the attributes on that token.
No API change, no behaviour change for callers that don't attach attributes.
How it came up
Adding find-in-text search to an app that renders notes with Textual. Matches inside fenced code blocks were located and highlighted correctly by a custom
MarkupParser, then vanished on screen — the highlight renders for one frame before.onChangetriggershighlight(tokens:...)and replaces the attributed text wholesale.Tests
Adds
Tests/TextualTests/Internal/Highlighter/HighlightedTextFragmentTests.swiftcovering:All five fail against the previous plain-string rebuild and pass with the fix.
Also verified in a real macOS 26 app: search highlighting now renders inside fenced code blocks, and syntax colouring in those blocks is unchanged.
Local verification
make test-macos,make test-maccatalystandmake build-demoall pass.make test-ioshas one pre-existing snapshot failure on my machine (TextLayoutCollectionTests.twoParagraphsBidiStructuredTextLayout) — 164 of 165 pass. Running the same suite against a different simulator turns that into four layout snapshot failures, so these look device/OS sensitive rather than related to this change, which touches only code block rendering. Happy to look if CI disagrees.I could not run the Xcode 16.4 backward-compatibility job (no 16.4 locally), nor tvOS/visionOS (runtimes not installed).