Summary
Saving escapes a backtick that cannot open a code span. A code span needs a closing run of the same length inside the same block, and a backtick with no such run is literal text as written. The serializer escapes it anyway, so the file gains a backslash the author did not write and the document is unchanged.
corpus/commonmark/blocks.md carries the case under the heading Block structure takes precedence over inline parsing, where two list items each hold one backtick and the span cannot cross the boundary between them. That is one instance of the general rule rather than a case of its own.
Steps to reproduce
- Open a document containing
a followed by a single backtick and b.
- Save it without editing.
- Read the file.
Expected behavior
The line is unchanged. Nothing closes the backtick, so nothing needs holding literal.
Actual behavior
Measured against 8bc36754 by driving the editor mount used by the plugin tests.
| Document |
Saved |
a ` b |
a \` b |
`unclosed |
\`unclosed |
- `a code span does not / - cross a list-item boundary` |
both backticks escaped |
a `\n\nb ` (one in each of two paragraphs) |
both backticks escaped |
> a ` b |
> a \` b |
# a ` b |
# a \` b |
A backtick that does close is unaffected, because the pair parses as a code span and never reaches this path: a ` b ` c is read as an inlineCode node and written a `b` c.
Related context
Done when
Notes, logs, screenshots
Diagnosis
No relaxation pass reconsiders a backtick. serializeMarkdownText runs relaxAttentionEscapes, relaxBlockMarkerEscapes, relaxBracketEscapes, relaxAngleEscapes, relaxAutolinkLiteralEscapes, and relaxCharacterReferenceEscapes. A backtick falls into none of them: ATTENTION_CHARACTERS is *_~, and relaxBlockMarkerEscapes branches only on #, the ordered-list markers, and |. The escape state.safe emits is therefore never revisited.
The machinery to decide it already exists. skipCodeSpan walks a block for exactly this question and returns the opening offset unchanged when no closing run of equal length follows, which is the condition under which the escape is unnecessary. It is currently used only by findBracketLinks.
Out of scope
Summary
Saving escapes a backtick that cannot open a code span. A code span needs a closing run of the same length inside the same block, and a backtick with no such run is literal text as written. The serializer escapes it anyway, so the file gains a backslash the author did not write and the document is unchanged.
corpus/commonmark/blocks.mdcarries the case under the headingBlock structure takes precedence over inline parsing, where two list items each hold one backtick and the span cannot cross the boundary between them. That is one instance of the general rule rather than a case of its own.Steps to reproduce
afollowed by a single backtick andb.Expected behavior
The line is unchanged. Nothing closes the backtick, so nothing needs holding literal.
Actual behavior
Measured against
8bc36754by driving the editor mount used by the plugin tests.a ` ba \` b`unclosed\`unclosed- `a code span does not/- cross a list-item boundary`a `\n\nb `(one in each of two paragraphs)> a ` b> a \` b# a ` b# a \` bA backtick that does close is unaffected, because the pair parses as a code span and never reaches this path:
a ` b ` cis read as aninlineCodenode and writtena `b` c.Related context
docs/decisions.mdunderPreserve the form a file was written in;corpus/commonmark/blocks.mdunderBlock structure takes precedence over inline parsing.Done when
Notes, logs, screenshots
Diagnosis
No relaxation pass reconsiders a backtick.
serializeMarkdownTextrunsrelaxAttentionEscapes,relaxBlockMarkerEscapes,relaxBracketEscapes,relaxAngleEscapes,relaxAutolinkLiteralEscapes, andrelaxCharacterReferenceEscapes. A backtick falls into none of them:ATTENTION_CHARACTERSis*_~, andrelaxBlockMarkerEscapesbranches only on#, the ordered-list markers, and|. The escapestate.safeemits is therefore never revisited.The machinery to decide it already exists.
skipCodeSpanwalks a block for exactly this question and returns the opening offset unchanged when no closing run of equal length follows, which is the condition under which the escape is unnecessary. It is currently used only byfindBracketLinks.Out of scope
**and--block-marker escapes in the same file, which are The serializer escapes a marker run too short to open a thematic break #373 and its hyphen sibling.