Skip to content

A | block scalar loses its trailing newline when it is the last key in the frontmatter (and CombineFrontmatter rewrites it as >-) #153

Description

@matt-edmondson

What's wrong

The text handed to the YAML parser has its final line ending removed, so the same YAML parses differently depending on where a key sits:

  • TrySplitFrontmatterBlocks slices each block to lines[close - 1].End (Frontmatter/Frontmatter.cs, ~lines 385–387), which excludes the last line's line ending.
  • The parse path then does string section = block.Trim(); (~line 329).

A literal (|) or keep (|+) block scalar on the last key therefore loses its trailing newline(s).

Repro

Frontmatter.ExtractFrontmatter("---\ntitle: T\nnotes: |\n  line1\n  line2\n---\nbody\n");
// notes = "line1\nline2"      <- clip chomping should give "line1\nline2\n"

Frontmatter.ExtractFrontmatter("---\nnotes: |\n  line1\n  line2\ntitle: T\n---\nbody\n");
// notes = "line1\nline2\n"    <- correct, only because another key follows

// keep chomping: "---\ncode: |+\n  x = 1\n\n\n---\nbody\n"
// code = "x = 1"   (YamlDotNet on the same YAML directly gives "x = 1\n\n\n")

CombineFrontmatter on the first document writes the value back as

notes: >-
  line1

  line2

so the trailing newline is permanently lost from the file, and the scalar style changes on what should be a no-op round trip.

Why it matters

Moving a key, or adding one after it, changes its value. Consumers that rely on a trailing newline (code snippets, |+ content, text compared byte-for-byte) get different data. Every CombineFrontmatter pass also rewrites the file.

Suggested fix

Parse the block with its final line ending kept: slice to lines[close].Start instead of lines[close - 1].End. Use string.IsNullOrWhiteSpace only for the empty-block check, and don't Trim() the text that gets parsed. Leading Trim() also strips first-line indentation, which would break uniformly indented frontmatter.

Acceptance criteria

  • notes parses to "line1\nline2\n" regardless of key position.
  • The |+ example yields "x = 1\n\n\n".
  • Combine with AsIs naming and no merging leaves a trailing | block's value unchanged.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions