Developing with Lua
Overview
The programming language used to create filters and shortcodes is Lua, a lightweight, high-level scripting language designed primarily for embedded use in applications. Lua is the extension language for Pandoc (which includes an embedded Lua interpreter). This means that Quarto extensions have no additional runtime dependencies or requirements.
This article will start by providing an orientation to learning Lua for those new to the language. Then, we’ll provide a reference to the built in Lua functions provide by Quarto to assist in developing extensions.
Learning Lua
Lua is a scripting language similar to Python, R, Julia, and JavaScript. If you are familiar with one or more of those languages you won’t have trouble picking up Lua.
The best way to begin with Lua is to read the documentation on Pandoc Lua Filters. This has the benefit of demonstrating the use of Lua in concrete scenarios related to document publishing. Another excellent resources is the Learn Lua in 15 Minutes article which provides annotated examples of how to do things in Lua you might already be familiar with in other languages.
Once you’ve brushed up on the basics, a good next step is to examine the source code of the extensions published in the Quarto Extensions GitHub organization (these are extensions maintained by the Quarto core team). Once you are able to read and understand that code you are ready to start developing your own extensions!
When creating an extension, there are many built-in variables and functions provided directly by Pandoc — you can learn about these in the article on Pandoc Lua Filters. The remainder of this article describes some additional Lua functions provided by Quarto which you may also need when developing extensions.
Utilities
Various utility functions are provided, the most of important of which is the quarto.utils.dump()
function (indispensable for debugging).
Function | Description |
---|---|
quarto.utils.dump(obj) |
Dump a text representation of the passed object to stdout. |
quarto.utils.resolvePath(path) |
Compute the full path to a file that is installed alongside your extension’s Lua script. This is useful for internal resources that your filter needs but should not be visible to the user. |
For example, you can dump an element passed to a filter function as follows:
function Div(el)
.utils.dump(el)
quartoend
Format Detection
Extensions will often need to detect the current format to create custom content depending on the target output medium. The quarto.doc.isFormat()
function
Function | Description |
---|---|
quarto.doc.isFormat(name) |
Detect if the current format matches name . |
quarto.doc.hasBootstrap() |
Query whether Bootstrap CSS is available within the current document (it is by default for standard html documents but this may have been overridden by e.g. theme: none ). |
The name
parameter can match an exact Pandoc format name (e.g. docx
, latex
, etc. or can match based on an alias that groups commonly targeted formats together. The following values format aliases are handled specially by quarto.doc.isFormat()
:
Alias | Formats |
---|---|
latex |
latex , pdf |
pdf |
latex , pdf |
epub |
epub* |
html |
html* , epub* , revealjs |
html:js |
html* , revealjs |
markdown |
markdown* , commonmark* , gfm , markua |
Note that the html:js
alias indicates that the target format is capable of executing JavaScript (this maps to all HTML formats save for ePub).
For example, here we check for PDF and HTML output:
if quarto.doc.isFormat("pdf") then
-- pdf specific output
elseif quarto.doc.isFormat("html") then
-- html specific output
else
-- output for other formats
end
For LaTeX output, you may need to additionally detect which citation utility and pdf engine are being used for the current render. You can use these functions to do that detection:
Function | Description |
---|---|
quarto.doc.citeMethod() |
Returns a string (citeproc , natbib , or biblatex) indicating the cite method in use. |
quarto.doc.pdfEngine() |
Returns a string (pdflatex , xelatex , lualatex , or tectonic ) indicating the PDF engine being used to render the document. |
Includes
Sometimes extensions need to inject content into the target document. There are three locations that content can be included (pass one of these locations as the first argument of the include functions):
Location | Description |
---|---|
in-header |
In the header of the document (HTML <head> tag or LaTeX preamble) |
before-body |
Before the document body |
after-body |
After the document body |
Note that the included content should use the raw target format (e.g. HTML or LaTeX) rather than markdown. You can use these functions to include text or the contents of a file:
Function | Description |
---|---|
quarto.doc.includeText(location, text) |
Include text at the specified location (in-header , before-body , or after-body ) |
quarto.doc.includeFile(location, file) |
Include file at the specified location (in-header , before-body , or after-body ). The path to the file should relative to the Lua script calling this function. |
For example the following code includes an HTML file after the body in the rendered document:
.doc.includeFile("after-body", "comments.html") quarto
Dependencies
Extensions will sometimes want to add external dependencies (for example, a JavaScript library and related CSS, or the usage of a LaTeX package). This can be accomplished with the following functions:
Function | Description |
---|---|
quarto.doc.addHtmlDependency(dep) |
Add an HTML dependency (additional resources and content) to a document. See docs on the HTML Dependencies below for additional details. |
quarto.doc.useLatexPackage(pkg, opt) |
Adds a \usepackage statement to the LaTeX output (along an options string specified in opt ) |
quarto.doc.addFormatResource(path) |
Add a format resource to the document. Format resources will be copied into the directory next to the rendered output. This is useful, for example, if your format references a bst or cls file which must be copied into the LaTeX output directory. |
For example, here we add a LaTeX package dependency:
.doc.useLatexPackage("gamebook") quarto
HTML Dependencies
HTML Dependencies can bundle together JavaScript, CSS, and even arbitrary content to inject into the <head>
of the document. These dependencies have a name and a version, which is used to ensure that the same dependency isn’t bundled into the document more than once.
The dep
object passed to quarto.doc.addHtmlDependency()
has the following fields:
Field | Description |
---|---|
name |
Unique name. Required. |
version |
Version number (as a string). Required. |
scripts |
List of scripts to include (paths should be relative to the Lua file calling the function). Scripts can be either a simple path or a script object. |
stylesheets |
List of CSS style-sheets to include (paths should be relative to the Lua file calling the function). Stylesheets can either be a simple path or a stylesheet object |
links |
List of link tags to add to the document. Each tag should be a table with rel and ref (required) and optionally type |
resources |
Additional files to copy to the input directory (each resource is an object with name (target file name in input directory) and path (source file name relative to Lua script). |
meta |
Table of optional key = value meta tags to insert into the document <head> |
head |
Arbitrary string to include in document <head> |
For example, here we add a dependency to a JavaScript library:
.doc.addHtmlDependency({
quarto= "glightbox",
name = "3.2.0",
version = {"glightbox.min.js"},
scripts = {"glightbox.min.css"}
stylesheets })
Script Object
The easiest way to specify scripts
is with simple paths. However, in some cases you may need to add attributes to the <script>
tag or specify that the script should go after the body. In those cases pass a script object:
Field | Description |
---|---|
path |
Path to the script (relative to the calling Lua script) |
attribs |
Table with key = value attributes to add to the <script> tag |
afterBody |
Specify that the <script> tag should be inserted after the body |
For example, here update the previous example to add an integrity
attribute to the script:
.doc.addHtmlDependency({
quarto= "glightbox",
name = "3.2.0",
version = {
scripts { path = "glightbox.min.js ", attribs = {integrity = "R9GqQ8K/uxy9rx"} }
},
= {"glightbox.min.css"}
stylesheets })
Stylesheet Object
The easiest way to specify stylesheets
is with simple paths. However, in some cases you may need to add attributes to the <link>
tag generated for the stylesheet. In those cases pass a stylesheet object:
Field | Description |
---|---|
path |
Path to the stylesheet (relative to the calling Lua script) |
attribs |
Table with key = value attributes to add to the <link> tag |
For example, here we update the previous example to add an integrity
attribute to the stylesheet:
.doc.addHtmlDependency({
quarto= "glightbox",
name = "3.2.0",
version = {
scripts {
= "glightbox.min.js ",
path = {integrity = "R9GqQ8K/uxy9rx"}
attribs }
},
= {
stylesheets {
= "glightbox.min.css ",
path = {integrity = "GYl1kPzQho1wx"}
attribs }
}
})
JSON Encoding
Quarto includes a copy of json.lua. a lightweight JSOn library for Lua. You can access the JSON functions as follows:
Function | Description |
---|---|
quarto.json.encode(input) |
Encode a Lua table into a JSON string. |
quarto.json.decode(str) |
Parse a JSON string into a Lua table. |
For example, here we encode and then decode a table:
local json = quarto.json.encode({foo = "bar"})
local obj = quarto.json.decode(json)