Finished(?) annotation parsing

Small fixes for the parser
This commit is contained in:
Robert Jelic
2025-03-14 03:46:54 +01:00
parent 414703d18a
commit f14e0ce204
8 changed files with 323 additions and 2612 deletions

View File

@@ -29,8 +29,11 @@ jobs:
run: |
lua tools/generate-config.lua
# Step 2: Generate LuaLS Definitions
- name: Generate LuaLS
run: |
lua tools/generate-annotations.lua src
# Step 3: Bundle and Minify
- name: Bundle and Minify
run: |
@@ -74,6 +77,6 @@ jobs:
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add config.lua src/LuaLS.lua release/basalt.lua CHANGELOG.md
git add config.lua LuaLS.lua release/basalt.lua CHANGELOG.md
git commit -m "Update config, LuaLS definitions, bundle and changelog" || exit 0
git push

1
.gitignore vendored
View File

@@ -6,4 +6,3 @@ ascii.lua
tests
testWorkflows
.vscode
generate-annotations.lua

File diff suppressed because it is too large Load Diff

View File

@@ -51,19 +51,22 @@ function Display:init(props, basalt)
end
self:observe("width", function(self, width)
local window = self.get("window")
local window = self._window
if window then
window.reposition(1, 1, width, self.get("height"))
end
end)
self:observe("height", function(self, height)
local window = self.get("window")
local window = self._window
if window then
window.reposition(1, 1, self.get("width"), height)
end
end)
end
--- Returns the current window object
--- @shortDescription Returns the current window object
--- @return table window The current window object
function Display:getWindow()
return self._window
end

View File

@@ -419,8 +419,7 @@ function VisualElement:setCursor(x, y, blink, color)
return self
end
--- This function is used to prioritize the element by moving it to the top of its parent's children.
--- It removes the element from its parent and adds it back, effectively changing its order.
--- This function is used to prioritize the element by moving it to the top of its parent's children. It removes the element from its parent and adds it back, effectively changing its order.
--- @shortDescription Prioritizes the element by moving it to the top of its parent's children
--- @return VisualElement self The VisualElement instance
function VisualElement:prioritize()

View File

@@ -163,6 +163,7 @@ function BaseElement:applyTheme()
self.set(prop, value)
end
end
return self
end
--- Gets the theme properties for this element
@@ -179,26 +180,26 @@ end
--- The Theme API provides methods for managing themes globally
---@class ThemeAPI
local themeAPI = {}
local ThemeAPI = {}
--- Sets the current theme
--- @shortDescription Sets a new theme
--- @param newTheme table The theme configuration to set
function themeAPI.setTheme(newTheme)
function ThemeAPI.setTheme(newTheme)
themes.default = newTheme
end
--- Gets the current theme configuration
--- @shortDescription Gets the current theme
--- @return table theme The current theme configuration
function themeAPI.getTheme()
function ThemeAPI.getTheme()
return themes.default
end
--- Loads a theme from a JSON file
--- @shortDescription Loads theme from JSON file
--- @param path string Path to the theme JSON file
function themeAPI.loadTheme(path)
function ThemeAPI.loadTheme(path)
local file = fs.open(path, "r")
if file then
local content = file.readAll()
@@ -209,5 +210,5 @@ end
return {
BaseElement = BaseElement,
API = themeAPI
API = ThemeAPI
}

View File

@@ -375,7 +375,7 @@ function PropertySystem:observe(name, callback)
end
--- Removes an observer from a property
--- @NshortDescription Removes an observer from a property
--- @shortDescription Removes an observer from a property
--- @param name string The name of the property
--- @param callback function The callback function to remove
--- @return table self The PropertySystem

View 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