Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .claude/rules/filters/lua-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,36 @@ if node.is_custom_node then
end
```

### AST Pattern Matching with `quarto.utils.match`

Use `quarto.utils.match()` for safe structural checks on AST nodes. It handles nil/empty content internally — no manual length or nil guards needed. Defined in `src/resources/pandoc/datadir/_utils.lua`.

```lua
-- ✅ Correct - safe, handles empty content
if quarto.utils.match("[1]/BulletList")(b) then ...

-- ❌ Wrong - crashes on empty content (0 is truthy in Lua)
if #b.content and b.content[1].t == "BulletList" then ...

-- ❌ Fragile - manual nil guard, verbose
if #b.content > 0 and b.content[1].t == "BulletList" then ...
```

Selector syntax supports `/` child traversal, `[n]` nth-child, `{Type}` capture, `:child`/`:descendant` search, and CSS-like class matching:

```lua
-- Check nested structure
quarto.utils.match("Figure/[1]/Plain/[1]/Image")(fig)

-- Capture matched nodes (returned in a list)
quarto.utils.match("[1]/{Plain}")(content)

-- Search direct children for class
quarto.utils.match(".cell-output-display/:child/{Para}")(div)
```

Prefer `match()` over manual `.content[1].t ==` checks — it's nil-safe, readable, and already used in 20+ filter callsites.

### Slot Assignment

Use the proxy pattern for slot modification:
Expand Down Expand Up @@ -285,3 +315,4 @@ These type definition files document the complete API surface.
8. **Gate format inside element fns** - Constructor runs before format is resolved
9. **Detect meta shape with `quarto.utils.type`** - Meta values have no `.t` tag
10. **Gate expensive filters with `flags`** - Skips the whole pass when unneeded
11. **Use `quarto.utils.match()` for content checks** - Nil-safe, prefer over manual `.content[1].t`
1 change: 1 addition & 0 deletions news/changelog-1.10.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ All changes included in 1.10:
### `revealjs`

- ([#14354](https://github.com/quarto-dev/quarto-cli/pull/14354)): Fix trailing whitespace after author name on title slide when ORCID is not set. (author: @jnkatz)
- ([#14585](https://github.com/quarto-dev/quarto-cli/issues/14585)): Fix empty blockquote (`> `) crashing render for revealjs format.

## Projects

Expand Down
2 changes: 1 addition & 1 deletion src/resources/filters/quarto-post/reveal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function render_reveal_fixups()
-- Prevent BulletList in blockquote to be made incremental with .fragment class
-- https://github.com/quarto-dev/quarto-cli/issues/7715
BlockQuote = function(b)
if #b.content and b.content[1].t == "BulletList" then
if quarto.utils.match("[1]/BulletList")(b) then
b.content = pandoc.Div(b.content, pandoc.Attr('', {'blockquote-list-scaffold'}))
return b
end
Expand Down
18 changes: 18 additions & 0 deletions tests/docs/smoke-all/revealjs/empty-blockquote.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: "Empty blockquote"
format: revealjs
_quarto:
tests:
revealjs:
ensureHtmlElements:
- ["blockquote"]
noErrors: []
---

## Slide with empty blockquote

>

## Slide with content blockquote

> This is a normal blockquote
Loading