Skip to content

Support Quarto 2's div syntax for definition lists - #15140

Merged
cderv merged 7 commits into
mainfrom
feature/definition-list-div
Oct 22, 2026
Merged

cderv merged 7 commits into
mainfrom
feature/definition-list-div

Conversation

@cscheid

@cscheid cscheid commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Description

Adds support for Quarto 2's div syntax for definition lists, so authors can move to it while still on Quarto 1, ahead of a migration:

::: {.definition-list}
* `time`
  - A timestamp that identifies when the request was initiated.
  - Type: string
* `id`
  - A unique identifier.
:::

Each top-level bullet is a term, and the bullets nested under it are its definitions. Quarto 2 doesn't support Pandoc's term / : definition syntax, and this is what it uses instead.

This follows the list-table precedent (#13528):

  • New module src/resources/filters/modules/definitionlist.lua, registered in _quarto.modules.
  • New astpipeline-process-definition-lists pass in normalize/astpipeline.lua, right after the list-table pass, so it runs before crossrefs and user filters.

The structural rules match Quarto 2's (is_valid_definition_list_div / transform_definition_list_div in pampa):

  • The div contains exactly one bullet list.
  • Each item is a term (Plain or Para) followed by a nested bullet list of definitions.
  • A div with any other shape is left as is, without a warning.
  • The div's id, classes and attributes are dropped, since DefinitionList has no Attr.

Like listtable.lua, it skips the data-pos wrapper divs added by the sourcepos extension.

No flags gate: compute_flags runs after the AST pipeline, so a flag couldn't skip this pass (the same reason has_list_tables is set but unused).

Open questions

  1. Malformed divs. list-table stops the render with an error; Quarto 2 leaves a malformed .definition-list div alone silently. This PR matches Quarto 2. A warning in Quarto 1 could help people learning the syntax, but would flag what Quarto 2 accepts.
  2. Dropped attributes. ::: {#foo .definition-list} loses #foo, as in Quarto 2. Keeping it would mean wrapping the list in a Div, which differs from Quarto 2.

Testing

  • New smoke test tests/docs/smoke-all/markdown/definition-list/definition-list-div.qmd, with html, latex and typst checks for tight, loose and nested lists and for malformed divs staying divs.
  • I checked the module directly with pandoc 3.12.1 (pandoc markdown, and commonmark with +sourcepos), and rendered the test document with a dev quarto pointed at this branch's filters through QUARTO_SHARE_PATH; the html, latex and typst assertions match that output.
  • I have not yet run ./run-tests.sh on this test or the wider suite.

Documentation: quarto-dev/quarto-web#2305

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes
  • updated the appropriate changelog in the PR
  • ensured the present test suite passes
  • added new tests
  • created a separate documentation PR in Quarto's website repo and linked it to this PR
AI-assisted PR
  • AI tool used: Claude Code
  • Codebase grounding: local clones of quarto-cli, quarto-web, and the Quarto 2 source
  • Human review: draft opened for review; not yet reviewed and verified by a human.

🤖 Generated with Claude Code

A `definition-list` div holding a bullet list of terms, each with a
nested bullet list of definitions, now becomes a DefinitionList:

    ::: {.definition-list}
    * `time`
      - A timestamp that identifies when the request was initiated.
      - Type: string
    :::

This is the syntax Quarto 2 uses in place of Pandoc's `: ` definition
lists, so authors can move to it while still on Quarto 1. It follows the
list-table precedent: a module in `modules/`, registered in
`_quarto.modules`, run as an astpipeline pass so it lands before
crossrefs and user filters.

The shape rules match Quarto 2's (`is_valid_definition_list_div` in
pampa): exactly one bullet list in the div, and each item exactly a
Plain/Para term followed by a bullet list of definitions. A div with any
other shape is left alone silently, and the div's attributes are dropped
since DefinitionList has no Attr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@posit-snyk-bot

posit-snyk-bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

cscheid added a commit to quarto-dev/quarto-web that referenced this pull request Oct 21, 2026
Adds a Definition Lists section to Markdown Basics for the div syntax
from quarto-dev/quarto-cli#15140, and a 1.11 highlight for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cscheid and others added 2 commits October 21, 2026 17:29
@cderv

cderv commented Oct 22, 2026

Copy link
Copy Markdown
Member

Regarding CI, we have linux issues due to Koma script update, I'll fix it.

Regarding this PR:

Thanks for this. I ran the new test locally to see, and I also tried a few cases outside the test with 🤖 : a callout and a figure with a crossref inside a definition, a .definition-list div inside a callout, a div with one valid and one invalid item (stays a div), and from: commonmark_x+sourcepos. All of them render as expected.

One difference with Quarto 2 I think, about nesting. With this PR, a .definition-list div inside a definition becomes a nested DefinitionList too, and the #nested case of the test asserts it. In pampa the rewrite does not recurse into its result, on purpose it seems:

source

/// Items 1 and 2 of the set, for one div: a valid list-table becomes a
/// `Table`, a valid definition-list a `DefinitionList`, an invalid
/// list-table warns Q-2-35 into `collector` and stays a div, and any other
/// div is unchanged. Neither rewrite recurses into its result, matching
/// the reader.
...
            if is_valid_definition_list_div(&div) {
                FilterResult(vec![transform_definition_list_div(div)], false)

I rendered the test document with a q2 nightly (0.36.0-nightly.20261022): #tight, #loose and the two malformed divs give the same output as this PR, but in #nested the inner div stays a <div class="definition-list"> with its bullet list. Same for a .list-table div inside a definition: q2 leaves it as a div, while our earlier list-table pass converts it to a table here. As the docs PR says this syntax lets documents work in both versions, which behavior do we want ? Either Quarto 2 recurses, or we keep this PR to one level and drop the #nested case.

About the two open questions:

  1. Staying silent on a malformed div like Quarto 2 looks right to me for now. pampa has a FUTURE: issue linter warning comment at each rejected shape, so if Quarto 2 adds those warnings we could add the same ones here then.
  2. Dropping the attributes is fine I think, as Quarto 2 does the same and the docs PR says it.

@cscheid

cscheid commented Oct 22, 2026 •

Copy link
Copy Markdown
Member Author

As the docs PR says this syntax lets documents work in both versions, which behavior do we want ? Either Quarto 2 recurses, or we keep this PR to one level and drop the #nested case.

Good catch. I think we should match whatever Pandoc's syntax. I'll ask the robot.

EDIT: Pandoc parses nested definition lists easily. You found a Quarto 2 bug! We'll fix there: quarto-dev/q2#895.

cscheid and others added 2 commits October 22, 2026 09:02
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cscheid
cscheid marked this pull request as ready for review October 22, 2026 13:43
@cscheid

cscheid commented Oct 22, 2026

Copy link
Copy Markdown
Member Author

@cderv We have a q2 fix incoming, so let's assume the behavior in this PR is intentional. Feel free to merge it, and there's a corresponding doc PR over at quarto-dev/quarto-web#2305

@cderv
cderv merged commit 4b29a10 into main Oct 22, 2026
52 checks passed
@cderv
cderv deleted the feature/definition-list-div branch October 22, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants