aboutsummaryrefslogtreecommitdiff
path: root/pandoc/filters/include-files.lua
blob: a42ae1ecdecef59447326fa5b7ae0ff3d31073c9 (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
129
130
131
132
133
134
135
136
137
138
139
140
--- include-files.lua – filter to include Markdown files
---
--- Copyright: © 2019–2021 Albert Krewinkel
--- License:   MIT – see LICENSE file for details

-- Module pandoc.path is required and was added in version 2.12
PANDOC_VERSION:must_be_at_least '2.12'

local List = require 'pandoc.List'
local path = require 'pandoc.path'
local system = require 'pandoc.system'

--- Get include auto mode
local include_auto = false
function get_vars (meta)
  if meta['include-auto'] then
    include_auto = true
  end
end

--- Keep last heading level found
local last_heading_level = 0
function update_last_level(header)
  last_heading_level = header.level
end

--- Update contents of included file
local function update_contents(blocks, shift_by, include_path)
  local update_contents_filter = {
    -- Shift headings in block list by given number
    Header = function (header)
      if shift_by then
        header.level = header.level + shift_by
      end
      return header
    end,
    -- If image paths are relative then prepend include file path
    Image = function (image)
      if path.is_relative(image.src) and image.src:sub(1,5) ~= "logos" then
        image.src = path.normalize(path.join({include_path, image.src}))
      end
      return image
    end,
    -- If tex input paths are relative then prepend include file path
    RawBlock = function (el)
      io.stderr:write("include-filter: Raw Block found.\n")
      -- Only handle tex RawBlocks
      if el.format ~= 'tex' then
        return el
      end
      io.stderr:write("include-filter: Raw Tex Block found.\n")

      el.text = el.text:gsub('\\input{([^}]+)}', function(match)
        local input_path = match
        if path.is_relative(include_path) then
          include_path = path.normalize(path.join({include_path, input_path}))
        end

        return '\\input{' .. include_path .. '}'
      end)

      return el
    end,
    -- Update path for include-code-files.lua filter style CodeBlocks
    CodeBlock = function (cb)
      if cb.attributes.include and path.is_relative(cb.attributes.include) then
        cb.attributes.include =
          path.normalize(path.join({include_path, cb.attributes.include}))
        end
      return cb
    end
  }

  return pandoc.walk_block(pandoc.Div(blocks), update_contents_filter).content
end

--- Filter function for code blocks
local transclude
function transclude (cb)
  -- ignore code blocks which are not of class "include".
  if not cb.classes:includes 'include' then
    return
  end

  -- Markdown is used if this is nil.
  local format = cb.attributes['format']

  -- Attributes shift headings
  local shift_heading_level_by = 0
  local shift_input = cb.attributes['shift-heading-level-by']
  if shift_input then
    shift_heading_level_by = tonumber(shift_input)
  else
    if include_auto then
      -- Auto shift headings
      shift_heading_level_by = last_heading_level
    end
  end

  --- keep track of level before recursion
  local buffer_last_heading_level = last_heading_level

  local blocks = List:new()
  for line in cb.text:gmatch('[^\n]+') do
    if line:sub(1,2) ~= '//' then
      local fh = io.open(line)
      if not fh then
        io.stderr:write("Cannot open file " .. line .. " | Skipping includes\n")
      else
        -- read file as the given format with global reader options
        local contents = pandoc.read(
          fh:read '*a',
          format,
          PANDOC_READER_OPTIONS
        ).blocks
        last_heading_level = 0
        -- recursive transclusion
        contents = system.with_working_directory(
            path.directory(line),
            function ()
              return pandoc.walk_block(
                pandoc.Div(contents),
                { Header = update_last_level, CodeBlock = transclude }
              )
            end).content
        --- reset to level before recursion
        last_heading_level = buffer_last_heading_level
        blocks:extend(update_contents(contents, shift_heading_level_by,
                                      path.directory(line)))
        fh:close()
      end
    end
  end
  return blocks
end

return {
  { Meta = get_vars },
  { Header = update_last_level, CodeBlock = transclude }
}