summaryrefslogtreecommitdiff
path: root/Data/Libraries/LDoc/ldoc/html.lua
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2021-10-30 11:42:13 +0800
committerchai <chaifix@163.com>2021-10-30 11:42:13 +0800
commit53364ddc2e09362cb17432abf4fb598557554a9f (patch)
tree8d2deafc82aceb13db31938a2aecc70927fc1457 /Data/Libraries/LDoc/ldoc/html.lua
parent42ec7286b2d36a9ba22925f816a17cb1cc2aa5ce (diff)
+ LDoc
Diffstat (limited to 'Data/Libraries/LDoc/ldoc/html.lua')
-rw-r--r--Data/Libraries/LDoc/ldoc/html.lua396
1 files changed, 396 insertions, 0 deletions
diff --git a/Data/Libraries/LDoc/ldoc/html.lua b/Data/Libraries/LDoc/ldoc/html.lua
new file mode 100644
index 0000000..3941802
--- /dev/null
+++ b/Data/Libraries/LDoc/ldoc/html.lua
@@ -0,0 +1,396 @@
+------ generating HTML output ---------
+-- Although this can be generalized for outputting any format, since the template
+-- is language-agnostic, this implementation concentrates on HTML.
+-- This does the actual generation of HTML, and provides support functions in the ldoc
+-- table for the template
+--
+-- A fair amount of the complexity comes from operating in two basic modes; first, where
+-- there is a number of modules (classic LuaDoc) or otherwise, where there is only one
+-- module and the index contains the documentation for that module.
+--
+-- Like LuaDoc, LDoc puts similar kinds of documentation files in their own directories.
+-- So module docs go into 'modules/', scripts go into 'scripts/', and so forth. LDoc
+-- generalizes the idea of these project-level categories and in fact custom categories
+-- can be created (refered to as 'kinds' in the code)
+
+local List = require 'pl.List'
+local utils = require 'pl.utils'
+local path = require 'pl.path'
+local stringx = require 'pl.stringx'
+local template = require 'pl.template'
+local tablex = require 'pl.tablex'
+local OrderedMap = require 'pl.OrderedMap'
+local tools = require 'ldoc.tools'
+local markup = require 'ldoc.markup'
+local prettify = require 'ldoc.prettify'
+local doc = require 'ldoc.doc'
+local unpack = utils.unpack
+local html = {}
+
+
+local quit = utils.quit
+
+local function cleanup_whitespaces(text)
+ local lines = stringx.splitlines(text)
+ for i = 1, #lines do
+ lines[i] = stringx.rstrip(lines[i])
+ end
+ lines[#lines + 1] = "" -- Little trick: file should end with newline
+ return table.concat(lines, "\n")
+end
+
+local function get_module_info(m)
+ local info = OrderedMap()
+ for tag in doc.module_info_tags() do
+ local val = m.tags[tag]
+ if type(val)=='table' then
+ val = table.concat(val,',')
+ end
+ tag = stringx.title(tag)
+ info:set(tag,val)
+ end
+ if #info:keys() > 0 then
+ return info
+ end
+end
+
+local escape_table = { ["'"] = "&apos;", ["\""] = "&quot;", ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" }
+
+function html.generate_output(ldoc, args, project)
+ local check_directory, check_file, writefile = tools.check_directory, tools.check_file, tools.writefile
+ local original_ldoc
+
+ local function save_and_set_ldoc (set)
+ if not set then return end
+ if not original_ldoc then
+ original_ldoc = tablex.copy(ldoc)
+ end
+ for s in set:iter() do
+ local var,val = s:match('([^=]+)=(.+)')
+ local num = tonumber(val)
+ if num then val = num
+ elseif val == 'true' then val = true
+ elseif val == 'false' then val = false
+ end
+ print('setting',var,val)
+ ldoc[var] = val
+ end
+ end
+
+ local function restore_ldoc ()
+ if original_ldoc then
+ ldoc = original_ldoc
+ end
+ end
+
+ function ldoc.escape(str)
+ return (str:gsub("['&<>\"]", escape_table))
+ end
+
+ function ldoc.prettify(str)
+ return prettify.code('lua','usage',str,0,false)
+ end
+
+ -- Item descriptions come from combining the summary and description fields
+ function ldoc.descript(item)
+ return tools.join(' ', item.summary, item.description)
+ end
+
+ function ldoc.module_name (mod)
+ local name = mod.name
+ if args.unqualified and (mod.type == 'module' or mod.type == 'classmod') then -- leave out package
+ name = name:gsub('^.-%.','')
+ elseif mod.type == 'topic' then
+ if mod.display_name then
+ name = mod.display_name
+ else -- leave out md extension
+ name = name:gsub('%..*$','')
+ end
+ end
+ return name
+ end
+
+ -- this generates the internal module/function references
+ function ldoc.href(see)
+ if see.href then -- explict reference, e.g. to Lua manual
+ return see.href
+ elseif doc.Module:class_of(see) then
+ return ldoc.ref_to_module(see)
+ else
+ return ldoc.ref_to_module(see.mod)..'#'..see.name
+ end
+ end
+
+ -- this is either called from the 'root' (index or single module) or
+ -- from the 'modules' etc directories. If we are in one of those directories,
+ -- then linking to another kind is `../kind/name`; to the same kind is just `name`.
+ -- If we are in the root, then it is `kind/name`.
+ function ldoc.ref_to_module (mod)
+ local base = "" -- default: same directory
+ mod = mod or ldoc.module
+ local kind, module = mod.kind, ldoc.module
+ local name = mod.name -- default: name of module
+ if not ldoc.single then
+ if module then -- we are in kind/
+ if module.type ~= type then -- cross ref to ../kind/
+ base = "../"..kind.."/"
+ end
+ else -- we are in root: index
+ base = kind..'/'
+ end
+ else -- single module
+ if mod == ldoc.single then
+ name = ldoc.output
+ if not ldoc.root then base = '../' end
+ elseif ldoc.root then -- ref to other kinds (like examples)
+ base = kind..'/'
+ else
+ if module.type ~= type then -- cross ref to ../kind/
+ base = "../"..kind.."/"
+ end
+ end
+ end
+ return base..name..'.html'
+ end
+
+ function ldoc.include_file (file)
+ local text,_ = utils.readfile(file)
+ if not text then quit("unable to include "..file)
+ else
+ return text
+ end
+ end
+
+-- these references are never from the index...?
+function ldoc.source_ref (fun)
+ local modname = fun.module.name
+ local pack,name = tools.split_dotted_name(modname)
+ if not pack then
+ name = modname
+ end
+ return (ldoc.single and "" or "../").."source/"..name..'.lua.html#'..fun.lineno
+ end
+
+ function ldoc.use_li(ls)
+ if #ls > 1 then return '<li>','</li>' else return '','' end
+ end
+
+ function ldoc.default_display_name(item)
+ -- Project-level items:
+ if doc.project_level(item.type) then
+ return ldoc.module_name(item)
+ end
+ -- Module-level items:
+ local name = item.display_name or item.name
+ if item.type == 'function' or item.type == 'lfunction' then
+ if not ldoc.no_space_before_args then
+ name = name..' '
+ end
+ return name..item.args
+ else
+ return name
+ end
+ end
+
+ function ldoc.display_name(item)
+ if ldoc.custom_display_name_handler then
+ return ldoc.custom_display_name_handler(item, ldoc.default_display_name)
+ else
+ return ldoc.default_display_name(item)
+ end
+ end
+
+ function ldoc.no_spaces(s)
+ s = s:gsub('%s*$','')
+ return (s:gsub('%W','_'))
+ end
+
+ function ldoc.module_typename(m)
+ return doc.presentation_name(m.type)
+ end
+
+ function ldoc.is_list (t)
+ return type(t) == 'table' and t.append
+ end
+
+ function ldoc.strip_header (s)
+ if not s then return s end
+ return s:gsub('^%s*#+%s+','')
+ end
+
+ function ldoc.typename (tp)
+ if not tp or tp == '' or tp:match '^@' then return '' end
+ local optional
+ -- ?<type> is short for ?nil|<type>
+ if tp:match("^%?") and not tp:match '|' then
+ tp = '?|'..tp:sub(2)
+ end
+ local tp2 = tp:match("%?|?(.*)")
+ if tp2 then
+ optional = true
+ tp = tp2
+ end
+
+ local types = {}
+ for name in tp:gmatch("[^|]+") do
+ local sym = name:match '([%w%.%:]+)'
+ local ref,_ = markup.process_reference(sym,true)
+ if ref then
+ if ref.label and sym == name then
+ name = ref.label
+ end
+ types[#types+1] = ('<a class="type" href="%s">%s</a>'):format(ldoc.href(ref),name)
+ else
+ types[#types+1] = '<span class="type">'..name..'</span>'
+ end
+ end
+ local names = table.concat(types, ", ", 1, math.max(#types-1, 1))
+ if #types > 1 then names = names.." or "..types[#types] end
+ if optional then
+ if names ~= '' then
+ if #types == 1 then names = "optional "..names end
+ else
+ names = "optional"
+ end
+ end
+ return names
+ end
+
+ -- the somewhat tangled logic that controls whether a type appears in the
+ -- navigation sidebar. (At least it's no longer in the template ;))
+ function ldoc.allowed_in_contents(type,module)
+ local allowed = true
+ if ldoc.kinds_allowed then
+ allowed = ldoc.kinds_allowed[type]
+ elseif ldoc.prettify_files and type == 'file' then
+ allowed = ldoc.prettify_files == 'show' or (module and module.type == 'file')
+ end
+ return allowed
+ end
+
+ local function set_charset (ldoc,m)
+ m = m or ldoc.module
+ ldoc.doc_charset = (m and m.tags.charset) or ldoc.charset
+ end
+
+ local module_template,_ = utils.readfile (path.join(args.template,ldoc.templ))
+ if not module_template then
+ quit("template not found at '"..args.template.."' Use -l to specify directory containing ldoc.ltp")
+ end
+
+ -- Runs a template on a module to generate HTML page.
+ local function templatize(template_str, ldoc, module)
+ local out, err = template.substitute(template_str, {
+ ldoc = ldoc,
+ module = module,
+ _escape = ldoc.template_escape
+ })
+ if not out then
+ quit(("template failed for %s: %s"):format(
+ module and module.name or ldoc.output or "index",
+ err))
+ end
+ if ldoc.postprocess_html then
+ out = ldoc.postprocess_html(out, module)
+ end
+ return cleanup_whitespaces(out)
+ end
+
+ local css, custom_css = ldoc.css, ldoc.custom_css
+ ldoc.output = args.output
+ ldoc.ipairs = ipairs
+ ldoc.pairs = pairs
+ ldoc.print = print
+
+ -- Bang out the index.
+ -- in single mode there is one module and the 'index' is the
+ -- documentation for that module.
+ ldoc.module = ldoc.single
+ if ldoc.single and args.one then
+ ldoc.kinds_allowed = {module = true, topic = true}
+ ldoc.one = true
+ end
+ ldoc.root = true
+ if ldoc.module then
+ ldoc.module.info = get_module_info(ldoc.module)
+ ldoc.module.ldoc = ldoc
+ save_and_set_ldoc(ldoc.module.tags.set)
+ end
+ set_charset(ldoc)
+ local out = templatize(module_template, ldoc, ldoc.module)
+ ldoc.root = false
+ restore_ldoc()
+
+ check_directory(args.dir) -- make sure output directory is ok
+
+ -- project icon
+ if ldoc.icon then
+ local dir_data = args.dir .. '/data'
+ if not path.isdir(dir_data) then
+ -- luacheck: push ignore lfs
+ lfs.mkdir(dir_data)
+ -- luacheck: pop
+ end
+ local file = require 'pl.file'
+ file.copy(ldoc.icon, dir_data)
+ end
+
+ args.dir = args.dir .. path.sep
+
+ if css then -- has CSS been copied?
+ check_file(args.dir..css, path.join(args.style,css))
+ end
+
+ if custom_css then -- has custom CSS been copied?
+ check_file(args.dir..custom_css, custom_css)
+ end
+
+ -- write out the module index
+ out = cleanup_whitespaces(out)
+ writefile(args.dir..args.output..args.ext,out)
+
+ -- in single mode, we exclude any modules since the module has been done;
+ -- ext step is then only for putting out any examples or topics
+ local mods = List()
+ for kind, modules in project() do
+ local lkind = kind:lower()
+ if not ldoc.single or ldoc.single and lkind ~= 'modules' then
+ mods:append {kind, lkind, modules}
+ end
+ end
+
+ -- write out the per-module documentation
+ -- note that we reset the internal ordering of the 'kinds' so that
+ -- e.g. when reading a topic the other Topics will be listed first.
+ if css then
+ ldoc.css = '../'..css
+ end
+ if custom_css then
+ ldoc.custom_css = '../'..custom_css
+ end
+ for m in mods:iter() do
+ local kind, lkind, modules = unpack(m)
+ check_directory(args.dir..lkind)
+ project:put_kind_first(kind)
+ for m in modules() do
+ ldoc.module = m
+ ldoc.body = m.body
+ m.ldoc = ldoc
+ if m.tags.set then
+ save_and_set_ldoc(m.tags.set)
+ end
+ set_charset(ldoc)
+ m.info = get_module_info(m)
+ if ldoc.body and m.postprocess then
+ ldoc.body = m.postprocess(ldoc.body)
+ end
+ local out = templatize(module_template, ldoc, m)
+ writefile(args.dir..lkind..'/'..m.name..args.ext,out)
+ restore_ldoc()
+ end
+ end
+ if not args.quiet then print('output written to '..tools.abspath(args.dir)) end
+end
+
+return html
+