-- 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]]
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
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
-- @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)
return line
end
end)
-
+
-- parse first line of code
if code ~= nil then
local func_info = check_function(code)
-- 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
-- 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
-------------------------------------------------------------------------------
-- 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
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
-- @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
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
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
doc.modules[modulename].functions[f.name] = f
end
end
-
+
-- make tables table
doc.modules[modulename].tables = {}
for t in class_iterator(blocks, "table")() do
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
return true
end
end)
-
+
if valid then
logger:info(string.format("processing file `%s'", filepath))
doc = parse_file(filepath, doc)
end
-
+
return doc
end
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
function start (files, doc)
assert(files, "file list not specified")
-
+
-- Create an empty document, or use the given one
doc = doc or {
files = {},
}
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)