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