Skip to content

馃悰 Use the CommonMark whitespace set, not the Python one - #433

Merged
chrisjsewell merged 2 commits into
masterfrom
claude/pr-418
Sep 17, 2026
Merged

chrisjsewell merged 2 commits into
masterfrom
claude/pr-418

Conversation

@chrisjsewell

@chrisjsewell chrisjsewell commented Sep 17, 2026

Copy link
Copy Markdown
Member

Supersedes #418 by @Nexory, whose commit is preserved as the first commit here (the fork does not allow maintainer edits, so it could not be brought up to date in place).

Summary

str.strip() and str.split() without arguments use str.isspace(), which treats U+001C, U+001D, U+001E, U+001F and U+0085 as whitespace. CommonMark and String.prototype.trim do not, so upstream markdown-it keeps those characters. Ten call sites relied on the Python behaviour, with two visible consequences (both measured against markdown-it@14.1.0):

  • normalizeReference folded distinct labels together, so [a\x85b]: url resolved a usage [a b] in Python and not in JavaScript.
  • The characters were dropped from paragraphs, headings, table cells and fence info strings.

This adds MD_TRIM_CHARS and mdTrim() to common/utils and uses them at those sites; the fence renderer splits the info string on the same set. U+FEFF is deliberately excluded, since trim removing it is the same label-folding defect in the other direction. See #418 for the full rationale, including what was deliberately left unchanged (validateLink, the backticks edge rule).

Changes on top of #418

  • The per-call re.sub("[" + re.escape(MD_TRIM_CHARS) + "]+", ...) in normalizeReference and RendererHTML.fence is hoisted to a module-level compiled MD_TRIM_RE.
  • Changelog entry added.

Validation

  • pre-commit (ruff, ruff format, mypy strict) clean.
  • Full suite: 1032 passed (the 15 new cases in tests/test_port/test_whitespace.py plus the existing 1017).
  • Differential render of 47,936 corpus inputs across 7 presets (HTML, inline HTML and token streams) is byte-identical to master; the corpus contains none of the five affected characters, so the only behaviour change is the one the new tests cover.
  • Local Sphinx build with -W and nitpicky passes (only the usual egress-blocked stdlib intersphinx refs).

Nexory and others added 2 commits September 17, 2026 11:29
str.strip() and str.split() without arguments use str.isspace(), which
treats U+001C, U+001D, U+001E, U+001F and U+0085 as whitespace. CommonMark
and String.prototype.trim do not, so upstream markdown-it keeps them. Ten
call sites relied on the Python behaviour, while the module already defines
the correct set a few lines above them in MD_WHITESPACE.

Two consequences, both measured against markdown-it@14.1.0:

- normalizeReference folded distinct labels together, so a link reference
  definition whose label differs from the usage resolved it anyway. The
  input "[a b]" with a definition "[a\x85b]: http://evil" produced a link
  in Python and did not in JavaScript.
- The characters were dropped from paragraphs, headings, table cells and
  fence info strings.

Add MD_TRIM_CHARS and mdTrim() to common/utils, and use them at the sites
that were relying on str. In the fence renderer, replace str.split() with a
split over the same set, for the same reason.

MD_TRIM_CHARS is String.prototype.trim minus U+FEFF. trim() does remove
U+FEFF, and doing the same here would reintroduce exactly the label folding
this change removes.

Deliberately unchanged: validateLink() in common/normalize_url.py also uses
str.strip(), but there the wider Python set is stricter rather than looser,
and aligning it would let more URLs through.
Hoist the per-call regex in normalizeReference and RendererHTML.fence to a
module-level MD_TRIM_RE, and add the changelog entry for #418.
@chrisjsewell
chrisjsewell merged commit 6f58654 into master Sep 17, 2026
13 checks passed
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.

2 participants