From 6512bbedff18dd15a37fac2397ba78f65f11af9a Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 22 Nov 2024 23:19:01 +0100 Subject: [PATCH] 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. --- src/resources/pandoc/datadir/_utils.lua | 110 ++++++++++++++---------- 1 file changed, 63 insertions(+), 47 deletions(-) diff --git a/src/resources/pandoc/datadir/_utils.lua b/src/resources/pandoc/datadir/_utils.lua index e7fcdc8bb1..2c9bd6d17f 100644 --- a/src/resources/pandoc/datadir/_utils.lua +++ b/src/resources/pandoc/datadir/_utils.lua @@ -265,59 +265,75 @@ local function get_type(v) return pandoc_type end -local function as_inlines(v) - if v == nil then - return pandoc.Inlines({}) - end - local t = pandoc.utils.type(v) - if t == "Inlines" then - ---@cast v pandoc.Inlines - return v - elseif t == "Blocks" then - return pandoc.utils.blocks_to_inlines(v) - elseif t == "Inline" then - return pandoc.Inlines({v}) - elseif t == "Block" then - return pandoc.utils.blocks_to_inlines({v}) - end +--- Blocks metatable +local BlocksMT = getmetatable(pandoc.Blocks{}) +--- Inlines metatable +local InlinesMT = getmetatable(pandoc.Inlines{}) - if type(v) == "table" then - local result = pandoc.Inlines({}) - for i, v in ipairs(v) do - tappend(result, as_inlines(v)) +--- Turns the given object into a `Inlines` list. +-- +-- Works mostly like `pandoc.Inlines`, but doesn't a do a full +-- unmarshal/marshal roundtrip. This buys performance, at the cost of +-- less thorough type checks. +-- +-- NOTE: The input object might be modified *destructively*! +local function as_inlines(obj) + local pt = pandoc.utils.type(obj) + if pt == 'Inlines' then + return obj + elseif pt == "Inline" then + -- Faster than calling pandoc.Inlines + return setmetatable({obj}, InlinesMT) + elseif pt == 'List' or pt == 'table' then + if obj[1] and pandoc.utils.type(obj[1]) == 'Block' then + return pandoc.utils.blocks_to_inlines(obj) end - return result + -- Faster than calling pandoc.Inlines + return setmetatable(obj, InlinesMT) + elseif pt == "Block" then + return pandoc.utils.blocks_to_inlines({obj}) + elseif pt == "Blocks" then + return pandoc.utils.blocks_to_inlines(obj) + else + return pandoc.Inlines(obj or {}) end - - -- luacov: disable - fatal("as_inlines: invalid type " .. t) - return pandoc.Inlines({}) - -- luacov: enable end -local function as_blocks(v) - if v == nil then - return pandoc.Blocks({}) - end - local t = pandoc.utils.type(v) - if t == "Blocks" then - return v - elseif t == "Inlines" then - return pandoc.Blocks({pandoc.Plain(v)}) - elseif t == "Block" then - return pandoc.Blocks({v}) - elseif t == "Inline" then - return pandoc.Blocks({pandoc.Plain(v)}) - end - - if type(v) == "table" then - return pandoc.Blocks(v) +--- Turns the given object into a `Blocks` list. +-- +-- Works mostly like `pandoc.Blocks`, but doesn't a do a full +-- unmarshal/marshal roundtrip. This buys performance, at the cost of +-- less thorough type checks. +-- +-- NOTE: The input object might be modified *destructively*! +-- +-- This might need some benchmarking. +local function as_blocks(obj) + local pt = pandoc.utils.type(obj) + if pt == 'Blocks' then + return obj + elseif pt == 'Block' then + -- Assigning a metatable directly is faster than calling + -- `pandoc.Blocks`. + return setmetatable({obj}, BlocksMT) + elseif pt == 'Inline' then + return setmetatable({pandoc.Plain{obj}}, BlocksMT) + elseif pt == 'Inlines' then + if next(obj) then + return setmetatable({pandoc.Plain(obj)}, BlocksMT) + end + return setmetatable({}, BlocksMT) + elseif pt == 'List' or (pt == 'table' and obj[1]) then + if pandoc.utils.type(obj[1]) == 'Inline' then + obj = {pandoc.Plain(obj)} + end + return setmetatable(obj, BlocksMT) + elseif (pt == 'table' and obj.long) or pt == 'Caption' then + -- Looks like a Caption + return as_blocks(obj.long) + else + return pandoc.Blocks(obj or {}) end - - -- luacov: disable - fatal("as_blocks: invalid type " .. t) - return pandoc.Blocks({}) - -- luacov: enable end local function match_fun(reset, ...)