Skip to content

Commit 6661062

Browse files
authored
Merge pull request #12421 from quarto-dev/feature/12326
lua - new quarto.shortcode API
2 parents a61b2f9 + 92dc6a3 commit 6661062

File tree

9 files changed

+85
-39
lines changed

9 files changed

+85
-39
lines changed

news/changelog-1.7.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ All changes included in 1.7:
109109
- ([#11896](https://github.com/quarto-dev/quarto-cli/pull/11896)): fix `\f` (`{{< pagebreak >}}`) form feed character not valid in PowerPoint (`pptx`).
110110
- ([#11664](https://github.com/quarto-dev/quarto-cli/issues/11664)): `lipsum` shortcode is no longer randomly generated by default, use `{{< lipsum random=true >}}` to restore randomness.
111111
- ([#11379](https://github.com/quarto-dev/quarto-cli/issues/11379)): Add `version` shortcode to display the current Quarto version.
112+
- ([#12326](https://github.com/quarto-dev/quarto-cli/issues/12326)): Add `quarto.shortcode.*` API entry points for shortcode developers.
112113

113114
## Quarto Lua API
114115

src/resources/filters/quarto-pre/shortcodes-handlers.lua

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,7 @@ end
5959

6060
local handlers = {}
6161

62-
local function read_arg(args, n)
63-
local arg = args[n or 1]
64-
local varName
65-
if arg == nil then
66-
return nil
67-
end
68-
if type(arg) ~= "string" then
69-
varName = inlinesToString(arg)
70-
else
71-
varName = arg
72-
end
73-
return varName
74-
end
62+
local read_arg = quarto.shortcode.read_arg
7563

7664
function initShortcodeHandlers()
7765

@@ -121,16 +109,7 @@ function initShortcodeHandlers()
121109

122110
local warn_bad_brand_command = function()
123111
warn("Unknown brand command " .. brandCommand .. " specified in a brand shortcode.")
124-
if context == "block" then
125-
return pandoc.Blocks { pandoc.Strong({pandoc.Str("?brand:" .. table.concat(args, " "))}) }
126-
elseif context == "inline" then
127-
return pandoc.Inlines { pandoc.Strong({pandoc.Str("?brand:" .. table.concat(args, " "))}) }
128-
elseif context == "text" then
129-
return "?brand:" .. table.concat(args, " ")
130-
else
131-
warn("Unknown context for brand shortcode error: " .. context)
132-
return { }
133-
end
112+
return quarto.shortcode.error_output("brand", args, context)
134113
end
135114

136115
if brandCommand == "color" then
@@ -202,19 +181,19 @@ function handlerForShortcode(shortCode)
202181
end
203182

204183
-- Implements reading values from envrionment variables
205-
function handleEnv(args)
184+
function handleEnv(args, _kwargs, _meta, _raw_args, context)
206185
if #args > 0 then
207186
-- the args are the var name
208187
local varName = read_arg(args)
209188
local defaultValue = read_arg(args, 2)
210189

211190
-- read the environment variable
212-
local envValue = os.getenv(varName) or defaultValue
191+
local envValue = varName and os.getenv(varName) or defaultValue
213192
if envValue ~= nil then
214193
return { pandoc.Str(envValue) }
215194
else
216195
warn("Unknown variable " .. varName .. " specified in an env Shortcode.")
217-
return { pandoc.Strong({pandoc.Str("?env:" .. varName)}) }
196+
return quarto.shortcode.error_output("env", args, context)
218197
end
219198
else
220199
-- no args, we can't do anything
@@ -226,10 +205,10 @@ end
226205
-- as {{< meta title >}}
227206
-- or {{< meta key.subkey.subkey >}}
228207
-- This only supports emitting simple types (not arrays or maps)
229-
function handleMeta(args)
208+
function handleMeta(args, _kwargs, _meta, _raw_args, context)
230209
if #args > 0 then
231210
-- the args are the var name
232-
local varName = read_arg(args)
211+
local varName = read_arg(args) or ""
233212

234213
-- strip quotes if present
235214
-- works around the real bug that we don't have
@@ -247,7 +226,7 @@ function handleMeta(args)
247226
return processValue(optionValue, varName, "meta")
248227
else
249228
warn("Unknown meta key " .. varName .. " specified in a metadata Shortcode.")
250-
return { pandoc.Strong({pandoc.Str("?meta:" .. varName)}) }
229+
return { pandoc.Strong(pandoc.Inlines {pandoc.Str("?meta:" .. varName)}) }
251230
end
252231
else
253232
-- no args, we can't do anything
@@ -259,7 +238,7 @@ end
259238
-- as {{< var title >}}
260239
-- or {{< var key.subkey.subkey >}}
261240
-- This only supports emitting simple types (not arrays or maps)
262-
function handleVars(args)
241+
function handleVars(args, _kwargs, _meta, _raw_args, context)
263242
if #args > 0 then
264243
-- the args are the var name
265244
local varName = read_arg(args)
@@ -270,7 +249,7 @@ function handleVars(args)
270249
return processValue(varValue, varName, "var")
271250
else
272251
warn("Unknown var " .. varName .. " specified in a var shortcode.")
273-
return { pandoc.Strong({pandoc.Str("?var:" .. varName)}) }
252+
return quarto.shortcode.error_output("var", args, context)
274253
end
275254

276255
else
@@ -291,10 +270,10 @@ function processValue(val, name, t)
291270
return processValue(val[1])
292271
else
293272
warn("Unsupported type '" .. pandoc.utils.type(val) .. "' for key " .. name .. " in a " .. t .. " shortcode.")
294-
return { pandoc.Strong({pandoc.Str("?invalid " .. t .. " type:" .. name)}) }
273+
return { pandoc.Strong(pandoc.Inlines { pandoc.Str("?invalid " .. t .. " type:" .. name) } ) }
295274
end
296275
else
297-
return { pandoc.Str( tostring(val) ) }
276+
return { pandoc.Str( tostring(val) ) }
298277
end
299278
end
300279

@@ -329,7 +308,7 @@ function handlePagebreak()
329308
return pandoc.RawBlock('context', pagebreak.context)
330309
else
331310
-- fall back to insert a form feed character
332-
return pandoc.Para{pandoc.Str '\f'}
311+
return pandoc.Para( pandoc.Inlines { pandoc.Str '\f'} )
333312
end
334313

335314
end

src/resources/lua-types/pandoc/blocks.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pandoc.Blocks = {}
3333
Create a blocks list
3434
]]
3535
---@param blocks pandoc.Block|pandoc.List Block or list of blocks
36-
---@return pandoc.List
36+
---@return pandoc.Blocks
3737
function pandoc.Blocks(blocks) end
3838

3939

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---@meta
2+
3+
quarto.shortcode = {}
4+
5+
--[[
6+
Read the `n`-th shortcode argument from the passed `args` table.
7+
]]
8+
---@param args string[] arguments from the handler
9+
---@param n number|nil index of the argument to read (default: 1)
10+
---@return string|nil
11+
function quarto.shortcode.read_arg(args, n) end
12+
13+
--[[
14+
Produce output for a shortcode that failed to execute properly.
15+
16+
This is useful for shortcode developers to provide error output
17+
consistent with how Quarto shortcodes provide error output.
18+
]]
19+
---@param name string Name of the shortcode
20+
---@param message_or_args string[]|string shortcode args or optional error message to display
21+
---@param context "block"|"inline"|"text" context of the shortcode
22+
---@return pandoc.Blocks|pandoc.Inlines|string
23+
function quarto.shortcode.error_output(name, message_or_args, context) end

src/resources/pandoc/datadir/_utils.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function tdump (tbl, raw)
9797
for k, v in pairsByKeys(tbl, typesThenValues) do
9898
if shouldPrint(k, v, tbl) then
9999
empty = false
100-
formatting = indentStr .. " " .. k .. ": "
100+
local formatting = indentStr .. " " .. k .. ": "
101101
v = asLua(v)
102102
if type(v) == "table" or type(v) == "userdata" and v.is_emulated then
103103
printInner(formatting)

src/resources/pandoc/datadir/init.lua

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2139,7 +2139,38 @@ quarto = {
21392139
config = {
21402140
cli_path = function() return param('quarto-cli-path', nil) end,
21412141
version = function() return version() end
2142-
}
2142+
},
2143+
shortcode = {
2144+
read_arg = function (args, n)
2145+
local arg = args[n or 1]
2146+
local varName
2147+
if arg == nil then
2148+
return nil
2149+
end
2150+
if type(arg) ~= "string" then
2151+
varName = inlinesToString(arg)
2152+
else
2153+
varName = arg --[[@as string]]
2154+
end
2155+
return varName
2156+
end,
2157+
error_output = function (shortcode, message_or_args, context)
2158+
if type(message_or_args) == "table" then
2159+
message_or_args = table.concat(message_or_args, " ")
2160+
end
2161+
local message = "?" .. shortcode .. ":" .. message_or_args
2162+
if context == "block" then
2163+
return pandoc.Blocks { pandoc.Strong( pandoc.Inlines { pandoc.Str(message) } ) }
2164+
elseif context == "inline" then
2165+
return pandoc.Inlines { pandoc.Strong( pandoc.Inlines { pandoc.Str(message) } ) }
2166+
elseif context == "text" then
2167+
return message
2168+
else
2169+
warn("Unknown context for " .. shortcode .. " shortcode error: " .. context)
2170+
return { }
2171+
end
2172+
end,
2173+
},
21432174
}
21442175

21452176
-- alias old names for backwards compatibility

tests/docs/shortcodes/custom.qmd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ shortcodes:
55
---
66

77
{{< shorty _bringit_ >}}
8+
9+
{{< shorty error >}}
10+
11+
{{< shorty error_args >}}

tests/docs/shortcodes/shorty.lua

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
return {
44
shorty = function(args)
5-
return pandoc.Strong(args[1])
5+
if args[1] == "error_args" then
6+
return quarto.shortcode.error_output("shorty", args, "inline")
7+
elseif args[1] == "error" then
8+
return quarto.shortcode.error_output("shorty", "error message", "inline")
9+
else
10+
return pandoc.Strong(args[1])
11+
end
612
end
713
}
814

tests/smoke/shortcodes/shortcodes-core.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ const outputCustom = outputForInput(inputCustom, "html");
2020
testRender(inputCustom, "html", false, [
2121
ensureFileRegexMatches(outputCustom.outputPath, [
2222
/<strong>_bringit_<\/strong>/,
23+
/\?shorty:error_args/,
24+
/\?shorty:error message/,
2325
], [
24-
/\?shorty/,
26+
/\?shorty:_bringit_/,
2527
]),
2628
]);
2729

0 commit comments

Comments
 (0)