perf: Rewriting convert_deflists for performance.

This commit is contained in:
Spencer Brower
2026-09-02 14:59:04 -04:00
parent 0392bfe587
commit 9f7db3a916
41 changed files with 688 additions and 117 deletions
+60
View File
@@ -0,0 +1,60 @@
# 3-way behavior comparison: ours vs Pandoc vs Hugo
Captured for later review (e.g. if we ever pursue "Option B" — full CommonMark
block semantics inside definitions). Compares `convert_deflists` output (Option A,
inline/flat) against Pandoc (`-f markdown -t html5`) and Hugo/Goldmark for the
four fixtures where behavior is interesting. Raw renderings live in `pandoc/` and
`hugo/`; ours is in `../expected/`. Whitespace/formatting differences ignored
below — only structural differences are described.
## 03-multiline-def — definition with a continuation line + a second paragraph
*How much multi-line content belongs to the `<dd>`?*
- **Ours:** only the **first line** goes in the `<dd>`. The continuation line and
the second paragraph fall out of the `<dl>` as **raw, unrendered markdown**.
- **Pandoc:** folds the continuation line **into** the `<dd>` (lazy continuation),
then **ejects** the second paragraph as a sibling `<p>` after `</dl>`.
- **Hugo:** keeps **everything inside** the `<dd>` — continuation line as text,
second paragraph as a nested `<p>`.
- All three differ. We capture the least, Pandoc the middle, Hugo the most.
## 04-markdown-in-def — definition containing a bullet list (with a blockquote)
*What happens to the nested list?*
- **Ours:** intro line becomes the `<dd>`; the **entire bullet list falls out as
raw markdown** (unrendered `- …` lines after `</dl>`).
- **Pandoc:** bullets **flattened into the `<dd>`** as inline text — no `<ul>`, the
`-` become literal dashes.
- **Hugo:** bullets become a **real nested `<ul>`/`<li>`** (blockquote and all)
inside the `<dd>`.
- All three differ; same shape as 03. We drop it, Pandoc absorbs-but-flattens,
Hugo builds true structure.
## 05-nested — a definition list nested under another definition
*Nest or flatten?*
- **Ours:** **flatten** — one flat `<dl>`; "Inner Term" is just another `<dt>`.
- **Pandoc:** **flatten** — structurally **identical to ours**.
- **Hugo:** **nests** a real inner `<dl>` inside the outer `<dd>`.
- Ours == Pandoc; Hugo is the odd one out.
## 07-extra-space — term then a 2-space-indented `: Definition`
*Is a 2-space colon a definition marker?*
- **Ours:** **yes** → clean `<dl>`.
- **Pandoc:** **yes** → structurally **identical to ours**.
- **Hugo:** **no** → rejects it, emits a plain `<p>`.
- Ours == Pandoc; Hugo is the odd one out.
## Pattern
Two clean groups:
- **Marker / flattening decisions (05, 07):** we land **exactly on Pandoc**. These
are the calls our CommonMark-marker + flat-inline design makes cleanly; Pandoc
agrees, Hugo diverges (nests / stricter marker).
- **Block-content-in-a-definition (03, 04):** we diverge from **both** tools, doing
**less** — we emit the `<dl>` for the term/first line and let the rest fall
through as raw markdown, rather than absorbing it into the `<dd>` (flattened like
Pandoc, or structured like Hugo).
In short: our behavior is **"Pandoc, minus the ability to pull block content into
a `<dd>`."** Single inline-line definitions (01, 02, 05, 07, 08, 09) match a real
engine; genuinely multi-block definitions (03, 04) stop at the first line and pass
the remainder through. Closing that gap is what Option B would entail.
+44
View File
@@ -0,0 +1,44 @@
# Definition-list test fixtures
Inputs (`../NN-name.md`) are the definition-list examples from
<https://www.markdownlang.com/extended/definition-lists.html>.
`pandoc/` and `hugo/` hold each input rendered by Pandoc (`-f markdown -t html5`)
and Hugo (Goldmark with `markup.goldmark.extensions.definitionList = true`).
They are retained as reference for a possible future "Option B" (full CommonMark
block semantics inside definitions). They are NOT the test oracle for the current
implementation.
`../expected/NN.html` is the oracle for `convert_deflists`: our inline fragment
format (no inter-tag newlines, no trailing newline), derived from the trusted
tool per the decisions below.
## Decisions (Option A — simple / inline)
Marker whitespace follows CommonMark: `:` with 0–3 spaces of indent is a
definition; 4+ (tab = 4) is a code block. Definition bodies are inline/flat.
Every fixture has a concrete exact-match oracle in `../expected/NN.html`.
| Fixture | Pandoc | Hugo | Decision | expected/ source |
|---------|--------|------|----------|------------------|
| 01-basic | `<dl>` | `<dl>` | support | hugo (agreed) |
| 02-multiple-defs | multi `<dd>` | multi `<dd>` | support | hugo (agreed) |
| 06-compact (`Term: Definition`) | `<p>` | `<p>` | NOT a deflist → passthrough | raw input |
| 07-extra-space (2-space `:`) | `<dl>` | `<p>` | support (accept, CommonMark ≤3-space rule) | hand-written |
| 08-glossary | `<dl>` | `<dl>` | support | hugo (agreed) |
| 09-api-params | multi `<dd>` | multi `<dd>` | support | hugo (agreed) |
| 05-nested | flattens | nested `<dl>` | flatten (matches Pandoc) | snapshot (== pandoc) |
| 03-multiline-def | ejects 2nd para | nests 2nd para in `<dd>` | degrade | snapshot (current output) |
| 04-markdown-in-def | flattens list | nested `<ul>`/`<blockquote>` | degrade | snapshot (current output) |
A detailed 3-way behavior comparison (ours vs Pandoc vs Hugo) for the diverging
fixtures is in `COMPARISON.md`, kept for Option-B review.
03/04/05 require block-level content inside `<dd>` (multi-paragraph, nested
lists/blockquotes, nested `<dl>`), which the inline preprocessor does not
produce. 05 flattens exactly as Pandoc does, so its snapshot equals Pandoc's
output. 03/04 emit a `<dl>` for the term and first line, then let the remaining
block content fall through as markdown; their `expected/` files snapshot that
current degraded behavior as a regression guard, not an endorsed rendering.
Revisit via Option B (full CommonMark block semantics) if real content needs it.
@@ -0,0 +1,4 @@
<dl>
<dt>Term</dt>
<dd>Definition content</dd>
</dl>
@@ -0,0 +1,8 @@
<dl>
<dt>Markdown</dt>
<dd>A lightweight markup language</dd>
<dd>Created by John Gruber in 2004</dd>
<dt>HTML</dt>
<dd>A standard markup language used to create web pages</dd>
<dd>Used to create web pages</dd>
</dl>
@@ -0,0 +1,7 @@
<dl>
<dt>Term</dt>
<dd>This is the first line of definition content
This is the second line, needs at least one space indentation
<p>This is a new paragraph, needs at least one space indentation and a preceding blank line</p>
</dd>
</dl>
@@ -0,0 +1,16 @@
<dl>
<dt>Markdown Syntax</dt>
<dd><strong>Markdown</strong> supports multiple text formatting:
<ul>
<li><em>Italic</em> and <strong>Bold</strong></li>
<li><a href="https://www.markdownlang.com">Link</a></li>
<li><code>Inline code</code></li>
<li>
<blockquote>
<p>Block quote</p>
</blockquote>
</li>
<li>Lists and other elements</li>
</ul>
</dd>
</dl>
@@ -0,0 +1,10 @@
<dl>
<dt>Outer Term</dt>
<dd>Outer Definition
<dl>
<dt>Inner Term</dt>
<dd>Inner Definition</dd>
<dd>Another Inner Definition</dd>
</dl>
</dd>
</dl>
@@ -0,0 +1 @@
<p>Term: Definition</p>
@@ -0,0 +1,2 @@
<p>Term
: Definition</p>
@@ -0,0 +1,10 @@
<dl>
<dt>API</dt>
<dd>An application programming interface, a set of rules that allow different software applications to communicate with each other.</dd>
<dt>Framework</dt>
<dd>A software library that provides a standardized structure for application development.</dd>
<dt>Git</dt>
<dd>A distributed version control system used to track changes in a project development process.</dd>
<dt>IDE</dt>
<dd>An integrated development environment, a software application that includes a code editor and various development tools.</dd>
</dl>
@@ -0,0 +1,14 @@
<dl>
<dt>user_id</dt>
<dd><strong>Required</strong> - The unique identifier for the user.</dd>
<dd>Type: <code>integer</code></dd>
<dt>name</dt>
<dd><strong>Optional</strong> - The display name for the user.</dd>
<dd>Type: <code>string</code></dd>
<dd>Default: <code>null</code></dd>
<dt>status</dt>
<dd><strong>Optional</strong> - The user status.</dd>
<dd>Type: <code>string</code></dd>
<dd>Allowed values: <code>active</code>, <code>inactive</code>, <code>suspended</code></dd>
<dd>Default: <code>active</code></dd>
</dl>
@@ -0,0 +1,6 @@
<dl>
<dt>Term</dt>
<dd>
Definition content
</dd>
</dl>
@@ -0,0 +1,16 @@
<dl>
<dt>Markdown</dt>
<dd>
A lightweight markup language
</dd>
<dd>
Created by John Gruber in 2004
</dd>
<dt>HTML</dt>
<dd>
A standard markup language used to create web pages
</dd>
<dd>
Used to create web pages
</dd>
</dl>
@@ -0,0 +1,9 @@
<dl>
<dt>Term</dt>
<dd>
This is the first line of definition content This is the second line,
needs at least one space indentation
</dd>
</dl>
<p>This is a new paragraph, needs at least one space indentation and a
preceding blank line</p>
@@ -0,0 +1,9 @@
<dl>
<dt>Markdown Syntax</dt>
<dd>
<strong>Markdown</strong> supports multiple text formatting: -
<em>Italic</em> and <strong>Bold</strong> - <a
href="https://www.markdownlang.com">Link</a> - <code>Inline code</code>
- &gt; Block quote - Lists and other elements
</dd>
</dl>
@@ -0,0 +1,13 @@
<dl>
<dt>Outer Term</dt>
<dd>
Outer Definition
</dd>
<dt>Inner Term</dt>
<dd>
Inner Definition
</dd>
<dd>
Another Inner Definition
</dd>
</dl>
@@ -0,0 +1 @@
<p>Term: Definition</p>
@@ -0,0 +1,6 @@
<dl>
<dt>Term</dt>
<dd>
Definition
</dd>
</dl>
@@ -0,0 +1,22 @@
<dl>
<dt>API</dt>
<dd>
An application programming interface, a set of rules that allow
different software applications to communicate with each other.
</dd>
<dt>Framework</dt>
<dd>
A software library that provides a standardized structure for
application development.
</dd>
<dt>Git</dt>
<dd>
A distributed version control system used to track changes in a project
development process.
</dd>
<dt>IDE</dt>
<dd>
An integrated development environment, a software application that
includes a code editor and various development tools.
</dd>
</dl>
@@ -0,0 +1,33 @@
<dl>
<dt>user_id</dt>
<dd>
<strong>Required</strong> - The unique identifier for the user.
</dd>
<dd>
Type: <code>integer</code>
</dd>
<dt>name</dt>
<dd>
<strong>Optional</strong> - The display name for the user.
</dd>
<dd>
Type: <code>string</code>
</dd>
<dd>
Default: <code>null</code>
</dd>
<dt>status</dt>
<dd>
<strong>Optional</strong> - The user status.
</dd>
<dd>
Type: <code>string</code>
</dd>
<dd>
Allowed values: <code>active</code>, <code>inactive</code>,
<code>suspended</code>
</dd>
<dd>
Default: <code>active</code>
</dd>
</dl>