diff options
Diffstat (limited to '')
-rwxr-xr-x | notYetAnotherWiki.lua | 213 |
1 files changed, 145 insertions, 68 deletions
diff --git a/notYetAnotherWiki.lua b/notYetAnotherWiki.lua index 7268170..8c9a5c9 100755 --- a/notYetAnotherWiki.lua +++ b/notYetAnotherWiki.lua | |||
@@ -2,13 +2,63 @@ | |||
2 | 2 | ||
3 | -- Read the README file for what this is all about. If there is no README or similar, then you can find the link to the source below. | 3 | -- Read the README file for what this is all about. If there is no README or similar, then you can find the link to the source below. |
4 | 4 | ||
5 | local lcmark = require("lcmark") -- https://github.com/jgm/lcmark | 5 | local lunamark = require("lunamark") -- https://github.com/jgm/lunamark |
6 | |||
7 | local opts = { | ||
8 | layout='compact', | ||
9 | -- This list is copied from the lunamark source code, until I discover a way to discover it. The descriptions are useful to. | ||
10 | containers=false, -- Put sections in containers (e.g. div or section tags) | ||
11 | slides=false, -- Like containers, but do not nest them | ||
12 | startnum=true, -- Start number of an ordered list is significant | ||
13 | smart=false, -- Smart typography (quotes, dashes, ellipses) | ||
14 | preserve_tabs=true, -- Don't expand tabs to spaces | ||
15 | notes=true, -- Footnotes | ||
16 | inline_notes=true, -- Inline footnotes | ||
17 | definition_lists=true, -- Definition lists | ||
18 | citations=true, -- Citations | ||
19 | citation_nbsps=true, -- Turn spacing into non-breaking spaces in citations | ||
20 | fenced_code_blocks=true, -- Fenced code blocks | ||
21 | lua_metadata=true, -- Lua metadata | ||
22 | pandoc_title_blocks=true, -- Pandoc style title blocks | ||
23 | hash_enumerators=true, -- may be used as ordered list enumerator | ||
24 | require_blank_before_blockquote=false, | ||
25 | require_blank_before_header=false, | ||
26 | require_blank_before_fenced_code_block=false, | ||
27 | fancy_lists=true, -- Pandoc style fancy lists | ||
28 | task_list=true, -- GitHub-Flavored Markdown task list | ||
29 | strikeout=true, -- Strike-through with double tildes | ||
30 | mark=true, -- Highlight with double equals | ||
31 | subscript=true, -- Subscripted text between tildes | ||
32 | superscript=true, -- Superscripted text between circumflexes | ||
33 | bracketed_spans=true, -- Spans with attributes | ||
34 | fenced_divs=true, -- Divs with attributes | ||
35 | raw_attribute=true, -- Raw pass-through on code elements | ||
36 | fenced_code_attributes=true, -- Fenced code block attributes | ||
37 | link_attributes=true, -- Link attributes | ||
38 | pipe_tables=true, -- PHP Markdown Extra pipe table support | ||
39 | table_captions=true, -- Table caption syntax extension | ||
40 | header_attributes=true, -- Header attributes | ||
41 | line_blocks=true, -- Line blocks | ||
42 | escaped_line_breaks=true, -- Pandoc-style escaped hard line breaks | ||
43 | } | ||
44 | local writer = lunamark.writer.html5.new(opts) | ||
45 | local template = '' | ||
46 | local h = io.open("default.template", 'r') | ||
47 | if nil ~= h then | ||
48 | template = h:read('*a') | ||
49 | h:close() | ||
50 | else | ||
51 | print('oops! No such file ' .. 'default.template') | ||
52 | end | ||
53 | -- Can override the various writer functions, there's something for each of the basic HTML elements. | ||
54 | -- Override anything that's generic. | ||
55 | local parse = lunamark.reader.markdown.new(writer, opts) | ||
6 | 56 | ||
7 | 57 | ||
8 | 58 | ||
9 | -- Some global data. | 59 | -- Some global data. |
10 | local globalData = { | 60 | local globalData = { |
11 | ['_'] = ' ', ['dlr'] = '$', | 61 | ['s'] = ' ', ['_'] = ' ', ['dlr'] = '$', |
12 | ['devuanCinnabarDark'] = '#310202', ['devuanCinnabarLight'] = '#510505', | 62 | ['devuanCinnabarDark'] = '#310202', ['devuanCinnabarLight'] = '#510505', |
13 | ['devuanDarkPurpyDark'] = '#33313b', ['devuanDarkPurpyLight'] = '#3c3a45', | 63 | ['devuanDarkPurpyDark'] = '#33313b', ['devuanDarkPurpyLight'] = '#3c3a45', |
14 | ['devuanDeepSeaDark'] = '#132f40', ['devuanDeepSeaLight'] = '#1a4562', | 64 | ['devuanDeepSeaDark'] = '#132f40', ['devuanDeepSeaLight'] = '#1a4562', |
@@ -23,7 +73,7 @@ local Sites, Files, Subs = {}, {}, {} | |||
23 | 73 | ||
24 | -- Useful functions, part 0. | 74 | -- Useful functions, part 0. |
25 | 75 | ||
26 | -- A simple table.subtable = subtable wont work, you end up with a reference so that changes to the later get applaid to the former. | 76 | -- A simple table.subtable = subtable wont work, you end up with a reference so that changes to the later get applied to the former. |
27 | local derefiTable = function(t, strip) | 77 | local derefiTable = function(t, strip) |
28 | local argh = {} | 78 | local argh = {} |
29 | for l, y in ipairs(t) do if (l ~= y.name) and strip then table.insert(argh, y) end end | 79 | for l, y in ipairs(t) do if (l ~= y.name) and strip then table.insert(argh, y) end end |
@@ -50,29 +100,47 @@ local stringBits = function(l) | |||
50 | end | 100 | end |
51 | 101 | ||
52 | 102 | ||
103 | -- Put a value into the Files or Subs table, creating things if needed. | ||
104 | local toFile = function(name, key, value) | ||
105 | if nil == Files[name] then Files[name] = {} end | ||
106 | if nil ~= key then Files[name][key] = value end | ||
107 | for i, v in ipairs{'metadata', 'bits', } do | ||
108 | if nil == Files[name][v] then Files[name][v] = {} end | ||
109 | end | ||
110 | end | ||
111 | local toSub = function(name, key, value) | ||
112 | if nil == Subs[name] then Subs[name] = {} end | ||
113 | if nil ~= key then Subs[name][key] = value end | ||
114 | for i, v in ipairs{'metadata', 'bits', 'files', 'subs'} do | ||
115 | if nil == Subs[name][v] then Subs[name][v] = {} end | ||
116 | end | ||
117 | end | ||
118 | |||
119 | |||
53 | 120 | ||
54 | -- Actually start doing things. | 121 | -- Actually start doing things. |
55 | 122 | ||
56 | -- Scan the subdirectories looking for .md files. | 123 | -- Scan the subdirectories looking for .md files. |
57 | local directory = arg[1] | 124 | local directory = arg[1] |
125 | toSub('') --Subs[''] = {files = {}, subs = {}, bits = {}} | ||
58 | if nil == directory then directory = '.' end | 126 | if nil == directory then directory = '.' end |
59 | if '.' ~= directory then | 127 | if '.' ~= directory then |
60 | for l in io.popen('find . -name "*.md" -type f,l -printf "%P\n"'):lines() do | 128 | for l in io.popen('find . -name "*.md" -type f,l -printf "%P\n"'):lines() do |
61 | Files[string.gsub(l, '%.md$', '')] = {} | 129 | toFile(string.gsub(l, '%.md$', '')) |
62 | end | 130 | end |
63 | end | 131 | end |
64 | -- Can add in a distant directory to, for putting it's results in the current directory. | 132 | -- Can add in a distant directory to, for putting it's results in the current directory. |
65 | for l in io.popen('find ' .. directory .. ' -name "*.md" -type f,l -printf "%P\n"'):lines() do | 133 | for l in io.popen('find ' .. directory .. ' -name "*.md" -type f,l -printf "%P\n"'):lines() do |
66 | local n = string.gsub(l, '%.md$', '') | 134 | local n = string.gsub(l, '%.md$', '') |
67 | if nil == Files[n] then Files[n] = {} end | 135 | if nil == Files[n] then toFile(n) end |
68 | end | 136 | end |
69 | 137 | ||
70 | -- Gotta figure out all the files and subs first. File and sub metadata comes along for the ride. | 138 | -- Gotta figure out all the files and subs first. File and sub metadata comes along for the ride. |
71 | Subs[''] = {files = {}, subs = {}, bits = {}} | ||
72 | for name, file in pairs(Files) do | 139 | for name, file in pairs(Files) do |
73 | local bitter, path = '', '' | 140 | local bitter, path = '', '' |
74 | local bits, bit = stringBits(name) | 141 | local bits, bit = stringBits(name) |
75 | local ln = #bits | 142 | local ln = #bits |
143 | local body, metadata = '', {} | ||
76 | 144 | ||
77 | -- Go through our bits, construct Subs with bits. | 145 | -- Go through our bits, construct Subs with bits. |
78 | Files[name].bits = bits | 146 | Files[name].bits = bits |
@@ -82,41 +150,61 @@ for name, file in pairs(Files) do | |||
82 | for i, d in ipairs(bits) do | 150 | for i, d in ipairs(bits) do |
83 | if '' ~= path then path = path .. '/' end | 151 | if '' ~= path then path = path .. '/' end |
84 | path = path .. d | 152 | path = path .. d |
85 | if nil == Subs[path] then Subs[path] = {files = {}, subs = {}} end | 153 | toSub(path, 'bits', derefiTable(bits, true)) |
86 | if i < ln then Subs[path].subs[bits[i + 1]] = bits[i + 1] end | 154 | if i < ln then Subs[path].subs[bits[i + 1]] = bits[i + 1] end |
87 | Subs[path].bits = derefiTable(bits, true) | ||
88 | if i < ln then table.remove(Subs[path].bits, #bits) end | 155 | if i < ln then table.remove(Subs[path].bits, #bits) end |
89 | end | 156 | end |
90 | 157 | ||
91 | -- Start the file parsing here, coz we need it's metadata. | 158 | if '.md' == string.sub(name, -3, -1) then |
92 | -- print('Parsing ' .. name .. '.md') | 159 | -- This is a metadata only file, no content, stash the matadata. |
93 | local h = io.open(name .. '.md', 'r') | 160 | for l in io.open(name .. '.md'):lines() do |
94 | -- TODO - should bail here on error? | 161 | for k, v in string.gmatch(l, "(%w+)%s*=%s*(.+)") do metadata[k] = v end |
95 | if nil ~= h then file.cm = h:read('*a') ; h:close() else print('oops! No such name ' .. name) end | 162 | end |
96 | -- Convert the CommonMark to HTML, including the metadata. | 163 | if '.md' == name then toSub(path, 'metadata', metadata) |
97 | local body, metadata, err = lcmark.convert(file.cm, "html", {smart = true, yaml_metadata = true, columns = 0}) | 164 | elseif '/.md' == string.sub(name, -4, -1) then toSub(path, 'metadata', metadata) |
98 | if nil == body then print('oops! ' .. err) | 165 | else toFile(string.sub(name, 1, -4), 'metadata', metadata) |
99 | elseif '' == body then | 166 | end |
100 | -- This is a metadata only file, no content, stash the matadata in it's directory. | ||
101 | Subs[path].metadata = metadata | ||
102 | Files[name] = nil | 167 | Files[name] = nil |
103 | else | 168 | else |
104 | -- Ordinary md file, stash it's metadata and parsed body. | 169 | |
105 | -- I need cmark-gfm, coz lcmark doesn't support tables and stuff, it only does basic cmark. | 170 | -- Start the file parsing here, coz we need it's metadata. |
106 | local out = '' | 171 | -- print('Parsing ' .. name .. '.md') |
107 | h = io.popen('if which cmark-gfm ; then echo "found" ; fi | tail -n 1') | 172 | h = io.open(name .. '.md', 'r') |
108 | if nil ~= h then out = h:read('*a') ; h:close() end | 173 | if nil ~= h then |
109 | if 'found\n' == out then | 174 | body = h:read('*a') ; h:close() |
110 | -- Have to strip out the metadata first, coz cmark-gfm doesn't grok that. | 175 | local f0, f1, token |
111 | h = io.popen('cp "' .. name .. '.md" "' .. name .. '02" ; csplit -ksz -f "' .. name .. '" "' .. name .. '.md" "/^---$/+1" "{1}" 2>/dev/null ; rm "' .. name .. '00" ; rm "' .. name .. '01" 2>/dev/null') | 176 | -- Deal with my typical double spaced sentence endings. |
112 | if nil ~= h then h:close() end | 177 | local result = '' |
113 | h = io.popen('cmark-gfm -t html -e footnotes -e table -e strikethrough -e autolink -e tagfilter -e tasklist "' .. name .. '02" ; rm "' .. name .. '02"') | 178 | local start = 1 |
114 | if nil ~= h then body = h:read('*a') ; h:close() end | 179 | repeat |
115 | else | 180 | f0, f1, token = string.find(body, '([%.%?%!] )', start) |
116 | print('cmark-gfm not found') | 181 | if fail ~= f0 then |
182 | result = result .. string.sub(body, start, f0) | ||
183 | if ' ' == string.sub(body, f1 + 1, f1 + 1) then | ||
184 | result = result .. '$s$ ' -- ' ' gets turned into hex 0xA0 by parse(). So feed it another metadata token that gets translated to . | ||
185 | else | ||
186 | result = result .. ' ' | ||
187 | end | ||
188 | start = f1 + 1 | ||
189 | end | ||
190 | until fail == f0 | ||
191 | result = result .. string.sub(body, start) | ||
192 | -- Do the same for fixing the \' \" \| etc mess pandoc left that sed can't fix. | ||
193 | body = result | ||
194 | result = '' | ||
195 | start = 1 | ||
196 | repeat | ||
197 | f0, f1, token = string.find(body, '(%\\[%"%\'%|%$])', start) | ||
198 | if fail ~= f0 then | ||
199 | result = result .. string.sub(body, start, f0 - 1) | ||
200 | start = f1 | ||
201 | end | ||
202 | until fail == f0 | ||
203 | result = result .. string.sub(body, start) | ||
204 | |||
205 | body = parse(result) | ||
117 | end | 206 | end |
118 | Files[name].metadata = metadata | 207 | toFile(name, 'body', body) |
119 | Files[name].body = body | ||
120 | table.insert(Subs[path].files, bit) | 208 | table.insert(Subs[path].files, bit) |
121 | end | 209 | end |
122 | end | 210 | end |
@@ -181,6 +269,10 @@ end | |||
181 | 269 | ||
182 | 270 | ||
183 | 271 | ||
272 | -- TODO - loop through Subs, doing whichPage and inheritance. | ||
273 | |||
274 | |||
275 | |||
184 | -- More of this actually doing things nonsense. | 276 | -- More of this actually doing things nonsense. |
185 | 277 | ||
186 | -- Loop through the files we found and actually create their HTML files. | 278 | -- Loop through the files we found and actually create their HTML files. |
@@ -193,14 +285,6 @@ for name, file in pairs(Files) do | |||
193 | path = table.concat(bits, '/', 1, ln) | 285 | path = table.concat(bits, '/', 1, ln) |
194 | 286 | ||
195 | if '' ~= body then | 287 | if '' ~= body then |
196 | -- Continue the parsing and conversion. Start by turning our parsed body into something the lcmark template system can grock. | ||
197 | local bod, err = lcmark.compile_template(body) | ||
198 | if nil == bod then print('oops! ' .. err) else | ||
199 | local templateFile = metadata.template | ||
200 | if nil == templateFile then templateFile = 'default' end | ||
201 | file.template = templateFile .. '.template' | ||
202 | end | ||
203 | |||
204 | -- Copy any metadata found in parent directories. | 288 | -- Copy any metadata found in parent directories. |
205 | local pth = '' | 289 | local pth = '' |
206 | for i, d in ipairs(bits) do | 290 | for i, d in ipairs(bits) do |
@@ -287,7 +371,6 @@ for name, file in pairs(Files) do | |||
287 | metadata.trail = metadata.trail .. b .. ' ' | 371 | metadata.trail = metadata.trail .. b .. ' ' |
288 | end | 372 | end |
289 | end | 373 | end |
290 | -- if '' == metadata.trail then metadata.trail = '<a href="' .. string.rep('../', ln) .. '/' .. whichPage('') .. '">home</a> ' end | ||
291 | 374 | ||
292 | -- Figure out this pages footer links. | 375 | -- Figure out this pages footer links. |
293 | if nil ~= metadata.pagehistory then metadata.history = '<p>Page <a href="' .. metadata.pagehistory .. '/' .. name .. '.md">history</a></p>' end | 376 | if nil ~= metadata.pagehistory then metadata.history = '<p>Page <a href="' .. metadata.pagehistory .. '/' .. name .. '.md">history</a></p>' end |
@@ -296,29 +379,26 @@ for name, file in pairs(Files) do | |||
296 | if metadata.footer ~= globalData.footer then metadata.footer = 'Web site ' .. metadata.footer end | 379 | if metadata.footer ~= globalData.footer then metadata.footer = 'Web site ' .. metadata.footer end |
297 | metadata.footer = '<p>' .. metadata.footer .. '</p>' | 380 | metadata.footer = '<p>' .. metadata.footer .. '</p>' |
298 | 381 | ||
299 | -- Apply the template to the body. | 382 | -- Do our own metadata replacement, it's simple and works better. |
300 | metadata.body = lcmark.apply_template(bod, metadata) | 383 | local temp = template |
301 | 384 | local start = 1 | |
302 | -- Put it all in the template. | 385 | metadata.body = nil |
303 | local tm = '' | 386 | local f0, f1, token |
304 | if nil ~= file.template then | 387 | -- Toss the body in first, so the scan can deal with it to. |
305 | local h = io.open(file.template, 'r') | 388 | f0, f1, token = string.find(temp, '%$(body)%$') |
306 | if nil ~= h then | 389 | if fail ~= f0 then |
307 | tm = tm .. h:read('*a') | 390 | temp = string.sub(temp, 1, f0 - 1) .. body .. string.sub(temp, f1 + 1) |
308 | h:close() | ||
309 | else | ||
310 | print('oops! No such file ' .. file.template) | ||
311 | end | ||
312 | |||
313 | -- TODO - Um not sure why this is here AND at the top of the loop. Here makes more sense. | ||
314 | local template, err = lcmark.compile_template(tm) | ||
315 | if nil == template then print('oops! ' .. err) else | ||
316 | result = lcmark.apply_template(template, metadata) | ||
317 | end | ||
318 | else | ||
319 | print('No template for ' .. name) | ||
320 | result = body | ||
321 | end | 391 | end |
392 | -- The actual metadata replacement scan. | ||
393 | result = '' | ||
394 | repeat | ||
395 | f0, f1, token = string.find(temp, '%$([%w_]+)%$', start) | ||
396 | if fail ~= f0 then | ||
397 | if nil ~= metadata[token] then result = result .. string.sub(temp, start, f0 - 1) .. metadata[token] end | ||
398 | start = f1 + 1 | ||
399 | end | ||
400 | until fail == f0 | ||
401 | result = result .. string.sub(temp, start) | ||
322 | 402 | ||
323 | -- Write the file. | 403 | -- Write the file. |
324 | if '' ~= result then | 404 | if '' ~= result then |
@@ -330,9 +410,6 @@ for name, file in pairs(Files) do | |||
330 | a:close() | 410 | a:close() |
331 | end | 411 | end |
332 | end | 412 | end |
333 | |||
334 | -- else | ||
335 | -- print('') | ||
336 | end | 413 | end |
337 | end | 414 | end |
338 | 415 | ||