Skip to content

Commit 2d7ef41

Browse files
committed
Rewrite as_inlines, as_blocks with focus on performance
This also fixes unwanted behavior of `as_blocks`, which would treat a list of Inline elements as a list of singleton Plain elements, leading to bad results.
1 parent c7e4597 commit 2d7ef41

File tree

1 file changed

+67
-47
lines changed

1 file changed

+67
-47
lines changed

src/resources/pandoc/datadir/_utils.lua

Lines changed: 67 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
-- _utils.lua
22
-- Copyright (C) 2020-2022 Posit Software, PBC
33

4+
--- The pandoc module. Imported to avoid 'no such variable'
5+
--- warnings in some editors.
6+
local pandoc = require 'pandoc'
7+
48
-- improved formatting for dumping tables and quarto's emulated pandoc nodes
59
function tdump (tbl, raw)
610

@@ -265,59 +269,75 @@ local function get_type(v)
265269
return pandoc_type
266270
end
267271

268-
local function as_inlines(v)
269-
if v == nil then
270-
return pandoc.Inlines({})
271-
end
272-
local t = pandoc.utils.type(v)
273-
if t == "Inlines" then
274-
---@cast v pandoc.Inlines
275-
return v
276-
elseif t == "Blocks" then
277-
return pandoc.utils.blocks_to_inlines(v)
278-
elseif t == "Inline" then
279-
return pandoc.Inlines({v})
280-
elseif t == "Block" then
281-
return pandoc.utils.blocks_to_inlines({v})
282-
end
272+
--- Blocks metatable
273+
local BlocksMT = getmetatable(pandoc.Blocks{})
274+
--- Inlines metatable
275+
local InlinesMT = getmetatable(pandoc.Inlines{})
283276

284-
if type(v) == "table" then
285-
local result = pandoc.Inlines({})
286-
for i, v in ipairs(v) do
287-
tappend(result, as_inlines(v))
277+
--- Turns the given object into a `Inlines` list.
278+
--
279+
-- Works mostly like `pandoc.Inlines`, but doesn't a do a full
280+
-- unmarshal/marshal roundtrip. This buys performance, at the cost of
281+
-- less thorough type checks.
282+
--
283+
-- NOTE: The input object might be modified *destructively*!
284+
local function as_inlines(obj)
285+
local pt = pandoc.utils.type(obj)
286+
if pt == 'Inlines' then
287+
return obj
288+
elseif pt == "Inline" then
289+
-- Faster than calling pandoc.Inlines
290+
return setmetatable({obj}, InlinesMT)
291+
elseif pt == 'List' or pt == 'table' then
292+
if obj[1] and pandoc.utils.type(obj[1]) == 'Block' then
293+
return pandoc.utils.blocks_to_inlines(obj)
288294
end
289-
return result
295+
-- Faster than calling pandoc.Inlines
296+
return setmetatable(obj, InlinesMT)
297+
elseif pt == "Block" then
298+
return pandoc.utils.blocks_to_inlines({obj})
299+
elseif pt == "Blocks" then
300+
return pandoc.utils.blocks_to_inlines(obj)
301+
else
302+
return pandoc.Inlines(obj or {})
290303
end
291-
292-
-- luacov: disable
293-
fatal("as_inlines: invalid type " .. t)
294-
return pandoc.Inlines({})
295-
-- luacov: enable
296304
end
297305

298-
local function as_blocks(v)
299-
if v == nil then
300-
return pandoc.Blocks({})
301-
end
302-
local t = pandoc.utils.type(v)
303-
if t == "Blocks" then
304-
return v
305-
elseif t == "Inlines" then
306-
return pandoc.Blocks({pandoc.Plain(v)})
307-
elseif t == "Block" then
308-
return pandoc.Blocks({v})
309-
elseif t == "Inline" then
310-
return pandoc.Blocks({pandoc.Plain(v)})
311-
end
312-
313-
if type(v) == "table" then
314-
return pandoc.Blocks(v)
306+
--- Turns the given object into a `Blocks` list.
307+
--
308+
-- Works mostly like `pandoc.Blocks`, but doesn't a do a full
309+
-- unmarshal/marshal roundtrip. This buys performance, at the cost of
310+
-- less thorough type checks.
311+
--
312+
-- NOTE: The input object might be modified *destructively*!
313+
--
314+
-- This might need some benchmarking.
315+
local function as_blocks(obj)
316+
local pt = pandoc.utils.type(obj)
317+
if pt == 'Blocks' then
318+
return obj
319+
elseif pt == 'Block' then
320+
-- Assigning a metatable directly is faster than calling
321+
-- `pandoc.Blocks`.
322+
return setmetatable({obj}, BlocksMT)
323+
elseif pt == 'Inline' then
324+
return setmetatable({pandoc.Plain{obj}}, BlocksMT)
325+
elseif pt == 'Inlines' then
326+
if next(obj) then
327+
return setmetatable({pandoc.Plain(obj)}, BlocksMT)
328+
end
329+
return setmetatable({}, BlocksMT)
330+
elseif pt == 'List' or (pt == 'table' and obj[1]) then
331+
if pandoc.utils.type(obj[1]) == 'Inline' then
332+
obj = {pandoc.Plain(obj)}
333+
end
334+
return setmetatable(obj, BlocksMT)
335+
elseif (pt == 'table' and obj.long) or pt == 'Caption' then
336+
-- Looks like a Caption
337+
return as_blocks(obj.long)
338+
else
339+
return pandoc.Blocks(obj or {})
315340
end
316-
317-
-- luacov: disable
318-
fatal("as_blocks: invalid type " .. t)
319-
return pandoc.Blocks({})
320-
-- luacov: enable
321341
end
322342

323343
local function match_fun(reset, ...)

0 commit comments

Comments
 (0)