removed scrollbar from tables
added enabled property
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
--- 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
|
||||
@@ -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
|
||||
--- 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
|
||||
Reference in New Issue
Block a user