From 72f9ac2a1eeef461d513b9f4b5c9aba0b25b492e Mon Sep 17 00:00:00 2001 From: Robert Jelic <36573031+NoryiE@users.noreply.github.com> Date: Sat, 13 Sep 2025 09:54:09 +0200 Subject: [PATCH] Small Docs test --- .github/workflows/main.yml | 112 ++++---- .gitignore | 6 +- tools/BasaltDoc/Button.lua | 49 ---- tools/BasaltDoc/init.lua | 270 ++++++++++++++++++ .../BasaltDoc/ldoc-markdown-parser/README.md | 36 --- .../src/parser/block_parsers/function.lua | 64 ----- .../src/parser/comment_extractor.lua | 81 ------ .../ldoc-markdown-parser/src/parser/init.lua | 32 --- .../src/parser/markdown_generator.lua | 143 ---------- .../src/parser/tag_parser.lua | 117 -------- .../src/parser/tag_parser_registry.lua | 59 ---- .../src/parser/tag_parsers/property.lua | 21 -- .../parser/tag_parsers/shortDescription.lua | 19 -- .../src/utils/file_reader.lua | 35 --- .../ldoc-markdown-parser/src/utils/init.lua | 16 -- .../src/utils/string_utils.lua | 27 -- tools/BasaltDoc/parsers/classParser.lua | 34 +++ tools/BasaltDoc/parsers/eventParser.lua | 37 +++ tools/BasaltDoc/parsers/functionParser.lua | 48 ++++ tools/BasaltDoc/parsers/globalParser.lua | 21 ++ tools/BasaltDoc/parsers/propertyParser.lua | 30 ++ tools/BasaltDoc/utils/helper.lua | 59 ++++ tools/BasaltDoc/utils/logger.lua | 26 ++ tools/BasaltDoc/utils/markdownGenerator.lua | 222 ++++++++++++++ tools/generate-docs.lua | 129 ++++++--- 25 files changed, 894 insertions(+), 799 deletions(-) delete mode 100644 tools/BasaltDoc/Button.lua create mode 100644 tools/BasaltDoc/init.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/README.md delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/parser/block_parsers/function.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/parser/comment_extractor.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/parser/init.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/parser/markdown_generator.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser_registry.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parsers/property.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parsers/shortDescription.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/utils/file_reader.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/utils/init.lua delete mode 100644 tools/BasaltDoc/ldoc-markdown-parser/src/utils/string_utils.lua create mode 100644 tools/BasaltDoc/parsers/classParser.lua create mode 100644 tools/BasaltDoc/parsers/eventParser.lua create mode 100644 tools/BasaltDoc/parsers/functionParser.lua create mode 100644 tools/BasaltDoc/parsers/globalParser.lua create mode 100644 tools/BasaltDoc/parsers/propertyParser.lua create mode 100644 tools/BasaltDoc/utils/helper.lua create mode 100644 tools/BasaltDoc/utils/logger.lua create mode 100644 tools/BasaltDoc/utils/markdownGenerator.lua diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9781144..9221b3a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,69 +14,69 @@ jobs: contents: write steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: Setup Lua - uses: leafo/gh-actions-lua@v8 - with: - luaVersion: "5.4" + - name: Setup Lua + uses: leafo/gh-actions-lua@v8 + with: + luaVersion: "5.4" - # Step 1: Config Generation - - name: Generate Config - run: | - lua tools/generate-config.lua + # Step 1: Config Generation + - name: Generate Config + run: | + lua tools/generate-config.lua - # Step 2: Generate LuaLS Definitions - - name: Generate LuaLS - run: | - lua tools/generate-annotations.lua src + # Step 2: Generate LuaLS Definitions + - name: Generate LuaLS + run: | + lua tools/generate-annotations.lua src - # Step 3: Bundle and Minify - - name: Bundle and Minify - run: | - mkdir -p release - lua tools/bundler.lua + # Step 3: Bundle and Minify + - name: Bundle and Minify + run: | + mkdir -p release + lua tools/bundler.lua - # Step 4: Prepare and Generate Documentation - - name: Prepare docs directory - run: | - # Checkout gh-pages branch in a separate directory - git worktree add gh-pages gh-pages - - # Only clean references directory - rm -rf gh-pages/docs/references - mkdir -p gh-pages/docs/references + # Step 4: Prepare and Generate Documentation + - name: Prepare docs directory + run: | + # Checkout gh-pages branch in a separate directory + git worktree add gh-pages gh-pages + + # Only clean references directory + rm -rf gh-pages/docs/references + mkdir -p gh-pages/docs/references - #- name: Generate Documentation - # run: | - # lua tools/generate-docs.lua + - name: Generate Documentation + run: | + lua tools/generate-docs.lua - # cp -r build_docs/docs/references/* gh-pages/docs/references/ + cp -r build_docs/docs/references/* gh-pages/docs/references/ - # Step 5: Deploy Documentation - - name: Deploy Documentation - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./gh-pages - keep_files: true + # Step 5: Deploy Documentation + - name: Deploy Documentation + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./gh-pages + keep_files: true - # Step 6: Generate Changelog - #- name: Generate Changelog - # id: changelog - # uses: heinrichreimer/github-changelog-generator-action@v2.3 - # with: - # token: ${{ secrets.GITHUB_TOKEN }} + # Step 6: Generate Changelog + #- name: Generate Changelog + # id: changelog + # uses: heinrichreimer/github-changelog-generator-action@v2.3 + # with: + # token: ${{ secrets.GITHUB_TOKEN }} - # Step 7: Commit all changes - - name: Commit Changes - run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add config.lua BasaltLS.lua release/basalt.lua CHANGELOG.md - git commit -m "Update config, BasaltLS definitions, bundle and changelog" || exit 0 - git push + # Step 7: Commit all changes + - name: Commit Changes + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add config.lua BasaltLS.lua release/basalt.lua CHANGELOG.md + git commit -m "Update config, BasaltLS definitions, bundle and changelog" || exit 0 + git push \ No newline at end of file diff --git a/.gitignore b/.gitignore index 496e19c..0050dc2 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,5 @@ testWorkflows .vscode todo.txt Flexbox2.lua -markdown2.lua -BasaltDoc -generate-docs.lua -io.lua \ No newline at end of file +markdown.lua +markdown2.lua \ No newline at end of file diff --git a/tools/BasaltDoc/Button.lua b/tools/BasaltDoc/Button.lua deleted file mode 100644 index b1d90f9..0000000 --- a/tools/BasaltDoc/Button.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tools/BasaltDoc/init.lua b/tools/BasaltDoc/init.lua new file mode 100644 index 0000000..7cee19b --- /dev/null +++ b/tools/BasaltDoc/init.lua @@ -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 \ No newline at end of file diff --git a/tools/BasaltDoc/ldoc-markdown-parser/README.md b/tools/BasaltDoc/ldoc-markdown-parser/README.md deleted file mode 100644 index cb65ac9..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/README.md +++ /dev/null @@ -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 -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. \ No newline at end of file diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/block_parsers/function.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/block_parsers/function.lua deleted file mode 100644 index b2d4249..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/block_parsers/function.lua +++ /dev/null @@ -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 diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/comment_extractor.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/comment_extractor.lua deleted file mode 100644 index ef958cf..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/comment_extractor.lua +++ /dev/null @@ -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 \ 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 deleted file mode 100644 index b8a136f..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/init.lua +++ /dev/null @@ -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 \ 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 deleted file mode 100644 index c68a55e..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/markdown_generator.lua +++ /dev/null @@ -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 \ 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 deleted file mode 100644 index 7b1d4fd..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser_registry.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser_registry.lua deleted file mode 100644 index 972a8b3..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parser_registry.lua +++ /dev/null @@ -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 diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parsers/property.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parsers/property.lua deleted file mode 100644 index 1714195..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parsers/property.lua +++ /dev/null @@ -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 diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parsers/shortDescription.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parsers/shortDescription.lua deleted file mode 100644 index e5319fd..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/parser/tag_parsers/shortDescription.lua +++ /dev/null @@ -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 diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/utils/file_reader.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/utils/file_reader.lua deleted file mode 100644 index 24dd8a0..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/utils/file_reader.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/utils/init.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/utils/init.lua deleted file mode 100644 index a731777..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/utils/init.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tools/BasaltDoc/ldoc-markdown-parser/src/utils/string_utils.lua b/tools/BasaltDoc/ldoc-markdown-parser/src/utils/string_utils.lua deleted file mode 100644 index d3d010a..0000000 --- a/tools/BasaltDoc/ldoc-markdown-parser/src/utils/string_utils.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tools/BasaltDoc/parsers/classParser.lua b/tools/BasaltDoc/parsers/classParser.lua new file mode 100644 index 0000000..f9de7c1 --- /dev/null +++ b/tools/BasaltDoc/parsers/classParser.lua @@ -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 \ No newline at end of file diff --git a/tools/BasaltDoc/parsers/eventParser.lua b/tools/BasaltDoc/parsers/eventParser.lua new file mode 100644 index 0000000..0710cef --- /dev/null +++ b/tools/BasaltDoc/parsers/eventParser.lua @@ -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 + diff --git a/tools/BasaltDoc/parsers/functionParser.lua b/tools/BasaltDoc/parsers/functionParser.lua new file mode 100644 index 0000000..edc7d9f --- /dev/null +++ b/tools/BasaltDoc/parsers/functionParser.lua @@ -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 diff --git a/tools/BasaltDoc/parsers/globalParser.lua b/tools/BasaltDoc/parsers/globalParser.lua new file mode 100644 index 0000000..bed693b --- /dev/null +++ b/tools/BasaltDoc/parsers/globalParser.lua @@ -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 + diff --git a/tools/BasaltDoc/parsers/propertyParser.lua b/tools/BasaltDoc/parsers/propertyParser.lua new file mode 100644 index 0000000..116ffed --- /dev/null +++ b/tools/BasaltDoc/parsers/propertyParser.lua @@ -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 diff --git a/tools/BasaltDoc/utils/helper.lua b/tools/BasaltDoc/utils/helper.lua new file mode 100644 index 0000000..9f9df55 --- /dev/null +++ b/tools/BasaltDoc/utils/helper.lua @@ -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 \ No newline at end of file diff --git a/tools/BasaltDoc/utils/logger.lua b/tools/BasaltDoc/utils/logger.lua new file mode 100644 index 0000000..ef2f0f5 --- /dev/null +++ b/tools/BasaltDoc/utils/logger.lua @@ -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 diff --git a/tools/BasaltDoc/utils/markdownGenerator.lua b/tools/BasaltDoc/utils/markdownGenerator.lua new file mode 100644 index 0000000..67088fd --- /dev/null +++ b/tools/BasaltDoc/utils/markdownGenerator.lua @@ -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 \ No newline at end of file diff --git a/tools/generate-docs.lua b/tools/generate-docs.lua index b9c0979..33091e5 100644 --- a/tools/generate-docs.lua +++ b/tools/generate-docs.lua @@ -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.") \ No newline at end of file