diff --git a/.gitignore b/.gitignore index 094a853..453075a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ testWorkflows todo.txt Flexbox2.lua markdown2.lua +BasaltDoc \ No newline at end of file diff --git a/src/elements/BaseElement.lua b/src/elements/BaseElement.lua index b2c0c6c..114ae88 100644 --- a/src/elements/BaseElement.lua +++ b/src/elements/BaseElement.lua @@ -31,6 +31,9 @@ BaseElement.defineProperty(BaseElement, "name", {default = "", type = "string"}) --- @property eventCallbacks table BaseElement The event callbacks for the element BaseElement.defineProperty(BaseElement, "eventCallbacks", {default = {}, type = "table"}) +--- @property enabled boolean BaseElement Whether the element is enabled or not +BaseElement.defineProperty(BaseElement, "enabled", {default = true, type = "boolean" }) + --- Registers a new event listener for the element (on class level) --- @shortDescription Registers a new event listener for the element (on class level) --- @param class table The class to register @@ -215,6 +218,9 @@ end --- @return boolean? handled Whether the event was handled --- @protected function BaseElement:dispatchEvent(event, ...) + if self.get("enabled") == false then + return false + end if self[event] then return self[event](self, ...) end diff --git a/src/elements/Table.lua b/src/elements/Table.lua index 4c57384..7a9d018 100644 --- a/src/elements/Table.lua +++ b/src/elements/Table.lua @@ -216,7 +216,7 @@ function Table:render() local finalText = string.sub(paddedText, 1, col.width) local finalForeground = string.rep(tHex[self.get("foreground")], #finalText) local finalBackground = string.rep(tHex[bg], #finalText) - + self:blit(currentX, y, finalText, finalForeground, finalBackground) currentX = currentX + col.width end @@ -226,27 +226,6 @@ function Table:render() string.rep(tHex[self.get("background")], self.get("width"))) end end - - if #data > height - 2 then - local scrollbarHeight = height - 2 - local thumbSize = math.max(1, math.floor(scrollbarHeight * (height - 2) / #data)) - - local maxScroll = #data - (height - 2) + 1 - local scrollPercent = scrollOffset / maxScroll - local thumbPos = 2 + math.floor(scrollPercent * (scrollbarHeight - thumbSize)) - - if scrollOffset >= maxScroll then - thumbPos = height - thumbSize - end - - for y = 2, height do - self:blit(self.get("width"), y, "\127", tHex[colors.gray], tHex[colors.gray]) - end - - for y = thumbPos, math.min(height, thumbPos + thumbSize - 1) do - self:blit(self.get("width"), y, "\127", tHex[colors.white], tHex[colors.white]) - end - end end return Table \ No newline at end of file diff --git a/src/elements/VisualElement.lua b/src/elements/VisualElement.lua index d3cfc6b..a35aa7b 100644 --- a/src/elements/VisualElement.lua +++ b/src/elements/VisualElement.lua @@ -99,7 +99,7 @@ VisualElement.registerEventCallback(VisualElement, "ClickUp", "mouse_up", "mouse VisualElement.registerEventCallback(VisualElement, "Drag", "mouse_drag", "mouse_click", "mouse_up") VisualElement.registerEventCallback(VisualElement, "Scroll", "mouse_scroll") VisualElement.registerEventCallback(VisualElement, "Enter", "mouse_enter", "mouse_move") -VisualElement.registerEventCallback(VisualElement, "LeEave", "mouse_leave", "mouse_move") +VisualElement.registerEventCallback(VisualElement, "Leave", "mouse_leave", "mouse_move") VisualElement.registerEventCallback(VisualElement, "Focus", "focus", "blur") VisualElement.registerEventCallback(VisualElement, "Blur", "blur", "focus") VisualElement.registerEventCallback(VisualElement, "Key", "key", "key_up") diff --git a/tools/BasaltDoc/Button.lua b/tools/BasaltDoc/Button.lua index fe13885..b1d90f9 100644 --- a/tools/BasaltDoc/Button.lua +++ b/tools/BasaltDoc/Button.lua @@ -26,6 +26,7 @@ function Button.new() return self end +--- Initializes the Button instance --- @shortDescription Initializes the Button instance --- @param props table The properties to initialize the element with --- @param basalt table The basalt instance diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/comment_extractor.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/comment_extractor.lua index eef506f..ef958cf 100644 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/comment_extractor.lua +++ b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/comment_extractor.lua @@ -1,101 +1,81 @@ local CommentExtractor = {} ---- Extracts comments with their associated code context --- @param lines table The lines of the Lua file as a table of strings. --- @return table A table containing comment blocks with context. -function CommentExtractor.extractComments(lines) +--- Extracts comment blocks that belong together +function CommentExtractor.extractBlocks(lines) local blocks = {} - local currentCommentBlock = {} - local i = 1 + local currentBlock = { + comments = {}, + codeContext = nil + } + local i = 1 while i <= #lines do - local line = lines[i]:match("^%s*(.*)") -- Trim leading whitespace + local line = lines[i] + local trimmed = line:match("^%s*(.-)%s*$") -- Check if this is a comment line - if line:find("^%-%-%-") or line:find("^%-%-") then - table.insert(currentCommentBlock, line) - elseif #currentCommentBlock > 0 then - -- We have accumulated comments, check if next non-empty line is code - local codeContext = nil - local j = i - - -- Skip empty lines to find the actual code - while j <= #lines and lines[j]:match("^%s*$") do - j = j + 1 + if trimmed:match("^%-%-%-") or trimmed:match("^%-%-") then + table.insert(currentBlock.comments, trimmed) + elseif #currentBlock.comments > 0 then + -- We have comments, now look for the code that follows + local codeLineIndex = CommentExtractor.findNextCodeLine(lines, i) + if codeLineIndex then + local codeLine = lines[codeLineIndex]:match("^%s*(.-)%s*$") + currentBlock.codeContext = CommentExtractor.analyzeCode(codeLine, codeLineIndex) end - if j <= #lines then - local codeLine = lines[j]:match("^%s*(.*)") - -- Check if it's a function, class, property, etc. - if codeLine:find("^function") or - codeLine:find("^local function") or - codeLine:find("^local%s+%w+%s*=") or - codeLine:find("^%w+%.%w+") then - codeContext = { - type = CommentExtractor.getCodeType(codeLine), - name = CommentExtractor.extractName(codeLine), - line = codeLine, - lineNumber = j - } - end - end - - -- Add the comment block with its context - table.insert(blocks, { - comments = currentCommentBlock, - context = codeContext - }) - - currentCommentBlock = {} + -- Save this block and start a new one + table.insert(blocks, currentBlock) + currentBlock = {comments = {}, codeContext = nil} end i = i + 1 end - -- Handle any remaining comments - if #currentCommentBlock > 0 then - table.insert(blocks, { - comments = currentCommentBlock, - context = nil - }) + -- Handle remaining comments + if #currentBlock.comments > 0 then + table.insert(blocks, currentBlock) end return blocks end ---- Determines the type of code (function, class, property, etc.) -function CommentExtractor.getCodeType(codeLine) - if codeLine:find("^function") or codeLine:find("^local function") then - return "function" - elseif codeLine:find("^local%s+%w+%s*=%s*setmetatable") then - return "class" - elseif codeLine:find("^local%s+%w+%s*=") then - return "variable" - elseif codeLine:find("^%w+%.defineProperty") then - return "property_definition" - else - return "unknown" +--- Find the next non-empty code line +function CommentExtractor.findNextCodeLine(lines, startIndex) + for i = startIndex, #lines do + local trimmed = lines[i]:match("^%s*(.-)%s*$") + if trimmed ~= "" and not trimmed:match("^%-%-") then + return i + end end + return nil end ---- Extracts the name from a code line -function CommentExtractor.extractName(codeLine) +--- Analyze what kind of code this is +function CommentExtractor.analyzeCode(codeLine, lineNumber) -- Function patterns - local funcName = codeLine:match("^function%s+([%w%.%:]+)") - if funcName then return funcName end + if codeLine:match("^function%s+([%w%.%:]+)") then + local name = codeLine:match("^function%s+([%w%.%:]+)") + return {type = "function", name = name, line = codeLine, lineNumber = lineNumber} + end - local localFuncName = codeLine:match("^local%s+function%s+([%w%.%:]+)") - if localFuncName then return localFuncName end + if codeLine:match("^local%s+function%s+([%w%.%:]+)") then + local name = codeLine:match("^local%s+function%s+([%w%.%:]+)") + return {type = "function", name = name, line = codeLine, lineNumber = lineNumber} + end - -- Variable/class patterns - local varName = codeLine:match("^local%s+([%w_]+)%s*=") - if varName then return varName end + -- Class/variable patterns + if codeLine:match("^local%s+([%w_]+)%s*=%s*setmetatable") then + local name = codeLine:match("^local%s+([%w_]+)%s*=") + return {type = "class", name = name, line = codeLine, lineNumber = lineNumber} + end - -- Method patterns - local methodName = codeLine:match("^([%w%.%:]+)%s*=") - if methodName then return methodName end + if codeLine:match("^local%s+([%w_]+)%s*=") then + local name = codeLine:match("^local%s+([%w_]+)%s*=") + return {type = "variable", name = name, line = codeLine, lineNumber = lineNumber} + end - return "unknown" + return {type = "unknown", name = "unknown", line = codeLine, lineNumber = lineNumber} end return CommentExtractor \ No newline at end of file diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/init.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/init.lua index c95090a..b8a136f 100644 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/init.lua +++ b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/init.lua @@ -1,4 +1,5 @@ local CommentExtractor = require("parser.comment_extractor") +local TagParser = require("parser.tag_parser") local MarkdownGenerator = require("parser.markdown_generator") local Parser = {} @@ -9,21 +10,23 @@ function Parser.extractComments(content) for line in content:gmatch("[^\r\n]+") do table.insert(lines, line) end - return CommentExtractor.extractComments(lines) + return CommentExtractor.extractBlocks(lines) end --- Generate markdown from comment blocks function Parser.generateMarkdown(commentBlocks) - local parsedBlocks = {} + local markdown = {} - -- Parse each block using the appropriate parser + -- Parse each block and generate markdown for _, block in ipairs(commentBlocks) do - local parsedBlock = MarkdownGenerator.parseBlock(block) - table.insert(parsedBlocks, parsedBlock) + local tags = TagParser.parseAllTags(block.comments) + local blockMarkdown = MarkdownGenerator.generateBlock(block, tags) + if blockMarkdown and blockMarkdown ~= "" then + table.insert(markdown, blockMarkdown) + end end - -- Generate markdown from parsed blocks - return MarkdownGenerator.generateMarkdown(parsedBlocks) + return table.concat(markdown, "\n\n") end return Parser \ No newline at end of file diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/markdown_generator.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/markdown_generator.lua index a622d12..c68a55e 100644 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/markdown_generator.lua +++ b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/markdown_generator.lua @@ -1,142 +1,143 @@ -local markdownGenerator = {} -local TagParserRegistry = require("parser.tag_parser_registry") +local MarkdownGenerator = {} ---- Determines which block parser should handle a given block ---- @param block table The comment block with context ---- @return string|nil The block type that can handle this block -function markdownGenerator.detectBlockType(block) - local blockParsers = TagParserRegistry.getAllBlocks() - - for blockType, parser in pairs(blockParsers) do - if parser.canParse and parser.canParse(block) then - return blockType - end +--- Generate markdown for a block +function MarkdownGenerator.generateBlock(block, tags) + if not block.codeContext then + return MarkdownGenerator.generateStandaloneComment(tags) end - return nil -- No specific block parser found, use generic parsing -end - ---- Parses a block using the appropriate block parser ---- @param block table The comment block to parse ---- @return table The parsed block data -function markdownGenerator.parseBlock(block) - local blockType = markdownGenerator.detectBlockType(block) - - if blockType then - local parser = TagParserRegistry.getBlock(blockType) - return parser.parse(block) + if block.codeContext.type == "function" then + return MarkdownGenerator.generateFunction(block.codeContext, tags) + elseif block.codeContext.type == "class" then + return MarkdownGenerator.generateClass(block.codeContext, tags) + elseif block.codeContext.type == "variable" then + return MarkdownGenerator.generateVariable(block.codeContext, tags) else - -- Generic parsing using individual tag parsers - return markdownGenerator.parseGenericBlock(block) + return MarkdownGenerator.generateGeneric(block.codeContext, tags) end end ---- Generic block parsing using individual tag parsers ---- @param block table The comment block to parse ---- @return table The parsed block data -function markdownGenerator.parseGenericBlock(block) - local parsedBlock = { - type = "generic", - tags = {}, - content = {}, - context = block.context - } - - for _, line in ipairs(block.comments) do - local parsed = false - local tagParsers = TagParserRegistry.getAllTags() - - -- Try each registered tag parser - for tagName, parser in pairs(tagParsers) do - local result = parser.parse(line) - if result then - table.insert(parsedBlock.tags, result) - parsed = true - break - end - end - - -- If no tag parser matched, treat as regular content - if not parsed then - table.insert(parsedBlock.content, line) - end - end - - return parsedBlock -end - ---- Converts parsed blocks to markdown ---- @param parsedBlocks table Array of parsed blocks ---- @return string The generated markdown -function markdownGenerator.generateMarkdown(parsedBlocks) - local markdown = {} - - for _, block in ipairs(parsedBlocks) do - if block.type == "function" then - table.insert(markdown, markdownGenerator.generateFunctionMarkdown(block)) - else - table.insert(markdown, markdownGenerator.generateGenericMarkdown(block)) - end - table.insert(markdown, "") -- Empty line between blocks - end - - return table.concat(markdown, "\n") -end - ---- Generate markdown for function blocks ---- @param functionBlock table The parsed function block ---- @return string The generated markdown -function markdownGenerator.generateFunctionMarkdown(functionBlock) +--- Generate markdown for function +function MarkdownGenerator.generateFunction(context, tags) local md = {} - table.insert(md, string.format("## Function: %s", functionBlock.name)) + table.insert(md, string.format("### %s", context.name)) + table.insert(md, "") - if functionBlock.shortDescription then - table.insert(md, string.format("**Description:** %s", functionBlock.shortDescription)) + if tags.shortDescription then + table.insert(md, tags.shortDescription) + table.insert(md, "") end - if #functionBlock.params > 0 then + if #tags.description > 0 then + table.insert(md, table.concat(tags.description, " ")) + table.insert(md, "") + end + + if #tags.params > 0 then table.insert(md, "**Parameters:**") - for _, param in ipairs(functionBlock.params) do - table.insert(md, string.format("- `%s` (%s): %s", param.name, param.type, param.description)) + for _, param in ipairs(tags.params) do + table.insert(md, string.format("- `%s` (%s): %s", param.name, param.dataType, param.description)) end + table.insert(md, "") end - if #functionBlock.returns > 0 then + if #tags.returns > 0 then table.insert(md, "**Returns:**") - for _, ret in ipairs(functionBlock.returns) do - table.insert(md, string.format("- `%s` (%s): %s", ret.name, ret.type, ret.description)) + for _, ret in ipairs(tags.returns) do + table.insert(md, string.format("- `%s` (%s): %s", ret.name, ret.dataType, ret.description)) end + table.insert(md, "") end - if functionBlock.visibility ~= "public" then - table.insert(md, string.format("**Visibility:** %s", functionBlock.visibility)) + if tags.visibility ~= "public" then + table.insert(md, string.format("**Visibility:** %s", tags.visibility)) + table.insert(md, "") end return table.concat(md, "\n") end ---- Generate markdown for generic blocks ---- @param block table The parsed generic block ---- @return string The generated markdown -function markdownGenerator.generateGenericMarkdown(block) +--- Generate markdown for class +function MarkdownGenerator.generateClass(context, tags) local md = {} - -- Generate markdown for tags - if #block.tags > 0 then - table.insert(md, "### Tags") - for _, tag in ipairs(block.tags) do - table.insert(md, string.format("- **%s**: %s", tag.name, tag.description)) - end + table.insert(md, string.format("## Class: %s", context.name)) + if tags.class and tags.class.parent then + table.insert(md, string.format("*Extends: %s*", tags.class.parent)) + end + table.insert(md, "") + + if tags.shortDescription then + table.insert(md, tags.shortDescription) + table.insert(md, "") end - -- Generate markdown for content - if #block.content > 0 then - table.insert(md, "### Content") - table.insert(md, table.concat(block.content, "\n")) + if #tags.description > 0 then + table.insert(md, table.concat(tags.description, " ")) + table.insert(md, "") end - return table.concat(md, "\n\n") + return table.concat(md, "\n") end -return markdownGenerator \ No newline at end of file +--- Generate markdown for standalone comments or properties +function MarkdownGenerator.generateStandaloneComment(tags) + local md = {} + + if #tags.properties > 0 then + for _, prop in ipairs(tags.properties) do + table.insert(md, string.format("**Property:** `%s` (%s) - %s", prop.name, prop.dataType, prop.description)) + end + table.insert(md, "") + end + + if #tags.description > 0 then + table.insert(md, table.concat(tags.description, " ")) + table.insert(md, "") + end + + return table.concat(md, "\n") +end + +--- Generate markdown for variables +function MarkdownGenerator.generateVariable(context, tags) + local md = {} + + table.insert(md, string.format("### %s", context.name)) + table.insert(md, "") + + if tags.shortDescription then + table.insert(md, tags.shortDescription) + table.insert(md, "") + end + + if #tags.description > 0 then + table.insert(md, table.concat(tags.description, " ")) + table.insert(md, "") + end + + return table.concat(md, "\n") +end + +--- Generate markdown for generic code +function MarkdownGenerator.generateGeneric(context, tags) + local md = {} + + table.insert(md, string.format("### %s", context.name)) + table.insert(md, "") + + if tags.shortDescription then + table.insert(md, tags.shortDescription) + table.insert(md, "") + end + + if #tags.description > 0 then + table.insert(md, table.concat(tags.description, " ")) + table.insert(md, "") + end + + return table.concat(md, "\n") +end + +return MarkdownGenerator \ No newline at end of file diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser.lua index 1b838e9..7b1d4fd 100644 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser.lua +++ b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser.lua @@ -1,19 +1,117 @@ -local tagParser = {} +local TagParser = {} -local function parseTag(tagLine) - local tag, content = tagLine:match("^%s*@(.-)%s*(.*)$") - return tag, content -end - -function tagParser.parseTags(commentLines) - local tags = {} +--- Parse all tags from a list of comment lines +function TagParser.parseAllTags(commentLines) + local tags = { + shortDescription = nil, + description = {}, + params = {}, + returns = {}, + visibility = "public", + properties = {}, + class = nil, + other = {} + } + for _, line in ipairs(commentLines) do - local tag, content = parseTag(line) - if tag then - tags[tag] = content + local parsed = TagParser.parseSingleLine(line) + if parsed then + TagParser.addToTags(tags, parsed) + else + -- Regular description line + local desc = line:match("^%-*%s*(.+)$") + if desc and not desc:match("^@") then + table.insert(tags.description, desc) + end end end + return tags end -return tagParser \ No newline at end of file +--- Parse a single comment line for tags +function TagParser.parseSingleLine(line) + -- @shortDescription + local shortDesc = line:match("^%-*%s*@shortDescription%s+(.+)$") + if shortDesc then + return {type = "shortDescription", value = shortDesc} + end + + -- @param name type description + local paramName, paramType, paramDesc = line:match("^%-*%s*@param%s+(%S+)%s+(%S+)%s*(.*)$") + if paramName then + return { + type = "param", + name = paramName, + dataType = paramType, + description = paramDesc or "" + } + end + + -- @return type name description + local returnType, returnName, returnDesc = line:match("^%-*%s*@return%s+(%S+)%s+(%S+)%s*(.*)$") + if returnType then + return { + type = "return", + dataType = returnType, + name = returnName or "", + description = returnDesc or "" + } + end + + -- @property name type description + local propName, propType, propDesc = line:match("^%-*%s*@property%s+(%S+)%s+(%S+)%s*(.*)$") + if propName then + return { + type = "property", + name = propName, + dataType = propType, + description = propDesc or "" + } + end + + -- @class name : parent + local className, parentClass = line:match("^%-*%s*@class%s+(%S+)%s*:%s*(%S+)") + if not className then + className = line:match("^%-*%s*@class%s+(%S+)") + end + if className then + return { + type = "class", + name = className, + parent = parentClass or nil + } + end + + -- Visibility tags + if line:match("^%-*%s*@private%s*$") then + return {type = "visibility", value = "private"} + end + + if line:match("^%-*%s*@protected%s*$") then + return {type = "visibility", value = "protected"} + end + + return nil +end + +--- Add parsed tag to the tags collection +function TagParser.addToTags(tags, parsed) + if parsed.type == "shortDescription" then + tags.shortDescription = parsed.value + elseif parsed.type == "param" then + table.insert(tags.params, parsed) + elseif parsed.type == "return" then + table.insert(tags.returns, parsed) + elseif parsed.type == "property" then + table.insert(tags.properties, parsed) + elseif parsed.type == "class" then + tags.class = parsed + elseif parsed.type == "visibility" then + tags.visibility = parsed.value + else + table.insert(tags.other, parsed) + end +end + +return TagParser \ No newline at end of file