Sashmark / Blog

YAML front matter multiline string: three forms in pandoc

Bobby Huang ·

Input lines, the template and the commands sit under The test file with three header values. Under Template output from three readers, the HTML lines pandoc printed are copied from the log. Pandoc's native lines sit under Native output for keep, fold and plain. A second test file and its runs appear under An empty line within the value. The header run without yaml_metadata_block is quoted in full under Plain commonmark with the extension off. Line numbers from pandoc's extension lists are cited under Checking the three extension lists. Numbered steps for repeating the runs are laid out under Running the commands yourself.

Key takeaways

  • For the HTML pandoc wrote, see Template output from three readers.
  • Look under Native output for keep, fold and plain for the tree lines quoted from the log.
  • Under An empty line within the value, a second test file and its output are shown.
  • Turn to Checking the three extension lists for the cited lines and a control run.
  • The section Running the commands yourself gives numbered steps with the pandoc lines to type.

As used here, a yaml front matter multiline string is a header value typed across two or more lines of the file. I wrote one such value three ways: after keep: |, after fold: >, and as a plain value whose second line is indented under the key. Here, yaml block scalar front matter means the first two of those, the forms opened by a | or a >.

On one Mac, I fed the test file to pandoc, whose version line said 3.8.3, under markdown, commonmark+yaml_metadata_block and commonmark_x, plus one run with plain commonmark. I copied each result from the test log and kept pandoc's line breaks where it put them. How an app or a site shows a header value was never part of the test. For a primer first, read the guide to Markdown headers written in YAML.

The test file with three header values

The input is multiline.md. The log gives it a line count of 13:

---
title: Doe plan
keep: |
  Jane Doe owns step one.
  John Doe owns step two.
fold: >
  Jane Doe owns step one.
  John Doe owns step two.
plain: Jane Doe owns step one.
  John Doe owns step two.
---

Body line.

The header holds four keys. title is a short value on one line. keep and fold hold the same two sentences, one under a | and one under a >. The value of plain begins on the key line and carries on to an indented second line. For the separate question of when a header value needs quote marks, see the guide to quoting YAML values.

To print the three values, the runs used a pandoc template, multiline-tpl.html:

keep=[$keep$]
fold=[$fold$]
plain=[$plain$]

In a pandoc template, a key name between dollar signs prints that key's value. The square brackets are literal characters in that file; they mark where a value starts and stops in the output.

These are the three commands, one per reader:

pandoc -f markdown -s --template multiline-tpl.html -t html multiline.md
pandoc -f commonmark+yaml_metadata_block -s --template multiline-tpl.html -t html multiline.md
pandoc -f commonmark_x -s --template multiline-tpl.html -t html multiline.md

In the log, rc=0 follows the output of these three runs.

Template output from three readers

These lines came out under markdown:

keep=[<p>Jane Doe owns step one. John Doe owns step two.</p>]
fold=[<p>Jane Doe owns step one. John Doe owns step two.</p>]
plain=[Jane Doe owns step one. John Doe owns step two.]

Byte for byte, the commonmark+yaml_metadata_block run repeated those three lines, as did the commonmark_x run.

Under these three readers, pandoc wrapped the keep value in a <p> element and did the same for fold. The plain value came back as bare words with no tag around them. In the HTML, keep puts its two sentences on one line, split by a single space, and fold does too. Going by the HTML alone, you can't tell the | value apart from the > value. The native runs below show where they part.

Native output for keep, fold and plain

The extra script asked pandoc for its native format, a text dump of the document tree that includes the header:

pandoc -f markdown -s -t native multiline.md
pandoc -f commonmark+yaml_metadata_block -s -t native multiline.md
pandoc -f commonmark_x -s -t native multiline.md

