diff options
author | ubq323 <ubq323@ubq323.website> | 2024-10-31 18:41:04 +0000 |
---|---|---|
committer | ubq323 <ubq323@ubq323.website> | 2024-10-31 18:41:04 +0000 |
commit | d5e53ee2a2b83adf91a0c3bbbc02c450b6747d59 (patch) | |
tree | 6d08a0aa3e8eaa341e402b5881735ba79094ddc4 /init.lua | |
parent | 664f471c774d6182efb0f9c4d4c9af9857b1bf52 (diff) |
highlighting, better whitespace, make into a module
Diffstat (limited to 'init.lua')
-rw-r--r-- | init.lua | 143 |
1 files changed, 143 insertions, 0 deletions
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 |