Small Docs test
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
local elementManager = require("elementManager")
|
||||
local VisualElement = elementManager.getElement("VisualElement")
|
||||
local getCenteredPosition = require("libraries/utils").getCenteredPosition
|
||||
---@cofnigDescription The Button is a standard button element with click handling and state management.
|
||||
|
||||
--- The Button is a standard button element with click handling and state management.
|
||||
---@class Button : VisualElement
|
||||
local Button = setmetatable({}, VisualElement)
|
||||
Button.__index = Button
|
||||
|
||||
---@property text string Button Button text
|
||||
Button.defineProperty(Button, "text", {default = "Button", type = "string", canTriggerRender = true})
|
||||
|
||||
Button.defineEvent(Button, "mouse_click")
|
||||
Button.defineEvent(Button, "mouse_up")
|
||||
|
||||
--- @shortDescription Creates a new Button instance
|
||||
--- @return table self The created instance
|
||||
--- @private
|
||||
function Button.new()
|
||||
local self = setmetatable({}, Button):__init()
|
||||
self.class = Button
|
||||
self.set("width", 10)
|
||||
self.set("height", 3)
|
||||
self.set("z", 5)
|
||||
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
|
||||
--- @protected
|
||||
function Button:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Button")
|
||||
end
|
||||
|
||||
--- @shortDescription Renders the Button
|
||||
--- @protected
|
||||
function Button:render()
|
||||
VisualElement.render(self)
|
||||
local text = self.get("text")
|
||||
text = text:sub(1, self.get("width"))
|
||||
local xO, yO = getCenteredPosition(text, self.get("width"), self.get("height"))
|
||||
self:textFg(xO, yO, text, self.get("foreground"))
|
||||
end
|
||||
|
||||
return Button
|
||||
270
tools/BasaltDoc/init.lua
Normal file
270
tools/BasaltDoc/init.lua
Normal file
@@ -0,0 +1,270 @@
|
||||
local BasaltDoc = {}
|
||||
|
||||
local args = {...}
|
||||
local docsPath = fs.getDir(args[2])
|
||||
local defaultPath = package.path
|
||||
local format = "path;/path/?.lua;/path/?/init.lua;"
|
||||
local main = format:gsub("path", docsPath)
|
||||
package.path = main.."rom/?;"..defaultPath
|
||||
|
||||
local ok1, classParser = pcall(require, "parsers.classParser")
|
||||
|
||||
local ok2, functionParser = pcall(require, "parsers.functionParser")
|
||||
|
||||
local ok3, propertyParser = pcall(require, "parsers.propertyParser")
|
||||
|
||||
local ok4, eventParser = pcall(require, "parsers.eventParser")
|
||||
|
||||
local ok6, globalParser = pcall(require, "parsers.globalParser")
|
||||
|
||||
local ok5, markdownGenerator = pcall(require, "utils.markdownGenerator")
|
||||
|
||||
BasaltDoc.annotationHandlers = {}
|
||||
|
||||
function BasaltDoc.registerAnnotation(tag, handler)
|
||||
BasaltDoc.annotationHandlers[tag] = handler
|
||||
end
|
||||
|
||||
----------------------------------------------------------------
|
||||
-- Standard Annotation Handlers
|
||||
----------------------------------------------------------------
|
||||
BasaltDoc.registerAnnotation("@param", function(target, args)
|
||||
if target.type == "function" then
|
||||
local paramName, optional, paramType, paramDesc = args:match("([%w_]+)(%??)[%s]*([%w_|]+)[%s]*(.*)")
|
||||
if paramName then
|
||||
table.insert(target.params, {
|
||||
name = paramName,
|
||||
type = paramType or "any",
|
||||
description = paramDesc or "",
|
||||
optional = optional == "?"
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@return", function(target, args)
|
||||
if target.type == "function" then
|
||||
local returnType, returnName, returnDesc = args:match("([%w_|]+)[%s]+([%w_]+)[%s]+(.*)")
|
||||
if returnType then
|
||||
table.insert(target.returns, {
|
||||
type = returnType,
|
||||
name = returnName or "",
|
||||
description = returnDesc or ""
|
||||
})
|
||||
else
|
||||
table.insert(target.returns, {
|
||||
type = args:match("([%w_|]+)") or "any",
|
||||
name = "",
|
||||
description = args:match("[%w_|]+[%s]+(.*)" ) or ""
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@returns", function(target, args)
|
||||
BasaltDoc.annotationHandlers["@return"](target, args)
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@usage", function(target, args)
|
||||
if not target.usage then target.usage = {} end
|
||||
table.insert(target.usage, args)
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@example", function(target, args)
|
||||
if not target.examples then target.examples = {} end
|
||||
table.insert(target.examples, args)
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@see", function(target, args)
|
||||
if not target.see then target.see = {} end
|
||||
table.insert(target.see, args)
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@since", function(target, args)
|
||||
target.since = args
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@deprecated", function(target, args)
|
||||
target.deprecated = args ~= "" and args or true
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@private", function(target, args)
|
||||
target.visibility = "private"
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@protected", function(target, args)
|
||||
target.visibility = "protected"
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@configDescription", function(target, args)
|
||||
if target.type == "class" then
|
||||
target.configDescription = args
|
||||
end
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@shortDescription", function(target, args)
|
||||
if target.type == "function" then
|
||||
target.shortDescription = args
|
||||
end
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@run", function(target, args)
|
||||
if not target.run then target.run = {} end
|
||||
table.insert(target.run, args)
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@title", function(target, args)
|
||||
target.title = args
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@skipFunctionList", function(target, args)
|
||||
target.skipFunctionList = true
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@skipPropertyList", function(target, args)
|
||||
target.skipPropertyList = true
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@skipDetailedFunctionList", function(target, args)
|
||||
target.skipDetailedFunctionList = true
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@skip", function(target, args)
|
||||
target.skip = true
|
||||
end)
|
||||
|
||||
BasaltDoc.registerAnnotation("@globalDescription", function(target, args)
|
||||
if args and args ~= "" then
|
||||
target.description = args
|
||||
end
|
||||
end)
|
||||
|
||||
if ok1 then classParser.setHandlers(BasaltDoc.annotationHandlers) end
|
||||
if ok2 then functionParser.setHandlers(BasaltDoc.annotationHandlers) end
|
||||
if ok3 then propertyParser.setHandlers(BasaltDoc.annotationHandlers) end
|
||||
if ok4 then eventParser.setHandlers(BasaltDoc.annotationHandlers) end
|
||||
if ok6 then globalParser.setHandlers(BasaltDoc.annotationHandlers) end
|
||||
|
||||
----------------------------------------------------------------
|
||||
-- Main Parser
|
||||
----------------------------------------------------------------
|
||||
function BasaltDoc.parse(content)
|
||||
local ast = { classes = {}, global = nil }
|
||||
|
||||
local rawLines = {}
|
||||
for line in content:gmatch("([^\r\n]*)\r?\n?") do
|
||||
table.insert(rawLines, line)
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
local i = 1
|
||||
while i <= #rawLines do
|
||||
local line = rawLines[i]
|
||||
if line:match("@globalDescription") then
|
||||
local globalAnnotations = {line}
|
||||
i = i + 1
|
||||
if i <= #rawLines and rawLines[i]:match("%-%-?%[%[") then
|
||||
i = i + 1
|
||||
while i <= #rawLines and not rawLines[i]:match("%]%]") do
|
||||
table.insert(globalAnnotations, "--- " .. rawLines[i])
|
||||
i = i + 1
|
||||
end
|
||||
if i <= #rawLines and rawLines[i]:match("%]%]") then
|
||||
i = i + 1
|
||||
end
|
||||
if #globalAnnotations > 0 and ok6 then
|
||||
local global = globalParser.parse(globalAnnotations, table.concat(globalAnnotations, "\n"))
|
||||
ast.global = global
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(lines, line)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
local annotationBuffer = {}
|
||||
local currentClass = nil
|
||||
local firstTag = nil
|
||||
|
||||
local blockStartTags = {
|
||||
["@class"] = true,
|
||||
["@property"] = true,
|
||||
["@event"] = true,
|
||||
["@skip"] = true
|
||||
}
|
||||
|
||||
local i = 1
|
||||
while i <= #lines do
|
||||
local line = lines[i]
|
||||
if line:match("^%-%-%-?") then
|
||||
table.insert(annotationBuffer, line)
|
||||
if not firstTag then
|
||||
local tag = line:match("@%S+")
|
||||
if tag and blockStartTags[tag] then
|
||||
firstTag = tag
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
elseif #annotationBuffer > 0 then
|
||||
local nextLine = lines[i]
|
||||
local skip = false
|
||||
if nextLine and nextLine:match("^function") and currentClass and ok2 then
|
||||
local fn = functionParser.parse(annotationBuffer, nextLine)
|
||||
if fn then
|
||||
table.insert(currentClass.functions, fn)
|
||||
end
|
||||
skip = true
|
||||
elseif firstTag then
|
||||
if firstTag == "@class" and ok1 then
|
||||
local class = classParser.parse(annotationBuffer, table.concat(annotationBuffer, "\n"))
|
||||
if class and not class.skip then
|
||||
table.insert(ast.classes, class)
|
||||
currentClass = class
|
||||
end
|
||||
elseif firstTag == "@property" and currentClass and ok3 then
|
||||
local prop = propertyParser.parse(annotationBuffer, table.concat(annotationBuffer, "\n"))
|
||||
if prop then
|
||||
table.insert(currentClass.properties, prop)
|
||||
end
|
||||
elseif firstTag == "@event" and currentClass and ok4 then
|
||||
local evt = eventParser.parse(annotationBuffer, table.concat(annotationBuffer, "\n"))
|
||||
if evt then
|
||||
table.insert(currentClass.events, evt)
|
||||
end
|
||||
end
|
||||
end
|
||||
if skip then i = i + 1 end
|
||||
annotationBuffer = {}
|
||||
firstTag = nil
|
||||
i = i + 1
|
||||
else
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
if #annotationBuffer > 0 and firstTag then
|
||||
if firstTag == "@class" and ok1 then
|
||||
local class = classParser.parse(annotationBuffer, table.concat(annotationBuffer, "\n"))
|
||||
if class and not class.skip then
|
||||
table.insert(ast.classes, class)
|
||||
currentClass = class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ast
|
||||
end
|
||||
|
||||
function BasaltDoc.generateMarkdown(ast)
|
||||
if ok5 then
|
||||
local result = markdownGenerator.generate(ast)
|
||||
return result
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
package.path = defaultPath
|
||||
|
||||
return BasaltDoc
|
||||
@@ -1,36 +0,0 @@
|
||||
# ldoc-markdown-parser
|
||||
|
||||
## Overview
|
||||
`ldoc-markdown-parser` is a simple and extensible Lua documentation parser that converts Lua documentation comments into Markdown format. It supports both single-line and multi-line comments, making it easy to document your Lua code in a structured way.
|
||||
|
||||
## Features
|
||||
- Extracts single-line and multi-line comments from Lua files.
|
||||
- Parses custom tags such as `@property`, `@shortDescription`, `@param`, and `@return`.
|
||||
- Converts extracted comments and parsed tags into Markdown format.
|
||||
- Easy to extend and customize for additional tags or formatting options.
|
||||
|
||||
## Installation
|
||||
To install the `ldoc-markdown-parser`, clone the repository and navigate to the project directory:
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd ldoc-markdown-parser
|
||||
```
|
||||
|
||||
## Usage
|
||||
To use the parser, run the `main.lua` file with the Lua interpreter, providing the path to the Lua file you want to parse:
|
||||
|
||||
```bash
|
||||
lua src/main.lua path/to/your/lua_file.lua
|
||||
```
|
||||
|
||||
The output will be generated in Markdown format and can be found in the specified output directory.
|
||||
|
||||
## Example
|
||||
An example Lua file can be found in the `examples/input/example.lua`, and the expected output Markdown file is located in `examples/output/example.md`.
|
||||
|
||||
## Contributing
|
||||
Contributions are welcome! Please feel free to submit a pull request or open an issue for any enhancements or bug fixes.
|
||||
|
||||
## License
|
||||
This project is licensed under the MIT License. See the LICENSE file for more details.
|
||||
@@ -1,64 +0,0 @@
|
||||
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
|
||||
@@ -1,81 +0,0 @@
|
||||
local CommentExtractor = {}
|
||||
|
||||
--- Extracts comment blocks that belong together
|
||||
function CommentExtractor.extractBlocks(lines)
|
||||
local blocks = {}
|
||||
local currentBlock = {
|
||||
comments = {},
|
||||
codeContext = nil
|
||||
}
|
||||
|
||||
local i = 1
|
||||
while i <= #lines do
|
||||
local line = lines[i]
|
||||
local trimmed = line:match("^%s*(.-)%s*$")
|
||||
|
||||
-- Check if this is a comment line
|
||||
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
|
||||
|
||||
-- Save this block and start a new one
|
||||
table.insert(blocks, currentBlock)
|
||||
currentBlock = {comments = {}, codeContext = nil}
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- Handle remaining comments
|
||||
if #currentBlock.comments > 0 then
|
||||
table.insert(blocks, currentBlock)
|
||||
end
|
||||
|
||||
return blocks
|
||||
end
|
||||
|
||||
--- 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
|
||||
|
||||
--- Analyze what kind of code this is
|
||||
function CommentExtractor.analyzeCode(codeLine, lineNumber)
|
||||
-- Function patterns
|
||||
if codeLine:match("^function%s+([%w%.%:]+)") then
|
||||
local name = codeLine:match("^function%s+([%w%.%:]+)")
|
||||
return {type = "function", name = name, line = codeLine, lineNumber = lineNumber}
|
||||
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
|
||||
|
||||
-- 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
|
||||
|
||||
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 {type = "unknown", name = "unknown", line = codeLine, lineNumber = lineNumber}
|
||||
end
|
||||
|
||||
return CommentExtractor
|
||||
@@ -1,32 +0,0 @@
|
||||
local CommentExtractor = require("parser.comment_extractor")
|
||||
local TagParser = require("parser.tag_parser")
|
||||
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.extractBlocks(lines)
|
||||
end
|
||||
|
||||
--- Generate markdown from comment blocks
|
||||
function Parser.generateMarkdown(commentBlocks)
|
||||
local markdown = {}
|
||||
|
||||
-- Parse each block and generate markdown
|
||||
for _, block in ipairs(commentBlocks) do
|
||||
local tags = TagParser.parseAllTags(block.comments)
|
||||
local blockMarkdown = MarkdownGenerator.generateBlock(block, tags)
|
||||
if blockMarkdown and blockMarkdown ~= "" then
|
||||
table.insert(markdown, blockMarkdown)
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(markdown, "\n\n")
|
||||
end
|
||||
|
||||
return Parser
|
||||
@@ -1,143 +0,0 @@
|
||||
local MarkdownGenerator = {}
|
||||
|
||||
--- Generate markdown for a block
|
||||
function MarkdownGenerator.generateBlock(block, tags)
|
||||
if not block.codeContext then
|
||||
return MarkdownGenerator.generateStandaloneComment(tags)
|
||||
end
|
||||
|
||||
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
|
||||
return MarkdownGenerator.generateGeneric(block.codeContext, tags)
|
||||
end
|
||||
end
|
||||
|
||||
--- Generate markdown for function
|
||||
function MarkdownGenerator.generateFunction(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
|
||||
|
||||
if #tags.params > 0 then
|
||||
table.insert(md, "**Parameters:**")
|
||||
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 #tags.returns > 0 then
|
||||
table.insert(md, "**Returns:**")
|
||||
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 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 class
|
||||
function MarkdownGenerator.generateClass(context, tags)
|
||||
local md = {}
|
||||
|
||||
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
|
||||
|
||||
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 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,117 +0,0 @@
|
||||
local TagParser = {}
|
||||
|
||||
--- 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 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
|
||||
|
||||
--- 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
|
||||
@@ -1,59 +0,0 @@
|
||||
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
|
||||
@@ -1,21 +0,0 @@
|
||||
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
|
||||
@@ -1,19 +0,0 @@
|
||||
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
|
||||
@@ -1,35 +0,0 @@
|
||||
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
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
@@ -1,27 +0,0 @@
|
||||
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
|
||||
34
tools/BasaltDoc/parsers/classParser.lua
Normal file
34
tools/BasaltDoc/parsers/classParser.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
local helper = require("utils.helper")
|
||||
local logger = require("utils.logger")
|
||||
local classParser = {}
|
||||
|
||||
function classParser.parse(annotations, line)
|
||||
local classLine = helper.findAnnotationLine(annotations, "class")
|
||||
if not classLine then
|
||||
return nil
|
||||
end
|
||||
local name, extends = classLine:match("^%-%-%-?%s*@class%s*([%w_%.]+)%s*:?%s*([%w_%.]*)")
|
||||
|
||||
local class = {
|
||||
type = "class",
|
||||
name = name,
|
||||
extends = extends ~= "" and extends or nil,
|
||||
description = nil,
|
||||
properties = {},
|
||||
events = {},
|
||||
functions = {},
|
||||
skip = false
|
||||
}
|
||||
|
||||
if classParser.handlers then
|
||||
helper.applyAnnotations(annotations, class, classParser.handlers)
|
||||
end
|
||||
|
||||
return class
|
||||
end
|
||||
|
||||
function classParser.setHandlers(handlers)
|
||||
classParser.handlers = handlers
|
||||
end
|
||||
|
||||
return classParser
|
||||
37
tools/BasaltDoc/parsers/eventParser.lua
Normal file
37
tools/BasaltDoc/parsers/eventParser.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
local helper = require("utils.helper")
|
||||
local eventParser = {}
|
||||
|
||||
function eventParser.parse(annotations, line)
|
||||
local eventLine = helper.findAnnotationLine(annotations, "event")
|
||||
if not eventLine then return nil end
|
||||
local content = table.concat(annotations, " ")
|
||||
local name, params, desc = eventLine:match("^%-%-%-?%s*@event%s*([%w_]+)%s*({[^}]*})%s*(.*)")
|
||||
|
||||
if not name then
|
||||
name, desc = eventLine:match("^%-%-%-?%s*@event%s*([%w_]+)%s+(.*)")
|
||||
params = "{}"
|
||||
end
|
||||
|
||||
if not name then
|
||||
print("Warning: Could not parse @event annotation: " .. eventLine)
|
||||
return nil
|
||||
end
|
||||
|
||||
local evt = {
|
||||
type = "event",
|
||||
name = name,
|
||||
params = params or "{}",
|
||||
description = desc or ""
|
||||
}
|
||||
|
||||
helper.applyAnnotations(annotations, evt, eventParser.handlers)
|
||||
|
||||
return evt
|
||||
end
|
||||
|
||||
function eventParser.setHandlers(handlers)
|
||||
eventParser.handlers = handlers
|
||||
end
|
||||
|
||||
return eventParser
|
||||
|
||||
48
tools/BasaltDoc/parsers/functionParser.lua
Normal file
48
tools/BasaltDoc/parsers/functionParser.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
local helper = require("utils.helper")
|
||||
local functionParser = {}
|
||||
|
||||
function functionParser.parse(annotations, line)
|
||||
local name = line:match("^function%s+([%w_%.]+[:.]?[%w_]+)") or line:match("^function%s+([%w_]+)")
|
||||
if not name then
|
||||
print("Warning: Could not extract function name from line: " .. line)
|
||||
return nil
|
||||
end
|
||||
local f = {
|
||||
type = "function",
|
||||
name = name,
|
||||
description = nil,
|
||||
shortDescription = nil,
|
||||
params = {},
|
||||
returns = {},
|
||||
visibility = "public"
|
||||
}
|
||||
|
||||
if functionParser.handlers then
|
||||
helper.applyAnnotations(annotations, f, functionParser.handlers)
|
||||
end
|
||||
|
||||
local funcName = line:match("function ([%w_%.]+)")
|
||||
if funcName then
|
||||
if funcName:find(":") then
|
||||
f.name = funcName:match(":([%w_]+)")
|
||||
elseif funcName:find("%.") then
|
||||
f.name = funcName:match("%.([%w_]+)")
|
||||
else
|
||||
f.name = funcName
|
||||
end
|
||||
end
|
||||
|
||||
if line:match("function [%w_%.]+:") then
|
||||
f.static = false
|
||||
else
|
||||
f.static = true
|
||||
end
|
||||
|
||||
return f
|
||||
end
|
||||
|
||||
function functionParser.setHandlers(handlers)
|
||||
functionParser.handlers = handlers
|
||||
end
|
||||
|
||||
return functionParser
|
||||
21
tools/BasaltDoc/parsers/globalParser.lua
Normal file
21
tools/BasaltDoc/parsers/globalParser.lua
Normal file
@@ -0,0 +1,21 @@
|
||||
local helper = require("utils.helper")
|
||||
local logger = require("utils.logger")
|
||||
local globalParser = {}
|
||||
|
||||
function globalParser.parse(annotations, line)
|
||||
local global = {
|
||||
description = nil
|
||||
}
|
||||
|
||||
if globalParser.handlers then
|
||||
helper.applyAnnotations(annotations, global, globalParser.handlers)
|
||||
end
|
||||
return global
|
||||
end
|
||||
|
||||
function globalParser.setHandlers(handlers)
|
||||
globalParser.handlers = handlers
|
||||
end
|
||||
|
||||
return globalParser
|
||||
|
||||
30
tools/BasaltDoc/parsers/propertyParser.lua
Normal file
30
tools/BasaltDoc/parsers/propertyParser.lua
Normal file
@@ -0,0 +1,30 @@
|
||||
local helper = require("utils.helper")
|
||||
local propertyParser = {}
|
||||
|
||||
function propertyParser.parse(annotations, line)
|
||||
local propLine = helper.findAnnotationLine(annotations, "property")
|
||||
if not propLine then return nil end
|
||||
local content = table.concat(annotations, " ")
|
||||
local name, type, default, desc = propLine:match("^%-%-%-?%s*@property%s*([%w_]+)%s+([%w_|%[%]]+)%s+([^%s]+)%s*(.*)")
|
||||
if not name then
|
||||
print("Warning: Could not parse @property annotation: " .. propLine)
|
||||
return nil
|
||||
end
|
||||
local prop = {
|
||||
type = "property",
|
||||
name = name,
|
||||
propType = type or "any",
|
||||
default = default or "nil",
|
||||
description = desc or ""
|
||||
}
|
||||
|
||||
helper.applyAnnotations(annotations, prop, propertyParser.handlers)
|
||||
|
||||
return prop
|
||||
end
|
||||
|
||||
function propertyParser.setHandlers(handlers)
|
||||
propertyParser.handlers = handlers
|
||||
end
|
||||
|
||||
return propertyParser
|
||||
59
tools/BasaltDoc/utils/helper.lua
Normal file
59
tools/BasaltDoc/utils/helper.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
local helper = {}
|
||||
|
||||
function helper.applyAnnotations(annotations, target, handlers)
|
||||
local i = 1
|
||||
while i <= #annotations do
|
||||
local ann = annotations[i]
|
||||
|
||||
local tag, args = ann:match("^%-%-%-?%s*(@%S+)%s*(.*)")
|
||||
if tag then
|
||||
if args == ">" then
|
||||
local multiArgs = ""
|
||||
i = i + 1
|
||||
|
||||
while i <= #annotations do
|
||||
local nextAnn = annotations[i]
|
||||
local nextTag = nextAnn:match("^%-%-%-?%s*(@%S+)")
|
||||
if nextTag then
|
||||
i = i - 1
|
||||
break
|
||||
else
|
||||
local content = nextAnn:match("^%-%-%-?%s*(.*)") or nextAnn
|
||||
if multiArgs ~= "" then
|
||||
multiArgs = multiArgs .. "\n" .. content
|
||||
else
|
||||
multiArgs = content
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
args = multiArgs
|
||||
end
|
||||
|
||||
if handlers and handlers[tag] then
|
||||
handlers[tag](target, args)
|
||||
end
|
||||
else
|
||||
local comment = ann:match("^%-%-%-?%s*(.*)") or ann
|
||||
if comment and not comment:match("^@%S+") then
|
||||
if target.description then
|
||||
target.description = target.description .. "\n" .. comment
|
||||
else
|
||||
target.description = comment
|
||||
end
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function helper.findAnnotationLine(annotations, tag)
|
||||
for _, l in ipairs(annotations) do
|
||||
if l:match("@" .. tag) then
|
||||
return l
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
return helper
|
||||
26
tools/BasaltDoc/utils/logger.lua
Normal file
26
tools/BasaltDoc/utils/logger.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
local logger = {}
|
||||
|
||||
local logFile
|
||||
|
||||
function logger.init()
|
||||
if not logFile then
|
||||
logFile = io.open("BasaltDoc/debug_log.txt", "w")
|
||||
end
|
||||
end
|
||||
|
||||
function logger.log(message)
|
||||
if not logFile then logger.init() end
|
||||
if logFile then
|
||||
logFile:write(os.date("%Y-%m-%d %H:%M:%S") .. ": " .. message .. "\n")
|
||||
logFile:flush()
|
||||
end
|
||||
end
|
||||
|
||||
function logger.close()
|
||||
if logFile then
|
||||
logFile:close()
|
||||
logFile = nil
|
||||
end
|
||||
end
|
||||
|
||||
return logger
|
||||
222
tools/BasaltDoc/utils/markdownGenerator.lua
Normal file
222
tools/BasaltDoc/utils/markdownGenerator.lua
Normal file
@@ -0,0 +1,222 @@
|
||||
local markdownGenerator = {}
|
||||
|
||||
local function generateFunctionMarkdown(class, functions)
|
||||
local md = {}
|
||||
|
||||
for _, f in ipairs(functions) do
|
||||
local sig = "## "
|
||||
if class.name then
|
||||
sig = sig .. class.name .. (f.static and "." or ":")
|
||||
end
|
||||
sig = sig .. (f.name or "unknown") .. "("
|
||||
|
||||
for i, p in ipairs(f.params) do
|
||||
sig = sig .. p.name
|
||||
if p.optional then sig = sig .. "?" end
|
||||
if i < #f.params then sig = sig .. ", " end
|
||||
end
|
||||
sig = sig .. ")"
|
||||
|
||||
table.insert(md, sig)
|
||||
|
||||
if f.description and f.description ~= "" then
|
||||
table.insert(md, "")
|
||||
table.insert(md, f.description)
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if #f.params > 0 then
|
||||
table.insert(md, "### Parameters")
|
||||
for _, p in ipairs(f.params) do
|
||||
local paramLine = "* `" .. p.name .. "`"
|
||||
if p.optional then paramLine = paramLine .. " *(optional)*" end
|
||||
paramLine = paramLine .. " `" .. p.type .. "`"
|
||||
if p.description and p.description ~= "" then
|
||||
paramLine = paramLine .. " " .. p.description
|
||||
end
|
||||
table.insert(md, paramLine)
|
||||
end
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if #f.returns > 0 then
|
||||
table.insert(md, "### Returns")
|
||||
for _, r in ipairs(f.returns) do
|
||||
local returnLine = "* `" .. r.type .. "`"
|
||||
if r.name and r.name ~= "" then
|
||||
returnLine = returnLine .. " `" .. r.name .. "`"
|
||||
end
|
||||
if r.description and r.description ~= "" then
|
||||
returnLine = returnLine .. " " .. r.description
|
||||
end
|
||||
table.insert(md, returnLine)
|
||||
end
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if f.usage then
|
||||
table.insert(md, "### Usage")
|
||||
table.insert(md, "```lua")
|
||||
for _, usage in ipairs(f.usage) do
|
||||
if usage == "" then
|
||||
table.insert(md, "")
|
||||
else
|
||||
table.insert(md, usage)
|
||||
end
|
||||
end
|
||||
table.insert(md, "```")
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if f.run then
|
||||
table.insert(md, "### Usage (Executable)")
|
||||
table.insert(md, "```lua run")
|
||||
for _, run in ipairs(f.run) do
|
||||
if run == "" then
|
||||
table.insert(md, "")
|
||||
else
|
||||
table.insert(md, run)
|
||||
end
|
||||
end
|
||||
table.insert(md, "```")
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if f.examples then
|
||||
table.insert(md, "### Examples")
|
||||
for _, example in ipairs(f.examples) do
|
||||
table.insert(md, "```lua")
|
||||
table.insert(md, example)
|
||||
table.insert(md, "```")
|
||||
end
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if f.see then
|
||||
table.insert(md, "### See Also")
|
||||
for _, seeRef in ipairs(f.see) do
|
||||
table.insert(md, "* " .. seeRef)
|
||||
end
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if f.deprecated then
|
||||
table.insert(md, "> **⚠️ Deprecated** " .. (type(f.deprecated) == "string" and f.deprecated or "This function is deprecated"))
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if f.since then
|
||||
table.insert(md, "*Since: " .. f.since .. "*")
|
||||
table.insert(md, "")
|
||||
end
|
||||
end
|
||||
|
||||
return md
|
||||
end
|
||||
|
||||
function markdownGenerator.generate(ast)
|
||||
local md = {}
|
||||
|
||||
if ast.global then
|
||||
if ast.global.description then
|
||||
table.insert(md, ast.global.description)
|
||||
end
|
||||
end
|
||||
|
||||
local seen = {}
|
||||
for _, class in ipairs(ast.classes) do
|
||||
if not class.skip and not seen[class.name] then
|
||||
seen[class.name] = true
|
||||
local title = class.title or class.name
|
||||
table.insert(md, "# " .. title)
|
||||
if class.description then
|
||||
table.insert(md, "_" .. class.description .. "_")
|
||||
end
|
||||
if class.extends then
|
||||
table.insert(md, "")
|
||||
table.insert(md, "Extends: `" .. class.extends .. "`")
|
||||
end
|
||||
table.insert(md, "")
|
||||
|
||||
if not class.skipPropertyList and #class.properties > 0 then
|
||||
table.insert(md, "## Properties")
|
||||
table.insert(md, "")
|
||||
table.insert(md, "|Property|Type|Default|Description|")
|
||||
table.insert(md, "|---|---|---|---|")
|
||||
for _, p in ipairs(class.properties) do
|
||||
table.insert(md, string.format("|%s|%s|%s|%s|",
|
||||
p.name or "",
|
||||
p.propType or "any",
|
||||
p.default or "nil",
|
||||
p.description or ""))
|
||||
end
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if #class.events > 0 then
|
||||
table.insert(md, "## Events")
|
||||
table.insert(md, "")
|
||||
table.insert(md, "|Event|Parameters|Description|")
|
||||
table.insert(md, "|---|---|---|")
|
||||
for _, e in ipairs(class.events) do
|
||||
local params = e.params or ""
|
||||
if params:match("^{.*}$") then
|
||||
params = params:sub(2, -2)
|
||||
end
|
||||
if params == "" then params = "-" else params = "`" .. params .. "`" end
|
||||
|
||||
table.insert(md, string.format("|%s|%s|%s|",
|
||||
e.name or "", params, e.description or ""))
|
||||
end
|
||||
table.insert(md, "")
|
||||
end
|
||||
|
||||
if not class.skipFunctionList and #class.functions > 0 then
|
||||
table.insert(md, "## Functions")
|
||||
table.insert(md, "")
|
||||
table.insert(md, "|Method|Returns|Description|")
|
||||
table.insert(md, "|---|---|---|")
|
||||
|
||||
for _, f in ipairs(class.functions) do
|
||||
local methodName = (class.name or "") .. (f.static and "." or ":") .. (f.name or "")
|
||||
|
||||
local anchor = methodName:lower()
|
||||
if #f.params > 0 then
|
||||
for _, p in ipairs(f.params) do
|
||||
anchor = anchor .. "-" .. p.name:lower()
|
||||
end
|
||||
end
|
||||
anchor = anchor:gsub("[^%w%-]", "-")
|
||||
|
||||
local returnType = "-"
|
||||
if #f.returns > 0 then
|
||||
local types = {}
|
||||
for _, r in ipairs(f.returns) do
|
||||
table.insert(types, r.type)
|
||||
end
|
||||
returnType = table.concat(types, ", ")
|
||||
end
|
||||
|
||||
local shortDesc = f.shortDescription or f.description or ""
|
||||
table.insert(md, string.format("|[%s](#%s)|%s|%s|",
|
||||
methodName,
|
||||
anchor,
|
||||
returnType,
|
||||
shortDesc))
|
||||
end
|
||||
table.insert(md, "")
|
||||
|
||||
if not class.skipDetailedFunctionList then
|
||||
local functionMd = generateFunctionMarkdown(class, class.functions)
|
||||
for _, line in ipairs(functionMd) do
|
||||
table.insert(md, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return md
|
||||
end
|
||||
|
||||
return markdownGenerator
|
||||
@@ -1,50 +1,99 @@
|
||||
local arg = arg or {...}
|
||||
|
||||
local oldPath = package.path
|
||||
local SRC_DIR = arg[1] or 'src'
|
||||
local OUT_DIR = arg[2] or 'docs'
|
||||
|
||||
local scriptSource = debug.getinfo(1, "S").source
|
||||
local scriptDir = nil
|
||||
if scriptSource and scriptSource:sub(1,1) == "@" then
|
||||
local scriptPath = scriptSource:sub(2)
|
||||
scriptDir = scriptPath:match("^(.*)[/\\]") or "."
|
||||
local BasaltDoc = require('BasaltDoc')
|
||||
|
||||
local fileSystem
|
||||
if fs then
|
||||
fileSystem = {
|
||||
list = fs.list,
|
||||
combine = fs.combine,
|
||||
isDir = fs.isDir,
|
||||
exists = fs.exists,
|
||||
makeDir = fs.makeDir,
|
||||
open = function(path, mode) return fs.open(path, mode) end,
|
||||
getDir = fs.getDir,
|
||||
readAll = function(file) return file.readAll() end,
|
||||
write = function(file, data) file.write(data) end,
|
||||
close = function(file) file.close() end
|
||||
}
|
||||
else
|
||||
scriptDir = "."
|
||||
local lfs = require("lfs")
|
||||
fileSystem = {
|
||||
list = function(dir)
|
||||
local items = {}
|
||||
for item in lfs.dir(dir) do
|
||||
if item ~= "." and item ~= ".." then
|
||||
table.insert(items, item)
|
||||
end
|
||||
end
|
||||
return items
|
||||
end,
|
||||
combine = function(a, b) return a .. "/" .. b end,
|
||||
isDir = function(path) return lfs.attributes(path).mode == "directory" end,
|
||||
exists = function(path) return lfs.attributes(path) ~= nil end,
|
||||
makeDir = lfs.mkdir,
|
||||
open = io.open,
|
||||
getDir = function(path) return path:match("(.+)/") end,
|
||||
readAll = function(file) return file:read("*all") end,
|
||||
write = function(file, data) file:write(data) end,
|
||||
close = function(file) file:close() end
|
||||
}
|
||||
end
|
||||
|
||||
local parserSrc = scriptDir .. "/BasaltDoc/ldoc-markdown-parser/src/"
|
||||
package.path = package.path .. ";" .. parserSrc .. "?.lua;" .. parserSrc .. "?/init.lua"
|
||||
local ok, parser = pcall(require, "parser.init")
|
||||
local ioAdaptor = require("tools.io")
|
||||
package.path = oldPath
|
||||
if not fileSystem.exists(OUT_DIR) then
|
||||
fileSystem.makeDir(OUT_DIR)
|
||||
end
|
||||
|
||||
if not ok or not parser then
|
||||
-- try dofile fallback
|
||||
local initPath = parserSrc .. "/parser/init.lua"
|
||||
local ok2, module = pcall(dofile, initPath)
|
||||
if ok2 and module then
|
||||
parser = module
|
||||
local function getLuaFiles(dir)
|
||||
local files = {}
|
||||
local list = fileSystem.list(dir)
|
||||
for _, item in ipairs(list) do
|
||||
local path = fileSystem.combine(dir, item)
|
||||
if fileSystem.isDir(path) then
|
||||
local subFiles = getLuaFiles(path)
|
||||
for _, subFile in ipairs(subFiles) do
|
||||
table.insert(files, subFile)
|
||||
end
|
||||
elseif item:match("%.lua$") then
|
||||
table.insert(files, path)
|
||||
end
|
||||
end
|
||||
return files
|
||||
end
|
||||
|
||||
local luaFiles = getLuaFiles(SRC_DIR)
|
||||
|
||||
for _, filePath in ipairs(luaFiles) do
|
||||
local file = fileSystem.open(filePath, "r")
|
||||
if file then
|
||||
local content = fileSystem.readAll(file)
|
||||
fileSystem.close(file)
|
||||
|
||||
local ast = BasaltDoc.parse(content)
|
||||
local markdown = BasaltDoc.generateMarkdown(ast)
|
||||
|
||||
local relativePath = filePath:gsub("^" .. SRC_DIR .. "/", ""):gsub("%.lua$", ".md")
|
||||
local outPath = fileSystem.combine(OUT_DIR, relativePath)
|
||||
|
||||
local outDir = fileSystem.getDir(outPath)
|
||||
if outDir and not fileSystem.exists(outDir) then
|
||||
fileSystem.makeDir(outDir)
|
||||
end
|
||||
|
||||
local outFile = fileSystem.open(outPath, "w")
|
||||
if outFile then
|
||||
fileSystem.write(outFile, table.concat(markdown, "\n"))
|
||||
fileSystem.close(outFile)
|
||||
print("Generated: " .. outPath)
|
||||
else
|
||||
print("Error writing: " .. outPath)
|
||||
end
|
||||
else
|
||||
error("Failed to load parser.init via require and dofile (tried: "..tostring(initPath)..")")
|
||||
print("Error reading: " .. filePath)
|
||||
end
|
||||
end
|
||||
|
||||
local function processFile(inputFile)
|
||||
local content = ioAdaptor.readFile(inputFile)
|
||||
if not content then
|
||||
io.stderr:write("Failed to read: " .. tostring(inputFile) .. "\n")
|
||||
return
|
||||
end
|
||||
|
||||
local commentBlocks = parser.extractComments(content)
|
||||
local md = parser.generateMarkdown(commentBlocks)
|
||||
|
||||
local outputFile = "build_docs/docs/references/" .. inputFile:match("^src/(.+)"):gsub("%.lua$", "")
|
||||
ioAdaptor.ensureDirectory(outputFile)
|
||||
ioAdaptor.writeFile(outputFile .. ".md", md)
|
||||
end
|
||||
|
||||
local files = ioAdaptor.listFiles("src", "*.lua")
|
||||
for _, file in ipairs(files) do
|
||||
if not file:match("LuaLS.lua$") then
|
||||
processFile(file)
|
||||
end
|
||||
end
|
||||
print("Documentation generation complete.")
|
||||
Reference in New Issue
Block a user