2012-03-13 09:37:07 +01:00
|
|
|
#!/usr/bin/env lua
|
2011-04-12 19:07:47 +02:00
|
|
|
---------------
|
2011-09-26 12:47:13 +02:00
|
|
|
-- ## ldoc, a Lua documentation generator.
|
|
|
|
--
|
|
|
|
-- Compatible with luadoc-style annotations, but providing
|
2011-09-19 15:53:43 +02:00
|
|
|
-- easier customization options.
|
|
|
|
--
|
|
|
|
-- C/C++ support for Lua extensions is provided.
|
|
|
|
--
|
2013-08-25 14:29:30 +02:00
|
|
|
-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.4.0.zip)
|
2011-12-21 14:04:16 +01:00
|
|
|
--
|
|
|
|
-- [Github Page](https://github.com/stevedonovan/ldoc)
|
|
|
|
--
|
2011-09-19 15:53:43 +02:00
|
|
|
-- @author Steve Donovan
|
|
|
|
-- @copyright 2011
|
|
|
|
-- @license MIT/X11
|
|
|
|
-- @script ldoc
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2012-10-28 18:51:00 +01:00
|
|
|
local class = require 'pl.class'
|
|
|
|
local app = require 'pl.app'
|
|
|
|
local path = require 'pl.path'
|
2012-10-29 01:14:54 +01:00
|
|
|
local dir = require 'pl.dir'
|
2012-10-28 18:51:00 +01:00
|
|
|
local utils = require 'pl.utils'
|
|
|
|
local List = require 'pl.List'
|
|
|
|
local stringx = require 'pl.stringx'
|
|
|
|
local tablex = require 'pl.tablex'
|
|
|
|
|
2013-08-26 09:59:10 +02:00
|
|
|
-- Penlight compatibility
|
|
|
|
utils.unpack = utils.unpack or unpack or table.unpack
|
2011-04-12 19:07:47 +02:00
|
|
|
|
|
|
|
local append = table.insert
|
2011-07-12 14:14:55 +02:00
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
local lapp = require 'pl.lapp'
|
|
|
|
|
|
|
|
-- so we can find our private modules
|
|
|
|
app.require_here()
|
|
|
|
|
2011-09-19 15:53:43 +02:00
|
|
|
--- @usage
|
|
|
|
local usage = [[
|
2013-08-23 13:50:34 +02:00
|
|
|
ldoc, a documentation generator for Lua, vs 1.4.0
|
2013-05-15 15:30:47 +02:00
|
|
|
-d,--dir (default doc) output directory
|
2011-06-06 18:38:02 +02:00
|
|
|
-o,--output (default 'index') output name
|
2011-04-12 19:07:47 +02:00
|
|
|
-v,--verbose verbose
|
2011-06-15 16:33:13 +02:00
|
|
|
-a,--all show local functions, etc, in docs
|
2011-04-12 19:07:47 +02:00
|
|
|
-q,--quiet suppress output
|
|
|
|
-m,--module module docs as text
|
2011-06-10 11:44:12 +02:00
|
|
|
-s,--style (default !) directory for style sheet (ldoc.css)
|
|
|
|
-l,--template (default !) directory for template (ldoc.ltp)
|
2011-04-12 19:07:47 +02:00
|
|
|
-p,--project (default ldoc) project name
|
|
|
|
-t,--title (default Reference) page title
|
2011-09-26 12:47:13 +02:00
|
|
|
-f,--format (default plain) formatting - can be markdown, discount or plain
|
2011-04-17 16:31:28 +02:00
|
|
|
-b,--package (default .) top-level package basename (needed for module(...))
|
2011-06-10 11:44:12 +02:00
|
|
|
-x,--ext (default html) output file extension
|
2011-07-07 14:28:41 +02:00
|
|
|
-c,--config (default config.ld) configuration name
|
2012-12-10 13:45:54 +01:00
|
|
|
-i,--ignore ignore any 'no doc comment or no module' warnings
|
2013-08-22 09:43:26 +02:00
|
|
|
-X,--not_luadoc break LuaDoc compatibility. Descriptions may continue after tags.
|
2012-12-29 11:01:40 +01:00
|
|
|
-D,--define (default none) set a flag to be used in config.ld
|
2013-01-25 08:09:40 +01:00
|
|
|
-C,--colon use colon style
|
2013-01-02 12:04:14 +01:00
|
|
|
-B,--boilerplate ignore first comment in source files
|
2013-01-28 10:23:31 +01:00
|
|
|
-M,--merge allow module merging
|
2013-03-28 12:28:05 +01:00
|
|
|
-S,--simple no return or params, no summary
|
|
|
|
-O,--one one-column output layout
|
2011-04-13 18:45:41 +02:00
|
|
|
--dump debug output dump
|
2011-07-07 14:28:41 +02:00
|
|
|
--filter (default none) filter output as Lua data (e.g pl.pretty.dump)
|
2011-09-19 15:53:43 +02:00
|
|
|
--tags (default none) show all references to given tags, comma-separated
|
2011-04-12 19:07:47 +02:00
|
|
|
<file> (string) source file or directory containing source
|
2011-09-19 15:53:43 +02:00
|
|
|
|
2011-12-09 14:43:37 +01:00
|
|
|
`ldoc .` reads options from an `config.ld` file in same directory;
|
2013-05-15 15:30:47 +02:00
|
|
|
`ldoc -c path/to/myconfig.ld <file>` reads options from `path/to/myconfig.ld`
|
|
|
|
and processes <file> if 'file' was not defined in the ld file.
|
2011-04-12 19:07:47 +02:00
|
|
|
]]
|
2011-09-19 15:53:43 +02:00
|
|
|
local args = lapp(usage)
|
2012-02-29 18:20:57 +01:00
|
|
|
local lfs = require 'lfs'
|
2011-06-14 10:54:51 +02:00
|
|
|
local doc = require 'ldoc.doc'
|
2011-06-16 11:09:24 +02:00
|
|
|
local lang = require 'ldoc.lang'
|
2011-06-14 10:54:51 +02:00
|
|
|
local tools = require 'ldoc.tools'
|
2011-09-20 12:59:34 +02:00
|
|
|
local global = require 'ldoc.builtin.globals'
|
2011-07-05 16:30:49 +02:00
|
|
|
local markup = require 'ldoc.markup'
|
2011-07-12 14:14:55 +02:00
|
|
|
local parse = require 'ldoc.parse'
|
2011-04-12 19:07:47 +02:00
|
|
|
local KindMap = tools.KindMap
|
2011-07-12 14:14:55 +02:00
|
|
|
local Item,File,Module = doc.Item,doc.File,doc.Module
|
|
|
|
local quit = utils.quit
|
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
|
|
|
|
class.ModuleMap(KindMap)
|
2013-02-13 13:15:29 +01:00
|
|
|
local ModuleMap = ModuleMap
|
2011-04-12 19:07:47 +02:00
|
|
|
|
|
|
|
function ModuleMap:_init ()
|
2011-06-06 18:38:02 +02:00
|
|
|
self.klass = ModuleMap
|
|
|
|
self.fieldname = 'section'
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
ModuleMap:add_kind('function','Functions','Parameters')
|
|
|
|
ModuleMap:add_kind('table','Tables','Fields')
|
|
|
|
ModuleMap:add_kind('field','Fields')
|
2011-06-13 15:42:42 +02:00
|
|
|
ModuleMap:add_kind('lfunction','Local Functions','Parameters')
|
2011-09-19 13:34:01 +02:00
|
|
|
ModuleMap:add_kind('annotation','Issues')
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2011-06-06 18:38:02 +02:00
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
class.ProjectMap(KindMap)
|
|
|
|
ProjectMap.project_level = true
|
|
|
|
|
|
|
|
function ProjectMap:_init ()
|
2011-06-06 18:38:02 +02:00
|
|
|
self.klass = ProjectMap
|
|
|
|
self.fieldname = 'type'
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
ProjectMap:add_kind('module','Modules')
|
|
|
|
ProjectMap:add_kind('script','Scripts')
|
2013-08-07 15:19:46 +02:00
|
|
|
ProjectMap:add_kind('classmod','Classes')
|
2011-07-11 15:40:44 +02:00
|
|
|
ProjectMap:add_kind('topic','Topics')
|
2011-07-08 15:58:40 +02:00
|
|
|
ProjectMap:add_kind('example','Examples')
|
|
|
|
|
2011-07-12 14:14:55 +02:00
|
|
|
local lua, cc = lang.lua, lang.cc
|
|
|
|
|
|
|
|
local file_types = {
|
|
|
|
['.lua'] = lua,
|
|
|
|
['.ldoc'] = lua,
|
|
|
|
['.luadoc'] = lua,
|
|
|
|
['.c'] = cc,
|
|
|
|
['.cpp'] = cc,
|
|
|
|
['.cxx'] = cc,
|
2013-03-04 14:34:34 +01:00
|
|
|
['.C'] = cc,
|
2013-08-05 19:27:42 +02:00
|
|
|
['.mm'] = cc,
|
|
|
|
['.moon'] = lang.moon,
|
2011-07-12 14:14:55 +02:00
|
|
|
}
|
2011-04-12 19:07:47 +02:00
|
|
|
|
|
|
|
------- ldoc external API ------------
|
|
|
|
|
|
|
|
-- the ldoc table represents the API available in `config.ld`.
|
2013-03-27 14:50:37 +01:00
|
|
|
local ldoc = { charset = 'UTF-8' }
|
2011-06-06 18:38:02 +02:00
|
|
|
local add_language_extension
|
2013-08-27 12:47:47 +02:00
|
|
|
-- hacky way for doc module to be passed options...
|
|
|
|
doc.ldoc = ldoc
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2011-09-17 17:57:22 +02:00
|
|
|
local function override (field)
|
2013-01-02 12:04:14 +01:00
|
|
|
if ldoc[field] ~= nil then args[field] = ldoc[field] end
|
2011-09-17 17:57:22 +02:00
|
|
|
end
|
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
-- aliases to existing tags can be defined. E.g. just 'p' for 'param'
|
|
|
|
function ldoc.alias (a,tag)
|
2011-06-06 18:38:02 +02:00
|
|
|
doc.add_alias(a,tag)
|
|
|
|
end
|
|
|
|
|
2011-12-06 18:19:09 +01:00
|
|
|
-- standard aliases --
|
|
|
|
|
|
|
|
ldoc.alias('tparam',{'param',modifiers={type="$1"}})
|
|
|
|
ldoc.alias('treturn',{'return',modifiers={type="$1"}})
|
|
|
|
ldoc.alias('tfield',{'field',modifiers={type="$1"}})
|
|
|
|
|
2012-03-04 17:57:17 +01:00
|
|
|
function ldoc.tparam_alias (name,type)
|
|
|
|
type = type or name
|
|
|
|
ldoc.alias(name,{'param',modifiers={type=type}})
|
|
|
|
end
|
|
|
|
|
2013-08-26 09:59:10 +02:00
|
|
|
ldoc.alias ('error',doc.error_macro)
|
2013-08-24 13:21:41 +02:00
|
|
|
|
2012-11-07 17:42:16 +01:00
|
|
|
ldoc.tparam_alias 'string'
|
|
|
|
ldoc.tparam_alias 'number'
|
|
|
|
ldoc.tparam_alias 'int'
|
2012-11-08 18:38:30 +01:00
|
|
|
ldoc.tparam_alias 'bool'
|
|
|
|
ldoc.tparam_alias 'func'
|
|
|
|
ldoc.tparam_alias 'tab'
|
2012-11-07 17:42:16 +01:00
|
|
|
ldoc.tparam_alias 'thread'
|
|
|
|
|
2012-07-30 18:36:28 +02:00
|
|
|
function ldoc.add_language_extension(ext, lang)
|
2011-07-12 14:14:55 +02:00
|
|
|
lang = (lang=='c' and cc) or (lang=='lua' and lua) or quit('unknown language')
|
|
|
|
if ext:sub(1,1) ~= '.' then ext = '.'..ext end
|
|
|
|
file_types[ext] = lang
|
2011-06-06 18:38:02 +02:00
|
|
|
end
|
|
|
|
|
2012-07-30 18:36:28 +02:00
|
|
|
function ldoc.add_section (name, title, subname)
|
2011-06-06 18:38:02 +02:00
|
|
|
ModuleMap:add_kind(name,title,subname)
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
-- new tags can be added, which can be on a project level.
|
2013-01-25 08:09:40 +01:00
|
|
|
function ldoc.new_type (tag, header, project_level,subfield)
|
2011-06-06 18:38:02 +02:00
|
|
|
doc.add_tag(tag,doc.TAG_TYPE,project_level)
|
|
|
|
if project_level then
|
2013-01-25 08:09:40 +01:00
|
|
|
ProjectMap:add_kind(tag,header,subfield)
|
2011-06-06 18:38:02 +02:00
|
|
|
else
|
2013-01-25 08:09:40 +01:00
|
|
|
ModuleMap:add_kind(tag,header,subfield)
|
2011-06-06 18:38:02 +02:00
|
|
|
end
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
2011-09-19 14:42:54 +02:00
|
|
|
function ldoc.manual_url (url)
|
2013-08-23 13:50:34 +02:00
|
|
|
global.set_manual_url(url)
|
2011-09-19 14:42:54 +02:00
|
|
|
end
|
|
|
|
|
2012-07-30 18:36:28 +02:00
|
|
|
function ldoc.custom_see_handler(pat, handler)
|
2013-08-23 13:50:34 +02:00
|
|
|
doc.add_custom_see_handler(pat, handler)
|
2012-07-30 18:36:28 +02:00
|
|
|
end
|
|
|
|
|
2011-07-29 15:52:16 +02:00
|
|
|
local ldoc_contents = {
|
2012-03-04 17:57:17 +01:00
|
|
|
'alias','add_language_extension','new_type','add_section', 'tparam_alias',
|
2012-03-05 14:34:16 +01:00
|
|
|
'file','project','title','package','format','output','dir','ext', 'topics',
|
2013-05-09 12:52:15 +02:00
|
|
|
'one','style','template','description','examples', 'pretty', 'charset', 'plain',
|
2013-08-27 12:47:47 +02:00
|
|
|
'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars',
|
|
|
|
'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups',
|
2012-07-30 18:36:28 +02:00
|
|
|
'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler',
|
2013-08-20 15:26:49 +02:00
|
|
|
'no_space_before_args',
|
2011-07-29 15:52:16 +02:00
|
|
|
}
|
|
|
|
ldoc_contents = tablex.makeset(ldoc_contents)
|
|
|
|
|
2012-12-29 11:01:40 +01:00
|
|
|
local function loadstr (ldoc,txt)
|
|
|
|
local chunk, err
|
|
|
|
local load
|
|
|
|
-- Penlight's Lua 5.2 compatibility has wobbled over the years...
|
|
|
|
if not rawget(_G,'loadin') then -- Penlight 0.9.5
|
|
|
|
-- Penlight 0.9.7; no more global load() override
|
|
|
|
load = load or utils.load
|
|
|
|
chunk,err = load(txt,'config',nil,ldoc)
|
|
|
|
else
|
|
|
|
chunk,err = loadin(ldoc,txt)
|
|
|
|
end
|
|
|
|
return chunk, err
|
|
|
|
end
|
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
-- any file called 'config.ld' found in the source tree will be
|
|
|
|
-- handled specially. It will be loaded using 'ldoc' as the environment.
|
|
|
|
local function read_ldoc_config (fname)
|
2011-06-06 18:38:02 +02:00
|
|
|
local directory = path.dirname(fname)
|
2011-08-22 12:54:32 +02:00
|
|
|
if directory == '' then
|
|
|
|
directory = '.'
|
|
|
|
end
|
2012-12-29 11:01:40 +01:00
|
|
|
local chunk, err, ok
|
2011-12-06 18:19:09 +01:00
|
|
|
if args.filter == 'none' then
|
|
|
|
print('reading configuration from '..fname)
|
|
|
|
end
|
2011-06-09 12:31:05 +02:00
|
|
|
local txt,not_found = utils.readfile(fname)
|
|
|
|
if txt then
|
2012-12-29 11:01:40 +01:00
|
|
|
chunk, err = loadstr(ldoc,txt)
|
2011-07-05 13:33:27 +02:00
|
|
|
if chunk then
|
2012-12-29 11:01:40 +01:00
|
|
|
if args.define ~= 'none' then ldoc[args.define] = true end
|
2011-07-05 13:33:27 +02:00
|
|
|
ok,err = pcall(chunk)
|
2011-09-11 17:47:34 +02:00
|
|
|
end
|
2011-06-09 12:31:05 +02:00
|
|
|
end
|
2011-09-11 17:47:34 +02:00
|
|
|
if err then quit('error loading config file '..fname..': '..err) end
|
2011-07-29 15:52:16 +02:00
|
|
|
for k in pairs(ldoc) do
|
|
|
|
if not ldoc_contents[k] then
|
|
|
|
quit("this config file field/function is unrecognized: "..k)
|
|
|
|
end
|
|
|
|
end
|
2011-06-09 12:31:05 +02:00
|
|
|
return directory, not_found
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
2011-07-14 15:27:16 +02:00
|
|
|
local quote = tools.quote
|
2011-04-12 19:07:47 +02:00
|
|
|
--- processing command line and preparing for output ---
|
|
|
|
|
|
|
|
local F
|
2012-12-10 13:45:54 +01:00
|
|
|
local file_list = List()
|
|
|
|
File.list = file_list
|
2011-04-12 19:07:47 +02:00
|
|
|
local config_dir
|
|
|
|
|
|
|
|
|
2011-06-14 11:36:05 +02:00
|
|
|
local ldoc_dir = arg[0]:gsub('[^/\\]+$','')
|
2012-03-19 14:04:16 +01:00
|
|
|
local doc_path = ldoc_dir..'/ldoc/builtin/?.lua'
|
2011-06-14 11:36:05 +02:00
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
-- ldoc -m is expecting a Lua package; this converts this to a file path
|
|
|
|
if args.module then
|
2011-06-15 16:12:56 +02:00
|
|
|
-- first check if we've been given a global Lua lib function
|
2011-06-19 17:53:05 +02:00
|
|
|
if args.file:match '^%a+$' and global.functions[args.file] then
|
2011-06-14 11:36:05 +02:00
|
|
|
args.file = 'global.'..args.file
|
|
|
|
end
|
2013-01-02 12:04:14 +01:00
|
|
|
local fullpath,mod,on_docpath = tools.lookup_existing_module_or_function (args.file, doc_path)
|
2011-06-06 18:38:02 +02:00
|
|
|
if not fullpath then
|
2011-06-14 11:36:05 +02:00
|
|
|
quit(mod)
|
|
|
|
else
|
|
|
|
args.file = fullpath
|
|
|
|
args.module = mod
|
2011-06-06 18:38:02 +02:00
|
|
|
end
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
2011-07-29 15:52:16 +02:00
|
|
|
local abspath = tools.abspath
|
|
|
|
|
2011-06-09 12:31:05 +02:00
|
|
|
-- a special case: 'ldoc .' can get all its parameters from config.ld
|
2011-06-10 11:44:12 +02:00
|
|
|
if args.file == '.' then
|
|
|
|
local err
|
2011-08-21 19:01:31 +02:00
|
|
|
config_dir,err = read_ldoc_config(args.config)
|
|
|
|
if err then quit("no "..quote(args.config).." found") end
|
|
|
|
local config_path = path.dirname(args.config)
|
|
|
|
if config_path ~= '' then
|
2011-12-12 12:15:04 +01:00
|
|
|
print('changing to directory',config_path)
|
2011-08-21 19:01:31 +02:00
|
|
|
lfs.chdir(config_path)
|
|
|
|
end
|
2011-06-10 11:44:12 +02:00
|
|
|
config_is_read = true
|
|
|
|
args.file = ldoc.file or '.'
|
|
|
|
if args.file == '.' then
|
|
|
|
args.file = lfs.currentdir()
|
2011-07-03 10:24:14 +02:00
|
|
|
elseif type(args.file) == 'table' then
|
|
|
|
for i,f in ipairs(args.file) do
|
2011-07-29 15:52:16 +02:00
|
|
|
args.file[i] = abspath(f)
|
2011-07-03 10:24:14 +02:00
|
|
|
end
|
2011-06-10 11:44:12 +02:00
|
|
|
else
|
2011-07-29 15:52:16 +02:00
|
|
|
args.file = abspath(args.file)
|
2011-06-10 11:44:12 +02:00
|
|
|
end
|
2011-04-17 16:31:28 +02:00
|
|
|
else
|
2013-04-11 15:37:01 +02:00
|
|
|
-- user-provided config file
|
|
|
|
if args.config ~= 'config.ld' then
|
|
|
|
local err
|
|
|
|
config_dir,err = read_ldoc_config(args.config)
|
|
|
|
if err then quit("no "..quote(args.config).." found") end
|
|
|
|
end
|
|
|
|
-- with user-provided file
|
2011-07-29 15:52:16 +02:00
|
|
|
args.file = abspath(args.file)
|
2011-04-17 16:31:28 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
local source_dir = args.file
|
2011-07-29 15:52:16 +02:00
|
|
|
if type(source_dir) == 'table' then
|
|
|
|
source_dir = source_dir[1]
|
|
|
|
end
|
|
|
|
if type(source_dir) == 'string' and path.isfile(source_dir) then
|
2011-06-06 18:38:02 +02:00
|
|
|
source_dir = path.splitpath(source_dir)
|
2011-04-17 16:31:28 +02:00
|
|
|
end
|
2013-04-13 15:44:27 +02:00
|
|
|
source_dir = source_dir:gsub('[/\\]%.$','')
|
2011-04-17 16:31:28 +02:00
|
|
|
|
2011-06-19 17:53:05 +02:00
|
|
|
---------- specifying the package for inferring module names --------
|
|
|
|
-- If you use module(...), or forget to explicitly use @module, then
|
|
|
|
-- ldoc has to infer the module name. There are three sensible values for
|
|
|
|
-- `args.package`:
|
|
|
|
--
|
|
|
|
-- * '.' the actual source is in an immediate subdir of the path given
|
|
|
|
-- * '..' the path given points to the source directory
|
|
|
|
-- * 'NAME' explicitly give the base module package name
|
|
|
|
--
|
|
|
|
|
2011-06-20 19:04:21 +02:00
|
|
|
local function setup_package_base()
|
|
|
|
if ldoc.package then args.package = ldoc.package end
|
|
|
|
if args.package == '.' then
|
2011-06-19 17:53:05 +02:00
|
|
|
args.package = source_dir
|
2011-06-20 19:04:21 +02:00
|
|
|
elseif args.package == '..' then
|
|
|
|
args.package = path.splitpath(source_dir)
|
2012-02-29 18:20:57 +01:00
|
|
|
elseif not args.package:find '[\\/]' then
|
2011-06-20 19:04:21 +02:00
|
|
|
local subdir,dir = path.splitpath(source_dir)
|
|
|
|
if dir == args.package then
|
|
|
|
args.package = subdir
|
|
|
|
elseif path.isdir(path.join(source_dir,args.package)) then
|
|
|
|
args.package = source_dir
|
|
|
|
else
|
|
|
|
quit("args.package is not the name of the source directory")
|
|
|
|
end
|
2011-06-19 17:53:05 +02:00
|
|
|
end
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
2011-06-06 18:38:02 +02:00
|
|
|
|
2011-07-11 15:40:44 +02:00
|
|
|
--------- processing files ---------------------
|
|
|
|
-- ldoc may be given a file, or a directory. `args.file` may also be specified in config.ld
|
|
|
|
-- 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.
|
|
|
|
|
2012-12-10 13:45:54 +01:00
|
|
|
if ldoc.ignore then args.ignore = true end
|
|
|
|
|
2011-09-17 17:57:22 +02:00
|
|
|
local function process_file (f, flist)
|
2011-07-03 10:24:14 +02:00
|
|
|
local ext = path.extension(f)
|
|
|
|
local ftype = file_types[ext]
|
|
|
|
if ftype then
|
2013-08-01 12:14:57 +02:00
|
|
|
if args.verbose then print(f) end
|
2011-07-18 12:07:40 +02:00
|
|
|
local F,err = parse.file(f,ftype,args)
|
2011-12-06 18:19:09 +01:00
|
|
|
if err then
|
|
|
|
if F then
|
|
|
|
F:warning("internal LDoc error")
|
|
|
|
end
|
|
|
|
quit(err)
|
|
|
|
end
|
2011-09-17 17:57:22 +02:00
|
|
|
flist:append(F)
|
2011-07-03 10:24:14 +02:00
|
|
|
end
|
|
|
|
end
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2011-07-11 15:40:44 +02:00
|
|
|
local process_file_list = tools.process_file_list
|
2011-07-08 15:58:40 +02:00
|
|
|
|
2011-07-18 12:07:40 +02:00
|
|
|
setup_package_base()
|
|
|
|
|
2013-01-25 08:09:40 +01:00
|
|
|
override 'colon'
|
2013-01-28 10:23:31 +01:00
|
|
|
override 'merge'
|
2013-05-09 13:28:39 +02:00
|
|
|
override 'not_luadoc'
|
2013-08-02 14:18:41 +02:00
|
|
|
override 'module_file'
|
|
|
|
|
2013-08-27 12:47:47 +02:00
|
|
|
if ldoc.merge_error_groups == nil then
|
|
|
|
ldoc.merge_error_groups = 'Error Message'
|
|
|
|
end
|
|
|
|
|
2013-08-02 14:18:41 +02:00
|
|
|
-- ldoc.module_file establishes a partial ordering where the
|
|
|
|
-- master module files are processed first.
|
|
|
|
local function reorder_module_file ()
|
|
|
|
if args.module_file then
|
|
|
|
local mf = {}
|
|
|
|
for mname, f in pairs(args.module_file) do
|
|
|
|
local fullpath = abspath(f)
|
|
|
|
mf[fullpath] = true
|
|
|
|
end
|
|
|
|
return function(x,y)
|
|
|
|
return mf[x] and not mf[y]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2011-07-18 12:07:40 +02:00
|
|
|
|
2011-07-08 15:58:40 +02:00
|
|
|
if type(args.file) == 'table' then
|
|
|
|
-- this can only be set from config file so we can assume it's already read
|
2013-08-02 14:18:41 +02:00
|
|
|
args.file.sortfn = reorder_module_file()
|
2011-07-08 15:58:40 +02:00
|
|
|
process_file_list(args.file,'*.*',process_file, file_list)
|
2011-07-03 10:24:14 +02:00
|
|
|
if #file_list == 0 then quit "no source files specified" end
|
|
|
|
elseif path.isdir(args.file) then
|
2011-06-06 18:38:02 +02:00
|
|
|
local files = List(dir.getallfiles(args.file,'*.*'))
|
2011-07-08 15:58:40 +02:00
|
|
|
-- use any configuration file we find, if not already specified
|
2011-07-03 10:24:14 +02:00
|
|
|
if not config_dir then
|
|
|
|
local config_files = files:filter(function(f)
|
2011-07-07 14:28:41 +02:00
|
|
|
return path.basename(f) == args.config
|
2011-07-03 10:24:14 +02:00
|
|
|
end)
|
|
|
|
if #config_files > 0 then
|
|
|
|
config_dir = read_ldoc_config(config_files[1])
|
|
|
|
if #config_files > 1 then
|
|
|
|
print('warning: other config files found: '..config_files[2])
|
|
|
|
end
|
|
|
|
end
|
2011-06-06 18:38:02 +02:00
|
|
|
end
|
2013-08-02 14:18:41 +02:00
|
|
|
-- process files, optionally in order that respects master module files
|
|
|
|
local sortfn = reorder_module_file()
|
|
|
|
if sortfn then files:sort(sortfn) end
|
2011-06-06 18:38:02 +02:00
|
|
|
for f in files:iter() do
|
2011-07-03 10:24:14 +02:00
|
|
|
process_file(f, file_list)
|
2011-06-06 18:38:02 +02:00
|
|
|
end
|
2013-08-02 14:18:41 +02:00
|
|
|
|
2011-07-03 10:24:14 +02:00
|
|
|
if #file_list == 0 then
|
|
|
|
quit(quote(args.file).." contained no source files")
|
2011-06-21 18:39:38 +02:00
|
|
|
end
|
2011-04-12 19:07:47 +02:00
|
|
|
elseif path.isfile(args.file) then
|
2011-06-06 18:38:02 +02:00
|
|
|
-- a single file may be accompanied by a config.ld in the same dir
|
2011-06-10 11:44:12 +02:00
|
|
|
if not config_dir then
|
|
|
|
config_dir = path.dirname(args.file)
|
|
|
|
if config_dir == '' then config_dir = '.' end
|
2011-07-07 14:28:41 +02:00
|
|
|
local config = path.join(config_dir,args.config)
|
2011-07-03 10:24:14 +02:00
|
|
|
if path.isfile(config) then
|
2011-06-10 11:44:12 +02:00
|
|
|
read_ldoc_config(config)
|
|
|
|
end
|
2011-06-06 18:38:02 +02:00
|
|
|
end
|
2011-07-03 10:24:14 +02:00
|
|
|
process_file(args.file, file_list)
|
|
|
|
if #file_list == 0 then quit "unsupported file extension" end
|
2011-04-12 19:07:47 +02:00
|
|
|
else
|
2011-06-21 18:02:21 +02:00
|
|
|
quit ("file or directory does not exist: "..quote(args.file))
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
2011-09-17 17:57:22 +02:00
|
|
|
-- create the function that renders text (descriptions and summaries)
|
2013-03-06 16:07:28 +01:00
|
|
|
-- (this also will initialize the code prettifier used)
|
2011-09-17 17:57:22 +02:00
|
|
|
override 'format'
|
2013-03-06 16:07:28 +01:00
|
|
|
override 'pretty'
|
|
|
|
ldoc.markup = markup.create(ldoc, args.format,args.pretty)
|
2011-07-11 15:40:44 +02:00
|
|
|
|
|
|
|
------ '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
|
|
|
|
-- or as Markdown text. Treating them as 'modules' does stretch the meaning of
|
|
|
|
-- of the term, but allows them to be treated much as modules or scripts.
|
|
|
|
-- They define an item 'body' field (containing the file's text) and a 'postprocess'
|
|
|
|
-- field which is used later to convert them into HTML. They may contain @{ref}s.
|
|
|
|
|
|
|
|
local function add_special_project_entity (f,tags,process)
|
|
|
|
local F = File(f)
|
|
|
|
tags.name = path.basename(f)
|
|
|
|
local text = utils.readfile(f)
|
|
|
|
local item = F:new_item(tags,1)
|
|
|
|
if process then
|
|
|
|
text = process(F, text)
|
|
|
|
end
|
|
|
|
F:finish()
|
|
|
|
file_list:append(F)
|
|
|
|
item.body = text
|
2011-09-17 17:57:22 +02:00
|
|
|
return item, F
|
2011-07-11 15:40:44 +02:00
|
|
|
end
|
|
|
|
|
2011-07-29 15:52:16 +02:00
|
|
|
if type(ldoc.examples) == 'string' then
|
|
|
|
ldoc.examples = {ldoc.examples}
|
|
|
|
end
|
2011-07-10 19:12:35 +02:00
|
|
|
if type(ldoc.examples) == 'table' then
|
|
|
|
local prettify = require 'ldoc.prettify'
|
|
|
|
|
2011-09-17 17:57:22 +02:00
|
|
|
process_file_list (ldoc.examples, '*.lua', function(f)
|
2011-07-11 15:40:44 +02:00
|
|
|
local item = add_special_project_entity(f,{
|
2011-07-10 19:12:35 +02:00
|
|
|
class = 'example',
|
2011-07-11 15:40:44 +02:00
|
|
|
})
|
2011-09-17 17:57:22 +02:00
|
|
|
-- wrap prettify for this example so it knows which file to blame
|
|
|
|
-- if there's a problem
|
2013-08-25 14:29:30 +02:00
|
|
|
local ext = path.extension(f):sub(2)
|
|
|
|
item.postprocess = function(code) return prettify.lua(ext,f,code,0,true) end
|
2011-09-17 17:57:22 +02:00
|
|
|
end)
|
2011-07-10 19:12:35 +02:00
|
|
|
end
|
|
|
|
|
2013-03-28 12:28:05 +01:00
|
|
|
if args.simple then
|
|
|
|
ldoc.no_return_or_parms=true
|
|
|
|
ldoc.no_summary=true
|
|
|
|
end
|
|
|
|
|
2012-03-04 17:42:50 +01:00
|
|
|
ldoc.readme = ldoc.readme or ldoc.topics
|
2011-07-11 15:40:44 +02:00
|
|
|
if type(ldoc.readme) == 'string' then
|
2012-03-04 17:42:50 +01:00
|
|
|
ldoc.readme = {ldoc.readme}
|
|
|
|
end
|
|
|
|
if type(ldoc.readme) == 'table' then
|
|
|
|
process_file_list(ldoc.readme, '*.md', function(f)
|
|
|
|
local item, F = add_special_project_entity(f,{
|
|
|
|
class = 'topic'
|
|
|
|
}, markup.add_sections)
|
|
|
|
-- add_sections above has created sections corresponding to the 2nd level
|
|
|
|
-- headers in the readme, which are attached to the File. So
|
|
|
|
-- we pass the File to the postprocesser, which will insert the section markers
|
|
|
|
-- and resolve inline @ references.
|
|
|
|
item.postprocess = function(txt) return ldoc.markup(txt,F) end
|
|
|
|
end)
|
2011-07-11 15:40:44 +02:00
|
|
|
end
|
|
|
|
|
2011-09-19 15:53:43 +02:00
|
|
|
-- extract modules from the file objects, resolve references and sort appropriately ---
|
2011-07-03 10:24:14 +02:00
|
|
|
|
2012-12-10 13:45:54 +01:00
|
|
|
local first_module
|
2011-04-12 19:07:47 +02:00
|
|
|
local project = ProjectMap()
|
2012-12-10 13:45:54 +01:00
|
|
|
local module_list = List()
|
|
|
|
module_list.by_name = {}
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2012-12-29 11:01:40 +01:00
|
|
|
local modcount = 0
|
|
|
|
|
2011-07-03 10:24:14 +02:00
|
|
|
for F in file_list:iter() do
|
2011-07-08 15:58:40 +02:00
|
|
|
for mod in F.modules:iter() do
|
2011-07-10 19:12:35 +02:00
|
|
|
if not first_module then first_module = mod end
|
2012-12-29 11:01:40 +01:00
|
|
|
if doc.code_tag(mod.type) then modcount = modcount + 1 end
|
2011-07-08 15:58:40 +02:00
|
|
|
module_list:append(mod)
|
|
|
|
module_list.by_name[mod.name] = mod
|
|
|
|
end
|
2011-07-03 10:24:14 +02:00
|
|
|
end
|
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
for mod in module_list:iter() do
|
2012-03-17 11:17:22 +01:00
|
|
|
if not args.module then -- no point if we're just showing docs on the console
|
|
|
|
mod:resolve_references(module_list)
|
|
|
|
end
|
2011-06-06 18:38:02 +02:00
|
|
|
project:add(mod,module_list)
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
2011-06-15 16:33:13 +02:00
|
|
|
-- the default is not to show local functions in the documentation.
|
2011-07-29 15:52:16 +02:00
|
|
|
if not args.all and not ldoc.all then
|
2011-06-15 16:33:13 +02:00
|
|
|
for mod in module_list:iter() do
|
|
|
|
mod:mask_locals()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
table.sort(module_list,function(m1,m2)
|
2011-06-06 18:38:02 +02:00
|
|
|
return m1.name < m2.name
|
2011-04-12 19:07:47 +02:00
|
|
|
end)
|
|
|
|
|
2012-12-29 11:01:40 +01:00
|
|
|
ldoc.single = modcount == 1 and first_module or nil
|
|
|
|
|
|
|
|
|
2011-07-11 15:40:44 +02:00
|
|
|
-------- three ways to dump the object graph after processing -----
|
|
|
|
|
2011-04-12 19:07:47 +02:00
|
|
|
-- ldoc -m will give a quick & dirty dump of the module's documentation;
|
|
|
|
-- using -v will make it more verbose
|
|
|
|
if args.module then
|
2011-06-06 18:38:02 +02:00
|
|
|
if #module_list == 0 then quit("no modules found") end
|
|
|
|
if args.module == true then
|
2011-07-11 15:40:44 +02:00
|
|
|
file_list[1]:dump(args.verbose)
|
2011-06-06 18:38:02 +02:00
|
|
|
else
|
2013-08-24 15:39:41 +02:00
|
|
|
local M,name = module_list[1], args.module
|
|
|
|
local fun = M.items.by_name[name]
|
|
|
|
if not fun then
|
|
|
|
fun = M.items.by_name[M.mod_name..':'..name]
|
|
|
|
end
|
|
|
|
if not fun then quit(quote(name).." is not part of "..quote(args.file)) end
|
2011-06-06 18:38:02 +02:00
|
|
|
fun:dump(true)
|
|
|
|
end
|
|
|
|
return
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
2011-07-11 15:40:44 +02:00
|
|
|
-- ldoc --dump will do the same as -m, except for the currently specified files
|
2011-04-13 18:45:41 +02:00
|
|
|
if args.dump then
|
2011-06-06 18:38:02 +02:00
|
|
|
for mod in module_list:iter() do
|
|
|
|
mod:dump(true)
|
|
|
|
end
|
|
|
|
os.exit()
|
2011-04-13 18:45:41 +02:00
|
|
|
end
|
2011-08-26 15:41:38 +02:00
|
|
|
if args.tags ~= 'none' then
|
2011-09-18 20:45:36 +02:00
|
|
|
local tagset = {}
|
|
|
|
for t in stringx.split(args.tags,','):iter() do
|
|
|
|
tagset[t] = true
|
|
|
|
end
|
2011-08-26 15:34:42 +02:00
|
|
|
for mod in module_list:iter() do
|
2011-09-18 20:45:36 +02:00
|
|
|
mod:dump_tags(tagset)
|
2011-08-26 15:34:42 +02:00
|
|
|
end
|
|
|
|
os.exit()
|
|
|
|
end
|
2011-04-13 18:45:41 +02:00
|
|
|
|
2011-07-11 15:40:44 +02:00
|
|
|
-- ldoc --filter mod.name will load the module `mod` and pass the object graph
|
|
|
|
-- to the function `name`. As a special case --filter dump will use pl.pretty.dump.
|
2011-06-19 17:53:05 +02:00
|
|
|
if args.filter ~= 'none' then
|
2011-07-11 15:40:44 +02:00
|
|
|
doc.filter_objects_through_function(args.filter, module_list)
|
2011-06-19 17:53:05 +02:00
|
|
|
os.exit()
|
|
|
|
end
|
|
|
|
|
2013-08-25 19:38:01 +02:00
|
|
|
-- can specify format, output, dir and ext in config.ld
|
|
|
|
override 'output'
|
|
|
|
override 'dir'
|
|
|
|
override 'ext'
|
|
|
|
override 'one'
|
|
|
|
override 'boilerplate'
|
|
|
|
|
|
|
|
-- handling styling and templates --
|
2011-07-12 14:14:55 +02:00
|
|
|
ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp'
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2013-08-25 19:38:01 +02:00
|
|
|
-- special case: user wants to generate a .md file from a .lua file
|
2013-08-05 19:27:42 +02:00
|
|
|
if args.ext == 'md' then
|
2013-08-25 19:38:01 +02:00
|
|
|
if #module_list ~= 1 then
|
|
|
|
quit("can currently only generate Markdown output from one module only")
|
|
|
|
end
|
|
|
|
if ldoc.template == '!' then
|
|
|
|
ldoc.template = '!md'
|
|
|
|
end
|
|
|
|
args.output = module_list[1].name
|
|
|
|
args.dir = '.'
|
2013-08-05 19:27:42 +02:00
|
|
|
ldoc.template_escape = '>'
|
|
|
|
ldoc.style = false
|
|
|
|
args.ext = '.md'
|
|
|
|
end
|
|
|
|
|
2013-08-25 19:38:01 +02:00
|
|
|
local function match_bang (s)
|
|
|
|
if type(s) ~= 'string' then return end
|
|
|
|
return s:match '^!(.*)'
|
|
|
|
end
|
|
|
|
|
2011-06-10 11:44:12 +02:00
|
|
|
local function style_dir (sname)
|
|
|
|
local style = ldoc[sname]
|
|
|
|
local dir
|
2013-08-05 19:27:42 +02:00
|
|
|
if style==false and sname == 'style' then
|
|
|
|
args.style = false
|
|
|
|
ldoc.css = false
|
|
|
|
end
|
2011-06-10 11:44:12 +02:00
|
|
|
if style then
|
|
|
|
if style == true then
|
|
|
|
dir = config_dir
|
2013-08-25 19:38:01 +02:00
|
|
|
elseif type(style) == 'string' and (path.isdir(style) or match_bang(style)) then
|
2011-06-10 11:44:12 +02:00
|
|
|
dir = style
|
|
|
|
else
|
2013-02-13 13:15:29 +01:00
|
|
|
quit(quote(tostring(style)).." is not a directory")
|
2011-06-10 11:44:12 +02:00
|
|
|
end
|
|
|
|
args[sname] = dir
|
|
|
|
end
|
|
|
|
end
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2011-06-10 11:44:12 +02:00
|
|
|
-- the directories for template and stylesheet can be specified
|
|
|
|
-- either by command-line '--template','--style' arguments or by 'template and
|
|
|
|
-- 'style' fields in config.ld.
|
|
|
|
-- The assumption here is that if these variables are simply true then the directory
|
|
|
|
-- containing config.ld contains a ldoc.css and a ldoc.ltp respectively. Otherwise
|
|
|
|
-- they must be a valid subdirectory.
|
|
|
|
|
|
|
|
style_dir 'style'
|
|
|
|
style_dir 'template'
|
|
|
|
|
2011-06-19 17:53:05 +02:00
|
|
|
if not args.ext:find '^%.' then
|
|
|
|
args.ext = '.'..args.ext
|
|
|
|
end
|
2011-06-10 11:44:12 +02:00
|
|
|
|
2011-07-08 15:58:40 +02:00
|
|
|
if args.one then
|
2013-08-25 19:38:01 +02:00
|
|
|
ldoc.style = '!one'
|
2011-07-08 15:58:40 +02:00
|
|
|
end
|
|
|
|
|
2013-08-25 19:38:01 +02:00
|
|
|
local builtin_style, builtin_template = match_bang(args.style),match_bang(args.template)
|
|
|
|
if builtin_style or builtin_template then
|
2011-08-22 12:54:32 +02:00
|
|
|
-- '!' here means 'use built-in templates'
|
|
|
|
local tmpdir = path.join(path.is_windows and os.getenv('TMP') or '/tmp','ldoc')
|
|
|
|
if not path.isdir(tmpdir) then
|
|
|
|
lfs.mkdir(tmpdir)
|
|
|
|
end
|
|
|
|
local function tmpwrite (name)
|
2013-08-25 19:38:01 +02:00
|
|
|
local ok,text = pcall(require,'ldoc.html.'..name:gsub('%.','_'))
|
|
|
|
if not ok then
|
|
|
|
quit("cannot find builtin template "..name..": "..text)
|
|
|
|
end
|
|
|
|
if not utils.writefile(path.join(tmpdir,name),text) then
|
|
|
|
quit("cannot write to temp directory "..tmpdir)
|
|
|
|
end
|
2011-08-22 12:54:32 +02:00
|
|
|
end
|
2013-08-25 19:38:01 +02:00
|
|
|
if builtin_style then
|
|
|
|
if builtin_style ~= '' then
|
|
|
|
ldoc.css = 'ldoc_'..builtin_style..'.css'
|
|
|
|
end
|
2013-07-23 10:40:07 +02:00
|
|
|
tmpwrite(ldoc.css)
|
2011-08-22 12:54:32 +02:00
|
|
|
args.style = tmpdir
|
|
|
|
end
|
2013-08-25 19:38:01 +02:00
|
|
|
if builtin_template then
|
|
|
|
if builtin_template ~= '' then
|
|
|
|
ldoc.templ = 'ldoc_'..builtin_template..'.ltp'
|
|
|
|
end
|
2013-07-23 10:40:07 +02:00
|
|
|
tmpwrite(ldoc.templ)
|
2011-08-22 12:54:32 +02:00
|
|
|
args.template = tmpdir
|
|
|
|
end
|
|
|
|
end
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2011-07-12 14:14:55 +02:00
|
|
|
ldoc.log = print
|
|
|
|
ldoc.kinds = project
|
|
|
|
ldoc.modules = module_list
|
|
|
|
ldoc.title = ldoc.title or args.title
|
|
|
|
ldoc.project = ldoc.project or args.project
|
2012-03-14 10:38:54 +01:00
|
|
|
ldoc.package = args.package:match '%a+' and args.package or nil
|
2011-07-05 18:19:49 +02:00
|
|
|
|
2011-07-12 14:14:55 +02:00
|
|
|
local html = require 'ldoc.html'
|
2011-04-12 19:07:47 +02:00
|
|
|
|
2011-07-12 14:14:55 +02:00
|
|
|
html.generate_output(ldoc, args, project)
|
2011-04-12 19:07:47 +02:00
|
|
|
|
|
|
|
if args.verbose then
|
2011-06-06 18:38:02 +02:00
|
|
|
print 'modules'
|
|
|
|
for k in pairs(module_list.by_name) do print(k) end
|
2011-04-12 19:07:47 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|