|
| 1 | +--- include-code-files.lua – filter to include code from source files |
| 2 | +--- |
| 3 | +--- Copyright: © 2020 Bruno BEAUFILS |
| 4 | +--- License: MIT – see LICENSE file for details |
| 5 | + |
| 6 | +--- Dedent a line |
| 7 | +local function dedent(line, n) |
| 8 | + return line:sub(1, n):gsub(" ", "") .. line:sub(n + 1) |
| 9 | +end |
| 10 | + |
| 11 | +--- Find snippet start and end. |
| 12 | +-- |
| 13 | +-- Use this to populate startline and endline. |
| 14 | +-- This should work like pandocs snippet functionality: https://github.com/owickstrom/pandoc-include-code/tree/master |
| 15 | +local function snippet(cb, fh) |
| 16 | + if not cb.attributes.snippet then |
| 17 | + return |
| 18 | + end |
| 19 | + |
| 20 | + -- Cannot capture enum: http://lua-users.org/wiki/PatternsTutorial |
| 21 | + local comment |
| 22 | + local comment_stop = "" |
| 23 | + if |
| 24 | + string.match(cb.attributes.include, ".py$") |
| 25 | + or string.match(cb.attributes.include, ".jl$") |
| 26 | + or string.match(cb.attributes.include, ".r$") |
| 27 | + then |
| 28 | + comment = "#" |
| 29 | + elseif string.match(cb.attributes.include, ".o?js$") or string.match(cb.attributes.include, ".css$") then |
| 30 | + comment = "//" |
| 31 | + elseif string.match(cb.attributes.include, ".lua$") then |
| 32 | + comment = "--" |
| 33 | + elseif string.match(cb.attributes.include, ".html$") then |
| 34 | + comment = "<!%-%-" |
| 35 | + comment_stop = " *%-%->" |
| 36 | + else |
| 37 | + -- If not known assume that it is something one or two long and not alphanumeric. |
| 38 | + comment = "%W%W?" |
| 39 | + end |
| 40 | + |
| 41 | + local p_start = string.format("^ *%s start snippet %s%s", comment, cb.attributes.snippet, comment_stop) |
| 42 | + local p_stop = string.format("^ *%s end snippet %s%s", comment, cb.attributes.snippet, comment_stop) |
| 43 | + local start, stop = nil, nil |
| 44 | + |
| 45 | + -- Cannot use pairs. |
| 46 | + local line_no = 1 |
| 47 | + for line in fh:lines() do |
| 48 | + if start == nil then |
| 49 | + if string.match(line, p_start) then |
| 50 | + start = line_no + 1 |
| 51 | + end |
| 52 | + elseif stop == nil then |
| 53 | + if string.match(line, p_stop) then |
| 54 | + stop = line_no - 1 |
| 55 | + end |
| 56 | + else |
| 57 | + break |
| 58 | + end |
| 59 | + line_no = line_no + 1 |
| 60 | + end |
| 61 | + |
| 62 | + -- Reset so nothing is broken later on. |
| 63 | + fh:seek("set") |
| 64 | + |
| 65 | + -- If start and stop not found, just continue |
| 66 | + if start == nil or stop == nil then |
| 67 | + return nil |
| 68 | + end |
| 69 | + |
| 70 | + cb.attributes.startLine = tostring(start) |
| 71 | + cb.attributes.endLine = tostring(stop) |
| 72 | +end |
| 73 | + |
| 74 | +--- Filter function for code blocks |
| 75 | +local function transclude(cb) |
| 76 | + if cb.attributes.include then |
| 77 | + local content = "" |
| 78 | + local fh = io.open(cb.attributes.include) |
| 79 | + if not fh then |
| 80 | + io.stderr:write("Cannot open file " .. cb.attributes.include .. " | Skipping includes\n") |
| 81 | + else |
| 82 | + local number = 1 |
| 83 | + local start = 1 |
| 84 | + |
| 85 | + -- change hyphenated attributes to PascalCase |
| 86 | + for i, pascal in pairs({ "startLine", "endLine" }) do |
| 87 | + local hyphen = pascal:gsub("%u", "-%0"):lower() |
| 88 | + if cb.attributes[hyphen] then |
| 89 | + cb.attributes[pascal] = cb.attributes[hyphen] |
| 90 | + cb.attributes[hyphen] = nil |
| 91 | + end |
| 92 | + end |
| 93 | + |
| 94 | + -- Overwrite startLine and stopLine with the snippet if any. |
| 95 | + snippet(cb, fh) |
| 96 | + |
| 97 | + if cb.attributes.startLine then |
| 98 | + cb.attributes.startFrom = cb.attributes.startLine |
| 99 | + start = tonumber(cb.attributes.startLine) |
| 100 | + end |
| 101 | + |
| 102 | + for line in fh:lines("L") do |
| 103 | + if cb.attributes.dedent then |
| 104 | + line = dedent(line, cb.attributes.dedent) |
| 105 | + end |
| 106 | + if number >= start then |
| 107 | + if not cb.attributes.endLine or number <= tonumber(cb.attributes.endLine) then |
| 108 | + content = content .. line |
| 109 | + end |
| 110 | + end |
| 111 | + number = number + 1 |
| 112 | + end |
| 113 | + |
| 114 | + fh:close() |
| 115 | + end |
| 116 | + |
| 117 | + -- remove key-value pair for used keys |
| 118 | + cb.attributes.include = nil |
| 119 | + cb.attributes.startLine = nil |
| 120 | + cb.attributes.endLine = nil |
| 121 | + cb.attributes.dedent = nil |
| 122 | + |
| 123 | + -- return final code block |
| 124 | + return pandoc.CodeBlock(content, cb.attr) |
| 125 | + end |
| 126 | +end |
| 127 | + |
| 128 | +return { |
| 129 | + { CodeBlock = transclude }, |
| 130 | +} |
0 commit comments