- Fixed a container bug
- Added onDone to Program
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
local FunctionParser = {}
|
||||
|
||||
--- Check if a block represents a function
|
||||
function FunctionParser.canParse(block)
|
||||
return block.context and block.context.type == "function"
|
||||
end
|
||||
|
||||
--- Parse a complete function documentation block
|
||||
function FunctionParser.parse(block)
|
||||
local functionDoc = {
|
||||
type = "function",
|
||||
name = block.context.name,
|
||||
shortDescription = nil,
|
||||
params = {},
|
||||
returns = {},
|
||||
visibility = "public", -- default
|
||||
context = block.context,
|
||||
content = {}
|
||||
}
|
||||
|
||||
for _, line in ipairs(block.comments) do
|
||||
-- Parse shortDescription
|
||||
local shortDesc = line:match("^%-*%s*@shortDescription%s+(.*)$")
|
||||
if shortDesc then
|
||||
functionDoc.shortDescription = shortDesc
|
||||
|
||||
-- Parse param
|
||||
elseif line:match("^%-*%s*@param") then
|
||||
local paramName, paramType, paramDesc = line:match("^%-*%s*@param%s+(%S+)%s+(%S+)%s+(.*)$")
|
||||
if paramName then
|
||||
table.insert(functionDoc.params, {
|
||||
name = paramName,
|
||||
type = paramType,
|
||||
description = paramDesc or ""
|
||||
})
|
||||
end
|
||||
|
||||
-- Parse return
|
||||
elseif line:match("^%-*%s*@return") then
|
||||
local returnType, returnName, returnDesc = line:match("^%-*%s*@return%s+(%S+)%s+(%S+)%s+(.*)$")
|
||||
if returnType then
|
||||
table.insert(functionDoc.returns, {
|
||||
type = returnType,
|
||||
name = returnName or "",
|
||||
description = returnDesc or ""
|
||||
})
|
||||
end
|
||||
|
||||
-- Parse visibility
|
||||
elseif line:match("^%-*%s*@private%s*$") then
|
||||
functionDoc.visibility = "private"
|
||||
elseif line:match("^%-*%s*@protected%s*$") then
|
||||
functionDoc.visibility = "protected"
|
||||
|
||||
-- Regular content
|
||||
else
|
||||
table.insert(functionDoc.content, line)
|
||||
end
|
||||
end
|
||||
|
||||
return functionDoc
|
||||
end
|
||||
|
||||
return FunctionParser
|
||||
@@ -0,0 +1,101 @@
|
||||
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)
|
||||
local blocks = {}
|
||||
local currentCommentBlock = {}
|
||||
local i = 1
|
||||
|
||||
while i <= #lines do
|
||||
local line = lines[i]:match("^%s*(.*)") -- Trim leading whitespace
|
||||
|
||||
-- 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
|
||||
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 = {}
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- Handle any remaining comments
|
||||
if #currentCommentBlock > 0 then
|
||||
table.insert(blocks, {
|
||||
comments = currentCommentBlock,
|
||||
context = nil
|
||||
})
|
||||
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"
|
||||
end
|
||||
end
|
||||
|
||||
--- Extracts the name from a code line
|
||||
function CommentExtractor.extractName(codeLine)
|
||||
-- Function patterns
|
||||
local funcName = codeLine:match("^function%s+([%w%.%:]+)")
|
||||
if funcName then return funcName end
|
||||
|
||||
local localFuncName = codeLine:match("^local%s+function%s+([%w%.%:]+)")
|
||||
if localFuncName then return localFuncName end
|
||||
|
||||
-- Variable/class patterns
|
||||
local varName = codeLine:match("^local%s+([%w_]+)%s*=")
|
||||
if varName then return varName end
|
||||
|
||||
-- Method patterns
|
||||
local methodName = codeLine:match("^([%w%.%:]+)%s*=")
|
||||
if methodName then return methodName end
|
||||
|
||||
return "unknown"
|
||||
end
|
||||
|
||||
return CommentExtractor
|
||||
29
tools/BasaltDoc/ldoc-markdown-parser/src/parser/init.lua
Normal file
29
tools/BasaltDoc/ldoc-markdown-parser/src/parser/init.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
local CommentExtractor = require("parser.comment_extractor")
|
||||
local MarkdownGenerator = require("parser.markdown_generator")
|
||||
|
||||
local Parser = {}
|
||||
|
||||
--- Extract comments and generate markdown
|
||||
function Parser.extractComments(content)
|
||||
local lines = {}
|
||||
for line in content:gmatch("[^\r\n]+") do
|
||||
table.insert(lines, line)
|
||||
end
|
||||
return CommentExtractor.extractComments(lines)
|
||||
end
|
||||
|
||||
--- Generate markdown from comment blocks
|
||||
function Parser.generateMarkdown(commentBlocks)
|
||||
local parsedBlocks = {}
|
||||
|
||||
-- Parse each block using the appropriate parser
|
||||
for _, block in ipairs(commentBlocks) do
|
||||
local parsedBlock = MarkdownGenerator.parseBlock(block)
|
||||
table.insert(parsedBlocks, parsedBlock)
|
||||
end
|
||||
|
||||
-- Generate markdown from parsed blocks
|
||||
return MarkdownGenerator.generateMarkdown(parsedBlocks)
|
||||
end
|
||||
|
||||
return Parser
|
||||
@@ -0,0 +1,142 @@
|
||||
local markdownGenerator = {}
|
||||
local TagParserRegistry = require("parser.tag_parser_registry")
|
||||
|
||||
--- 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
|
||||
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)
|
||||
else
|
||||
-- Generic parsing using individual tag parsers
|
||||
return markdownGenerator.parseGenericBlock(block)
|
||||
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)
|
||||
local md = {}
|
||||
|
||||
table.insert(md, string.format("## Function: %s", functionBlock.name))
|
||||
|
||||
if functionBlock.shortDescription then
|
||||
table.insert(md, string.format("**Description:** %s", functionBlock.shortDescription))
|
||||
end
|
||||
|
||||
if #functionBlock.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))
|
||||
end
|
||||
end
|
||||
|
||||
if #functionBlock.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))
|
||||
end
|
||||
end
|
||||
|
||||
if functionBlock.visibility ~= "public" then
|
||||
table.insert(md, string.format("**Visibility:** %s", functionBlock.visibility))
|
||||
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)
|
||||
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
|
||||
end
|
||||
|
||||
-- Generate markdown for content
|
||||
if #block.content > 0 then
|
||||
table.insert(md, "### Content")
|
||||
table.insert(md, table.concat(block.content, "\n"))
|
||||
end
|
||||
|
||||
return table.concat(md, "\n\n")
|
||||
end
|
||||
|
||||
return markdownGenerator
|
||||
@@ -0,0 +1,19 @@
|
||||
local tagParser = {}
|
||||
|
||||
local function parseTag(tagLine)
|
||||
local tag, content = tagLine:match("^%s*@(.-)%s*(.*)$")
|
||||
return tag, content
|
||||
end
|
||||
|
||||
function tagParser.parseTags(commentLines)
|
||||
local tags = {}
|
||||
for _, line in ipairs(commentLines) do
|
||||
local tag, content = parseTag(line)
|
||||
if tag then
|
||||
tags[tag] = content
|
||||
end
|
||||
end
|
||||
return tags
|
||||
end
|
||||
|
||||
return tagParser
|
||||
@@ -0,0 +1,59 @@
|
||||
local TagParserRegistry = {}
|
||||
|
||||
local tagParsers = {}
|
||||
local blockParsers = {}
|
||||
|
||||
--- Register a new tag parser (for individual tags like @param, @return)
|
||||
function TagParserRegistry.registerTag(tagName, parser)
|
||||
tagParsers[tagName] = parser
|
||||
end
|
||||
|
||||
--- Register a new block parser (for complete blocks like functions, classes)
|
||||
function TagParserRegistry.registerBlock(blockType, parser)
|
||||
blockParsers[blockType] = parser
|
||||
end
|
||||
|
||||
--- Get a specific tag parser
|
||||
function TagParserRegistry.getTag(tagName)
|
||||
return tagParsers[tagName]
|
||||
end
|
||||
|
||||
--- Get a specific block parser
|
||||
function TagParserRegistry.getBlock(blockType)
|
||||
return blockParsers[blockType]
|
||||
end
|
||||
|
||||
--- Get all registered tag parsers
|
||||
function TagParserRegistry.getAllTags()
|
||||
return tagParsers
|
||||
end
|
||||
|
||||
--- Get all registered block parsers
|
||||
function TagParserRegistry.getAllBlocks()
|
||||
return blockParsers
|
||||
end
|
||||
|
||||
--- Auto-load all parsers
|
||||
local function loadAllParsers()
|
||||
-- Load tag parsers (individual tags)
|
||||
local tagParsersList = {"param", "return", "property", "private", "protected", "shortDescription"}
|
||||
for _, parserName in ipairs(tagParsersList) do
|
||||
local success, parser = pcall(require, "parser.tag_parsers." .. parserName)
|
||||
if success then
|
||||
TagParserRegistry.registerTag(parserName, parser)
|
||||
end
|
||||
end
|
||||
|
||||
-- Load block parsers (complete documentation blocks)
|
||||
local blockParsersList = {"function", "class", "property_definition"}
|
||||
for _, parserName in ipairs(blockParsersList) do
|
||||
local success, parser = pcall(require, "parser.block_parsers." .. parserName)
|
||||
if success then
|
||||
TagParserRegistry.registerBlock(parserName, parser)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
loadAllParsers()
|
||||
|
||||
return TagParserRegistry
|
||||
@@ -0,0 +1,21 @@
|
||||
local PropertyParser = {}
|
||||
|
||||
--- Parse @property tag
|
||||
--- Example: ---@property text string Button Button text
|
||||
function PropertyParser.parse(line)
|
||||
local pattern = "^%-*%s*@property%s+(%S+)%s+(%S+)%s+(.*)$"
|
||||
local name, dataType, description = line:match(pattern)
|
||||
|
||||
if name and dataType then
|
||||
return {
|
||||
type = "property",
|
||||
name = name,
|
||||
dataType = dataType,
|
||||
description = description or ""
|
||||
}
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return PropertyParser
|
||||
@@ -0,0 +1,19 @@
|
||||
local ShortDescriptionParser = {}
|
||||
|
||||
--- Parse @shortDescription tag
|
||||
--- Example: --- @shortDescription Creates a new Button instance
|
||||
function ShortDescriptionParser.parse(line)
|
||||
local pattern = "^%-*%s*@shortDescription%s+(.*)$"
|
||||
local description = line:match(pattern)
|
||||
|
||||
if description then
|
||||
return {
|
||||
type = "shortDescription",
|
||||
description = description
|
||||
}
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return ShortDescriptionParser
|
||||
@@ -0,0 +1,35 @@
|
||||
local fileReader = {}
|
||||
|
||||
--- Read file content
|
||||
--- @param filePath string Path to the file
|
||||
--- @return string|nil content File content or nil if error
|
||||
--- @return string|nil error Error message if any
|
||||
function fileReader.readFile(filePath)
|
||||
local file = io.open(filePath, "r")
|
||||
if not file then
|
||||
return nil, "Could not open file: " .. filePath
|
||||
end
|
||||
|
||||
local content = file:read("*all")
|
||||
file:close()
|
||||
|
||||
return content, nil
|
||||
end
|
||||
|
||||
--- Write content to file
|
||||
--- @param filePath string Path to the file
|
||||
--- @param content string Content to write
|
||||
--- @return string|nil error Error message if any
|
||||
function fileReader.writeFile(filePath, content)
|
||||
local file = io.open(filePath, "w")
|
||||
if not file then
|
||||
return "Could not open file for writing: " .. filePath
|
||||
end
|
||||
|
||||
file:write(content)
|
||||
file:close()
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return fileReader
|
||||
16
tools/BasaltDoc/ldoc-markdown-parser/src/utils/init.lua
Normal file
16
tools/BasaltDoc/ldoc-markdown-parser/src/utils/init.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
local string_utils = require("utils.string_utils")
|
||||
|
||||
local utils = {}
|
||||
|
||||
-- Re-export string utilities
|
||||
utils.trim = string_utils.trim
|
||||
utils.is_empty = string_utils.is_empty
|
||||
utils.starts_with = string_utils.starts_with
|
||||
utils.ends_with = string_utils.ends_with
|
||||
utils.split = string_utils.split
|
||||
|
||||
function utils.isClassMethod(name)
|
||||
return name:sub(1, 1):upper() == name:sub(1, 1)
|
||||
end
|
||||
|
||||
return utils
|
||||
@@ -0,0 +1,27 @@
|
||||
local string_utils = {}
|
||||
|
||||
function string_utils.trim(s)
|
||||
return s:match("^%s*(.-)%s*$")
|
||||
end
|
||||
|
||||
function string_utils.is_empty(s)
|
||||
return s == nil or s == ""
|
||||
end
|
||||
|
||||
function string_utils.starts_with(s, prefix)
|
||||
return s:sub(1, #prefix) == prefix
|
||||
end
|
||||
|
||||
function string_utils.ends_with(s, suffix)
|
||||
return s:sub(-#suffix) == suffix
|
||||
end
|
||||
|
||||
function string_utils.split(s, delimiter)
|
||||
local result = {}
|
||||
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
|
||||
table.insert(result, match)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
return string_utils
|
||||
Reference in New Issue
Block a user