Skip to content

fix(core/tree-sitter): render markdown backslash escapes as the escaped character - #1370

Open
3aKHP wants to merge 1 commit into
anomalyco:mainfrom
3aKHP:vesicle/fix-markdown-escape
Open

fix(core/tree-sitter): render markdown backslash escapes as the escaped character#1370
3aKHP wants to merge 1 commit into
anomalyco:mainfrom
3aKHP:vesicle/fix-markdown-escape

Conversation

@3aKHP

@3aKHP 3aKHP commented Aug 15, 2026

Copy link
Copy Markdown

Fixes #1369

Summary

Markdown renderables style paragraph text through the markdown_inline injection with conceal. The bundled highlight query captures (backslash_escape) as @string.escape with no conceal rule, so CommonMark escapes rendered literally with the backslash visible and escape-styled (\~ displayed as backslash + tilde in the escape color).

The escape cannot be fixed in the query language: the markdown_inline grammar emits backslash_escape as one atomic regex token spanning both characters, so no query can capture the backslash separately, and conceal replaces whole captured ranges with a static literal, so a whole-node conceal rule would remove the escaped character too.

Fix

Both halves in the worker's one-shot highlight pipeline:

  • processInjections spreads each injected capture node into a plain pseudo-node; web-tree-sitter's node type is a prototype getter, so the spread dropped it. Carry type explicitly so downstream consumers can dispatch on it.
  • getSimpleHighlights splits a backslash_escape capture that has no query-configured conceal into a one-byte conceal of the backslash and drops the escape styling from the remainder, so \~ renders as plain ~. An explicit #set! conceal on the node set by a query still wins.

The marked-based streaming placeholder path already decoded escapes (renderInlineToken's escape case), so the two internal render paths now agree.

Tests

client.test.ts gains a markdown escape test asserting that each string.escape highlight on "Escaped \~ and \* here" collapses to a one-byte conceal tuple covering only the backslash ([8,9) / [15,16), meta.conceal === ""), and that no full-range string.escape remains, so the escaped character renders unstyled.

tree-sitter suites: 63 pass, 1 pre-existing skip. The only packages/core failure (node-assets manifest) also fails on the pristine tag without build output and is unrelated.

Out of scope (deliberately)

  • (hard_line_break) keeps its current literal rendering; it shares the @string.escape capture but involves line-break semantics rather than character decoding.
  • #offset!-style partial-token conceal (nvim queries use it in commented form) would be a more general engine feature; this fix does not need it.

…ed character

Markdown renderables style paragraph text through the markdown_inline
injection with conceal. The bundled highlight query captures
(backslash_escape) as @string.escape with no conceal rule, so CommonMark
escapes rendered literally with the backslash visible and escape-styled
("\~" displayed as backslash + tilde in the escape color).

The escape cannot be fixed in the query language: the markdown_inline
grammar emits backslash_escape as one atomic regex token spanning both
characters, so no query can capture the backslash separately, and conceal
replaces whole captured ranges with a static literal, so a whole-node
conceal rule would remove the escaped character too.

Fix both halves in the worker's one-shot highlight pipeline:

- processInjections spreads each injected capture node into a plain
  pseudo-node; web-tree-sitter's node `type` is a prototype getter, so
  the spread dropped it. Carry `type` explicitly so downstream consumers
  can dispatch on it.
- getSimpleHighlights splits a backslash_escape capture that has no
  query-configured conceal into a one-byte conceal of the backslash and
  drops the escape styling from the remainder, so "\~" renders as plain
  "~".

The marked-based streaming placeholder path already decoded escapes
(renderInlineToken's escape case), so the two internal render paths now
agree.

Tests: client.test.ts gains a markdown escape test asserting the conceal
tuple covers only the backslash byte and that no full-range
string.escape remains. tree-sitter suites: 63 pass, 1 pre-existing
skip; the only packages/core failure (node-assets manifest) also fails
on the pristine tag without build output and is unrelated.
3aKHP added a commit to 3aKHP/opentui that referenced this pull request Aug 15, 2026
anomalyco#1369 (issue) and anomalyco#1370 (PR, branch
vesicle/fix-markdown-escape). Also record the 2026-08-15 maintenance
posture: the fork is the self-maintained Vesicle baseline; upstream
review is courtesy, nothing waits on it.
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.

Markdown renderable: backslash escapes render literally instead of the escaped character

1 participant