Files
Examples/Lua/rpbot/deps/discordia/docgen.lua
Benjamin Kyd 6761ddab08 smh
2019-04-09 20:55:44 +01:00

237 lines
5.7 KiB
Lua

local fs = require('fs')
local pathjoin = require('pathjoin')
local insert, sort, concat = table.insert, table.sort, table.concat
local format = string.format
local pathJoin = pathjoin.pathJoin
local function scan(dir)
for fileName, fileType in fs.scandirSync(dir) do
local path = pathJoin(dir, fileName)
if fileType == 'file' then
coroutine.yield(path)
else
scan(path)
end
end
end
local function checkType(docstring, token)
return docstring:find(token) == 1
end
local function match(s, pattern) -- only useful for one return value
return assert(s:match(pattern), s)
end
local docs = {}
for f in coroutine.wrap(function() scan('./libs') end) do
local d = assert(fs.readFileSync(f))
local class = {
methods = {},
statics = {},
properties = {},
parents = {},
}
for s in d:gmatch('--%[=%[%s*(.-)%s*%]=%]') do
if checkType(s, '@i?c') then
class.name = match(s, '@i?c (%w+)')
class.userInitialized = checkType(s, '@ic')
for parent in s:gmatch('x (%w+)') do
insert(class.parents, parent)
end
class.desc = match(s, '@d (.+)'):gsub('\r?\n', ' ')
class.parameters = {}
for optional, paramName, paramType in s:gmatch('@(o?)p ([%w%p]+)%s+([%w%p]+)') do
insert(class.parameters, {paramName, paramType, optional == 'o'})
end
elseif checkType(s, '@s?m') then
local method = {parameters = {}}
method.name = match(s, '@s?m ([%w%p]+)')
for optional, paramName, paramType in s:gmatch('@(o?)p ([%w%p]+)%s+([%w%p]+)') do
insert(method.parameters, {paramName, paramType, optional == 'o'})
end
method.returnType = match(s, '@r ([%w%p]+)')
method.desc = match(s, '@d (.+)'):gsub('\r?\n', ' ')
insert(checkType(s, '@sm') and class.statics or class.methods, method)
elseif checkType(s, '@p') then
local propertyName, propertyType, propertyDesc = s:match('@p (%w+)%s+([%w%p]+)%s+(.+)')
assert(propertyName, s); assert(propertyType, s); assert(propertyDesc, s)
propertyDesc = propertyDesc:gsub('\r?\n', ' ')
insert(class.properties, {
name = propertyName,
type = propertyType,
desc = propertyDesc,
})
end
end
if class.name then
docs[class.name] = class
end
end
local function link(str)
local ret = {}
for t in str:gmatch('[^/]+') do
insert(ret, docs[t] and format('[[%s]]', t) or t)
end
return concat(ret, '/')
end
local function sorter(a, b)
return a.name < b.name
end
local function writeProperties(f, properties)
sort(properties, sorter)
f:write('| Name | Type | Description |\n')
f:write('|-|-|-|\n')
for _, v in ipairs(properties) do
f:write('| ', v.name, ' | ', link(v.type), ' | ', v.desc, ' |\n')
end
end
local function writeParameters(f, parameters)
f:write('(')
local optional
if parameters[1] then
for i, param in ipairs(parameters) do
f:write(param[1])
if i < #parameters then
f:write(', ')
end
if param[3] then
optional = true
end
end
f:write(')\n')
if optional then
f:write('>| Parameter | Type | Optional |\n')
f:write('>|-|-|:-:|\n')
for _, param in ipairs(parameters) do
local o = param[3] and '' or ''
f:write('>| ', param[1], ' | ', param[2], ' | ', o, ' |\n')
end
else
f:write('>| Parameter | Type |\n')
f:write('>|-|-|\n')
for _, param in ipairs(parameters) do
f:write('>| ', param[1], ' | ', link(param[2]), '|\n')
end
end
else
f:write(')\n')
end
end
local function writeMethods(f, methods)
sort(methods, sorter)
for _, method in ipairs(methods) do
f:write('### ', method.name)
writeParameters(f, method.parameters)
f:write('>\n>', method.desc, '\n>\n')
f:write('>Returns: ', link(method.returnType), '\n\n')
end
end
if not fs.existsSync('docs') then
fs.mkdirSync('docs')
end
local function clean(input, seen)
local fields = {}
for _, field in ipairs(input) do
if not seen[field.name] then
insert(fields, field)
end
end
return fields
end
for _, class in pairs(docs) do
local seen = {}
for _, v in pairs(class.properties) do seen[v.name] = true end
for _, v in pairs(class.statics) do seen[v.name] = true end
for _, v in pairs(class.methods) do seen[v.name] = true end
local f = io.open(pathJoin('docs', class.name .. '.md'), 'w')
if next(class.parents) then
f:write('#### *extends ', '[[', concat(class.parents, ']], [['), ']]*\n\n')
end
f:write(class.desc, '\n\n')
if class.userInitialized then
f:write('## Constructor\n\n')
f:write('### ', class.name)
writeParameters(f, class.parameters)
f:write('\n')
else
f:write('*Instances of this class should not be constructed by users.*\n\n')
end
for _, parent in ipairs(class.parents) do
if docs[parent] and next(docs[parent].properties) then
local properties = docs[parent].properties
if next(properties) then
f:write('## Properties Inherited From ', link(parent), '\n\n')
writeProperties(f, clean(properties, seen))
end
end
end
if next(class.properties) then
f:write('## Properties\n\n')
writeProperties(f, class.properties)
end
for _, parent in ipairs(class.parents) do
if docs[parent] and next(docs[parent].statics) then
local statics = docs[parent].statics
if next(statics) then
f:write('## Static Methods Inherited From ', link(parent), '\n\n')
writeMethods(f, clean(statics, seen))
end
end
end
for _, parent in ipairs(class.parents) do
if docs[parent] and next(docs[parent].methods) then
local methods = docs[parent].methods
if next(methods) then
f:write('## Methods Inherited From ', link(parent), '\n\n')
writeMethods(f, clean(methods, seen))
end
end
end
if next(class.statics) then
f:write('## Static Methods\n\n')
writeMethods(f, class.statics)
end
if next(class.methods) then
f:write('## Methods\n\n')
writeMethods(f, class.methods)
end
f:close()
end