local html = require'garkup.html'
local T = html.T
local prose = require'garkup.prose'
local function highlight(code, lang)
local run = require'run'
return html.safe(run{
"highlight", "-f", "-S", lang, "--inline-css", "--enclose-pre",
input = 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
if lang then
S:emit(highlight(text, lang))
else
S:emit(T.pre(text))
end
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 -- because () capture is offset by 1
level = level + (S.heading_level or 0)
S:emit(html.tag('h'..level, prose(S,line)))
end
local function mode_hr(S)
local line = S:line()
S:emit(html.T.hr"")
end
local mode_patterns = {
{ "^```", mode_code },
{ "^*", mode_list },
{ "^#", mode_heading },
{ "^%-%-%-", mode_hr },
}
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()
local processed = prose(S, para)
if tostring(html.html(processed)):match("%S") then
S:emit(T.p(processed))
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
finish()
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, options)
options = options or {}
local self = {
the_lines = split_lines(input),
output = "",
}
for k,v in pairs(options) do self[k] = v end
return setmetatable(self, {__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(...)
local S = State.new(...)
return S:toplevel(), S
end
-- print(garkup(io.read"a")
return garkup