bavbavhaus.net/pandoc/sitemap.lua

140 lines
3.9 KiB
Lua

-- local l = require("pandoc.logging")
local read = pandoc.read
assert(#arg == 2, "\n" ..
"[ERROR] usage: pandoc lua sitemap.lua <index file> <output file>")
local index_file, output_file = table.unpack(arg)
local node_len = 1
local node_list, node_map = {}, {}
local function collect (fp)
local f = assert(io.open(fp), "\n" ..
"[ERROR] could not open " .. fp .. " for reading.")
table.insert(node_list, fp)
node_map[fp] = { order = #node_list, nodes = {} }
if fp:len() > node_len then node_len = fp:len() end -- track node len
local data = f:read("a")
f:close()
local visited = {}
read(data):walk({ Link = function (link)
if not link.target:find(".%.md$") then return end
local f = io.open(link.target)
if f == nil then return else f:close() end
if visited[link.target] then return end
visited[link.target] = true
table.insert(node_map[fp]["nodes"], link.target)
if not node_map[link.target] then collect(link.target) end
end })
end
collect(index_file)
local L = {
L = "<",
R = ">",
NW = ",", N = "-", NE = ".",
W = "|", X = " ", E = "|",
SW = "'", S = "-", SE = "'",
}
local arm_a, arm_b = {}, {}
for i,_ in ipairs(node_list) do
arm_a[i], arm_b[i] = {}, {}
end
local arm_row_len = 1
for _,node in ipairs(node_list) do
local node_a = node_map[node]
for _,node in ipairs(node_a["nodes"]) do
local a, b = node_a["order"], node_map[node]["order"]
local i_a, i_b = math.min(a,b), math.max(a,b)
local arm = a < b and arm_a or arm_b
-- find the first free column
local col = 1
while true do
for i=i_a,i_b do
local cell = arm[i][col]
if cell ~= nil and cell ~= L.X then
col = col + 1
goto continue
end
end
break
::continue::
end
col = col == 1 and 2 or col
if col > arm_row_len then arm_row_len = col end -- track row len
-- layout starting row
local row_a = arm[i_a]
if row_a[1] == nil or row_a[1] == L.X then
row_a[1] = a < b and L.N or L.L
end
for i=2,col-1 do
if row_a[i] == nil or row_a[i] == L.X then row_a[i] = L.N end
end
row_a[col] = a < b and L.NW or L.NE
-- layout rows between start and end
for i=i_a+1,i_b-1 do
local row_i = arm[i]
for j=1,col-1 do
if row_i[j] == nil then row_i[j] = L.X end
end
row_i[col] = L.W
end
-- layout ending row
local row_b = arm[i_b]
if row_b[1] == nil or row_b[1] == L.X then
row_b[1] = a < b and L.R or L.S
end
for i=2,col-1 do
if row_b[i] == nil or row_b[i] == L.X then row_b[i] = L.S end
end
row_b[col] = a < b and L.SW or L.SE
end
end
-- construct data lines
local lines = {}
table.insert(lines, "<article id=\"sitemap\" class=\"post tail\">")
table.insert(lines, "<header>")
table.insert(lines, "<h1>sitemap</h1>")
table.insert(lines, "</header>")
table.insert(lines, "<pre>")
for i,node in ipairs(node_list) do
local line = {}
local node_padded = ("%-"..node_len.."s"):format(node)
local node_anchor = node_padded:gsub("(.+)%.md", "<a href=\"%1.html\">%1</a>")
local row_a, row_b = arm_a[i], arm_b[i]
-- reverse arm a row
for j=1,#row_a//2 do
row_a[j], row_a[#row_a-j+1] = row_a[#row_a-j+1], row_a[j]
end
table.insert(line, (("%"..arm_row_len.."s"):format(table.concat(row_a))))
table.insert(line, ((" %s "):format(node_anchor)))
table.insert(line, (table.concat(row_b)))
table.insert(lines, table.concat(line))
end
table.insert(lines, "</pre>")
table.insert(lines, "</article>")
local data = table.concat(lines, "\n")
-- read existing data from output_file
local f = io.open(output_file)
local data_old
if f then data_old = f:read("a"); f:close() end
-- write to output_file if no output_file yet or data changed
if not data_old or data ~= data_old then
local f = assert(io.open(output_file, "w"), "\n" ..
"[ERROR] could not open " .. output_file .. " for writing.")
f:write(data)
f:close()
end