Finished(?) annotation parsing
Small fixes for the parser
This commit is contained in:
305
tools/generate-annotations.lua
Normal file
305
tools/generate-annotations.lua
Normal file
@@ -0,0 +1,305 @@
|
||||
local args = {...}
|
||||
|
||||
local commentTypes = {
|
||||
"module",
|
||||
"class",
|
||||
"param",
|
||||
"return",
|
||||
"usage",
|
||||
"function",
|
||||
"local",
|
||||
"property",
|
||||
"combinedProperty",
|
||||
"event",
|
||||
"private",
|
||||
"protected",
|
||||
"field",
|
||||
"vararg",
|
||||
"shortDescription",
|
||||
}
|
||||
|
||||
local elementList = {}
|
||||
|
||||
local function getFilesInCC(path)
|
||||
local files = {}
|
||||
|
||||
local function scanDir(dir)
|
||||
for _, item in ipairs(fs.list(dir)) do
|
||||
local fullPath = fs.combine(dir, item)
|
||||
if fs.isDir(fullPath) then
|
||||
scanDir(fullPath)
|
||||
elseif item:match("%.lua$") then
|
||||
if(fullPath:find("elements"))then
|
||||
local itemName = item:gsub("%.lua$", "")
|
||||
table.insert(elementList, itemName)
|
||||
end
|
||||
local file = fs.open(fullPath, "r")
|
||||
if file then
|
||||
files[fullPath] = file.readAll()
|
||||
file.close()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scanDir(path)
|
||||
return files
|
||||
end
|
||||
|
||||
local function getFilesInLua(path)
|
||||
local files = {}
|
||||
|
||||
local function scanDir(dir)
|
||||
local p = io.popen('dir "'..dir..'" /b /s')
|
||||
if not p then return end
|
||||
|
||||
for file in p:lines() do
|
||||
if file:match("%.lua$") then
|
||||
if(file:find("elements"))then
|
||||
local itemName = file:gsub("%.lua$", "")
|
||||
itemName = itemName:match("([^/\\]+)$")
|
||||
table.insert(elementList, itemName)
|
||||
end
|
||||
local f = io.open(file, "r")
|
||||
if f then
|
||||
files[file] = f:read("*a")
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
p:close()
|
||||
end
|
||||
|
||||
scanDir(path)
|
||||
return files
|
||||
end
|
||||
|
||||
local function extractComment(line)
|
||||
local tripleContent = line:match("^%-%-%-%s*(.*)")
|
||||
if tripleContent then
|
||||
return tripleContent, true
|
||||
end
|
||||
|
||||
local doubleContent = line:match("^%-%- %s*(.*)")
|
||||
if doubleContent then
|
||||
return doubleContent, false
|
||||
end
|
||||
|
||||
return nil, false
|
||||
end
|
||||
|
||||
local function getCommentType(comment)
|
||||
for _, pattern in pairs(commentTypes) do
|
||||
if comment:match("^@"..pattern) then
|
||||
local content = comment:sub(#pattern + 2):gsub("^%s*", "")
|
||||
return pattern, content
|
||||
end
|
||||
end
|
||||
|
||||
return "desc", comment
|
||||
end
|
||||
|
||||
local function hasBlockContent(block)
|
||||
for key, _ in pairs(block) do
|
||||
if(key~="type")and(key~="desc")then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if(#block.desc > 0)then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function getFunctionName(line)
|
||||
local pattern = "^function%s+([%w_%.:]-)%s*%("
|
||||
return line:match(pattern)
|
||||
end
|
||||
|
||||
local function split(str, delimiter)
|
||||
local result = {}
|
||||
for match in (str..delimiter):gmatch("(.-)"..delimiter) do
|
||||
table.insert(result, match)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function getClassName(content)
|
||||
return split(content:gsub("^%s*", ""):gsub("%s*$", ""):gsub(" ", ""), ":")
|
||||
end
|
||||
|
||||
local function parseFile(content)
|
||||
local fileContent = {}
|
||||
local class = {functions = {}, properties = {}, events = {}, fields = {}}
|
||||
local func = {params={}, returns={}, desc=""}
|
||||
local skipNextFunction = false
|
||||
|
||||
for line in content:gsub("\r\n", "\n"):gmatch("([^\n]*)\n?") do
|
||||
if line:match("^%s*$") or line == "" then
|
||||
-- Skip empty lines
|
||||
func = {params={}, returns={}, desc=""}
|
||||
else
|
||||
local comment, isDoc = extractComment(line)
|
||||
if comment then
|
||||
local commentType, content = getCommentType(comment)
|
||||
if(commentType=="module")then
|
||||
class.module = content
|
||||
elseif(commentType=="class")then
|
||||
if(class.class)then
|
||||
fileContent[class.class] = class
|
||||
class = {functions = {}, properties = {}, events = {}, fields = {}}
|
||||
end
|
||||
class.class, class.parent = table.unpack(getClassName(content))
|
||||
if(class.class=="Container")then
|
||||
for _,v in ipairs(elementList)do
|
||||
class.functions["Container:add"..v] = {params={{name="self", type="Container", desc="self"}, {name="props", type="table", desc="Optional: properties for the element.", optional=true}}, returns={{type=v, desc="element A new "..v.." element."}}, desc="Creates a new "..v.." element.\n"}
|
||||
end
|
||||
end
|
||||
elseif(commentType=="param")then
|
||||
if func then
|
||||
local paramName, paramType, paramDesc = content:match("^%s*([%w_]+)%s+([%w_]+)%s*(.*)$")
|
||||
if paramName then
|
||||
table.insert(func.params, {name=paramName, type=paramType, desc=paramDesc or ""})
|
||||
end
|
||||
end
|
||||
elseif(commentType=="return")then
|
||||
if func then
|
||||
local returnType, returnDesc = content:match("^%s*([%w_]+)%s*(.*)$")
|
||||
if returnType then
|
||||
table.insert(func.returns, {type=returnType, desc=returnDesc or ""})
|
||||
end
|
||||
end
|
||||
elseif(commentType=="usage")then
|
||||
if func then
|
||||
func.usage = content
|
||||
end
|
||||
elseif(commentType=="desc")then
|
||||
if func then
|
||||
func.desc = (func.desc or "") .. content .. "\n"
|
||||
end
|
||||
elseif(commentType=="private")then
|
||||
skipNextFunction = true
|
||||
elseif(commentType=="protected")then
|
||||
if func then
|
||||
func.protected = true
|
||||
end
|
||||
elseif(commentType=="shortDescription")then
|
||||
-- skip
|
||||
elseif(commentType=="property")then
|
||||
local propertyName, propertyType, propertyDesc = content:match("^%s*([%w_]+)%s+([%w_]+)%s*(.*)$")
|
||||
if propertyName then
|
||||
class.fields[propertyName] = {type=propertyType, desc=propertyDesc:gsub("^%S+%s*", "") or ""}
|
||||
propertyName = propertyName:sub(1,1):upper() .. propertyName:sub(2)
|
||||
class.functions[class.class..":get" .. propertyName] = {params={{name="self", type=class.class, desc="self"}}, returns={{type=propertyType, desc=propertyDesc or ""}}, desc="Gets the value of the " .. propertyName .. " property.\n"}
|
||||
class.functions[class.class..":set" .. propertyName] = {params={{name="self", type=class.class, desc="self"}, {name=propertyName, type=propertyType, desc=propertyDesc:gsub("^%S+%s*", "") or ""}}, returns={}, desc="Sets the value of the " .. propertyName .. " property.\n"}
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
if not skipNextFunction then
|
||||
local functionName = getFunctionName(line)
|
||||
if functionName then
|
||||
if func and hasBlockContent(func) then
|
||||
class.functions[functionName] = func
|
||||
end
|
||||
func = {params={}, returns={}, desc=""}
|
||||
end
|
||||
else
|
||||
skipNextFunction = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
if(class.class)then
|
||||
fileContent[class.class] = class
|
||||
end
|
||||
return fileContent
|
||||
end
|
||||
|
||||
local function generateLuaLS(finalContent)
|
||||
local output = {"---@meta\n\n"}
|
||||
|
||||
for filepath, block in pairs(finalContent) do
|
||||
if block.class then
|
||||
if(block.parent~=nil)then
|
||||
table.insert(output, string.format("---@class %s : %s\n", block.class, block.parent))
|
||||
else
|
||||
table.insert(output, string.format("---@class %s\n", block.class))
|
||||
end
|
||||
for k,v in pairs(block.fields)do
|
||||
table.insert(output, string.format("---@field %s %s %s\n", k, v.type, v.desc))
|
||||
end
|
||||
table.insert(output, string.format("local %s = {}\n\n", block.class))
|
||||
|
||||
for funcName, funcData in pairs(block.functions) do
|
||||
if funcData.desc~="" then
|
||||
table.insert(output, string.format("---%s", funcData.desc))
|
||||
end
|
||||
if(funcData.protected)then
|
||||
table.insert(output, string.format("---This function is protected und should not be called outside of basalt, however you can overwrite it if you know what you're doing.\n"))
|
||||
end
|
||||
for _, param in ipairs(funcData.params) do
|
||||
table.insert(output, string.format("---@param %s %s %s\n", (param.optional and param.name.."?" or param.name), param.type, param.desc))
|
||||
end
|
||||
for _, ret in ipairs(funcData.returns) do
|
||||
table.insert(output, string.format("---@return %s %s\n", ret.type, ret.desc))
|
||||
end
|
||||
if(funcData.protected)then
|
||||
table.insert(output, string.format("---@protected\n"))
|
||||
end
|
||||
|
||||
local paramNames = {}
|
||||
for _, param in ipairs(funcData.params) do
|
||||
table.insert(paramNames, param.name)
|
||||
end
|
||||
|
||||
table.insert(output, string.format("function %s(%s) end\n\n",
|
||||
funcName,
|
||||
table.concat(paramNames, ", ")))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(output)
|
||||
end
|
||||
|
||||
local function mergeTables(t1, t2)
|
||||
local merged = {}
|
||||
for k, v in pairs(t1) do
|
||||
merged[k] = v
|
||||
end
|
||||
for k, v in pairs(t2) do
|
||||
if type(v) == "table" and type(merged[k]) == "table" then
|
||||
merged[k] = mergeTables(merged[k], v)
|
||||
else
|
||||
merged[k] = v
|
||||
end
|
||||
end
|
||||
return merged
|
||||
end
|
||||
|
||||
local log = require(".Basalt2/src/log")
|
||||
local function parseFiles(files)
|
||||
local finalContent = {}
|
||||
for k,v in pairs(files)do
|
||||
local fileContent = parseFile(v)
|
||||
if fileContent then
|
||||
finalContent = mergeTables(finalContent, fileContent)
|
||||
end
|
||||
end
|
||||
|
||||
local lualsContent = generateLuaLS(finalContent)
|
||||
local outFile = fs.open("LuaLS.lua", "w")
|
||||
if outFile then
|
||||
outFile.write(lualsContent)
|
||||
outFile.close()
|
||||
end
|
||||
end
|
||||
|
||||
if _G.fs then
|
||||
parseFiles(getFilesInCC(args[1]))
|
||||
else
|
||||
parseFiles(getFilesInLua(args[1]))
|
||||
end
|
||||
Reference in New Issue
Block a user