local _,serial = pcall(require,"serialization") local doc = {} doc.searchers = {} doc.tctab = { ["string"] = 31, ["table"] = 32, ["number"] = 33, ["boolean"] = 35, ["function"] = 36 } function doc.parsefile(path) -- string -- table -- parses file from *path* to return a documentation table local fdoc = {} local f = io.open(path) local lines = {} for l in f:read("*a"):gmatch("[^\n]+") do if l:find("function") and not l:find("local") then lines[#lines+1] = l end end for k,v in pairs(lines) do local name, args, desc = v:match("function%s+(.+)%s*%((.*)%)%s*%-%-%s*(.+)") if name and args and desc then local fd = {["description"]=desc or desc,["args"]={},["atypes"]={}} for word in args:gmatch("[^%s,]+") do fd.args[#fd.args+1] = {word} fd.atypes[word] = "unknown" end local argtypes, outtypes, description = desc:match("(.-)%-%-(.-)%-%-%s*(.+)") if argtypes and outtypes and description then local wc = 1 for word in argtypes:gmatch("%S+") do fd.args[wc][2] = word fd.atypes[fd.args[wc][1]] = word wc = wc + 1 end local wc = 1 for word in outtypes:gmatch("%S+") do fd.outtypes = fd.outtypes or {} fd.outtypes[#fd.outtypes+1] = word end fd.description = description end fdoc[name] = fd end end return fdoc end function doc.format(fdoc) -- table -- string -- returns VT100 formatted documentation from documentation table *fdoc* local rs = "" -- string to return for fname,finfo in pairs(fdoc) do if rs:len() > 0 then rs = rs .. "\n\n" end local as = "" -- string containing arguments for a given function, with colours for type for k,v in ipairs(finfo.args) do local c = doc.tctab[v[2]] or 0 if k > 1 then as = as .. ", " end if v[2] then as = string.format("%s%s: \27[%im%s\27[0m",as,v[2],c,v[1]) else as = string.format("%s\27[%im%s\27[0m",as,c,v[1]) end end local rv = "" if finfo.outtypes then rv = ": " for k,v in ipairs(finfo.outtypes) do if k > 1 then rv = rv .. ", " end local c = doc.tctab[v] or 0 rv = string.format("%s\27[%im%s\27[0m",rv,c,v) end end local nd = finfo.description for k,v in pairs(finfo.atypes) do local c = doc.tctab[v] or 7 nd=nd:gsub("%*"..k.."%*","\27["..tostring(c).."m"..k.."\27[0m") end rs = string.format("%s\27[36m%s\27[0m(%s)%s\n%s",rs,fname,as,rv,nd) end return rs end function doc.searchers.lib(name) -- string -- string string -- Tries to find a documentation from a library with *name*. Returns either a string of documentation, or false and a reason. local lib = os.getenv("LIB") or "/boot/lib" local dt for d in lib:gmatch("[^\n]+") do if fs.exists(d.."/"..name) then dt = doc.parsefile(d.."/"..name) elseif fs.exists(d.."/"..name..".lua") then dt = doc.parsefile(d.."/"..name..".lua") end end if not dt then return false, "unable to find documentation for "..tostring(name) end return doc.format(dt) end function doc.searchers.cdoc(topic) -- string -- string string -- Searches for documentation labelled as *topic* in .dict files under /boot/doc/ if not serial then return end for k,v in ipairs(fs.list("/boot/doc")) do if v:sub(-5) == ".dict" then local f=io.open("/boot/doc/"..v,"rb") for line in f:lines() do local mname, docs = line:match("^(.-)\t(.+)$") if mname == topic or mname == topic..".lua" then return doc.format(serial.unserialize(docs)) end end end end end function doc.docs(topic) -- string -- boolean -- Displays the documentation for *topic*, returning true, or errors. Also callable as just doc(). local lib = os.getenv("LIB") or "/boot/lib" local dt for k,v in pairs(doc.searchers) do dt=v(topic) if dt then print(dt) return true end end error("unable to find documentation for "..tostring(name)) end return setmetatable(doc,{__call=function(_,topic) return doc.docs(topic) end})