* luci/contrib: added support for multiple modules per file in luadoc
authorJo-Philipp Wich <jow@openwrt.org>
Sat, 9 Aug 2008 16:01:31 +0000 (16:01 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Sat, 9 Aug 2008 16:01:31 +0000 (16:01 +0000)
contrib/luadoc/lua/luadoc/taglet/standard.lua

index 334f4a2..4d28655 100644 (file)
@@ -73,12 +73,12 @@ end
 -- Checks if the line contains a module definition.
 -- @param line string with line text
 -- @param currentmodule module already found, if any
--- @return the name of the defined module, or nil if there is no module 
+-- @return the name of the defined module, or nil if there is no module
 -- definition
 
 local function check_module (line, currentmodule)
        line = util.trim(line)
-       
+
        -- module"x.y"
        -- module'x.y'
        -- module[[x.y]]
@@ -97,9 +97,9 @@ local function check_module (line, currentmodule)
 end
 
 -------------------------------------------------------------------------------
--- Extracts summary information from a description. The first sentence of each 
--- doc comment should be a summary sentence, containing a concise but complete 
--- description of the item. It is important to write crisp and informative 
+-- Extracts summary information from a description. The first sentence of each
+-- doc comment should be a summary sentence, containing a concise but complete
+-- description of the item. It is important to write crisp and informative
 -- initial sentences that can stand on their own
 -- @param description text with item description
 -- @return summary string or nil if description is nil
@@ -107,16 +107,16 @@ end
 local function parse_summary (description)
        -- summary is never nil...
        description = description or ""
-       
+
        -- append an " " at the end to make the pattern work in all cases
        description = description.." "
 
-       -- read until the first period followed by a space or tab       
+       -- read until the first period followed by a space or tab
        local summary = string.match(description, "(.-%.)[%s\t]")
-       
+
        -- if pattern did not find the first sentence, summary is the whole description
        summary = summary or description
-       
+
        return summary
 end
 
@@ -151,7 +151,7 @@ end
 -- @param block block with comment field
 -- @return block parameter
 
-local function parse_comment (block, first_line)
+local function parse_comment (block, first_line, modulename)
 
        -- get the first non-empty line of code
        local code = table.foreachi(block.code, function(_, line)
@@ -167,7 +167,7 @@ local function parse_comment (block, first_line)
                        return line
                end
        end)
-       
+
        -- parse first line of code
        if code ~= nil then
                local func_info = check_function(code)
@@ -192,16 +192,16 @@ local function parse_comment (block, first_line)
        -- parse @ tags
        local currenttag = "description"
        local currenttext
-       
+
        table.foreachi(block.comment, function (_, line)
                line = util.trim_comment(line)
-               
+
                local r, _, tag, text = string.find(line, "@([_%w%.]+)%s+(.*)")
                if r ~= nil then
                        -- found new tag, add previous one, and start a new one
                        -- TODO: what to do with invalid tags? issue an error? or log a warning?
                        tags.handle(currenttag, block, currenttext)
-                       
+
                        currenttag = tag
                        currenttext = text
                else
@@ -214,8 +214,12 @@ local function parse_comment (block, first_line)
        -- extracts summary information from the description
        block.summary = parse_summary(block.description)
        assert(string.sub(block.description, 1, 1) ~= " ", string.format("`%s'", block.description))
-       
-       return block
+
+       if block.name and block.class == "module" then
+               modulename = block.name
+       end
+
+       return block, modulename
 end
 
 -------------------------------------------------------------------------------
@@ -239,9 +243,9 @@ local function parse_block (f, line, modulename, first)
                        -- reached end of comment, read the code below it
                        -- TODO: allow empty lines
                        line, block.code, modulename = parse_code(f, line, modulename)
-                       
+
                        -- parse information in block comment
-                       block = parse_comment(block, first)
+                       block, modulename = parse_comment(block, first, modulename)
 
                        return line, block, modulename
                else
@@ -250,10 +254,10 @@ local function parse_block (f, line, modulename, first)
                end
        end
        -- reached end of file
-       
+
        -- parse information in block comment
-       block = parse_comment(block, first)
-       
+       block, modulename = parse_comment(block, first, modulename)
+
        return line, block, modulename
 end
 
@@ -263,58 +267,78 @@ end
 -- @param doc table with documentation
 -- @return table with documentation
 
-function parse_file (filepath, doc)
-       local blocks = {}
-       local modulename = nil
-       
+function parse_file (filepath, doc, handle, prev_line, prev_block, prev_modname)
+       local blocks = { prev_block }
+       local modulename = prev_modname
+
        -- read each line
-       local f = io.open(filepath, "r")
+       local f = handle or io.open(filepath, "r")
        local i = 1
-       local line = f:read()
+       local line = prev_line or f:read()
        local first = true
        while line ~= nil do
+
                if string.find(line, "^[\t ]*%-%-%-") then
                        -- reached a luadoc block
-                       local block
-                       line, block, modulename = parse_block(f, line, modulename, first)
-                       table.insert(blocks, block)
+                       local block, newmodname
+                       line, block, newmodname = parse_block(f, line, modulename, first)
+
+                       if modulename and newmodname and newmodname ~= modulename then
+                               doc = parse_file( nil, doc, f, line, block, newmodname )
+                       else
+                               table.insert(blocks, block)
+                               modulename = newmodname
+                       end
                else
                        -- look for a module definition
-                       modulename = check_module(line, modulename)
-                       
+                       local newmodname = check_module(line, modulename)
+
+                       if modulename and newmodname and newmodname ~= modulename then
+                               parse_file( nil, doc, f )
+                       else
+                               modulename = newmodname
+                       end
+
                        -- TODO: keep beginning of file somewhere
-                       
+
                        line = f:read()
                end
                first = false
                i = i + 1
        end
-       f:close()
-       -- store blocks in file hierarchy
-       assert(doc.files[filepath] == nil, string.format("doc for file `%s' already defined", filepath))
-       table.insert(doc.files, filepath)
-       doc.files[filepath] = {
-               type = "file",
-               name = filepath,
-               doc = blocks,
---             functions = class_iterator(blocks, "function"),
---             tables = class_iterator(blocks, "table"),
-       }
---
-       local first = doc.files[filepath].doc[1]
-       if first and modulename then
-               doc.files[filepath].author = first.author
-               doc.files[filepath].copyright = first.copyright
-               doc.files[filepath].description = first.description
-               doc.files[filepath].release = first.release
-               doc.files[filepath].summary = first.summary
+
+       if not handle then
+               f:close()
+       end
+
+       if filepath then
+               -- store blocks in file hierarchy
+               assert(doc.files[filepath] == nil, string.format("doc for file `%s' already defined", filepath))
+               table.insert(doc.files, filepath)
+               doc.files[filepath] = {
+                       type = "file",
+                       name = filepath,
+                       doc = blocks,
+       --              functions = class_iterator(blocks, "function"),
+       --              tables = class_iterator(blocks, "table"),
+               }
+       --
+               local first = doc.files[filepath].doc[1]
+               if first and modulename then
+                       doc.files[filepath].author = first.author
+                       doc.files[filepath].copyright = first.copyright
+                       doc.files[filepath].description = first.description
+                       doc.files[filepath].release = first.release
+                       doc.files[filepath].summary = first.summary
+               end
        end
 
        -- if module definition is found, store in module hierarchy
        if modulename ~= nil then
                if modulename == "..." then
-                               modulename = string.gsub (filepath, "%.lua$", "")
-                               modulename = string.gsub (modulename, "/", ".")
+                       assert( filepath, "Can't determine name for virtual module from filepatch" )
+                       modulename = string.gsub (filepath, "%.lua$", "")
+                       modulename = string.gsub (modulename, "/", ".")
                end
                if doc.modules[modulename] ~= nil then
                        -- module is already defined, just add the blocks
@@ -336,14 +360,14 @@ function parse_file (filepath, doc)
                                release = first and first.release,
                                summary = "",
                        }
-                       
+
                        -- find module description
                        for m in class_iterator(blocks, "module")() do
                                doc.modules[modulename].description = util.concat(
-                                       doc.modules[modulename].description, 
+                                       doc.modules[modulename].description,
                                        m.description)
                                doc.modules[modulename].summary = util.concat(
-                                       doc.modules[modulename].summary, 
+                                       doc.modules[modulename].summary,
                                        m.summary)
                                if m.author then
                                        doc.modules[modulename].author = m.author
@@ -361,7 +385,7 @@ function parse_file (filepath, doc)
                        doc.modules[modulename].description = doc.modules[modulename].description or (first and first.description) or ""
                        doc.modules[modulename].summary = doc.modules[modulename].summary or (first and first.summary) or ""
                end
-               
+
                -- make functions table
                doc.modules[modulename].functions = {}
                for f in class_iterator(blocks, "function")() do
@@ -370,7 +394,7 @@ function parse_file (filepath, doc)
                                doc.modules[modulename].functions[f.name] = f
                        end
                end
-               
+
                -- make tables table
                doc.modules[modulename].tables = {}
                for t in class_iterator(blocks, "table")() do
@@ -380,30 +404,32 @@ function parse_file (filepath, doc)
                        end
                end
        end
-       
-       -- make functions table
-       doc.files[filepath].functions = {}
-       for f in class_iterator(blocks, "function")() do
-               if f and f.name then
-                       table.insert(doc.files[filepath].functions, f.name)
-                       doc.files[filepath].functions[f.name] = f
+
+       if filepath then
+               -- make functions table
+               doc.files[filepath].functions = {}
+               for f in class_iterator(blocks, "function")() do
+                       if f and f.name then
+                               table.insert(doc.files[filepath].functions, f.name)
+                               doc.files[filepath].functions[f.name] = f
+                       end
                end
-       end
-       
-       -- make tables table
-       doc.files[filepath].tables = {}
-       for t in class_iterator(blocks, "table")() do
-               if t and t.name then
-                       table.insert(doc.files[filepath].tables, t.name)
-                       doc.files[filepath].tables[t.name] = t
+
+               -- make tables table
+               doc.files[filepath].tables = {}
+               for t in class_iterator(blocks, "table")() do
+                       if t and t.name then
+                               table.insert(doc.files[filepath].tables, t.name)
+                               doc.files[filepath].tables[t.name] = t
+                       end
                end
        end
-       
+
        return doc
 end
 
 -------------------------------------------------------------------------------
--- Checks if the file is terminated by ".lua" or ".luadoc" and calls the 
+-- Checks if the file is terminated by ".lua" or ".luadoc" and calls the
 -- function that does the actual parsing
 -- @param filepath full path of the file to parse
 -- @param doc table with documentation
@@ -417,12 +443,12 @@ function file (filepath, doc)
                        return true
                end
        end)
