Skip to content

presenter: treat a tab-indented line in a folded scalar as more-indented - #780

Merged
puzrin merged 1 commit into
nodeca:masterfrom
gaoflow:fix-folded-scalar-tab-more-indented
Aug 1, 2026
Merged

presenter: treat a tab-indented line in a folded scalar as more-indented#780
puzrin merged 1 commit into
nodeca:masterfrom
gaoflow:fix-folded-scalar-tab-more-indented

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Round-tripping

k: >
  <TAB>
  detected

through parseEventseventsToAstpresent emits a blank line before detected, so the value goes from "\t\ndetected\n" to "\t\n\ndetected\n"; with the tab line between two folded lines it costs two extra newlines. YAML 1.2.2 §8.1.3 defines a more-indented line by production [175] s-nb-spaced-text, whose leading [33] s-white is a space or a tab, and whose surrounding breaks are preserved ([177] b-l-spaced) rather than folded — getBlockValue() in the parser tests for both (0x20 || 0x09), but the presenter tested only for a space, at four sites in foldBlockScalar/foldLine, including the fold-point regexp / [^ ]/ which would break at the space in aaa… \tzzz and move the tab to the start of the next line. Enumerating every two-line block-scalar body over a 7-line alphabet in both block styles gave 84 parseable sources, of which 12 changed value through present() — all folded style, all involving a tab, literal style clean at 0/42; all 12 were confirmed against PyYAML 6.0.3 and ruamel.yaml 0.19.1 reading the emitted bytes, and all 12 round-trip after this change.

To be straight about the blast radius: dump() is not affected, since it routes tab-containing strings to double-quoted or literal style and never enters the folded path (0 of 41 distinct values from the same family broke either way), so this only reaches code that presents a parsed AST — exported and typed, but not documented in the README. I left divergedFixtures alone: R4YG's dump comparison still fails after the fix because the suite double-quotes that scalar while js-yaml faithfully keeps >, which is a genuine style divergence. (Its label there is "missing block scalar indent indicator", but a leading tab needs no indicator — indentation auto-detection only counts spaces — so R4YG was really this bug.) npm test goes from 1613 tests / 1600 pass / 0 fail to 1614 / 1601 / 0 fail with the added case, and reverting each of the four predicates on its own makes that case fail.

YAML 1.2.2 8.1.3 defines a more-indented ("spaced text") line by production
[175] s-nb-spaced-text, whose leading [33] s-white is a space *or a tab*; the
breaks around such a line are preserved verbatim ([177] b-l-spaced) rather than
folded. getBlockValue() in the parser already tests for both (0x20 or 0x09),
but the presenter tested only for a space, at four sites in foldBlockScalar()
and foldLine(). It therefore doubled breaks the parser keeps literally, adding
one \n per adjacency:

  parseEvents/eventsToAst/present of

    k: >
      <TAB>
      detected

  emitted a blank line before "detected", so the value went from
  "\t\ndetected\n" to "\t\n\ndetected\n". A tab line between two folded lines
  cost two extra \n.

Extract the predicate as isMoreIndented() and use it for the prevMoreIndented
seed, the per-line test and the foldLine guard; the fold-point regexp becomes
/ [^ \t]/ so a break is never placed before a tab either (breaking at the space
in "aaa... \tzzz" moved the tab to the start of the next line).

Enumerating every two-line block-scalar body over a 7-line alphabet in both
block styles gave 84 parseable sources: 12 changed value through
present(), all folded style, all involving a tab; literal style was clean
(0/42). All 12 were confirmed against PyYAML 6.0.3 and ruamel.yaml 0.19.1
reading the emitted bytes, and all 12 round-trip after this change. The public
dump() API never enters this path (it quotes tab-containing strings), so it was
unaffected before and after.
@puzrin
puzrin merged commit 00209b6 into nodeca:master Aug 1, 2026
1 check passed
puzrin added a commit that referenced this pull request Aug 1, 2026
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.

3 participants