Skip to content

Don't treat an escaped backslash as the start of another escape in HtmlHelper.Unescape - #967

Merged
xoofx merged 3 commits into
xoofx:mainfrom
HuzaifaChaudary:fix/info-string-escaped-backslash
Oct 23, 2026
Merged

xoofx merged 3 commits into
xoofx:mainfrom
HuzaifaChaudary:fix/info-string-escaped-backslash

Conversation

@HuzaifaChaudary

@HuzaifaChaudary HuzaifaChaudary commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

HtmlHelper.Unescape keeps the character after a backslash by moving lastPos to it , but searchPos stayed on that character , so an escaped backslash was scanned again as the start of a new escape . a fenced code info string with \\ before a punctuation character lost the backslash :

info string cmark before after
~~~ \\* language-\* language-* language-\*
~~~ x\\_ language-x\_ language-x_ language-x\_
~~~ \\a language-\a same same

after an escape the scan now moves past the escaped character . the one exception is & : cmark unescapes backslashes before entities , so \& gives & in both today , and that stays as it is .

Unescape with removeBackSlash: true is only used for the fenced code info string and arguments and the auto identifier link , link urls and titles pass removeBackSlash: false and are not affected .

found by rendering 19200 random documents with Markdig and cmark-gfm in CommonMark mode : 8 differences fixed , none new . three cases are added to TestInfoAndArguments , the two with an escaped backslash fail before the change , the \& one pins the current behavior . dotnet test -c Release : 7375 passed on net8.0 and net10.0 .

Copilot AI balanced review requested due to automatic review settings October 22, 2026 21:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@xoofx xoofx added the bug label Oct 23, 2026
xoofx and others added 2 commits October 23, 2026 21:07
A backslash-escaped & is a literal character, so it must not start an entity reference (CommonMark: '\ö' is not an entity). This matches commonmark.js.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
# Conflicts:
#	src/Markdig.Tests/TestFencedCodeBlocks.cs
@xoofx
xoofx merged commit 18037fe into xoofx:main Oct 23, 2026
3 checks passed
@xoofx

xoofx commented Oct 23, 2026

Copy link
Copy Markdown
Owner

Thank you @HuzaifaChaudary for tracking this one down! 🙏

The fix for the escaped backslash was right. I pushed one small follow-up before merging: an escaped & now stays literal too, instead of still being decoded as an entity. cmark decodes entities before backslash escapes, so it turns \&amp; into &. The spec says a backslash-escaped character is literal (\&ouml; is "not a character entity"), and the commonmark.js reference implementation renders ~~~ \&amp; as language-&amp;amp;. Always skipping the escaped character gives that result and makes the code simpler. I updated the test to match and resolved the conflict with #966 in TestFencedCodeBlocks.cs.

Squash-merged.

Reviewed by CodeAlta + Opus 5.5/Low

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants