modules returning a single function are supported, see tests/styles/func.lua. Parameters may have subfields, see tests/styles/subparams.lua

This commit is contained in:
steve donovan 2013-03-18 15:51:28 +02:00
parent 717eac46e5
commit 4bc48dafc3
8 changed files with 204 additions and 85 deletions

View File

@ -493,6 +493,8 @@ local function read_del (tags,name)
return ret
end
local build_arg_list, split_iden -- forward declaration
function Item:finish()
local tags = self.tags
@ -538,74 +540,58 @@ function Item:finish()
self.modifiers.field = self.modifiers.param
end
end
local names, comments = List(), List()
local param_names, comments = List(), List()
if params then
for line in params:iter() do
local name, comment = line :match('%s*([%w_%.:]+)(.*)')
local name, comment = line:match('%s*([%w_%.:]+)(.*)')
if not name then
self:error("bad param name format '"..line.."'. Are you missing a parameter name?")
end
names:append(name)
param_names:append(name)
comments:append(comment)
end
end
self.modifiers['return'] = self.modifiers['return'] or List()
self.modifiers[field] = self.modifiers[field] or List()
-- not all arguments may be commented: we use the formal arguments
-- if available as the authoritative list, and warn if there's an inconsistency.
if self.formal_args then
local fargs = self.formal_args
if not self.ret and fargs.return_comment then
local retc = fargs.return_comment
local type,rest = retc:match '([^:]+):(.*)'
if type then
self.modifiers['return']:append{type=type}
retc = rest
-- we use the formal arguments (if available) as the authoritative list.
-- If there are both params and formal args, then they must match;
-- (A formal argument of ... may match any number of params at the end, however.)
-- If there are formal args and no params, we see if the args have any suitable comments.
-- Params may have subfields.
local fargs, formal = self.formal_args
if fargs then
if #param_names == 0 then
--docs may be embedded in argument comments; in either case, use formal arg names
formal = List()
if fargs.return_comment then
local retc = self:parse_argument_comment(fargs.return_comment,'return')
self.ret = List{retc}
end
self.ret = List{retc}
end
if #fargs ~= 0 then
local pnames, pcomments = names, comments
names, comments = List(),List()
for i, name in ipairs(fargs) do
formal:append(name)
comments:append(self:parse_argument_comment(fargs.comments[name],self.parameter))
end
elseif #fargs > 0 then
local varargs = fargs[#fargs] == '...'
for i,name in ipairs(fargs) do
if params then -- explicit set of param tags
if pnames[i] ~= name and not varargs then
if pnames[i] then
self:warning("param and formal argument name mismatch: "..quote(name).." "..quote(pnames[i]))
else
self:warning("undocumented formal argument: "..quote(name))
if varargs then table.remove(fargs) end
local k = 0
for _,pname in ipairs(param_names) do
local _,field = split_iden(pname)
if not field then
k = k + 1
if k > #fargs then
if not varargs then
self:warning("extra param with no formal argument: "..quote(pname))
end
elseif varargs then
name = pnames[i]
elseif pname ~= fargs[k] then
self:warning("param and formal argument name mismatch: "..quote(pname).." "..quote(fargs[k]))
end
end
names:append(name)
local comment = pcomments[i]
if not comment then
-- ldoc allows comments in the formal arg list to be used, if they aren't specified with @param
-- Further, these comments may start with a type followed by a colon, and are then equivalent
-- to a @tparam
comment = fargs.comments[name]
if comment then
comment = comment:gsub('^%-+%s*','')
local type,rest = comment:match '([^:]+):(.*)'
if type then
self.modifiers[field]:append {type = type}
comment = rest
end
end
end
comments:append (comment or '')
end
-- A formal argument of ... may match any number of params, however.
if #pnames > #fargs then
for i = #fargs+1,#pnames do
if not varargs then
self:warning("extra param with no formal argument: "..quote(pnames[i]))
else
names:append(pnames[i])
comments:append(pcomments[i] or '')
if k < #fargs then
for i = k+1,#fargs do
if fargs[i] ~= '...' then
self:warning("undocumented formal argument: "..quote(fargs[i]))
end
end
end
@ -615,38 +601,84 @@ function Item:finish()
-- the comments are associated with each parameter by
-- adding name-value pairs to the params list (this is
-- also done for any associated modifiers)
self.params = names
-- (At this point we patch up any subparameter references)
local pmods = self.modifiers[field]
for i,name in ipairs(self.params) do
self.params[name] = comments[i]
local params, fields = List()
local original_names = formal and formal or param_names
local names = List()
self.subparams = {}
for i,name in ipairs(original_names) do
local pname,field = split_iden(name)
if field then
if not fields then
fields = List()
self.subparams[pname] = fields
end
fields:append(name)
else
names:append(name)
params:append(name)
fields = nil
end
params[name] = comments[i]
if pmods then
pmods[name] = pmods[i]
end
end
-- build up the string representation of the argument list,
-- using any opt and optchain modifiers if present.
-- For instance, '(a [, b])' if b is marked as optional
-- with @param[opt] b
local buffer, npending = { }, 0
local function acc(x) table.insert(buffer, x) end
for i = 1, #names do
local m = pmods and pmods[i]
if m then
if not m.optchain then
acc ((']'):rep(npending))
npending=0
end
if m.opt or m.optchain then acc(' ['); npending=npending+1 end
end
if i>1 then acc (', ') end
acc(names[i])
end
acc ((']'):rep(npending))
self.args = '('..table.concat(buffer)..')'
self.params = params
self.args = build_arg_list (names,pmods)
end
end
-- ldoc allows comments in the formal arg list to be used, if they aren't specified with @param
-- Further, these comments may start with a type followed by a colon, and are then equivalent
-- to a @tparam
function Item:parse_argument_comment (comment,field)
if comment then
comment = comment:gsub('^%-+%s*','')
local type,rest = comment:match '([^:]+):(.*)'
if type then
self.modifiers[field]:append {type = type}
comment = rest
end
end
return comment or ''
end
function split_iden (name)
if name == '...' then return name end
local pname,field = name:match('(.-)%.(.+)')
if not pname then
return name
else
return pname,field
end
end
function build_arg_list (names,pmods)
-- build up the string representation of the argument list,
-- using any opt and optchain modifiers if present.
-- For instance, '(a [, b])' if b is marked as optional
-- with @param[opt] b
local buffer, npending = { }, 0
local function acc(x) table.insert(buffer, x) end
for i = 1, #names do
local m = pmods and pmods[i]
if m then
if not m.optchain then
acc ((']'):rep(npending))
npending=0
end
if m.opt or m.optchain then acc(' ['); npending=npending+1 end
end
if i>1 then acc (', ') end
acc(names[i])
end
acc ((']'):rep(npending))
return '('..table.concat(buffer)..')'
end
function Item:type_of_param(p)
local mods = self.modifiers[self.parameter]
if not mods then return '' end
@ -659,6 +691,23 @@ function Item:type_of_ret(idx)
return rparam and rparam.type or ''
end
function Item:subparam(p)
if self.subparams[p] then
return self.subparams[p],p
else
return {p},nil
end
end
function Item:display_name_of(p)
local pname,field = split_iden(p)
if field then
return field
else
return pname
end
end
function Item:warning(msg)
local file = self.file and self.file.filename
@ -675,6 +724,9 @@ end
Module.warning, Module.error = Item.warning, Item.error
-------- Resolving References -----------------
function Module:hunt_for_reference (packmod, modules)
local mod_ref
local package = self.package or ''

View File

@ -128,6 +128,10 @@ function html.generate_output(ldoc, args, project)
end))
end
function ldoc.is_list (t)
return type(t) == 'table' and t.append
end
function ldoc.typename (tp)
if not tp or tp == '' then return '' end
local optional
@ -170,6 +174,7 @@ function html.generate_output(ldoc, args, project)
ldoc.output = args.output
ldoc.ipairs = ipairs
ldoc.pairs = pairs
ldoc.print = print
-- in single mode there is one module and the 'index' is the
-- documentation for that module.

