fix: escape a marker run only where a thematic break can open - #377
Merged
Conversation
The three markers are counted across the line rather than inside one run of it, so `* * *` and `_ _ _` still open a break where `**` and `__` no longer do. That is why the check is not the run-length test the tilde branch beside it makes for a code fence. `\_ __a__` loses its escape as a consequence. An underscore opens no bullet marker, so the line's character class was the whole of what held it, unlike its `*` sibling, which the bullet-marker check still holds.
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.
Summary
opensBlockConstructtested the line's character class without its length, so a run of*or_too short to open a thematic break was treated as one that could and kept the escapestate.safegave it. A line holding**was written\*\*, and_and__were written\_and\_\_. The line is literal text as written and as escaped, so the document was unchanged and the file gained backslashes the author never wrote.THEMATIC_BREAK_PATTERNSnow spells the construct rather than its character class: three markers or more, spaces and tabs between them, and nothing else. The three are counted across the line rather than inside one run of it, which is why this is not the run-length test the tilde branch beside it makes. A code fence needs its three consecutive;* * *and_ _ _are breaks whose runs are one character each.The check still reads the line only as far as it is known, which is the node's own slots plus the tail
relaxAttentionEscapesassembles from the next sibling. A tail that cannot be read whole either adds markers, which keeps the escape, or adds something else, which fails the pattern and keeps it as well, so an unknown tail never drops a backslash the line turns out to need.Related Issue
Closes #373
Verification
markdownCompatibility.test.tsgains nine rows inEscape precision, where this behavior is already pinned character by character.Six lose an escape:
**,_, and__alone on a line,_ _spending two markers across one, a two-marker run opening a paragraph's continuation line, and\_ __a__. Three keep the escape on the run the line starts with while the runs after it relax —\*\* \*,\* \* \*, and\_ \_ \_— which is what pins the count to the line rather than to the run; those three pass before the change as well as after it.a paragraph line\n\*\*\*joinskeeps the escape the document needs, since a break interrupts a paragraph.Eight sources assert the written bytes and the reopened document together. Both spellings of a short run reopen as the same paragraph text, so closure holds either way and neither the corpus round trip nor the document-preservation guard can see the difference.
corpus/commonmark/blocks.mdwas measured by saving it once. Line 18 is now**as the file writes it, and the breaks above it still round-trip as***,---, and_ _ _. What still differs there belongs to other classes: the\--on line 20, the code span crossing a list-item boundary, and the blank lines the serializer inserts between adjacent headings.pnpm check:frontendpasses.Not verified: no manual pass in the assembled application, and
pnpm check:backendwas not run. The change is serializer behavior with no rendered surface of its own, and it touches no Rust or Tauri code.Notes
_and__as well as**. A lone*is untouched because nothing escapes it in the first place.\_ __a__moves out ofkeeps the escape the document needs. It stood there under a comment about a bullet marker, which only ever described its*sibling: an underscore opens no bullet, so the line's character class was the whole of what held it._ __a__is not a break, and the row now asserts the bytes and the reopened document together, which is what shows the strong span beside it survives.docs/update. This narrows the positional checkdocs/decisions.mdalready records rather than adding a rule to it, and the thematic break form those documents describe is the one a real break is written in, which is untouched.--two lines below inblocks.mdstays escaped, as the issue scopes it out. A hyphen is not an attention character and never reaches this check.