aboutsummaryrefslogtreecommitdiff
path: root/_extensions/quarto-ext/include-code-files
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2025-02-24 15:00:10 +0100
committerJonas Smedegaard <dr@jones.dk>2025-02-24 15:00:10 +0100
commit58042b5135ddf74bf5945c46eafd3828739e045c (patch)
tree76bca3d1d18c8979087660e052ddd4facd41ecc8 /_extensions/quarto-ext/include-code-files
initial draft
Diffstat (limited to '_extensions/quarto-ext/include-code-files')
-rw-r--r--_extensions/quarto-ext/include-code-files/_extension.yml9
-rw-r--r--_extensions/quarto-ext/include-code-files/include-code-files.lua130
2 files changed, 139 insertions, 0 deletions
diff --git a/_extensions/quarto-ext/include-code-files/_extension.yml b/_extensions/quarto-ext/include-code-files/_extension.yml
new file mode 100644
index 0000000..13c4960
--- /dev/null
+++ b/_extensions/quarto-ext/include-code-files/_extension.yml
@@ -0,0 +1,9 @@
+title: Include Code Files
+author: Bruno Beaufils
+version: 1.0.0
+quarto-required: ">=1.2"
+contributes:
+ filters:
+ - include-code-files.lua
+
+
diff --git a/_extensions/quarto-ext/include-code-files/include-code-files.lua b/_extensions/quarto-ext/include-code-files/include-code-files.lua
new file mode 100644
index 0000000..c74aa53
--- /dev/null
+++ b/_extensions/quarto-ext/include-code-files/include-code-files.lua
@@ -0,0 +1,130 @@
+--- include-code-files.lua – filter to include code from source files
+---
+--- Copyright: © 2020 Bruno BEAUFILS
+--- License: MIT – see LICENSE file for details
+
+--- Dedent a line
+local function dedent(line, n)
+ return line:sub(1, n):gsub(" ", "") .. line:sub(n + 1)
+end
+
+--- Find snippet start and end.
+--
+-- Use this to populate startline and endline.
+-- This should work like pandocs snippet functionality: https://github.com/owickstrom/pandoc-include-code/tree/master
+local function snippet(cb, fh)
+ if not cb.attributes.snippet then
+ return
+ end
+
+ -- Cannot capture enum: http://lua-users.org/wiki/PatternsTutorial
+ local comment
+ local comment_stop = ""
+ if
+ string.match(cb.attributes.include, ".py$")
+ or string.match(cb.attributes.include, ".jl$")
+ or string.match(cb.attributes.include, ".r$")
+ then
+ comment = "#"
+ elseif string.match(cb.attributes.include, ".o?js$") or string.match(cb.attributes.include, ".css$") then
+ comment = "//"
+ elseif string.match(cb.attributes.include, ".lua$") then
+ comment = "--"
+ elseif string.match(cb.attributes.include, ".html$") then
+ comment = "<!%-%-"
+ comment_stop = " *%-%->"
+ else
+ -- If not known assume that it is something one or two long and not alphanumeric.
+ comment = "%W%W?"
+ end
+
+ local p_start = string.format("^ *%s start snippet %s%s", comment, cb.attributes.snippet, comment_stop)
+ local p_stop = string.format("^ *%s end snippet %s%s", comment, cb.attributes.snippet, comment_stop)
+ local start, stop = nil, nil
+
+ -- Cannot use pairs.
+ local line_no = 1
+ for line in fh:lines() do
+ if start == nil then
+ if string.match(line, p_start) then
+ start = line_no + 1
+ end
+ elseif stop == nil then
+ if string.match(line, p_stop) then
+ stop = line_no - 1
+ end
+ else
+ break
+ end
+ line_no = line_no + 1
+ end
+
+ -- Reset so nothing is broken later on.
+ fh:seek("set")
+
+ -- If start and stop not found, just continue
+ if start == nil or stop == nil then
+ return nil
+ end
+
+ cb.attributes.startLine = tostring(start)
+ cb.attributes.endLine = tostring(stop)
+end
+
+--- Filter function for code blocks
+local function transclude(cb)
+ if cb.attributes.include then
+ local content = ""
+ local fh = io.open(cb.attributes.include)
+ if not fh then
+ io.stderr:write("Cannot open file " .. cb.attributes.include .. " | Skipping includes\n")
+ else
+ local number = 1
+ local start = 1
+
+ -- change hyphenated attributes to PascalCase
+ for i, pascal in pairs({ "startLine", "endLine" }) do
+ local hyphen = pascal:gsub("%u", "-%0"):lower()
+ if cb.attributes[hyphen] then
+ cb.attributes[pascal] = cb.attributes[hyphen]
+ cb.attributes[hyphen] = nil
+ end
+ end
+
+ -- Overwrite startLine and stopLine with the snippet if any.
+ snippet(cb, fh)
+
+ if cb.attributes.startLine then
+ cb.attributes.startFrom = cb.attributes.startLine
+ start = tonumber(cb.attributes.startLine)
+ end
+
+ for line in fh:lines("L") do
+ if cb.attributes.dedent then
+ line = dedent(line, cb.attributes.dedent)
+ end
+ if number >= start then
+ if not cb.attributes.endLine or number <= tonumber(cb.attributes.endLine) then
+ content = content .. line
+ end
+ end
+ number = number + 1
+ end
+
+ fh:close()
+ end
+
+ -- remove key-value pair for used keys
+ cb.attributes.include = nil
+ cb.attributes.startLine = nil
+ cb.attributes.endLine = nil
+ cb.attributes.dedent = nil
+
+ -- return final code block
+ return pandoc.CodeBlock(content, cb.attr)
+ end
+end
+
+return {
+ { CodeBlock = transclude },
+}