123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- -- We're disabling `@see` tags for the time being because they
- -- ended up confusing the language server a little, breaking
- -- all objects from `lovr.math.*`.
- local ENABLE_SEE_TAGS = false
- -- Whether to generate an extra file with the globals
- -- from the `lovr.math` module.
- local INCLUDE_GLOBALS = true
- local root = lovr.filesystem.getSource()
- local function getPath(filename, sep)
- sep = sep or "/"
- return filename:match("(.*" .. sep .. ")")
- end
- local function ensureDirectoryExists(path)
- os.execute("mkdir -p " .. path)
- end
- --- Writes `contents` into a file identified by `filename`.
- local function writeFile(filename, contents)
- local fullpath = ('%s/cats/%s'):format(root, filename)
- -- make sure the directory exists
- local path = getPath(fullpath)
- if path then
- ensureDirectoryExists(path)
- end
- local file = io.open(fullpath, "w")
- if not file then
- print("Failed to open file for writing: " .. fullpath)
- return
- end
- file:write(contents)
- file:write("\n")
- file:close()
- end
- --- Adds a single value to a table.
- local function add(t, v)
- t[#t + 1] = v
- end
- -- Shorthand to concatenate a table of strings with a delimiter.
- local function join(t, delimiter)
- return table.concat(t, delimiter)
- end
- -- Shorthand to map a table with a function.
- local function map(t, fn)
- local out = {}
- for i, v in ipairs(t) do
- out[i] = fn(v)
- end
- return out
- end
- --- Returns true if any element in the table `t` satisfies the predicate `fn`.
- local function any(t, fn)
- for _, v in ipairs(t) do
- if fn(v) then
- return true
- end
- end
- return false
- end
- --- Renders the given string as a docstring. If the string
- --- contains newlines, they are prefixed with `---`.
- local function doc(str)
- str = str or ""
- return "---" .. str:gsub("\n", "\n---"):gsub('%s*$', '')
- end
- --- Determines if a parameter is optional. If it is, returns a `?` suffix.
- --- If it's not, returns an empty string.
- local function optionalSuffix(param)
- if param.type ~= 'table' then
- return (param.default ~= nil) and '?' or ''
- elseif not param.table then
- return ''
- elseif any(param.table, function(field) return field.default == nil end) then
- return ''
- else
- return '?'
- end
- end
- local function docHeader(str)
- return doc("#### " .. str .. "\n")
- end
- --- Render `@see` tags for a definition.
- local function seeTags(out, def)
- if not ENABLE_SEE_TAGS then
- return
- end
- if def.related then
- for _, related in ipairs(def.related) do
- add(out, doc("@see " .. related))
- end
- end
- end
- -- Forward declaration for `argumentType`.
- local argumentType
- --- Returns the type of a table argument.
- local function tableType(arg)
- local out = {}
- for _, field in ipairs(arg.table) do
- add(out, ("%s: %s"):format(field.name, argumentType(field)))
- end
- return ("{%s}"):format(join(out, ", "))
- end
- --- Returns the type of a function argument.
- function argumentType(arg)
- if arg.type == "*" then
- return "any"
- elseif arg.type == 'table' and arg.table then
- return tableType(arg)
- else
- return arg.type
- end
- end
- --- Returns the name of a function argument.
- function argumentName(arg)
- if arg.name:sub(1, 3) == '...' then
- return '...'
- else
- return arg.name
- end
- end
- --- Renders the header (description, notes, examples, etc.) for anything that's like a function.
- local function renderFunctionHeader(out, func)
- add(out, "")
- add(out, doc(func.description))
- if (func.notes) then
- add(out, doc(""))
- add(out, docHeader("Notes:"))
- add(out, doc(func.notes))
- end
- if (func.examples) then
- for _, example in ipairs(func.examples) do
- add(out, doc(""))
- add(out, docHeader("Example:"))
- if example.description then
- add(out, doc(example.description))
- add(out, doc(""))
- end
- if example.code then
- add(out, doc("```lua"))
- add(out, doc(example.code))
- add(out, doc("```"))
- end
- end
- end
- add(out, doc(""))
- seeTags(out, func)
- end
- local function renderType(out, tag, variant)
- local returns
- if #variant.returns > 0 then
- returns = join(map(variant.returns, function(ret)
- return ret.type
- end), ", ")
- else
- returns = "nil"
- end
- local params = join(map(variant.arguments, function(arg)
- return ("%s%s: %s"):format(argumentName(arg), optionalSuffix(arg), argumentType(arg))
- end), ", ")
- add(out, doc(("%s fun(%s): %s"):format(tag, params, returns)))
- end
- local function renderFunctionVariant(out, func, variant)
- renderFunctionHeader(out, func)
- -- Document parameters
- if variant.arguments then
- for _, arg in ipairs(variant.arguments) do
- add(out, doc(("@param %s%s %s %s"):format(argumentName(arg), optionalSuffix(arg), argumentType(arg), arg.description)))
- end
- end
- -- Document return type
- if variant.returns then
- for _, ret in ipairs(variant.returns) do
- add(out, doc(("@return %s %s %s"):format(ret.type, ret.name, ret.description)))
- end
- end
- -- Build function signature
- local signature = join(map(variant.arguments, function(arg)
- return argumentName(arg)
- end), ", ")
- add(out, ("function %s(%s) end"):format(func.key, signature))
- end
- local function renderFunction(out, func)
- for _, variant in ipairs(func.variants) do
- renderFunctionVariant(out, func, variant)
- end
- end
- local function renderEnum(out, enum)
- add(out, "")
- add(out, doc(enum.description))
- add(out, doc("@alias " .. enum.key))
- for _, value in ipairs(enum.values) do
- add(out, doc(value.description))
- add(out, doc("| \"%s\""):format(value.name))
- end
- end
- local function generateSwizzlePermutations(fields, length)
- local permutations = {}
- local function go(swizzle)
- swizzle = swizzle or ""
- if #swizzle == length then
- add(permutations, swizzle)
- else
- for _, field in ipairs(fields) do
- go(swizzle .. field)
- end
- end
- end
- go()
- return permutations
- end
- local function renderSwizzleFields(out, fields)
- for i = 1, 4 do
- local type = i == 1 and "number" or "Vec" .. i
- local comp = i == 1 and "component" or "components"
- for _, swizzle in ipairs(generateSwizzlePermutations(fields, i)) do
- local desc = ("The %s %s of the vector."):format(swizzle, comp)
- add(out, doc(("@field %s %s %s"):format(swizzle, type, desc)))
- end
- end
- end
- --- Renders a module's information to CATS format.
- local function renderModule(mod)
- local out = {}
- add(out, doc("@meta"))
- add(out, "")
- if mod.description then
- add(out, doc(mod.description))
- add(out, doc(""))
- end
- add(out, doc(("@class %s"):format(mod.key)))
- add(out, ("%s = {}"):format(mod.key))
- -- Render functions
- for _, func in ipairs(mod.functions) do
- renderFunction(out, func)
- end
- -- Render objects
- for _, obj in ipairs(mod.objects) do
- add(out, "")
- add(out, doc(obj.description))
- add(out, doc("@class %s"):format(obj.key))
- -- fields
- if obj.fields then
- for _, field in ipairs(obj.fields) do
- add(out, doc(("@field %s %s %s"):format(field.name, field.type, field.description)))
- end
- end
- -- swizzles
- if obj.swizzles then
- for _, swizzle in ipairs(obj.swizzles.components) do
- renderSwizzleFields(out, swizzle)
- end
- end
- -- see tags
- seeTags(out, obj)
- -- definition
- add(out, ("local %s = {}"):format(obj.name))
- -- Render object methods
- for _, func in ipairs(obj.methods) do
- renderFunction(out, func)
- end
- end
- -- Render enums
- for _, enum in ipairs(mod.enums) do
- renderEnum(out, enum)
- end
- return join(out, "\n")
- end
- local function generateModuleDocumentation(api)
- for _, v in ipairs(api.modules) do
- local text = renderModule(v)
- local filename = v.name
- writeFile(("library/%s.lua"):format(filename), text)
- end
- end
- local function renderCallback(out, callback)
- renderFunctionHeader(out, callback)
- for i = 1, #callback.variants do
- local variant = callback.variants[i]
- renderType(out, "@type", variant)
- end
- add(out, ("%s = nil"):format(callback.key))
- end
- local function generateCallbackDocumentation(api)
- local out = {}
- add(out, doc("@meta"))
- for _, callback in ipairs(api.callbacks) do
- renderCallback(out, callback)
- end
- writeFile("library/callback.lua", join(out, "\n"))
- end
- local function generateAddonConfig()
- local out = {}
- add(out, '{')
- add(out, ' "name": "LÖVR",')
- add(out, ' "words": ["lovr%.%w+"],')
- add(out, ' "settings": {')
- add(out, ' "Lua.runtime.version": "LuaJIT",')
- add(out, ' "Lua.diagnostics.globals": ["lovr"]')
- add(out, ' }')
- add(out, '}')
- writeFile("config.json", join(out, "\n"))
- end
- local function generateGlobalsDocumentation()
- local out = {}
- add(out, doc("@meta"))
- add(out, "vec2 = lovr.math.vec2")
- add(out, "Vec2 = lovr.math.newVec2")
- add(out, "vec3 = lovr.math.vec3")
- add(out, "Vec3 = lovr.math.newVec3")
- add(out, "vec4 = lovr.math.vec4")
- add(out, "Vec4 = lovr.math.newVec4")
- add(out, "mat4 = lovr.math.mat4")
- add(out, "Mat4 = lovr.math.newMat4")
- add(out, "quat = lovr.math.quat")
- add(out, "Quat = lovr.math.newQuat")
- writeFile("library/globals.lua", join(out, "\n"))
- end
- return function(api)
- generateModuleDocumentation(api)
- generateCallbackDocumentation(api)
- generateAddonConfig()
- if INCLUDE_GLOBALS then
- generateGlobalsDocumentation()
- end
- end
|