fix: pair code regions by CommonMark rules in markdown preprocessing - #228
Merged
alecdotdev merged 1 commit intoJul 27, 2026
Merged
Conversation
The five source-level rewrites (![[embeds]], [[#wikilinks]], ^block-ids, ==highlight==, ^[inline footnotes]) protected code regions with the regex alternation ```.*?```|`.*?`, which cannot express CommonMark pairing: fences close only on a line-leading run of the same character at least as long as the opener, and a span of N backticks closes only on a run of exactly N. A single 4-backtick inline sample (or any ~~~ fence, which the pattern did not know at all) desynchronized protection for the entire rest of the document — later inline code containing ![[x]] was rewritten into <img> tags, with nonlocal collateral damage in unrelated blocks. Replace the alternation with a small scanner (code_region_ranges) that walks fences and backtick runs by the CommonMark rules, and skip any match that starts inside a protected range. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PathGao
added a commit
that referenced
this pull request
Aug 7, 2026
…g imports (#506) * chore: delete the code nothing reaches, and narrow the exports nothing imports Four functions in MarkdownViewer.svelte have no caller. Three of them are stale copies of render steps that moved elsewhere and kept evolving there: processTaskItems and processBlockIds moved into markdown.ts in 09fa88f, and the ==highlight== rewrite moved into the Rust renderer, where it later grew code-span protection (#228, #371). Each migration re-pointed the call site and left the old body behind, so the copies have been sitting there since b46a283 looking like reusable helpers. getSplitTransition is plain dead. Also removed: 24 exports nothing outside their own file imports, the escapeHtmlText alias that only forwards to escapeHtml, an addFrontMatterList- Item wrapper with no production caller, the toPlainRecord guard that isFrontMatterMapping already makes unreachable, and FrontMatterField's editableValue, which computes the same string as displayValue in every branch with nothing to keep the two from drifting apart. No behaviour change. The front matter tag test now calls the plural helper. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(tests): narrow the harness exports nothing imports Same sweep as the previous commit, finished in scripts/. Twenty-two names in keymapHarness.ts, renderProtocolDom.ts, renderProtocolFixtures.ts, windowTagEditor.ts and scrollSyncBlockMapping.test.ts are only used inside their own file, so the export widened the contract for nothing. RenderFixtureName had no use anywhere, including its own file, and is gone. tsconfig.json puts scripts/ inside `npm run check`, so a name another file still imports would fail the type check rather than pass silently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
PathGao
added a commit
that referenced
this pull request
Aug 7, 2026
… its offset parent (#515) * chore: delete the code nothing reaches, and narrow the exports nothing imports Four functions in MarkdownViewer.svelte have no caller. Three of them are stale copies of render steps that moved elsewhere and kept evolving there: processTaskItems and processBlockIds moved into markdown.ts in 09fa88f, and the ==highlight== rewrite moved into the Rust renderer, where it later grew code-span protection (#228, #371). Each migration re-pointed the call site and left the old body behind, so the copies have been sitting there since b46a283 looking like reusable helpers. getSplitTransition is plain dead. Also removed: 24 exports nothing outside their own file imports, the escapeHtmlText alias that only forwards to escapeHtml, an addFrontMatterList- Item wrapper with no production caller, the toPlainRecord guard that isFrontMatterMapping already makes unreachable, and FrontMatterField's editableValue, which computes the same string as displayValue in every branch with nothing to keep the two from drifting apart. No behaviour change. The front matter tag test now calls the plural helper. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(tests): narrow the harness exports nothing imports Same sweep as the previous commit, finished in scripts/. Twenty-two names in keymapHarness.ts, renderProtocolDom.ts, renderProtocolFixtures.ts, windowTagEditor.ts and scrollSyncBlockMapping.test.ts are only used inside their own file, so the export widened the contract for nothing. RenderFixtureName had no use anywhere, including its own file, and is gone. tsconfig.json puts scripts/ inside `npm run check`, so a name another file still imports would fail the type check rather than pass silently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(scroll-sync): measure a block's offset from the scroll container, not from its offset parent The per-block mapping resolves a position by descending to the annotated element that owns it and reading `offsetTop`. `offsetTop` is measured from the element's OFFSET PARENT, which CSSOM defines as the nearest positioned ancestor -- or the nearest `table`, `td` or `th`, positioned or not. The preview contains both kinds, and comrak stamps a source range on every table row and cell, so the descent goes inside a table and reads table-relative offsets as document offsets. Measured in Chrome over this app's own stylesheet: a row 945px down a document reports `offsetTop = 1`, and a `<pre>` 1,085px down reports 0 because `renderRichContent` wraps every code block in a `position: relative` shell for the copy button. Both halves of #205's surviving report follow. A line inside a table maps to a couple of hundred pixels, so the pane jumps to the top of the document; and every offset in the table is far below every row's box, so the reverse direction sticks on the last row until the reader is past the whole table. `measureAnchorBox` sums the offset-parent chain and subtracts the container's own. Subtracting rather than stopping at the container keeps it exact whether or not the container is itself an offset parent. A rendered Mermaid diagram now also inherits the source range of the `<pre>` it replaces. Mermaid's SVG keeps nothing of the code block, so a diagram was the one block in the preview that mapped to no source line at all -- not even through a descendant -- and several hundred pixels of preview were attributed to whatever block was nearest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
Fixes #227.
The five source-level rewrites (
![[embeds]],[[#wikilinks]],^block-ids,==highlight==,^[inline footnotes]) protected code regions with the alternation(?s)```.*?```|`.*?`|…, which cannot express CommonMark pairing. One 4-backtick inline sample — or any~~~fence, which the pattern did not know at all — desynchronized protection for the entire rest of the document, rewriting later inline code containing![[x]]into<img>tags with nonlocal collateral damage (details and minimal repro in #227).Change: a small scanner
code_region_ranges()computes protected byte ranges by the CommonMark rules —~~~, up to 3 leading spaces) closes only on a line-leading run of the same character at least as long as the opener (unclosed fence protects to EOF; backtick-fence info strings may not contain backticks);Each rewrite pass then skips matches that start inside a protected range. Ranges are recomputed per pass since replacements shift offsets. No new dependencies; rewrite behavior outside code regions is unchanged.
Tests: 8 new unit tests covering both rupture modes (4-backtick desync,
~~~fences), pairing rules (longer-run close, exact-N spans, unclosed fence), and unchanged rewrites outside code.cargo test: 10/10.🤖 Generated with Claude Code