fix(tokenizer): keep lone markup-opener chars as plain text - #6
Merged
Merged
Conversation
A markup-opener character (* ` ~ ! [) that does NOT begin a
recognized markup span was emitted as a 1-character "stray" token.
`markdown-table-wrap-cell' then rejoined tokens with single spaces
and re-wrapped, corrupting cells that contained a lone opener char.
Concretely, with a column width equal to the cell's natural width:
cell | before (buggy) | after (fixed)
-------------|-------------------------|-----------------
~68 | ("~ 68") spurious space | ("~68")
~36 hot | ("~ 36" "hot") 2 lines | ("~36 hot")
[raylib] | ("[" "raylib]") 2 lines | ("[raylib]")
[Zig] | ("[ Zig]") spurious space| ("[Zig]")
Root cause: `markdown-table-wrap--tokenize-cell-text' excluded the
opener chars (* ` ~ ! [) from plain-text runs and fell through to a
"stray markup char" branch that consumed exactly one character.
A bare `[label]' (no `(url)'), a lone `~' (not `~~'), or a stray
`*' / `!' thus split off its opener char as its own token.
Fix: in the plain-text branch, consume until whitespace OR a real
span start. Since every recognized span begins with one of those
opener chars, we only need to re-check `span-re' when we encounter
one; a real span embedded later in the run (e.g. `foo[bar](url)')
still tokenizes as a span. A lone opener that begins no span is
absorbed into the surrounding plain-text token.
Real markup spans (`**bold**', `*it*', `~~strike~~', `` `code' ``,
`[text](url)', `', `***bi***', `*a **b** c*') are
unaffected: they still tokenize as single span tokens (covered by
new regression tests).
Adds 12 tests under "Tokenizer: Lone markup-opener characters" and
"End-to-end: lone-opener cells" verifying tokenization and cell
wrapping for the bug cases and the no-regression spans. Full suite
passes (165 tests, 1 pre-existing expected failure).
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
Fixes a tokenizer bug where a lone markup-opener character (
*`~![) that does not begin a recognized markup span was emitted as its own 1-character token.markdown-table-wrap-cellthen rejoined tokens with single spaces and re-wrapped, corrupting cells that happened to contain one of these characters.The classic victims: bare bracketed labels like
[Zig]/[raylib], approximations like~68/~36 hot/~29–32, and any cell with a stray*or!.The bug, concretely
With a column width equal to the cell's natural width,
markdown-table-wrap-cellproduced spurious spaces and/or unwanted line breaks:~68("~ 68")— spurious space("~68")~36 hot("~ 36" "hot")— split across 2 lines("~36 hot")[raylib]("[" "raylib]")— torn across 2 lines("[raylib]")[Zig]("[ Zig]")— spurious space("[Zig]")A bracketed label like
[raylib](8 cols wide) assigned to an 8-col column would render as:instead of the obvious:
Root cause
markdown-table-wrap--tokenize-cell-textexcluded the opener chars (*`~![) from plain-text runs and fell through to a "stray markup char" branch that consumed exactly one character:So
[raylib]→ tokens("[" "raylib]"), and~68→("~" "68"). The opener char became a 1-char token even though it began no real span ([label]has no(url),~is not~~).The fix
In the plain-text branch, consume until whitespace or a real span start. Since every recognized span begins with one of those opener chars, we only re-check
span-rewhen we encounter one — so a real span embedded later in the run (e.g.foo[bar](url)) still tokenizes as a span. A lone opener that begins no span is absorbed into the surrounding plain-text token.No regression for real markup spans
Recognized markup spans are unaffected — they still tokenize as single span tokens (verified by
markdown-table-wrap-test-tokenize-real-spans-unaffected):**bold**("**bold**")*it*("*it*")~~strike~~("~~strike~~")`code`("code")[t](u)("[t](u)")("")***bi***("***bi***")*a **b** c*("*a **b** c*")foo[bar](url)("foo" "[bar](url)")Tests
Adds 12 tests under two new sections:
Tokenizer: Lone markup-opener characters(8 tests) — verify~,[,*,!that begin no span stay in the plain run; real spans after plain runs still split correctly; real spans are not collapsed.End-to-end: lone-opener cells(4 tests) — verifymarkdown-table-wrap-cellrenders~68,[raylib],~36 hot,[Zig]without spurious spaces or splits.Each lone-opener test documents the exact before/after behavior in its docstring.
The single expected failure (
markdown-table-wrap-test-e2e-no-orphan-markup) is pre-existing and documents known CJK/italic limitations unrelated to this change.