Step 1 of 5 · Reading · ~4 min
Read
Block Elements
Setext Headings
Markdown's older heading style underlines the text instead of prefixing it:
Release 2.1
===========
Known issues
------------
Same output as # Release 2.1 and ## Known issues. Only two levels exist: = gives H1, - gives H2, and there is no setext H3. A parser has to support both styles anyway, because a decade of real-world documents contains both. Your program will turn setext headings into <h1> and <h2>, print a bare row of dashes as <hr />, and leave everything else alone.
How long does the underline have to be?
Any length. The spec says "a sequence of = characters or a sequence of - characters" and imposes no minimum, so Release 2.1 followed by a single = is a perfectly valid H1. Length is decoration, not syntax.
What the underline may not contain is internal whitespace: = = is not an underline, and that document is just a paragraph. Up to three spaces of indentation before the underline is fine; four is too many and the line is no longer an underline.
Why is a row of dashes the hardest line in Markdown?
Because --- means three different things depending on what sits above it.
This is the first place in the course where a line cannot be classified on its own. The block scanner needs lookbehind: it has to know what the previous lines turned out to be.
| Line | Text above it? | Result |
|---|---|---|
=== | yes | setext H1 |
=== | no | paragraph text (a lone === is not a break of any kind) |
--- | yes | setext H2 |
--- | no | thematic break |
- | yes | setext H2 |
- | no | an empty bullet item |
What counts as "text above"?
Only lines that would otherwise be a paragraph. A blockquote line, a list item, an ATX heading or another thematic break above the dashes does not get underlined:
| Above the dashes | Result |
|---|---|
Release 2.1 | <h2>Release 2.1</h2> |
> Release 2.1 | the quote line, then a separate <hr /> |
- Release 2.1 | the list item, then a separate <hr /> |
# Release 2.1 | the heading, then a separate <hr /> |
| nothing (start of file or a blank line) | <hr /> |
And the sharpest edge of all: the heading swallows every paragraph line above it, not just one. Foo, then Bar, then --- is a single two-line H2, not a paragraph followed by a heading. If you want them separate, put a blank line between them.
A worked trace
Input lines, one per row. The scanner holds the current run of paragraph lines and looks at the line after the run:
| Lines held | Next line | Decision |
|---|---|---|
Title | ===== | underline: write <h1>Title</h1>, skip both |
Foo, Bar | --- | underline: write <h2>Foo Bar</h2> (course convention: join lines with a space) |
Foo, = = | blank | no underline, write the two lines unchanged |
| (nothing) | --- | thematic break, write <hr /> |
Common mistakes
- Checking for the thematic break first.
paraover---then comes out asparaand<hr />, when the spec wants one H2. - Underlining a single line only. The whole run of paragraph lines above the underline belongs to the heading.
- Forgetting to consume the underline. After you emit the heading, advance past both the text and the underline, or the
===reappears in the output. - Accepting
= =or a four-space indent. Neither is an underline.
Your exercise
Convert setext headings, print <hr /> for a bare thematic break, and pass every other line through unchanged. The starter supplies the classification helpers; you write the loop that groups paragraph lines and decides what follows them.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…