Step 1 of 5 · Reading · ~4 min
Read
Lists & Code
Tight vs Loose Lists
Two lists that differ by a single blank line produce different HTML. This is not a rendering quirk you can ignore: it is in the spec, every conforming parser does it, and it is the rule most weekend Markdown parsers get wrong. Your program will read one bullet list and decide, for the whole list, whether to print it tight or loose.
The same items, two shapes
Tight, with no blank line anywhere between the items, and the item text is bare:
<ul>
<li>alpha</li>
<li>beta</li>
</ul>
Loose, with one blank line somewhere between two items, and every item gains a paragraph:
<ul>
<li>
<p>alpha</p>
</li>
<li>
<p>beta</p>
</li>
</ul>
Note what changed: every item gained a <p> wrapper, not just the ones adjacent to the blank line. Looseness is a property of the whole list. That is what makes it awkward to implement: you cannot decide how to render item one until you have read item five.
What exactly makes a list loose?
Two conditions, either one is enough:
- Any two of its items are separated by a blank line.
- Any single item directly contains two block-level elements with a blank line between them. In this exercise that means two paragraphs: a
-line, a blank line, then a line indented by two spaces.
And what does not count is just as important:
| Situation | Verdict |
|---|---|
| blank line between item one and item two | loose |
| an item holding two paragraphs | loose |
| blank line before the first item | tight |
| blank line after the last item | tight: that blank just ends the list |
| indented lines with no blank line between them | tight: they continue the same paragraph |
| blank line inside a sublist | the outer list stays tight and the sublist is loose |
The sublist row catches people. Nesting means a document can hold a tight list whose child list is loose, side by side; the next lesson builds the recursion that makes that possible.
Deciding in one pass
Collect first, decide second, render third. While reading, remember whether you just passed a blank line. When the next non-blank line arrives, look at what it is:
| Held state | Next line | Effect |
|---|---|---|
| blank seen, items exist | - next item | the list is loose |
| blank seen | indented | the item gets a second paragraph; the list is loose |
| no blank | indented | the text joins the current paragraph with a space |
| blank seen | end of input | nothing: trailing blanks never loosen a list |
The last row is the whole trick. A trailing blank sets the flag, but no line ever follows it, so nothing is promoted.
Why the spec bothers
Tight lists render compactly because the item text is not a paragraph and gets no paragraph margin. Loose lists give every item full paragraph spacing, which is what you want when items are several sentences long or contain sub-blocks. The author signals the choice with whitespace; the parser has to honour it.
Common mistakes
- Applying looseness only after the blank line. If the gap is between items two and three, items one and two must be wrapped too.
- Counting the trailing blank line. A list that ends with an empty line is still tight.
- Missing the second paragraph. A blank line inside an item makes the list loose even when the items themselves touch.
- Printing
<p>for tight items. Tight items are bare text.
Your exercise
Render one list, given as - item lines with optional two-space-indented continuation lines, as tight or loose. The starter reads the lines and prints what you return.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…