expand abbreviations from a definition line

`*[TERM]: expansion` on its own line, PHP Markdown Extra's form, and every
whole-word use in the document becomes <abbr title="…">.

A transformer rather than an inline parser, because a definition may appear
after the use it explains and a parser only ever sees what it has already read.
A block parser rather than a pattern found later, because the line has to stop
being content — an author would notice that going wrong before anything else.

Whole-word matching is the part that would have bitten: without it a definition
of HTML quietly rewrites HTMLish and xHTML too, so both have cases. Code spans,
autolinks, raw HTML and an already-expanded term are skipped, the longest
definition wins where two could match, and the expansion is escaped into the
attribute so a quoted phrase cannot end it.

Definitions are document-scoped. A term defined in a page does not reach an
included fragment, which is parsed on its own bytes exactly as footnotes are —
stated in content-model.md rather than left to be discovered.

The nesting gate caught firstMatch four levels deep; the inner search is its own
function now, which reads better than it did before the warning.

core 2794/2800, ext 1483/2000, 34 gates green, 0 warnings.
This commit is contained in:
Claude Opus 5
2026-08-01 21:25:44 +06:00
committed by bdeshi
parent 53e9ef473a
commit 661fb469e8
10 changed files with 393 additions and 26 deletions
+3
View File
@@ -124,6 +124,9 @@ var exampleFeatures = []featureCase{
{what: "notation marks become elements, and a single tilde is a subscript not a strike", path: "/writing/notes-on-water/", code: 200,
expect: []string{"H<sub>2</sub>O", "10<sup>-3</sup>", "<mark>important</mark>", "<del>a struck phrase</del>"},
absent: []string{"<del>2</del>"}},
{what: "an abbreviation expands and its definition line renders nothing", path: "/writing/notes-on-water/", code: 200,
expect: []string{`<abbr title="National Institute of Water and Atmospheric Research">NIWA</abbr>`},
absent: []string{"*[NIWA]"}},
{what: "the dialect renders tables, definition lists and strikethrough", path: "/writing/notes-on-water/", code: 200,
expect: []string{"<table>", "<th>Gauge</th>", "<dl>", "<dt>Monsoon</dt>", "<del>a struck phrase</del>"}},
{what: "a fragment's footnote ids are namespaced, so the page's own keep working", path: "/writing/notes-on-water/", code: 200,