Skip to content
Lesson 8 of 20

Step 1 of 5 · Reading · ~3 min

Read

Inline Elements

Reference-Style Links

Inline link syntax, [text](url), gets unwieldy when you reuse the same URL many times or when URLs are long. The reference style separates the link text from the destination:

See the [Python docs][py] and the [Python source][py].

[py]: https://docs.python.org/3/

Both uses resolve to <a href="https://docs.python.org/3/">...</a>. This is the first construct in the course where a use can depend on something that appears later in the document, so it forces a design decision: your program makes two passes. The first collects every definition; the second resolves every use. Your program will do both and print the paragraphs with their links resolved.

Three ways to use a definition

FormSourceLookup keyLink text
Full[Python docs][py]pyPython docs
Collapsed[py][]pypy
Shortcut[py]pypy

The shortcut is the dangerous one: plain text in brackets, like a citation [1], is a link if and only if a definition with that label exists, and plain text otherwise. A label with no definition is never an error; the brackets are simply text.

What a definition looks like

[label]: destination "optional title"
  • It sits on its own line, indented by at most three spaces.
  • The label is matched after normalising: lowercase it, trim it, and collapse every run of whitespace to one space. So [Two Words] and [two words] are the same label.
  • The destination is a run of characters with no spaces. The title is optional and double-quoted.
  • If the same label is defined twice, the first definition wins.
  • A definition produces no output of its own: it is removed from the document.

Where may a definition appear?

Anywhere in the document, before or after its uses, with one exception that surprises people: a definition cannot interrupt a paragraph. Compare:

SourceWhat the second line is
Some text, blank line, [x]: /urla definition
Some text then immediately [x]: /urlmore paragraph text

So the first pass has to track whether it is inside a paragraph: a definition is recognised only at the start of a block, which means at the top of the input, after a blank line, or after another definition.

A worked trace

Input: See [docs][d] and [home]., blank, [d]: /docs "Docs", [home]: /.

PassStepResult
1line 1 starts a paragraphkept
1blank linekept, next line starts a block
1[d]: /docs "Docs"stored as d -> (/docs, Docs), removed
1[home]: /stored as home -> (/, no title), removed
2[docs][d]key d is defined: <a href="/docs" title="Docs">docs</a>
2[home]no second bracket pair: shortcut, key home is defined: <a href="/">home</a>

Resolving a use

Look at the first [...] (its label has no [ inside). If a second [...] follows directly, it is the lookup label, and an empty one means "use the first label". Otherwise the first label is the key. If the key is defined, emit the link and skip everything you used. If it is not, copy the [ as text and keep scanning from the next character, so [unknown][nope] comes out exactly as typed.

Destinations and titles are attribute values: escape &, <, > and " in them. The link text is copied as written.

Common mistakes

  • Resolving in one pass. A link near the top of the file whose definition is on the last line stays unresolved.
  • Case-sensitive or whitespace-sensitive labels. [Foo] must find [foo]:.
  • Letting a definition interrupt a paragraph. It is paragraph text, and it must stay in the output.
  • Treating an undefined label as an error. It is plain text.

Your exercise

Write the first pass (extract_refs) and the second (resolve). The starter gives you the definition pattern, label normalisation, escaping, and the paragraph joiner from the paragraphs lesson.

Up nextListsLists & Code

Discussion

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

Sign in to post a comment or reply.

Loading…