diff --git a/ldoc.lua b/ldoc.lua index eb041cc..bffc97c 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -51,6 +51,7 @@ ldoc, a documentation generator for Lua, vs 1.2.0 -b,--package (default .) top-level package basename (needed for module(...)) -x,--ext (default html) output file extension -c,--config (default config.ld) configuration name + -i,--ignore ignore any 'no doc comment or no module' warnings --dump debug output dump --filter (default none) filter output as Lua data (e.g pl.pretty.dump) --tags (default none) show all references to given tags, comma-separated @@ -176,7 +177,7 @@ end local ldoc_contents = { 'alias','add_language_extension','new_type','add_section', 'tparam_alias', 'file','project','title','package','format','output','dir','ext', 'topics', - 'one','style','template','description','examples','readme','all','manual_url', + 'one','style','template','description','examples','readme','all','manual_url', 'ignore', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', } ldoc_contents = tablex.makeset(ldoc_contents) @@ -220,15 +221,14 @@ local quote = tools.quote --- processing command line and preparing for output --- local F -local file_list,module_list = List(),List() -module_list.by_name = {} +local file_list = List() +File.list = file_list local config_dir local ldoc_dir = arg[0]:gsub('[^/\\]+$','') local doc_path = ldoc_dir..'/ldoc/builtin/?.lua' - -- ldoc -m is expecting a Lua package; this converts this to a file path if args.module then -- first check if we've been given a global Lua lib function @@ -314,6 +314,8 @@ end -- where it is a list of files or directories. If specified on the command-line, we have -- to find an optional associated config.ld, if not already loaded. +if ldoc.ignore then args.ignore = true end + local function process_file (f, flist) local ext = path.extension(f) local ftype = file_types[ext] @@ -379,9 +381,6 @@ end override 'format' ldoc.markup = markup.create(ldoc, args.format) -local multiple_files = #file_list > 1 -local first_module - ------ 'Special' Project-level entities --------------------------------------- -- Examples and Topics do not contain code to be processed for doc comments. -- Instead, they are intended to be rendered nicely as-is, whether as pretty-lua @@ -439,7 +438,10 @@ end -- extract modules from the file objects, resolve references and sort appropriately --- +local first_module local project = ProjectMap() +local module_list = List() +module_list.by_name = {} for F in file_list:iter() do for mod in F.modules:iter() do @@ -569,8 +571,7 @@ if args.style == '!' or args.template == '!' then end end - -ldoc.single = not multiple_files and first_module or nil +ldoc.single = #module_list == 1 and first_module or nil ldoc.log = print ldoc.kinds = project ldoc.modules = module_list diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 3bf6471..75534bd 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -11,32 +11,39 @@ local global = require 'ldoc.builtin.globals' local tools = require 'ldoc.tools' local split_dotted_name = tools.split_dotted_name +local TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG = 'M','id','S','T','N' + -- these are the basic tags known to ldoc. They come in several varieties: --- - tags with multiple values like 'param' (TAG_MULTI) --- - tags which are identifiers, like 'name' (TAG_ID) --- - tags with a single value, like 'release' (TAG_SINGLE) --- - tags which represent a type, like 'function' (TAG_TYPE) +-- - 'M' tags with multiple values like 'param' (TAG_MULTI) +-- - 'id' tags which are identifiers, like 'name' (TAG_ID) +-- - 'S' tags with a single value, like 'release' (TAG_SINGLE) +-- - 'N' tags which have no associated value, like 'local` (TAG_FLAG) +-- - 'T' tags which represent a type, like 'function' (TAG_TYPE) local known_tags = { param = 'M', see = 'M', usage = 'M', ['return'] = 'M', field = 'M', author='M'; - class = 'id', name = 'id', pragma = 'id', alias = 'id'; + class = 'id', name = 'id', pragma = 'id', alias = 'id', within = 'id', copyright = 'S', summary = 'S', description = 'S', release = 'S', license = 'S', - fixme = 'S', todo = 'S', warning = 'S', raise = 'S'; - module = 'T', script = 'T', example = 'T', topic = 'T', -- project-level - ['function'] = 'T', lfunction = 'T', table = 'T', section = 'T', type = 'T', - annotation = 'T', factory = 'T'; -- module-level + fixme = 'S', todo = 'S', warning = 'S', raise = 'S', ['local'] = 'N', export = 'N', private = 'N', constructor = 'N'; + -- project-level + module = 'T', script = 'T', example = 'T', topic = 'T', submodule='T', + -- module-level + ['function'] = 'T', lfunction = 'T', table = 'T', section = 'T', type = 'T', + annotation = 'T', factory = 'T'; + } known_tags._alias = {} known_tags._project_level = { module = true, script = true, example = true, - topic = true + topic = true, + submodule = true; } local see_reference_handlers = {} -local TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG = 'M','id','S','T','N' + doc.TAG_MULTI,doc.TAG_ID,doc.TAG_SINGLE,doc.TAG_TYPE,doc.TAG_FLAG = TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG @@ -148,9 +155,27 @@ local function mod_section_type (this_mod) return this_mod and this_mod.section and this_mod.section.type end +local function find_module_in_files (name) + for f in File.list:iter() do + for m in f.modules:iter() do + if m.name == name then + return m,f.filename + end + end + end +end + function File:finish() local this_mod local items = self.items + local tagged_inside + local function add_section (item, display_name) + display_name = display_name or item.display_name + this_mod.section = item + this_mod.kinds:add_kind(display_name,display_name) + this_mod.sections:append(item) + this_mod.sections.by_name[display_name:gsub('%A','_')] = item + end for item in items:iter() do if mod_section_type(this_mod) == 'factory' and item.tags then local klass = '@{'..this_mod.section.name..'}' @@ -164,19 +189,30 @@ function File:finish() item:finish() if doc.project_level(item.type) then this_mod = item - local package,mname + local package,mname,submodule if item.type == 'module' then -- if name is 'package.mod', then mod_name is 'mod' package,mname = split_dotted_name(this_mod.name) + elseif item.type == 'submodule' then + local mf + submodule = true + this_mod,mf = find_module_in_files(item.name) + if this_mod == nil then + self:error("'"..item.name.."' not found for submodule") + end + tagged_inside = tools.this_module_name(self.base,self.filename)..' Functions' + this_mod.kinds:add_kind(tagged_inside, tagged_inside) end if not package then mname = this_mod.name package = '' end - self.modules:append(this_mod) - this_mod.package = package - this_mod.mod_name = mname - this_mod.kinds = ModuleMap() -- the iterator over the module contents + if not submodule then + this_mod.package = package + this_mod.mod_name = mname + this_mod.kinds = ModuleMap() -- the iterator over the module contents + self.modules:append(this_mod) + end elseif doc.section_tag(item.type) then local display_name = item.name if display_name == 'end' then @@ -191,10 +227,7 @@ function File:finish() display_name = summary end item.display_name = display_name - this_mod.section = item - this_mod.kinds:add_kind(display_name,display_name) - this_mod.sections:append(item) - this_mod.sections.by_name[display_name:gsub('%A','_')] = item + add_section(item) end else local to_be_removed @@ -217,14 +250,22 @@ function File:finish() item.name = fname end + if tagged_inside then + item.tags.inside = tagged_inside + end + if item.tags.inside then + this_mod.section = nil + end + -- right, this item was within a section or a 'class' local section_description if this_mod.section then - item.section = this_mod.section.display_name + local this_section = this_mod.section + item.section = this_section.display_name -- if it was a class, then the name should be 'Class:foo' - local stype = this_mod.section.type + local stype = this_section.type if doc.class_tag(stype) then - local prefix = this_mod.section.name .. (not item.tags.constructor and ':' or '.') + local prefix = section.name .. (not item.tags.constructor and ':' or '.') if not has_prefix(item.name,prefix) then item.name = prefix .. item.name end @@ -238,7 +279,10 @@ function File:finish() end end end - section_description = this_mod.section.description + section_description = this_section.description + elseif item.tags.inside then + section_description = item.tags.inside + item.section = item.tags.inside else -- otherwise, just goes into the default sections (Functions,Tables,etc) item.section = item.type end @@ -423,7 +467,7 @@ function Item:finish() if params then for line in params:iter() do local name, comment = line :match('%s*([%w_%.:]+)(.*)') - assert(name, "bad param name format") + assert(name, "bad param name format '"..line.."'") names:append(name) comments:append(comment) end diff --git a/ldoc/lexer.lua b/ldoc/lexer.lua index 13251db..8f6bbe0 100644 --- a/ldoc/lexer.lua +++ b/ldoc/lexer.lua @@ -407,7 +407,7 @@ function lexer.get_separated_list(tok,endtoken,delim) token,value=tok() if not token then return nil,'EOS' end -- end of stream is an error! if is_end(token,value) and level == 1 then - if next(t1) then + if next(tl) then append(parm_values,tl) end break diff --git a/ldoc/parse.lua b/ldoc/parse.lua index df639c7..96fc181 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -119,12 +119,14 @@ end -- encountered, then ldoc looks for a call to module() to find the name of the -- module if there isn't an explicit module name specified. -local function parse_file(fname,lang, package) +local function parse_file(fname, lang, package, args) local line,f = 1 local F = File(fname) local module_found, first_comment = false,true local current_item, module_item + F.base = package + local tok,f = lang.lexer(fname) if not tok then return nil end @@ -168,7 +170,10 @@ local function parse_file(fname,lang, package) t,v = tnext(tok) end if not t then - F:warning("no module() call found; no initial doc comment") + if not args.ignore then + F:warning("no module() call found; no initial doc comment") + end + --return nil else mod,t,v = lang:parse_module_call(tok,t,v) if mod ~= '...' then @@ -208,8 +213,10 @@ local function parse_file(fname,lang, package) comment = table.concat(comment) if not ldoc_comment and first_comment then - F:warning("first comment must be a doc comment!") - break + if not args.ignore then + F:warning("first comment must be a doc comment!") + break + end end if first_comment then first_comment = false @@ -304,7 +311,7 @@ local function parse_file(fname,lang, package) end function parse.file(name,lang, args) - local F,err = parse_file(name,lang, args.package) + local F,err = parse_file(name,lang,args.package,args) if err or not F then return F,err end local ok,err = xpcall(function() F:finish() end,debug.traceback) if not ok then return F,err end diff --git a/ldoc/tools.lua b/ldoc/tools.lua index 57042c7..abe57fb 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -221,7 +221,7 @@ function M.this_module_name (basename,fname) --print('deduce',lpath,cnt,basename) if cnt ~= 1 then quit("module(...) name deduction failed: base "..basename.." "..fname) end lpath = lpath:gsub(path.sep,'.') - return M.name_of(lpath):gsub('%.init$','') + return (M.name_of(lpath):gsub('%.init$','')) end function M.find_existing_module (name, dname, searchfn)