Pandoc returned rc 0 for these three native runs. The dump is long, so I quote only the lines that matter, taken from the markdown run. The keep entry opens like this:

          , ( "keep"
            , MetaBlocks
                [ Para

Further down that entry, the first sentence meets the second here:

                    , Str "one."
                    , SoftBreak
                    , Str "John"

The fold entry in that run is also a MetaBlocks holding a Para. At the same join, it has a Space:

                    , Str "one."
                    , Space
                    , Str "John"

The plain entry is a MetaInlines list, with no Para around the words:

          , ( "plain"
            , MetaInlines
                [ Str "Jane"

At its join, plain also has a Space between Str "one." and Str "John".

The commonmark+yaml_metadata_block dump and the commonmark_x dump match on these points. Both put a SoftBreak at the join in keep, at log lines 174 and 259, and a Space at the join in fold, at lines 149 and 234. For plain, both show MetaInlines and a Space at the join.

So the | value held a SoftBreak between its two sentences, and the > value held a Space in that spot. Across the three readers, the two values typed MetaBlocks are the two that came back inside <p> tags in the template output, and the one typed MetaInlines came back without a tag.

An empty line within the value

A second file, multiline-blank.md, drops the plain key and inserts one empty line partway through keep and partway through fold, splitting the two sentences. Its count in the log is also 13 lines:

---
title: Doe plan
keep: |
  Jane Doe owns step one.

  John Doe owns step two.
fold: >
  Jane Doe owns step one.

  John Doe owns step two.
---

Body line.

Its template, multiline-blank-tpl.html, prints two values:

keep=[$keep$]
fold=[$fold$]

The commands follow the same pattern as before:

pandoc -f markdown -s --template multiline-blank-tpl.html -t html multiline-blank.md
pandoc -f commonmark+yaml_metadata_block -s --template multiline-blank-tpl.html -t html multiline-blank.md
pandoc -f commonmark_x -s --template multiline-blank-tpl.html -t html multiline-blank.md

Pandoc wrote this for markdown:

keep=[<p>Jane Doe owns step one.</p>
<p>John Doe owns step two.</p>]
fold=[<p>Jane Doe owns step one. John Doe owns step two.</p>]

Output from commonmark+yaml_metadata_block and from commonmark_x matched it line for line, and pandoc returned rc 0 each time. So the gap turned the | value into two <p> elements in this test, while the > value stayed a single <p> with both sentences inside.

Plain commonmark with the extension off

One run in the first script read multiline.md with the plain commonmark reader and no extension added:

pandoc -f commonmark -t html multiline.md

With plain commonmark, pandoc wrote an <hr /> line, then one <h2> element holding the header text, then the body paragraph, and exited with rc 0:

<hr />
<h2>title: Doe plan keep: | Jane Doe owns step one. John Doe owns step
two. fold: &gt; Jane Doe owns step one. John Doe owns step two. plain:
Jane Doe owns step one. John Doe owns step two.</h2>
<p>Body line.</p>

Inside that plain commonmark <h2>, keys and values run together as ordinary words. In the plain commonmark heading, the | is still there as typed, and the > came out as &gt;. That plain commonmark heading spans three output lines, and those breaks fall at other points than the line breaks in the input file.

Checking the three extension lists

Pandoc's extension lists for the three readers were saved from the same 3.8.3 build, one file per reader. I searched those files for yaml_metadata_block:

  1. For markdown, line 73 of the list says +yaml_metadata_block.
  2. For commonmark, line 30 of the list says -yaml_metadata_block.
  3. For commonmark_x, line 30 of the list says +yaml_metadata_block.

In these files, a plus before a name marks an extension the reader turns on by default, and a minus marks one it leaves off by default. That fits the runs: commonmark+yaml_metadata_block printed the three values through the template, and plain commonmark printed the <h2> you saw above.

The first script also ran a control, to show that pandoc checks extension names. With commonmark+bogus_ext as the reader, it stopped with rc 23 and printed The extension bogus_ext is not supported for commonmark. on its first line.

Running the commands yourself

The commands below give bare file names, so work inside the directory where you saved the files.

  1. Save multiline.md and multiline-tpl.html with the lines shown under The test file with three header values.
  2. For the template output, type pandoc -f markdown -s --template multiline-tpl.html -t html multiline.md, then the two other readers, and compare what you get with Template output from three readers.
  3. For the tree dump, type pandoc -f markdown -s -t native multiline.md and find the keep entry. Look for SoftBreak after Str "one.".
  4. Save multiline-blank.md and multiline-blank-tpl.html, then type pandoc -f markdown -s --template multiline-blank-tpl.html -t html multiline-blank.md and count the <p> tags in the keep value.
  5. Last, repeat the one command shown under Plain commonmark with the extension off, then look at the header lines.

I ran a single pass under 3.8.3 and tried no other pandoc version. For keys set inside other keys, read the sibling post on nested keys in YAML front matter.

Where Sashmark fits

You can't get Sashmark yet; its builders are still putting the desktop app together. The plan for its Round-Trip feature is easy to state: change one word in the formatted text, and the saved file should differ by that word and not one byte more. Its Changes View idea is to put a mark beside every stretch an agent redid since your previous visit, git repo or plain folder.

It is in development and has not been released. The app itself has no AI features. See the planned features, the help pages and the roadmap for where each part stands.

More at sashmark.com.

Questions

Does a value written after | keep its line break?
Compare your file with the tree lines quoted under Native output for keep, fold and plain.
What happens to an empty line inside a > value?
The runs for that file are reported in An empty line within the value.
Why did my header come out as a heading?
Check your reader against the run in Plain commonmark with the extension off.
Which pandoc readers have yaml_metadata_block switched on?
Pandoc's list lines are cited in Checking the three extension lists.

What Sashmark plans for v1: Features · Roadmap

View as Markdown