Skip to content

Keep Markdown labels apart under case-insensitive matching - #11

Merged
Maxanox merged 2 commits into
format-0-5-0from
cnd-38-case-safe-markdown-labels
Oct 14, 2026
Merged

Maxanox merged 2 commits into
format-0-5-0from
cnd-38-case-safe-markdown-labels

Conversation

@Maxanox

@Maxanox Maxanox commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Markdown compares link and footnote labels without regard to case. CommonMark and GFM do it with Unicode case folding; markdown-it and other JavaScript readers do it by lowercasing then uppercasing. Two CND labels that differ only by case, such as Fn and fn, or ß and SS, could therefore resolve to one definition.

markdown_label now keeps a letter, digit or -._: only when case folding, lowercasing, and an upper-then-lower round trip all leave it unchanged. Every other character is percent-encoded, as before. Two distinct labels therefore never meet under either comparison. This also covers the dotless ı, which a JavaScript-based reader matches with i.

Testing

  • The tests cover every code point of the Basic Multilingual Plane, used as a one-character label, under both comparisons, plus the known tricky pairs: Kelvin sign, final sigma, ß, ligatures, and ı.
  • An independent review's random collision probe (300k pairs) finds no collisions after the fix.
  • 912 tests pass. The goldens are unchanged.

Visible change

A label with capitals is now percent-encoded in Markdown output. For example, FIG-1 becomes %46%49%47-1. Lower-case labels are unchanged.

Closes CND-38

🤖 Generated with Claude Code

https://claude.ai/code/session_01XxXUiDn2QyzNTYnZpeVFVS

Maxanox and others added 2 commits October 14, 2026 10:36
CommonMark and GFM compare link and footnote labels after Unicode case
folding, so two CND labels that differ only by case (Fn and fn, ß and
SS) could resolve to one definition. markdown_label now keeps a
letter, digit or safe punctuation only when case folding leaves it
unchanged and percent-encodes everything else, so the mapping stays
injective under folding. Goldens are unchanged.

Closes CND-38

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XxXUiDn2QyzNTYnZpeVFVS
…s comparison

markdown-it compares labels with lower() then upper(), not Unicode case
folding, so dotless i and i still met after the previous commit. A
label character is now kept only when case folding, lowering, and an
upper-then-lower round trip all leave it unchanged. Tests sweep the
whole Basic Multilingual Plane under both comparisons.

Part of CND-38

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XxXUiDn2QyzNTYnZpeVFVS
@Maxanox
Maxanox merged commit 3211d2f into format-0-5-0 Oct 14, 2026
3 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.

1 participant