View File

@ -157,13 +157,23 @@ return [==[
# if show_parms and item.params and #item.params > 0 then
<h3>$(module.kinds:type_of(item).subnames):</h3>
<ul>
# for p in iter(item.params) do
<li><span class="parameter">$(p)</span>
# local tp = ldoc.typename(item:type_of_param(p))
# if tp ~= '' then
<span class="types">$(tp)</span>
# for parm in iter(item.params) do
# local param,sublist = item:subparam(parm)
# if sublist then
<li><span class="parameter">$(sublist)</span>$(M(item.params[sublist],item))
<ul>
# end
# for p in iter(param) do
# local name,tp = item:display_name_of(p), ldoc.typename(item:type_of_param(p))
<li><span class="parameter">$(name)</span>
# if tp ~= '' then
<span class="types">$(tp)</span>
# end
$(M(item.params[p],item))</li>
# end
# if sublist then
</li></ul>
# end
$(M(item.params[p],item))</li>
# end -- for
</ul>
# end -- if params

View File

@ -253,8 +253,24 @@ local function parse_file(fname, lang, package, args)
end
if item_follows or comment:find '@' or comment:find ': ' then
tags = extract_tags(comment,args)
-- explicitly named @module (which is recommended)
if doc.project_level(tags.class) then
module_found = tags.name
-- might be a module returning a single function!
if tags.param or tags['return'] then
local parms, ret, summ = tags.param, tags['return'],tags.summary
tags.param = nil
tags['return'] = nil
tags.summary = nil
add_module(tags,tags.name,false)
tags = {
summary = summ,
name = 'returns...',
class = 'function',
['return'] = ret,
param = parms
}
end
end
doc.expand_annotation_item(tags,current_item)
-- if the item has an explicit name or defined meaning

View File

@ -8,11 +8,15 @@
-- @copyright InfoReich 2013
--- a function with typed args.
-- Note the the standard tparam aliases
-- Note the the standard tparam aliases, and how the 'opt' and 'optchain'
-- modifiers may also be used. If the Lua function has varargs, then
-- you may document an indefinite number of extra arguments!
-- @string name person's name
-- @int age
-- @string[opt] calender optional calendar
-- @int[optchain] offset optional offset
-- @treturn string
function one (name,age)
function one (name,age,...)
end

15
tests/styles/func.lua Normal file
View File

@ -0,0 +1,15 @@
------------
-- Get length of string.
-- A (silly) module which returns a single function
--
-- @module func
-- @string some text
-- @param opts multibyte encoding options
-- @string opts.charset encoding used
-- @bool opts.strict be very pedantic
-- @bool verbose tell the world about everything
-- @return its length
return function(s,opts,verbose)
return #s
end

View File

@ -0,0 +1,17 @@
------------
-- Parameters may have named subfields, if they are tables.
--
-- @module subparams
module(...)
-------
-- A function with subfield arguments.
-- @param s string
-- @param opts multibyte encoding options
-- @param opts.charset string
-- @param opts.strict bool
-- @param verbose bool
-- @return its length
function with_options (s,opts,verbose)
end

View File

@ -1,5 +1,5 @@
------------
-- Classic Lua 5.1 module,
-- Classic Lua 5.1 module.
-- Description here
----