Operating system for OpenComputers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.6KB

  1. local _,serial = pcall(require,"serialization")
  2. local doc = {}
  3. doc.searchers = {}
  4. doc.tctab = {
  5. ["string"] = 31,
  6. ["table"] = 32,
  7. ["userdata"] = 32,
  8. ["number"] = 33,
  9. ["boolean"] = 35,
  10. ["function"] = 36
  11. }
  12. function doc.parsefile(path) -- string -- table -- parses file from *path* to return a documentation table
  13. local fdoc = {}
  14. local f = io.open(path)
  15. local lines = {}
  16. for l in f:read("*a"):gmatch("[^\n]+") do
  17. if l:find("function") and not l:find("local") then
  18. lines[#lines+1] = l
  19. end
  20. end
  21. for k,v in pairs(lines) do
  22. local name, args, desc = v:match("function%s+(.+)%s*%((.*)%)%s*%-%-%s*(.+)")
  23. if name and args and desc then
  24. local fd = {["description"]=desc or desc,["args"]={},["atypes"]={}}
  25. for word in args:gmatch("[^%s,]+") do
  26. fd.args[#fd.args+1] = {word}
  27. fd.atypes[word] = "unknown"
  28. end
  29. local argtypes, outtypes, description = desc:match("(.-)%-%-(.-)%-%-%s*(.+)")
  30. if argtypes and outtypes and description then
  31. local wc = 1
  32. for word in argtypes:gmatch("%S+") do
  33. fd.args[wc][2] = word
  34. fd.atypes[fd.args[wc][1]] = word
  35. wc = wc + 1
  36. end
  37. local wc = 1
  38. for word in outtypes:gmatch("%S+") do
  39. fd.outtypes = fd.outtypes or {}
  40. fd.outtypes[#fd.outtypes+1] = word
  41. end
  42. fd.description = description
  43. end
  44. fdoc[name] = fd
  45. end
  46. end
  47. return fdoc
  48. end
  49. function doc.format(fdoc) -- table -- string -- returns VT100 formatted documentation from documentation table *fdoc*
  50. local rs = "" -- string to return
  51. for fname,finfo in pairs(fdoc) do
  52. if rs:len() > 0 then rs = rs .. "\n\n" end
  53. local as = "" -- string containing arguments for a given function, with colours for type
  54. for k,v in ipairs(finfo.args) do
  55. local c = doc.tctab[v[2]] or 0
  56. if k > 1 then
  57. as = as .. ", "
  58. end
  59. if v[2] then
  60. as = string.format("%s%s: \27[%im%s\27[0m",as,v[2],c,v[1])
  61. else
  62. as = string.format("%s\27[%im%s\27[0m",as,c,v[1])
  63. end
  64. end
  65. local rv = ""
  66. if finfo.outtypes then
  67. rv = ": "
  68. for k,v in ipairs(finfo.outtypes) do
  69. if k > 1 then
  70. rv = rv .. ", "
  71. end
  72. local c = doc.tctab[v] or 0
  73. rv = string.format("%s\27[%im%s\27[0m",rv,c,v)
  74. end
  75. end
  76. local nd = finfo.description
  77. for k,v in pairs(finfo.atypes) do
  78. local c = doc.tctab[v] or 7
  79. nd=nd:gsub("%*"..k.."%*","\27["..tostring(c).."m"..k.."\27[0m")
  80. end
  81. rs = string.format("%s\27[36m%s\27[0m(%s)%s\n%s",rs,fname,as,rv,nd)
  82. end
  83. return rs
  84. end
  85. 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.
  86. local lib = os.getenv("LIB") or "/boot/lib"
  87. local dt
  88. for d in lib:gmatch("[^\n]+") do
  89. if fs.exists(d.."/"..name) then
  90. dt = doc.parsefile(d.."/"..name)
  91. elseif fs.exists(d.."/"..name..".lua") then
  92. dt = doc.parsefile(d.."/"..name..".lua")
  93. end
  94. end
  95. if not dt then return false, "unable to find documentation for "..tostring(name) end
  96. return doc.format(dt)
  97. end
  98. function doc.searchers.cdoc(topic) -- string -- string string -- Searches for documentation labelled as *topic* in .dict files under /boot/doc/
  99. if not serial then return end
  100. for k,v in ipairs(fs.list("/boot/doc")) do
  101. if v:sub(-5) == ".dict" then
  102. local f=io.open("/boot/doc/"..v,"rb")
  103. for line in f:lines() do
  104. local mname, docs = line:match("^(.-)\t(.+)$")
  105. if mname == topic or mname == topic..".lua" then
  106. return doc.format(serial.unserialize(docs))
  107. end
  108. end
  109. end
  110. end
  111. end
  112. function doc.searchers.component(name)
  113. local dt = {}
  114. local addr = component.list(name)()
  115. if addr then
  116. for fname,_ in pairs(component.methods(addr)) do
  117. fd = {args={},outtypes={},atypes={}}
  118. local ds = component.doc(addr,fname)
  119. local ins, outs, desc = ds:match("%((.-)%)") or "", ds:match("%):(.*)%-%-") or "", ds:match("%-%-(.+)") or ""
  120. for arg in ins:gmatch("[^,%s%[%]]+") do
  121. local an,at = arg:match("(.-):(.+)")
  122. at = at:match("(.-)=") or at
  123. fd.args[#fd.args+1] = {an,at}
  124. fd.atypes[an] = at
  125. end
  126. for out in outs:gmatch("[^,]+") do
  127. fd.outtypes[#fd.outtypes+1] = out:match("^%s*(.-)%s*$")
  128. end
  129. fd.description = desc or ""
  130. dt[name.."."..fname] = fd
  131. end
  132. else
  133. return
  134. end
  135. return doc.format(dt)
  136. end
  137. function doc.docs(topic) -- string -- boolean -- Displays the documentation for *topic*, returning true, or errors. Also callable as just doc().
  138. local lib = os.getenv("LIB") or "/boot/lib"
  139. local dt
  140. for k,v in pairs(doc.searchers) do
  141. dt=v(topic)
  142. if dt then
  143. print(dt)
  144. return true
  145. end
  146. end
  147. error("unable to find documentation for "..tostring(name))
  148. end
  149. return setmetatable(doc,{__call=function(_,topic) return doc.docs(topic) end})