|
| 1 | +--- |
| 2 | +title: Quarto AST |
| 3 | +summary: Quarto extends Pandoc's AST processing to allow more flexible customization in filters |
| 4 | +--- |
| 5 | + |
| 6 | +## Overview |
| 7 | + |
| 8 | +Quarto extends Pandoc's AST processing to allow more flexible customization in filters: |
| 9 | + |
| 10 | +* [Custom nodes](#custom-nodes): Quarto defines custom AST node types for Quarto specific types of content like Callouts, Tabsets etc. This allows filters to target, modify or create these elements. |
| 11 | + |
| 12 | +* [Custom renderers](#custom-formats-and-custom-renderers): Add custom renderers for Quarto's custom node types to facilitate handling Quarto specific elements in custom formats. |
| 13 | + |
| 14 | +* [Targeting AST Processing Phases](#targeting-of-ast-processing-phases): Apply filters at precise points in the AST processing. |
| 15 | + |
| 16 | +## Custom Nodes |
| 17 | + |
| 18 | +Quarto defines some custom AST nodes for use in Pandoc filters. |
| 19 | +This allows more flexibility in defining and using Lua filters. |
| 20 | +For example, by using the `Callout` custom node this filter forces every callout to be of type "caution": |
| 21 | + |
| 22 | +``` lua |
| 23 | +function Callout(callout) |
| 24 | + -- do something with the callout |
| 25 | + callout.type = "caution" |
| 26 | + |
| 27 | + -- note that custom AST nodes are passed by reference. You can |
| 28 | + -- return the value if you choose, but you do not need to. |
| 29 | +end |
| 30 | +``` |
| 31 | + |
| 32 | +Finally, custom AST node constructors are available in the `quarto` object: `quarto.Callout`, `quarto.Tabset`, etc. See the sections below for details. |
| 33 | + |
| 34 | +### Callouts |
| 35 | + |
| 36 | +You can create callout AST nodes in Lua filters with the `quarto.Callout` constructor. The constructor takes a single parameter, a table with entries `type`, `title`, and `content`, as described below. In Lua filters, callouts are represented as a table with the following fields: |
| 37 | + |
| 38 | +- `type`: the type of callout: `note`, `caution`, `warning`, etc (optional in the constructor). |
| 39 | +- `title`: The callout title (if any) (optional in the constructor), |
| 40 | +- `icon`: the callout icon (or `false` if none) (optional in the constructor) |
| 41 | +- `appearance`: `"minimal"`, `"simple"`, or `"default"` (optional in the constructor) |
| 42 | +- `collapse`: whether to render the callout as collapsible (optional in the constructor, default `false`) |
| 43 | +- `content`: the content of the callout (a `pandoc.Blocks` object, or a plain list in the constructor) |
| 44 | + |
| 45 | +### Tabsets |
| 46 | + |
| 47 | +You can create conditional blocks in Lua filters with the `quarto.Tabset` constructor, with parameters `tabs`, `level` and `attr` as described above. In |
| 48 | +addition, you can use `quarto.Tab` to create the tab objects for the `tabs` field. `quarto.Tab` is more lenient with parameter types, converting strings to `Blocks` and `Inlines` as needed. In Lua filters, tabsets are represented as a table with the following fields: |
| 49 | + |
| 50 | +- `tabs`: a table containing the content for each tab. Each entry is a table with two entries: `title` (a `pandoc.Inlines`) and `content` (a `pandoc.Blocks`) (optional in the contructor, default value `{}`) |
| 51 | +- `level`: the level of the tab headings to be used in rendering the tabset (optional in the constructor, default value `2`) |
| 52 | +- `attr`: the `Attr` object for the resulting tabset div (optional in the constructor) |
| 53 | + |
| 54 | +### Conditional Blocks |
| 55 | + |
| 56 | +You can create conditional block AST nodes in Lua filters with the `quarto.ConditionalBlock` constructor. The constructor takes a single parameter, a table with entries `node`, `behavior`, and `condition`, as described below. |
| 57 | + |
| 58 | +In Lua filters, conditional blocks are represented as a table with the following fields: |
| 59 | + |
| 60 | +- `node`: the div containing the content |
| 61 | +- `behavior`: either `content-visible` or `content-hidden` |
| 62 | +- `condition`: a list of 2-element lists, such as `{ { "unless-format", "html" } }` (optional in the constructor, default value `{}`). The first element of each sublist must be one of `when-format`, `unless-format`, `when-profile`, and `unless-profile`. The second element is the relevant format or profile. |
| 63 | + |
| 64 | +### Cross-referenceable Elements |
| 65 | + |
| 66 | +Crossreferenceable elements all have a single generic type, `FloatRefTarget`. |
| 67 | +This element can be constructed explicitly in a Lua filter. |
| 68 | +It can also be used as the element to be processed in a Lua filter directly. |
| 69 | + |
| 70 | +```{.lua} |
| 71 | +-- A filter targeting FloatRefTarget nodes |
| 72 | +return { |
| 73 | + FloatRefTarget = function(float) |
| 74 | + if float.caption_long then |
| 75 | + float.caption_long.content:insert(pandoc.Str("[This will appear at the beginning of every caption]")) |
| 76 | + return float |
| 77 | + end |
| 78 | + end |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +`FloatRefTarget` nodes have the following fields: |
| 83 | + |
| 84 | +- `type`: The type of element: `Figure`, `Table`, `Listing`, etc. Quarto v1.4 supports |
| 85 | + custom node types that can be declared at the document or project level. |
| 86 | +- `content`: The content of the Figure, Table, etc. Quarto v1.4 |
| 87 | + accepts any content in any `FloatRefTarget` type (so if your tables are better displayed |
| 88 | + as an image, you can use that.). |
| 89 | +- `caption_long`: The caption that appears in the main body of the document |
| 90 | +- `caption_short`: The caption that appears in the element collations (such as a list of tables, |
| 91 | + list of figures, etc.) |
| 92 | +- `identifier`, `attributes`, `classes`: these are analogous to `Attr` fields in Pandoc AST elements like spans and divs. |
| 93 | + `identifier`, in addition, needs to be the string that is used in a crossref (`fig-cars`, `tbl-votes`, `lst-query`, and so on). |
| 94 | +- `parent_id`: if a `FloatRefTarget` is a subfloat of a parent multiple-element float, then `parent_id` will hold the identifier |
| 95 | + of the parent float. |
| 96 | + |
| 97 | +## Custom Formats and Custom Renderers |
| 98 | + |
| 99 | +Quarto has support for extensible renderers of quarto AST nodes such as `FloatRefTarget`, `Callout` etc. |
| 100 | +In order to declare a custom renderer, add the following to a Lua filter: |
| 101 | + |
| 102 | +```lua |
| 103 | +local predicate = function(float) |
| 104 | + -- return true if this renderer should be used; |
| 105 | + -- typically, this will return true if the custom format is active. |
| 106 | +end |
| 107 | +local renderer = function(float) |
| 108 | + -- returns a plain Pandoc representation of the rendered figure. |
| 109 | +end |
| 110 | +quarto._quarto.ast.add_renderer( |
| 111 | + "FloatRefTarget", |
| 112 | + predicate, |
| 113 | + renderer) |
| 114 | +``` |
| 115 | + |
| 116 | +## Targeting of AST Processing Phases |
| 117 | + |
| 118 | +Quarto's AST processing phase is split into three parts: `ast`, `quarto`, and `render`. |
| 119 | + |
| 120 | +- `ast`: normalizes the input syntax from Pandoc, recognizing constructs such as `Callout`, `FloatRefTarget`, and so on. |
| 121 | +- `quarto`: processes the normalized syntax, for example by resolving cross-references. |
| 122 | +- `render`: produces format-specific output from the processed input. |
| 123 | + |
| 124 | +Lua filters can be inserted before or after any of these stages: |
| 125 | + |
| 126 | +```yaml |
| 127 | +filters: |
| 128 | + - at: pre-ast |
| 129 | + path: filter1.lua |
| 130 | + - at: post-quarto |
| 131 | + path: filter2.lua |
| 132 | + - at: post-render |
| 133 | + path: filter3.lua |
| 134 | +``` |
| 135 | +
|
| 136 | +Any of the stages can be prefixed by `pre-` or `post-`. |
| 137 | +Currently `pre-quarto` and `post-ast` correspond to the same insertion location in the filter chain, as do `post-quarto` and `pre-render`. |
| 138 | + |
| 139 | +You can also use JSON filters with this syntax. |
| 140 | +Either use `type: json` explicitly, or use a path that doesn't end in `.lua`. |
| 141 | +Conversely, `type: lua` will force the file to be treated as a Lua filter. |
| 142 | + |
| 143 | +Prior to Quarto 1.4, Lua filters were either "pre" filters (the default setting), or "post" filters. |
| 144 | +Those filters are specified like this: |
| 145 | + |
| 146 | +```yaml |
| 147 | +# "pre" filters: |
| 148 | +filters: |
| 149 | + - pre_filter_1.lua |
| 150 | + - pre_filter_2.lua |
| 151 | + # ... |
| 152 | +# "post" filters: |
| 153 | +filters: |
| 154 | + - quarto |
| 155 | + - post_filter_1.lua |
| 156 | + - post_filter_2.lua |
| 157 | + # ... |
| 158 | +``` |
| 159 | + |
| 160 | +This syntax continues to work. |
| 161 | +"Pre" filters are injected at the `pre-quarto` entry point, and "post" filters are injected at the `post-render` entry point. |
| 162 | + |
0 commit comments