-       
+
        if valid then
                logger:info(string.format("processing file `%s'", filepath))
                doc = parse_file(filepath, doc)
        end
-       
+
        return doc
 end
 
@@ -437,7 +463,7 @@ function directory (path, doc)
                local fullpath = path .. "/" .. f
                local attr = posix.stat(fullpath)
                assert(attr, string.format("error stating file `%s'", fullpath))
-               
+
                if attr.type == "regular" then
                        doc = file(fullpath, doc)
                elseif attr.type == "directory" and f ~= "." and f ~= ".." then
@@ -465,7 +491,7 @@ end
 
 function start (files, doc)
        assert(files, "file list not specified")
-       
+
        -- Create an empty document, or use the given one
        doc = doc or {
                files = {},
@@ -473,18 +499,18 @@ function start (files, doc)
        }
        assert(doc.files, "undefined `files' field")
        assert(doc.modules, "undefined `modules' field")
-       
+
        table.foreachi(files, function (_, path)
                local attr = posix.stat(path)
                assert(attr, string.format("error stating path `%s'", path))
-               
+
                if attr.type == "regular" then
                        doc = file(path, doc)
                elseif attr.type == "directory" then
                        doc = directory(path, doc)
                end
        end)
-       
+
        -- order arrays alphabetically
        recsort(doc.files)
        recsort(doc.modules)