From f4afc91c0785cb112bc507b0450f86278ad1cc2a Mon Sep 17 00:00:00 2001 From: Ekaitz Zarraga Date: Thu, 28 May 2020 16:10:52 +0200 Subject: ToC filter --- filters/toc.lua | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 filters/toc.lua (limited to 'filters') diff --git a/filters/toc.lua b/filters/toc.lua new file mode 100644 index 0000000..479b082 --- /dev/null +++ b/filters/toc.lua @@ -0,0 +1,128 @@ +--[[ +-- This filter creates the ToC from the headings so if it's applied before +-- heading transformations, transformations do not appear in the ToC. +-- +-- It's useful for HTML output where the anchors are added in an extra filter. +-- Default ToC included the anchor. Using this filter anchor is not included. +-- +-- It supports toc-depth metadata field. +--]] +pandoc.utils = require 'pandoc.utils' +list_of_headers = {} + + +if FORMAT:match 'html' then + + -- TODO: ReMOVE THIS + function headers2links_level(headers) + local links = {} + for i, v in ipairs(headers) do + local l = pandoc.Link( pandoc.utils.stringify(v), "#"..v.identifier ) + table.insert(links,{ v.level, pandoc.Plain( l ) }) + end + return links + end + + function create_ol(headers, toc_depth) + if #headers == 0 then + return pandoc.Null() + end + local links = headers2links_level(headers) + + -- min_level doesn't have to be 1, it could be larger + local min_level = -1 + for i, v in ipairs(links) do + if min_level == -1 or v[1] < min_level then min_level = v[1] end + end + + -- max level needed for grouping + local max_level = 0 + if toc_depth >= 1 then + -- toc_depth is set, so remove elements with larger level than + -- max_level + max_level = min_level + toc_depth - 1 + local i = 1 + while i <= #links do + if links[i][1] > max_level then + table.remove(links, i) + i = i - 1 + end + i = i + 1 + end + else + -- toc_depth not set so get the max_level from the contents + for i, v in ipairs(links) do + if v[1] > max_level then max_level = v[1] end + end + end + + -- Iterate trough valid levels + while max_level > min_level do + local i = 1 + local tmp = {} + while i <= #links do + local level = links[i][1] -- Element's level + if level == max_level then -- Found group + -- If group is found, it usually has a parent + local parent = links[i-1] + if parent[1] == level - 1 then + -- It's the direct parent, add it's contents + parent = parent[2] + else + -- Not direct parent, warn and discard + io.stderr:write("[WARNING] Element of level ") + io.stderr:write(tostring(parent[1])) + io.stderr:write(" has no direct children: ") + io.stderr:write(pandoc.utils.stringify(parent[2])) + io.stderr:write("\n") + parent = pandoc.Null() + end + + while i <= #links and links[i][1] == level do + -- Capture the group + table.insert(tmp, table.remove(links, i)[2]) + end + + if #tmp ~= 0 then + -- There was a group, convert it to + -- {parent, OrderedList(group)} + local children = pandoc.OrderedList(tmp) + local pos = i + if parent.t ~= "Null" then + -- There was a parent: extract it from links + table.remove(links, i-1) + pos = i - 1 + end + table.insert(links, pos, {level-1, {parent, children}}) + tmp = {} + end + end + i = i + 1 + end + max_level = max_level - 1 + end + + local lis = {} + for i,v in pairs(links) do + _,lis[i] = table.unpack(v) + end + return pandoc.OrderedList(lis) + + end + + function Meta(m) + local toc_depth + if m["toc-depth"] ~= nil then + toc_depth = tonumber(m["toc-depth"][1]["c"]) + else + toc_depth = -1 + end + m["table-of-contents"] = pandoc.MetaBlocks({create_ol(list_of_headers, toc_depth)}) + return m + end + + function Header(el) + table.insert(list_of_headers, el) + return el + end +end -- cgit v1.2.3