Skip to content

Don't apply emphasis or links inside inline code spans - #48

Open
jasonzondor wants to merge 3 commits into
omacom:masterfrom
jasonzondor:fix/code-span-emphasis
Open

Don't apply emphasis or links inside inline code spans#48
jasonzondor wants to merge 3 commits into
omacom:masterfrom
jasonzondor:fix/code-span-emphasis

Conversation

@jasonzondor

Copy link
Copy Markdown

Problem

Text inside an inline code span (backticks) still had Markdown emphasis applied. Typing `The_brown_fox` rendered "brown" in italics; `a **b**` bolded "b"; `[x](y)` got link styling. The caret also skipped over the underscores/asterisks as if they were hidden markers.

Fixes #47.

Cause

MarkdownHighlighter::inlineMarkup() is the single source of truth for inline spans (highlighter styling + Backend::hiddenRangesAt caret skipping). It ran the bold/italic/link regexes over the entire line with no awareness of backtick-delimited code spans, and highlightInline() applied those formats after the code format, overriding it.

Fix

Collect inline code-span ranges first (same `([^`]+)` pattern the highlighter already uses), then skip any emphasis/link match whose range overlaps a code span. Because the fix lives in inlineMarkup(), both the rendered styling and the caret-skip behaviour are corrected together.

Tests

Added ignoresInlineMarkdownInsideCodeSpans covering underscores, **/[]() inside code, and a mixed line where real emphasis outside the code span is still detected. Full suite passes (qmake6 && make && QT_QPA_PLATFORM=offscreen ./tst_omawrite).

🤖 Generated with Claude Code

inlineMarkup() ran the bold, italic, and link regexes over the whole
line without regard for backtick-delimited code spans, so text like
`The_brown_fox` had "brown" styled as italic (and the caret skipped the
underscores as if they were hidden markers).

Collect the inline code-span ranges first and skip any emphasis or link
match that overlaps one.

Fixes omacom#47

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MfBmFxT8EkK9Aan5bSopm5
@jasonzondor

Copy link
Copy Markdown
Author

Local build test:
image

omarchybot and others added 2 commits September 2, 2026 05:34
Rejecting every emphasis or link whose range overlaps a code span also rejects markup that merely encloses one. `_a `b` c_` lost its italics and `[see `code`](url)` stopped being a link, both of which worked before this branch. A code span makes its own contents literal; it does not make the text around it literal.

Testing the two delimiter positions instead keeps the reported case fixed -- in `` `The_brown_fox` `` both underscores sit inside the code span -- while leaving enclosing markup alone. It also still rejects markup that straddles a boundary, as in `_a `b_ c` d_`, where the closing underscore is inside the code span and so is not a marker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Now that emphasis may enclose a code span, the two passes overlap on the code span's own range, and setFormat replaces rather than merges -- Qt assigns the whole QTextCharFormat to every covered character. Running the code pass first meant the enclosing emphasis overwrote it, so `_a `b` c_` italicised the code span and dropped its background, which is the thing the issue asked to stop.

Running it last makes the code styling win on exactly the nested range: `a ` and ` c` stay italic, `` `b` `` keeps the code background and is not italicised. The markers are untouched either way, because markup with a delimiter inside a code span is already rejected.

The test reads the block layout's format ranges rather than asserting on spans, since this is about which pass wins where the two overlap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.com>
@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed, and the precedence rule needed one correction. I pushed two commits to this branch (5f60821, 0972a84).

The overlap test was too wide. insideCode() rejected any emphasis or link whose range intersected a code span, but a code span only makes its own contents literal — it does not make the text around it literal. So markup that merely encloses a code span was thrown away along with markup sourced from inside one:

input master this branch before the fix
_a `b` c_ italic nothing
[see `code`](url) link nothing
**a `b` c** bold nothing

Those three worked before the branch, so this was a regression rather than a case it merely failed to improve. It hit the caret too, not just the styling: inlineMarkup() feeds Backend::hiddenRangesAt, so the enclosing _ and ](url) markers stopped being skipped by the arrow keys.

5f60821 tests the two delimiter positions instead of the whole range. The reported case is unaffected — in ​`The_brown_fox`​ both underscores are inside the code span — and markup that straddles a boundary is still rejected, as in _a `b_ c` d_ where the closing underscore is inside the code span.

The code span then needed to win where emphasis encloses it. With enclosing emphasis restored, the two passes overlap on the code span's own range, and setFormat replaces rather than merges (Qt assigns the whole QTextCharFormat to every covered character). Running the code pass first meant the emphasis overwrote it, so _a `b` c_ italicised the code span and dropped its background — the thing this PR is meant to stop. 0972a84 moves the code pass last, giving a and c italic with ​`b`​ keeping the code background and no italics.

Tests: the repo's own ./bin/test on a disposable Omarchy 4.0.2 worker, 15 passed / 0 failed. Your ignoresInlineMarkdownInsideCodeSpans still passes unchanged, and your asserted index 18 checks out. I also ran the new tests against the pre-fix branch and against master first, to confirm the regression was real in both directions rather than assuming it.

Second opinion: Codex at xhigh reasoning reviewed this independently and reached the same conclusion about the predicate, naming **a `b` c** as a case I had not written a test for, and the format-ordering half above. Its independence is not currently guaranteed — it can read this session's own transcript — so read the agreement as agreement rather than as a separate confirmation; the bold case and the Qt setFormat citation are its contributions. Reviewed by Claude Opus 5 and Codex XHigh.

Two things left, neither pushed. Both are pre-existing and neither is a regression, so they are yours or the maintainer's call rather than mine:

  • The `([^`]+)` pattern cannot parse a doubled delimiter, so a b ``​`` is misread as a code span over `` a ` ``. Master does this too — the pattern is unchanged by this PR.
  • Filtering completed matches means a rejected delimiter inside a code span can still consume text the regex engine would otherwise have scanned, so in ​`_` a _b_ the later _b_ is never considered. Codex raised this; masking code spans with position-preserving filler before running the regexes would fix it, and would also let insideCode() go away. Master has the same gap, so it is a design question rather than a defect this PR introduced.

Heads-up on a collision: #15 adds a fourth pass to inlineMarkup() for strikethrough with no code-span guard. Whichever of the two lands second will merge cleanly and silently leave ​`a ~~b~~ c`​ striking through inside a code span. Worth adding the guard to that pass in whichever order they go in.

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.

Inline code spans still get emphasis applied: The_brown_fox renders "brown" italic

2 participants