From 9bad03be529be3e3ddaf0d87769c6a14821a2d04 Mon Sep 17 00:00:00 2001 From: Izaya Date: Sun, 15 Jul 2018 06:55:36 +1000 Subject: [PATCH] I forget --- README.md | 3 - mdbrowse.lua | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++ mdparse.lua | 35 +++++++-- 3 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 mdbrowse.lua diff --git a/README.md b/README.md index c873f38..2eb3a49 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,4 @@ A markdown FRequest browser for OpenComputers. ## mdparse -mdparse is the Markdown parsing library used for mdbrowser. It works under standard Lua as well as OpenComputers. -- `mdparse.parse(text:string): - table` Parses *text* into a table of tables. Each table should contain a .contents string, and if it is a link, then an .address string. -- `mdparse.reflow(text:string, linewidth:number): table, table` - Returns a table of formatted lines, and a table of links, as `{x, y, label, address}`. diff --git a/mdbrowse.lua b/mdbrowse.lua new file mode 100644 index 0000000..63297b6 --- /dev/null +++ b/mdbrowse.lua @@ -0,0 +1,196 @@ +local net = require "net" +local md = require "mdparse" +local event = require "event" +local fs = require "filesystem" +local computer = require "computer" +local home = "file:///usr/doc/mdbrowse.md" +local url = home +local cline = 1 +local lines, links, listeners, history = {}, {}, {}, {} +local width, height = 80, 23 +local run = true + +local function pushhistory(url) + history[#history+1] = url +end +local function pophistory() + local rurl = history[#history] or home + history[#history] = nil + return rurl +end + +local function parseurl(url) + local proto,addr = url:match("(.-)://(.+)") + addr = addr or url + local hp, path = addr:match("(.-)(/.*)") + hp, path = hp or addr, path or "/" + local host, port = hp:match("(.+):(.+)") + host = host or hp + path = fs.canonical(path) + return proto, host, port, path +end + +function addButton(x,y,l,a) + local len = l:len() + 2 + local function hf(_,_,tx,ty) + if y == ty and tx >= x and tx <= x+len then + io.write("\a") + computer.pushSignal("mdbrowsebutton",a) + end + end + event.listen("touch",hf) + listeners[#listeners+1] = hf +end +function dropButtons() + for k,v in pairs(listeners) do + event.ignore("touch",v) + end +end + +function loadpage() + local protocol, host, port, path = parseurl(url) + protocol = protocol or "file" + if protocol == "file" then + local f = io.open(host..path,"rb") + if not f then + url = pophistory() + return false, "not found" + end + lines, links = md.reflow(f:read("*a"),width) + f:close() + return true + elseif protocol == "fget" then + port = tonumber(port) or 70 + local socket = net.open(host,port) + if socket then + io.write("\27[u\27[2KConnection established.") + local buf = "" + socket:write("t"..path.."\n") + local c = socket:read(1) + repeat + c = socket:read(1) + os.sleep(0.5) + until c ~= "" + if c == "n" then + buf = path..": Not found.\n" + elseif c == "f" then + buf = "Failure: \n" + elseif c == "d" then + buf = "# Directory listing for "..path.."\n" + end + repeat + l = socket:read(1024) + buf = buf .. l + io.write("\27[u\27[2KRead "..tonumber(buf:len()).." bytes") + os.sleep(0.5) + until socket.state == "closed" and l == "" + if c == "d" then + local first, nbuf = false, "" + for line in buf:gmatch("[^\n]+") do + if first then + line = "- ["..line.."]("..line..")" + end + nbuf = nbuf .. line .. "\n" + first = true + end + buf = nbuf + end + lines, links = md.reflow(buf,width) + return true + else + url = pophistory() + return false, "unable to load" + end + end +end + +function drawpage() + dropButtons() + if cline < 1 then + cline = 1 + elseif cline > #lines then + cline = #lines + end + io.write("\27[2J\27[H") + for i = cline, cline+height do + print((lines[i] or ""):sub(1,width)) + end + for k,v in pairs(links) do + if v[1] >= cline and v[1] <= height+cline then + addButton(v[2],(v[1])-cline+1,v[3],v[4]) + end + end + local lstring = tostring(cline).."-"..tostring(cline+height).."/"..tostring(#lines) + io.write("\27[s"..lstring.." "..url) +end + +function gourl(nurl) + pushhistory(url) + -- check for a protocol:// part + if nurl:match(".+://") then + url = nurl + -- check for a / -- root relative + elseif nurl:sub(1,1) == "/" then + url = (url:match(".+://.-/" or url:match(".+://.+/?").."/")) .. nurl + -- fail and assume relative + else + local host = (url:match(".+://.-/") or url:match(".+://.+/?").."/") + local path = url:sub(host:len()+1) + local tPath = fs.segments(path) + local ntPath = fs.segments(nurl) + if path:sub(path:len(),path:len()) ~= "/" then + tPath[#tPath] = nil + end + for k,v in ipairs(ntPath) do + tPath[#tPath+1] = v + end + url = host:sub(1,-2) + for k,v in ipairs(tPath) do + url = url .. "/" .. v + end + if nurl:sub(nurl:len(),nurl:len()) == "/" then + url = url .. "/" + end + end +end + +loadpage() +drawpage() +while run do + local tev = {event.pull()} + if tev[1] == "key_down" then + if tev[3] == 0 and tev[4] == 200 then -- up + cline = cline - 1 + drawpage() + elseif tev[3] == 0 and tev[4] == 208 then -- down + cline = cline + 1 + drawpage() + elseif tev[3] == 0 and tev[4] == 203 then -- left + url = pophistory() + cline = 1 + loadpage() + drawpage() + elseif tev[3] == 0 and tev[4] == 209 then -- page down + cline = cline + height + drawpage() + elseif tev[3] == 0 and tev[4] == 201 then -- page up + cline = cline - height + drawpage() + elseif tev[3] == 113 and tev[4] == 16 then -- q + run = false + elseif tev[3] == 111 then -- o + io.write("\27[u\27[2K\27[sURL: ") + gourl(io.read()) + cline = 1 + loadpage() + drawpage() + end + elseif tev[1] == "mdbrowsebutton" then + gourl(tev[2]) + cline = 1 + loadpage() + drawpage() + end +end +dropButtons() +io.write("\27[2J\27[H") diff --git a/mdparse.lua b/mdparse.lua index 87ff852..dd16e85 100644 --- a/mdparse.lua +++ b/mdparse.lua @@ -6,7 +6,7 @@ function md.parse(md) it[#it+1] = {["content"]="",["bold"]=false,["italic"]=false} local lc,llc = "","" local function newpart() - it[#it+1] = {["content"]=""} + it[#it+1] = {["content"]="",["bold"]=it[#it].bold,["italic"]=it[#it].italic} end newpart() local lm = false @@ -24,6 +24,15 @@ function md.parse(md) it.l[#it.l+1] = it[#it].address it[#it].addrid = #it.l newpart() + elseif c == "*" then + if lc == "*" then + it[#it].italic = false + it[#it].italic = it[#it-1].italic + it[#it].bold = not it[#it].bold + else + newpart() + it[#it].italic = not it[#it].italic + end elseif c == "\n" and lc == "\n" then if it[#it-1].content == "\n" then table.remove(it,#it-1) @@ -33,11 +42,15 @@ function md.parse(md) newpart() elseif c == "\n" then local line = md:sub(1,cc):match(".+\n(.+)") or it[#it].content - if line:sub(1,1) == "-" then + if line:sub(line:find("%S")) == "-" then newpart() it[#it].content = "\n" newpart() - elseif line:sub(1,1) == "#" then + elseif line:sub(line:find("%S")) == "#" then + newpart() + it[#it].content = "\n" + newpart() + elseif line:find("%s-%d+%.") == 1 then newpart() it[#it].content = "\n" newpart() @@ -58,13 +71,27 @@ function md.parse(md) end function md.reflow(text,len) - local words, lines, links = {}, {""}, {} + local words, lines, links, lastitalic, lastbold = {}, {""}, {}, false, false for k,v in ipairs(md.parse(text)) do if v.content == "\n\n" or v.content == "\n" then words[#words+1] = {v.content} elseif not v.address then for word in v.content:gmatch("%S+") do words[#words+1] = {word} + if v.italic and not lastitalic then + words[#words][1] = "\27[30;47m"..words[#words][1] + lastitalic = not lastitalic + elseif not v.italic and lastitalic then + words[#words-1][1] = words[#words-1][1].."\27[0m" + lastitalic = not lastitalic + end + if v.bold and not lastbold then + words[#words][1] = "\27[31m"..words[#words][1] + lastbold = not lastbold + elseif not v.bold and lastbold then + words[#words-1][1] = words[#words-1][1].."\27[0m" + lastbold = not lastbold + end end else words[#words+1] = {v.content,v.address}