Repository navigation
blockmap: a bracket inside a code span must not arm the spanning guards - #17
Conversation
hasUnbalancedBracket, hasUnclosedDestParen and hasUnclosedAngleDestOpener count delimiters in the raw line, so a "[" inside an inline code span arms them when it straddles a soft line break. Inside a code span the bracket is literal: it cannot open a link label, a reference definition, or a destination, so the paragraph was never the hazard the guards exist for. The cost falls on documentation that describes Markdown or YAML syntax, where such spans are the subject matter. A "runs-on: [self-hosted, <label>]" matrix snippet split across two source lines freezes its whole paragraph at the old wrapping, permanently: the paragraph is skipped because the bracket spans a break, and being skipped is what stops the break being removed. maskCodeSpans follows CommonMark's code-span rule rather than "text between backticks": a run of N backticks opens a span only a run of exactly N closes, newlines included, and a run with no matching closer is literal text left unmasked. That keeps the change scoped to code spans, so every bracket outside one behaves exactly as before. Verified: full suite, the committed fuzz corpus, 4.46M FuzzFormat executions with no failure, and seeds covering multi-line spans, unmatched runs, mismatched run lengths, and a real link outside a span whose bracket sits inside one.
|
Rebased onto main after your four fuzz-find commits. One of them interacts with this PR in a way that is invisible in the diff, so flagging it rather than leaving it implicit.
It is safe as merged only because your guard returns before mine runs. I resolved the conflict to keep that order: if hasBacktickInBareURL(trimmed) {
return Paragraph{}, true
}
masked := maskCodeSpans(trimmed)
if hasUnbalancedBracket(masked) || ...So this PR now depends on Re-verified after the rebase: full suite green, 120s of FuzzFormat with no failures, on this branch and on both stacked follow-ups. |
golangci-lint's misspell check rejects the British spelling; CI runs it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Found while adopting sentence-per-line across a MkDocs docset (263 non-vendored files, 27.8k prose lines). Three PRs came out of that run. This is the first and the least contentious.
Problem
hasUnbalancedBracket,hasUnclosedDestParenandhasUnclosedAngleDestOpenercount delimiters in the raw line. A[inside an inline code span therefore arms them when it straddles a soft line break, even though a bracket in a code span is literal and cannot open a label, a definition, or a destination.Balanced on one line, the same span reflows fine, which is what makes the spanning case look unintended rather than deliberate:
It lands on documentation that describes Markdown or YAML syntax, where such spans are the subject matter rather than an accident.
Proposal
maskCodeSpansreplaces code-span interiors with a filler byte before the delimiter scans, preserving line structure and byte offsets so the existing scans see identical geometry.It follows CommonMark's code-span rule rather than "text between backticks": a run of N backticks opens a span that only a run of exactly N closes, newlines included. A run with no matching closer is literal text and is left unmasked. That last part is load-bearing. It keeps
`unclosed [bracketarming the guards, where there is no span and the bracket really is prose, so the change is scoped strictly to real code spans and every bracket outside one behaves as before.Depends on hasBacktickInBareURL for correctness
maskCodeSpanspairs backtick runs itself, so it shares the blind spot that guard was added for: a backtick consumed into a linkified bare URL is not a delimiter to goldmark, and my scan would pair one out of step on exactly the inputs seed41e98cb4c9e00729covers. It is safe only becausehasBacktickInBareURLreturns before the masking runs, and I resolved the rebase to keep that order. Narrowing or moving that guard later means givingmaskCodeSpansthe same treatment at the same time. Detail in the thread below.Verification
go test ./...green, including the committedFuzzFormatseeds.-fuzz FuzzFormat -fuzztime 180sbefore the rebase: 4,463,558 execs, 890 new interesting, no failures. Re-run for 120s after rebasing onto the four new fuzz-find commits, also clean.TestCodeSpanBracketsDoNotArmGuards, plus seven fuzz seeds covering multi-line spans, unmatched runs, mismatched run lengths, and a real link outside a span whose bracket sits inside one.Out of scope
inLinkRefDefZoneruns ahead of this and stays blunt about definition-shaped lines, so a`[label]: target`in a code span still pulls its paragraph into the zone. One of the new test cases records that interaction so it is not mistaken for the masking failing.I did try making the zone code-span aware. Your fuzzer killed it in under four seconds on
"[08\"z00]:00 0": masking the span removes the]:, the paragraph reflows, reflowing moves the line breaks, and the second pass sees different span boundaries. Not idempotent. That is the "fuzzing kept finding a seventh shape" outcome your own comment describes, so I left the zone alone.Two follow-ups build on this branch and I will send them separately.