Markdown heading not working after paragraph text
Bobby Huang ·
Under What the markdown reader did with hb.md, the single paragraph pandoc wrote for a hash line with text right above it is quoted. The section A blank line above the heading quotes pandoc's output for hb-blank.md. Pandoc's results once the extension was dropped sit in Turning off blank_before_header. Commonmark output for both test files, with the spec lines it matches, is in What commonmark and the spec say. Reader output for a sentence wrapped onto a hash line appears in A wrapped sentence and a stray hash. Steps to locate and repair the line are listed in Find and fix the heading.
Key takeaways
- Pandoc's one-paragraph result for hb.md is quoted under What the markdown reader did with hb.md.
- An h1 carrying the id notes is shown in A blank line above the heading.
- Runs with the extension removed are quoted in Turning off blank_before_header.
- Spec line numbers and commonmark output are paired in What commonmark and the spec say.
- Grep and pandoc commands that test a file are set out in Find and fix the heading.
When a markdown heading is not working after paragraph text, check the line sitting just above the hash. In pandoc's markdown reader, a # Notes line placed directly beneath a sentence stayed part of that sentence's paragraph in this test.
Each test here ran a single time on one Mac; the converter was pandoc 3.8.3. Quoted here are the elements pandoc wrote; nothing beyond that HTML got checked. For how blank lines split paragraphs in general, see the post on paragraphs and blank lines.
The test files
Running cat hb.md printed:
Jane Doe wrote this line.
# Notes
More text.
The second file, hb-blank.md, adds one empty line between the first line and # Notes. Running cat hb-blank.md printed:
Jane Doe wrote this line.
# Notes
More text.
Each file was converted under three reader settings: markdown, markdown-blank_before_header and commonmark.
What the markdown reader did with hb.md
The command:
pandoc -f markdown -t html hb.md
Output:
<p>Jane Doe wrote this line. # Notes More text.</p>
Pandoc wrote one p element and no h1. The hash sign, the space and the word Notes came through as paragraph text, and the line breaks around them turned into spaces. The third line, More text., joined the same paragraph.
Asking pandoc --list-extensions=markdown for its list printed a +blank_before_header line here, so that extension was on for the run above.
A level 2 line acted the same way. In hb-h2.md, where ## Notes sits under the sentence, markdown output read <p>Jane Doe wrote this line. ## Notes More text.</p>.
A blank line above the heading
The same reader on the second file:
pandoc -f markdown -t html hb-blank.md
Output:
<p>Jane Doe wrote this line.</p>
<h1 id="notes">Notes</h1>
<p>More text.</p>
The empty line split the file into three blocks. Pandoc wrote a paragraph, an h1 with the id notes, and a second paragraph. No blank line sat between # Notes and More text., yet pandoc still gave More text. a paragraph of its own. In this test, one blank line above # Notes was enough, and the line below could stay as it was.
Turning off blank_before_header
A minus sign after the reader name removes an extension for that run. This one took blank_before_header out:
pandoc -f markdown-blank_before_header -t html hb.md
Output:
<p>Jane Doe wrote this line.</p>
<h1 id="notes">Notes</h1>
<p>More text.</p>
Those three lines match what hb-blank.md gave in the section above, and the source file stayed untouched. Run on hb-blank.md, this setting printed those three lines again. On hb-h2.md, it wrote <h2 id="notes">Notes</h2> between two paragraphs.
What commonmark and the spec say
Plain commonmark ran on hb.md:
pandoc -f commonmark -t html hb.md
Output:
<p>Jane Doe wrote this line.</p>
<h1>Notes</h1>
<p>More text.</p>
Under commonmark, hb-blank.md gave those same three lines. Pandoc put no id on the h1 in either run.
ATX headings, in the words of spec lines 1209-1210 (CommonMark 0.31.2), "need not be separated from surrounding content by blank lines, and they can interrupt paragraphs". Its example 78 (lines 1222-1230) puts # baz straight under Foo bar and lists <h1>baz</h1> in the output.
Searching that spec text for blank_before_header with a fixed-string, case-insensitive grep returned 0, and so did a search for blank before. For a control, ATX heading under that grep returned 6. So here, the need for a blank line came from pandoc's markdown reader.
A wrapped sentence and a stray hash
The extension cuts both ways. The file hb-wrap.md holds a sentence split over two lines, and its second line happens to open with a hash and a space:
Jane Doe ranked the plan
# 1 on the list.
Under markdown, pandoc kept the sentence whole: <p>Jane Doe ranked the plan # 1 on the list.</p>. Under commonmark, the second line turned into a heading:
<p>Jane Doe ranked the plan</p>
<h1>1 on the list.</h1>
With markdown-blank_before_header, pandoc wrote a heading as well, <h1 id="on-the-list.">1 on the list.</h1>. The leading 1 was left out of that id.
So before you add a blank line, ask whether the hash line was meant to be a heading at all. If it belongs to a sentence, join it back onto the line above. A text line that opens with a hash and a space became an h1 under two of the three settings here.
Find and fix the heading
Work down this list when a heading line ends up stuck inside a paragraph.
- Print every hash line together with its previous line.
grep -n -B1 '^#' hb.mdprinted1-Jane Doe wrote this line.and then2:# Notes. Text right above a hash line is the case this post tests. - Run that search on the repaired file. For
hb-blank.mdit printed2-and then3:# Notes, so line 2 was empty. - Count headings in pandoc's output. Running
pandoc -f markdown -t html hb.md -o hb.htmland thengrep -c '<h1' hb.htmlprinted0. The same pair of commands onhb-blank.mdprinted1. - Put a blank line in the source directly before the heading. If you can't edit the file, add
-blank_before_headerto the reader name, as the section Turning off blank_before_header shows. - Let pandoc write the blank lines.
pandoc -f commonmark -t markdown hb.mdprinted the file back with an empty line before and after# Notes. Under-f markdown, that command printedJane Doe wrote this line. \# Notes More text., where a backslash now sat before the hash.
A hash lacking a space after it, as in #Notes, is a separate cause this post leaves aside. For that slip, see the syntax cheat sheet.
Where Sashmark fits
Under its v1 plan, Sashmark could have you retouch a sentence while seeing the page rendered. Saving would then rely on the plan's own clean Round-Trip piece: any line left untouched would go back to disk with identical bytes. Another planned part, a Changes View, would show you each spot that moved since your earlier pass through the file, and git would not be needed.
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
- Why did my heading stay inside the paragraph?
- The single p element pandoc wrote for that file is quoted under What the markdown reader did with hb.md.
- Is the blank line needed under commonmark?
- Under What commonmark and the spec say, its output for hb.md and hb-blank.md is quoted.
- What if there is no space after the hash?
- The section Find and fix the heading links the cheat sheet for a hash lacking a space after it.
- How do I repair the line?
- Five steps using grep and pandoc are listed in Find and fix the heading.