From d5e53ee2a2b83adf91a0c3bbbc02c450b6747d59 Mon Sep 17 00:00:00 2001 From: ubq323 Date: Thu, 31 Oct 2024 18:41:04 +0000 Subject: highlighting, better whitespace, make into a module --- garkup.lua | 116 ------------------------------------------------- html.lua | 10 ++++- init.lua | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ prose.lua | 19 +++++--- 4 files changed, 164 insertions(+), 124 deletions(-) delete mode 100644 garkup.lua create mode 100644 init.lua diff --git a/garkup.lua b/garkup.lua deleted file mode 100644 index 2469aa8..0000000 --- a/garkup.lua +++ /dev/null @@ -1,116 +0,0 @@ -local html = require'html' -local prose = require'prose' - -local function highlight(code) - -- todo - return html.T.pre(code) -end - -local function mode_code(S) - local lang = S:line():match("^```(%w+) ?(.*)") - - local text = "" - for line in S:lines() do - if line:match("^```") then - break - else - text = text .. line - end - end - S:emit(highlight(text)) -end - -local function mode_list(S) - local items = {} - for line in S:lines() do - if line:match("^%*") then - local content = line:gsub("^%*%s*","") - table.insert(items, html.T.li(prose(S,content))) - else - S:unline(line) - break - end - end - S:emit(html.T.ul(items)) -end - -local function mode_heading(S) - local level, line = S:line():match("^#*()%s*(.*)") - level = level - 1 - S:emit(html.tag('h'..level, prose(S,line))) -end - -local mode_patterns = { - { "^```", mode_code }, - { "^*", mode_list }, - { "^#", mode_heading }, -} - -local function match_mode_pattern(S, line) - for i, row in ipairs(mode_patterns) do - local pattern, mode = row[1], row[2] - if line:match(pattern) then return mode end - end -end - -local function mode_paragraphs(S) - local para = "" - local function finish() - if para:match("%S") then - S:emit(html.T.p(prose(S,para))) - end - para = "" - end - - for line in S:lines() do - local newmode = match_mode_pattern(S, line) - if newmode then - S:unline(line) - finish() - return newmode(S) - elseif line:match("%S") then - para = para .. line - else - finish() - end - end -end - -local function split_lines(text) - local lines = {} - for x in text:gmatch("[^\n]*") do - table.insert(lines, x.."\n") - end - return lines -end - -local State = {} -function State.new(input) - return setmetatable({ - the_lines = split_lines(input) - }, {__index=State}) -end - -function State.emit(self, x) - print(html.html(x)) -end -function State.line(self) - return table.remove(self.the_lines, 1) -end -function State.unline(self, line) - table.insert(self.the_lines, 1, line) -end -function State.lines(self) - return State.line, self -end -function State.remaining(self) - return #self.the_lines > 0 -end -function State.toplevel(self) - while self:remaining() do - mode_paragraphs(self) - end -end - - -State.new(io.read"a"):toplevel() diff --git a/html.lua b/html.lua index 87ecc53..e265578 100644 --- a/html.lua +++ b/html.lua @@ -19,7 +19,9 @@ local html local function fmt_tag(tag) local attrs = tag.attrs and fmt_attrs(tag.attrs) or "" local selfclosing = (tag.body == "") - local sameline = type(tag.body) == "string" or (type(tag.body) == "table" and tag.body.tag) + local sameline = type(tag.body) == "string" + or (type(tag.body) == "table" and tag.body.tag) + or (type(tag.body) == "table" and #tag.body <= 1) local maybenl = sameline and "" or "\n" if selfclosing then return ("<%s%s/>"):format(tag.tname,attrs) @@ -70,9 +72,13 @@ html = function (x) return safe( tostring(x) ) else -- just a regular list + local nl = "\n" + if #x <= 2 then + nl = "" + end local o = "" for _,item in ipairs(x) do - o = o .. tostring(html(item)) .. "\n" + o = o .. tostring(html(item)) .. nl end return safe(o) end diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..e393b13 --- /dev/null +++ b/init.lua @@ -0,0 +1,143 @@ +local html = require'garkup.html' +local T = html.T +local prose = require'garkup.prose' + +function empipe(prog, input) + local posix = require'posix' + local pipe = assert(posix.popen_pipeline({ + function() io.write(input) end, + prog + }, "r")) + local output = "" + repeat + local next = assert(posix.unistd.read(pipe.fd, 8192)) + output = output .. next + until #next == 0 + assert(posix.pclose(pipe)) + return output +end + +function highlight(code, lang) + local highlighted = empipe( + {"highlight", "-f", "-S", lang, "--inline-css", "--enclose-pre"}, + code + ) + return html.safe(highlighted) +end + +local function mode_code(S) + local lang = S:line():match("^```(%w+) ?(.*)") + + local text = "" + for line in S:lines() do + if line:match("^```") then + break + else + text = text .. line + end + end + S:emit(highlight(text, lang)) +end + +local function mode_list(S) + local items = {} + for line in S:lines() do + if line:match("^%*") then + local content = line:gsub("^%*%s*","") + table.insert(items, T.li(prose(S,content))) + else + S:unline(line) + break + end + end + S:emit(T.ul(items)) +end + +local function mode_heading(S) + local level, line = S:line():match("^#*()%s*(.*)") + level = level - 1 + S:emit(html.tag('h'..level, prose(S,line))) +end + +local mode_patterns = { + { "^```", mode_code }, + { "^*", mode_list }, + { "^#", mode_heading }, +} + +local function match_mode_pattern(S, line) + for i, row in ipairs(mode_patterns) do + local pattern, mode = row[1], row[2] + if line:match(pattern) then return mode end + end +end + +local function mode_paragraphs(S) + local para = "" + local function finish() + if para:match("%S") then + S:emit(T.p(prose(S,para))) + end + para = "" + end + + for line in S:lines() do + local newmode = match_mode_pattern(S, line) + if newmode then + S:unline(line) + finish() + return newmode(S) + elseif line:match("%S") then + para = para .. line + else + finish() + end + end +end + +local function split_lines(text) + local lines = {} + for x in text:gmatch("[^\n]*") do + table.insert(lines, x.."\n") + end + return lines +end + +local State = {} +function State.new(input) + return setmetatable({ + the_lines = split_lines(input), + output = "", + }, {__index=State}) +end + +function State.emit(self, x) + self.output = self.output..tostring(html.html(x)).."\n" +end +function State.line(self) + return table.remove(self.the_lines, 1) +end +function State.unline(self, line) + table.insert(self.the_lines, 1, line) +end +function State.lines(self) + return State.line, self +end +function State.remaining(self) + return #self.the_lines > 0 +end +function State.toplevel(self) + while self:remaining() do + mode_paragraphs(self) + end + return self.output +end + +local function garkup(text) + local S = State.new(text) + return S:toplevel(), S +end + + +-- print(garkup(io.read"a") +return garkup diff --git a/prose.lua b/prose.lua index ceadec0..99e298a 100644 --- a/prose.lua +++ b/prose.lua @@ -1,4 +1,5 @@ -local html = require'html' +local html = require'garkup.html' +local T = html.T local extensions = {} @@ -20,7 +21,7 @@ local function prose(S, text) local function simple_sub(delim, tagname) return { "(%s)"..delim..'(.-)'..delim..'(%W)', -- wo wimple - function(a,x,b) return a..emplace(html.T[tagname](x))..b end + function(a,x,b) return a..emplace(T[tagname](x))..b end } end @@ -76,15 +77,21 @@ extensions.fn = function(S,text) local n = #S.footnotes + 1 local link_id = "fnref_"..n local note_id = "fn_"..n - S.footnotes[n] = html.T.li({id=note_id}, { + S.footnotes[n] = true -- for nested footnotes + S.footnotes[n] = T.li({id=note_id}, { prose(S,text), html.safe" ", - html.T.a({href="#"..link_id, role="doc-backlink"}, html.safe"↩︎")}) - return html.T.sup({id=link_id}, html.T.a({href="#"..note_id, role='doc-noteref'}, '('..n..')')) + T.a({href="#"..link_id, role="doc-backlink"}, html.safe"↩︎")}) + return T.sup({id=link_id}, T.a({href="#"..note_id, role='doc-noteref'}, '('..n..')')) end extensions.fns = function(S) if not S.footnotes then return "" end - return {html.T.hr{}, html.T.ol(S.footnotes)} + return {T.hr"", T.ol(S.footnotes)} +end + +extensions.meta = function(S,text) + local fn = assert(load(string.format("return {%s}", text),"meta", "t")) + S.meta = fn() end return prose -- cgit v1.2.3