Skip to content

fix: a four-space indent no longer turns a paragraph into code (#38) - #45

Merged
elvogel merged 1 commit into
mainfrom
fix/38-leading-tabs
Oct 4, 2026
Merged

elvogel merged 1 commit into
mainfrom
fix/38-leading-tabs

Conversation

@elvogel

@elvogel elvogel commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Closes #38 — which filed this as a rendering bug. It is also a text-loss bug, and the issue
said the opposite. Corrected below.

It costs measured coverage

w:tab emits one space, so four or more at a paragraph start produced CommonMark's indented code
block. docmd's oracle re-parses its own Markdown with Markdig and compares word-for-word against
the source, and text inside a spurious code block does not come back as prose:

document main with fix
doc10 (41,220 words) 17 lost 4 lost
doc12 14 lost 14 lost — another cause
the other eleven 0 0

Thirteen words recovered, nothing lost anywhere.

#38 claimed the coverage check couldn't see this. It could. I had only checked a synthetic
fixture, where one short indented paragraph round-trips cleanly, instead of a real document where
those lines sit among thousands — the corpus-versus-fixture argument this repo makes everywhere,
demonstrated against me.

Scope: first line, four spaces

First physical line only. CommonMark does not let an indented code block interrupt a
paragraph, so a continuation line after a hard break is a lazy continuation whatever its
indentation — its whitespace is harmless and is layout the document carries.

Four spaces, not any. docs/deferred-work.md proposed a plain TrimStart on the first line.
That version was written and measured before it was kept:

documents changed lines changed lines that were the defect
broad TrimStart 13 of 13 1,100 25
at CommonMark's threshold 4 of 13 25 25

The other 1,075 were indents of one to three spaces, which CommonMark ignores outright — they
rendered identically before and after. docmd's output is meant to be committed and indexed, so a
reflow of every file is a cost its users pay in review, and 97.7% of it bought nothing. If you
want the full tidy it is a one-line change, and it deserves its own commit rather than riding in
on a bug fix.

Deliberately not symmetrical with TrimTrailingHorizontalWhitespace, which is unconditional:
trailing whitespace is stripped by editors and linters on save, so leaving it makes a
re-conversion look like a diff against nothing. Leading whitespace is ordinary indentation nothing
strips, so that argument does not carry over.

A real example

before (reads as code):  '     B  18-196'
after  (reads as prose): 'B  18-196'

Rows of a code-comparative table, rendered as a monospaced block.

Tests

377 total, 376 pass, 1 skip. The threshold and the scope are pinned in the serialiser, where the
trim lives:

  • ParagraphFirstLine_LosesAnIndentWideEnoughToBeCode
  • ParagraphFirstLine_KeepsAnIndentTooNarrowToBeCode
  • ParagraphContinuationLine_KeepsItsLeadingWhitespace
  • CodeBlock_KeepsItsOwnIndentation
  • AListItemsStructuralIndent_SurvivesTheTrim

One test is removed because it was passing for the wrong reason. It used an
HTMLPreformatted style to build what it called a code block, but md:code-block comes only
from a style-map rule — so it was asserting on an ordinary indented paragraph and would have
stayed green forever while testing nothing about code blocks. Only this change broke it into
honesty.

Not touched

doc10's remaining 4 lost words and doc12's 14, both pre-existing and reported as unattributed.

🤖 Generated with Claude Code

https://claude.ai/code/session_018wZEgtuzaZPswykiGEBxbz

w:tab emits one space, so four or more at a paragraph start produced
CommonMark's indented code block. Filed as a rendering bug. It is also a
TEXT LOSS bug, which the issue got wrong and this measured:

  doc10 of the corpus, 41,220 words     main 17 lost -> 4 lost
  doc12                                 14 lost -> 14 lost (another cause)
  the other eleven                      0 -> 0

Thirteen words recovered, none lost anywhere. The oracle re-parses docmd's own
Markdown with Markdig and compares word for word against the source, and text
inside a spurious code block does not come back as prose. #38 claimed the
coverage check could not see this; it could. I had only checked a synthetic
fixture, where one short indented paragraph round-trips cleanly, instead of a
real document where those lines sit among thousands.

FIRST PHYSICAL LINE ONLY. CommonMark does not let an indented code block
interrupt a paragraph, so a continuation line after a hard break is a lazy
continuation whatever its indentation: its whitespace is harmless and is layout
the document carries.

FOUR SPACES, NOT ANY. docs/deferred-work.md proposed a plain TrimStart on the
first line. That was written and measured before it was kept, and it moved
1,100 lines across all 13 corpus documents of which only 25 were the defect --
the other 1,075 were indents of one to three spaces, which CommonMark ignores
outright, so they rendered identically before and after. docmd's output is
meant to be committed and indexed, and a reflow of every file is a cost its
users pay in review. The threshold cuts it to exactly the defect:

  broad TrimStart    13 of 13 documents, 1,100 lines, 25 of them the bug
  at the threshold    4 of 13 documents,    25 lines, 25 of them the bug

Deliberately NOT symmetrical with TrimTrailingHorizontalWhitespace, which is
unconditional. Trailing whitespace is stripped by editors and linters on save,
so leaving it makes a re-conversion look like a diff against nothing. Leading
whitespace is ordinary indentation nothing strips, so that argument does not
carry over.

Tests pin both sides of the threshold and the scope, in the serialiser where
the trim lives:

  ParagraphFirstLine_LosesAnIndentWideEnoughToBeCode
  ParagraphFirstLine_KeepsAnIndentTooNarrowToBeCode
  ParagraphContinuationLine_KeepsItsLeadingWhitespace
  CodeBlock_KeepsItsOwnIndentation
  AListItemsStructuralIndent_SurvivesTheTrim

One test here was passing for the wrong reason and is removed. It used a
HTMLPreformatted style to build what it called a code block, but md:code-block
comes only from a style-map rule, so it was asserting on an ordinary indented
paragraph and would have stayed green forever while testing nothing about code
blocks. Only this change broke it into honesty.

Still outstanding, pre-existing and not touched here: doc10's remaining 4 lost
words and doc12's 14, both reported as unattributed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018wZEgtuzaZPswykiGEBxbz
@elvogel
elvogel merged commit c34a948 into main Oct 4, 2026
1 check passed
@elvogel
elvogel deleted the fix/38-leading-tabs branch October 4, 2026 02:39
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.

Four or more leading tabs silently turn a paragraph into a code block

1 participant