diff options
author | chai <chaifix@163.com> | 2021-10-30 11:42:13 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2021-10-30 11:42:13 +0800 |
commit | 53364ddc2e09362cb17432abf4fb598557554a9f (patch) | |
tree | 8d2deafc82aceb13db31938a2aecc70927fc1457 /Data/Libraries/LDoc/ldoc/html.lua | |
parent | 42ec7286b2d36a9ba22925f816a17cb1cc2aa5ce (diff) |
+ LDoc
Diffstat (limited to 'Data/Libraries/LDoc/ldoc/html.lua')
-rw-r--r-- | Data/Libraries/LDoc/ldoc/html.lua | 396 |
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 = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" } + +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 + |