Skip to content
Nested Containers
step 1/5

Reading — step 1 of 5

Learn

~3 min readLists & Code

Nested Containers

The blockquote lesson said a container is parsed by stripping its marker and running the block parser again on what is left. Every exercise so far has let you get away with not doing that — one loop over the lines, one branch per construct, done. This lesson is where that stops working, because a list inside a quote inside a list has no fixed line shape for a loop to match.

What makes a block a container?

Only two blocks in CommonMark hold other blocks: the block quote and the list item. Everything else — paragraphs, headings, code, thematic breaks — is a leaf, holding text or nothing. So the document is a tree, and the two container types are its only branch points.

Rendering diagram…

That shape comes from four lines of Markdown: a bullet, its text, then an indented quote line carrying another bullet. Each level of the tree is one marker peeled off the front of a line.

How much of the line is the marker?

For a block quote: up to three spaces of indent, a >, and one optional space. Strip exactly that and hand the remainder back to the parser.

python

After one peel the inner lines are an ordinary bullet list, and the same function that parsed the document parses them. No branch anywhere says "list inside a quote" — that case simply cannot come up.

For a list item the marker is not a fixed width. The rule is the item's content column: the marker plus the 1 to 4 spaces after it. Continuation lines belong to the item when they reach that column.

python

A fixed "nested lists indent by two" rule gets row two wrong by two columns, which is how sublists silently become paragraphs of the parent item.

Why a flat scanner cannot do this

A line-oriented loop asks "what is this line?" — but in a container the answer depends on how many markers have already been stripped, and by how much. > - x is a quote line at depth 0, a bullet at depth 1, and text at depth 2. The only way to keep that straight in a flat loop is a stack of open containers plus per-line bookkeeping of which ones still match, which is exactly the recursion written out by hand and harder to get right.

Recursion also gives you the leaf rules for free. A blank line inside an item belongs to that item, not to the document, because the item's slice of lines is all its parse ever sees.

Your exercise

Write the recursive parser: quotes, bullets, paragraphs, any depth.

The mistake the grader catches is flattening a nested list to the top level. A visible test feeds - outer and - inner; the inner <ul> has to sit inside the outer <li>, so the expected output closes </ul> before </li>. Collect the item's own lines first, dedent them by two, then recurse — do not dispatch on the indented line from the outer loop.

The second is the paragraph wrapper. Inside a list item a paragraph emits its text bare, which is what makes the list render tight, while a paragraph inside a block quote keeps its <p>. One flag, passed down through the recursion, decides which.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…

Nested Containers — Build a Markdown to HTML Converter