summaryrefslogtreecommitdiff
path: root/filters/toc.lua
blob: a1bc33e38716dd2096c050a031cb52ff038e54e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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

    function headers2link_and_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 = headers2link_and_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 = -1

        if m["toc-depth"] ~= nil then
            toc_depth = tonumber(m["toc-depth"][1].text)
        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