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