Skip to content

fix(emitter): emit plain multi-line scalars as literal blocks#4

Merged
ralflang merged 1 commit into
FRAMEWORK_6_0from
fix/emitter-plain-multiline-uses-literal-block
Jul 1, 2026
Merged

fix(emitter): emit plain multi-line scalars as literal blocks#4
ralflang merged 1 commit into
FRAMEWORK_6_0from
fix/emitter-plain-multiline-uses-literal-block

Conversation

@ralflang

@ralflang ralflang commented Jul 1, 2026

Copy link
Copy Markdown
Member

Summary

A Plain-styled ScalarNode whose string value contains \n used to fall through the Plain → SingleQuoted → DoubleQuoted upgrade ladder in Emitter::emitString and end up as "line1\nline2" — the newlines silently escaped as the two-character sequence \n. That is a round-trip data change for any caller that hands the AST a real multi-line string.

The concrete symptom: horde-components release writes doc/changelog.yml entries via horde/HordeYmlFile, which builds a plain ScalarNode from the multi-line notes string. Recent releases (e.g. horde/imp 7.0.1) show notes: "...\\n..." in the file on GitHub instead of a readable multi-line block.

Fix

Add two helpers on Emitter:

  • shouldUpgradePlainToLiteralBlock(ScalarNode) — returns true only for a Plain-styled node with a string value containing \n, no pinned rawSource, no tag, and content that a literal block can carry safely.
  • emitPlainAsLiteralBlock(ScalarNode, int, bool) — synthesises a | / |- / |+ block. Chomp is picked from the value's trailing newline count.

Wired into emitScalar (root-scalar path) and the ScalarNode branch of emitMapEntry. Pinned SingleQuoted / DoubleQuoted behavior is unchanged.

Gating is conservative — content with \r, other sub-0x20 bytes, lines starting with whitespace, or whitespace-only interior lines still goes to double-quoted, because those would need an explicit indent indicator this change does not synthesize.

Sequence-item scalars (emitSequenceItem) are out of scope. That path has no isBlockStyleScalar branch at all today and belongs to a separate PR.

Verification

  • New test file EmitterPlainMultilineLiteralBlockTest — 12 tests covering the changelog case, chomp-indicator selection, pinned-style / tagged / leading-whitespace / carriage-return / single-line fallbacks, plus a Yaml::load() round-trip.
  • Full suite: 1413 tests, 2253 assertions, all pass.
  • End-to-end sim of the AddChangelogEntryTask payload emits a proper notes: | block and reparses byte-for-byte.

@ralflang ralflang requested a review from TDannhauer July 1, 2026 16:04
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🔍 CI Results

Overall: ❌ 8/8 lanes failed

TL;DR: ❌ Quality issues: PHPUnit: 8 tests failed.

Summary by PHP Version

PHP dev stable
8.2
8.3
8.4
8.5

Quality Metrics

  • PHPUnit: 8 failures, 0 errors out of 1022 tests in 8 lanes ❌
    Per-test detail in the table below. Raw JUnit XML and JSON are uploaded as workflow artifacts.

    Which tests failed
    Type Test Lanes Message
    ❌ failure Horde\Yaml\Test\Integration\ManifestCoverageTest::testManifestHasNoOrphans php8.2-dev, php8.2-stable, php8.3-dev, php8.3-stable, php8.4-dev, php8.4-stable, php8.5-dev, php8.5-stable [PHPUnit\Framework\ExpectationFailedException] Manifest references 391 case(s) no longer in the suite: 229Q, 236B, 26DV, 27NA, 2AUY, 2CMS, 2EBW, 2G84/00, 2G84/01, 2G84/02... Failed asserting that two arrays are identical.
  • PHPStan: No errors found ✅ — 1 advisory at level 2

  • PHP-CS-Fixer: did not run on any lane ❌

❌ Failed Lanes

php8.2-dev

  • PHPUnit: 1 failure, 0 errors

php8.2-stable

  • PHPUnit: 1 failure, 0 errors

php8.3-dev

  • PHPUnit: 1 failure, 0 errors

php8.3-stable

  • PHPUnit: 1 failure, 0 errors

php8.4-dev

  • PHPUnit: 1 failure, 0 errors
  • PHP-CS-Fixer: ❌ Setup failed (No result file found)

php8.4-stable

  • PHPUnit: 1 failure, 0 errors

php8.5-dev

  • PHPUnit: 1 failure, 0 errors

php8.5-stable

  • PHPUnit: 1 failure, 0 errors

CI powered by horde-componentsView full results

@ralflang ralflang merged commit 344d8dc into FRAMEWORK_6_0 Jul 1, 2026
1 check failed
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