From d176dedcfcc58800766b92f4569d67cc1c95fbac Mon Sep 17 00:00:00 2001 From: steve donovan Date: Tue, 26 Mar 2013 11:53:42 +0200 Subject: [PATCH 001/106] bump version to 1.3.8 in usage; ignore empty docstrings (Dirk bug) --- ldoc.lua | 4 ++-- ldoc/parse.lua | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index fb209abf..7cd4e78e 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -7,7 +7,7 @@ -- -- C/C++ support for Lua extensions is provided. -- --- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.3.0.zip) +-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.3.8.zip) -- -- [Github Page](https://github.com/stevedonovan/ldoc) -- @@ -35,7 +35,7 @@ app.require_here() --- @usage local usage = [[ -ldoc, a documentation generator for Lua, vs 1.3.1 +ldoc, a documentation generator for Lua, vs 1.3.8 -d,--dir (default docs) output directory -o,--output (default 'index') output name -v,--verbose verbose diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 721e75b4..18293fd4 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -245,7 +245,11 @@ local function parse_file(fname, lang, package, args) local item_follows, tags, is_local, case if ldoc_comment then comment = table.concat(comment) - + if comment:match '^%s*$' then + ldoc_comment = nil + end + end + if ldoc_comment then if first_comment then first_comment = false else From 158aa9ff147a903c36da3d16cbdc0d688c09dcd9 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Wed, 27 Mar 2013 15:50:37 +0200 Subject: [PATCH 002/106] can override utf-8 with either 'charset=' or per-module with @charset --- ldoc.lua | 4 ++-- ldoc/doc.lua | 2 +- ldoc/html.lua | 7 +++++++ ldoc/html/ldoc_ltp.lua | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 7cd4e78e..b966598a 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -121,7 +121,7 @@ local file_types = { ------- ldoc external API ------------ -- the ldoc table represents the API available in `config.ld`. -local ldoc = {} +local ldoc = { charset = 'UTF-8' } local add_language_extension local function override (field) @@ -183,7 +183,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', 'pretty', + 'one','style','template','description','examples', 'pretty', 'charset', 'readme','all','manual_url', 'ignore', 'colon','boilerplate','merge', 'wrap', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', } diff --git a/ldoc/doc.lua b/ldoc/doc.lua index c2f239b4..e91e75f7 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -24,7 +24,7 @@ local known_tags = { param = 'M', see = 'M', usage = 'ML', ['return'] = 'M', field = 'M', author='M'; 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', + fixme = 'S', todo = 'S', warning = 'S', raise = 'S', charset = 'S', ['local'] = 'N', export = 'N', private = 'N', constructor = 'N', static = 'N'; -- project-level module = 'T', script = 'T', example = 'T', topic = 'T', submodule='T', diff --git a/ldoc/html.lua b/ldoc/html.lua index f584f807..7840d7d3 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -167,6 +167,11 @@ function html.generate_output(ldoc, args, project) return names end + local function set_charset (ldoc,m) + m = m or ldoc.module + ldoc.doc_charset = m.tags.charset or ldoc.charset + end + local module_template,err = 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") @@ -188,6 +193,7 @@ function html.generate_output(ldoc, args, project) if ldoc.module then ldoc.module.info = get_module_info(ldoc.module) end + set_charset(ldoc) local out,err = template.substitute(module_template,{ ldoc = ldoc, module = ldoc.module, @@ -226,6 +232,7 @@ function html.generate_output(ldoc, args, project) for m in modules() do ldoc.module = m ldoc.body = m.body + set_charset(ldoc) m.info = get_module_info(m) if ldoc.body and m.postprocess then ldoc.body = m.postprocess(ldoc.body) diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 60f2d729..9717c792 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -2,7 +2,7 @@ return [==[ - + $(ldoc.title) From 897061ac17542afa0e91cfe1b8727f1f22726239 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 28 Mar 2013 13:06:42 +0200 Subject: [PATCH 003/106] squashed trailing space problem with section titles; allowing numbers in links --- ldoc/doc.lua | 2 +- ldoc/html.lua | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index e91e75f7..d964bc0f 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -346,7 +346,7 @@ end -- is not empty. function File:add_document_section(title) - local section = title:gsub('%A','_') + local section = title:gsub('%W','_') self:new_item { name = section, class = 'section', diff --git a/ldoc/html.lua b/ldoc/html.lua index 7840d7d3..96843845 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -122,7 +122,10 @@ function html.generate_output(ldoc, args, project) else return name end end - function ldoc.no_spaces(s) return (s:gsub('%A','_')) end + function ldoc.no_spaces(s) + s = s:gsub('%s*$','') + return (s:gsub('%W','_')) + end function ldoc.titlecase(s) return (s:gsub('(%a)(%a*)',function(f,r) From 619d8e1710418adad67345a68d51f6f6f5a426b5 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 28 Mar 2013 13:28:05 +0200 Subject: [PATCH 004/106] -O for one col layout, -S for simple (no params,ret or summary); one col switches off function list. Bumped to 1.3.8 --- ldoc.lua | 12 +++++++++--- ldoc/html.lua | 1 + ldoc/html/ldoc_ltp.lua | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index b966598a..837e5ba6 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -7,7 +7,7 @@ -- -- C/C++ support for Lua extensions is provided. -- --- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.3.8.zip) +-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.3.9.zip) -- -- [Github Page](https://github.com/stevedonovan/ldoc) -- @@ -35,7 +35,7 @@ app.require_here() --- @usage local usage = [[ -ldoc, a documentation generator for Lua, vs 1.3.8 +ldoc, a documentation generator for Lua, vs 1.3.9 -d,--dir (default docs) output directory -o,--output (default 'index') output name -v,--verbose verbose @@ -44,7 +44,6 @@ ldoc, a documentation generator for Lua, vs 1.3.8 -m,--module module docs as text -s,--style (default !) directory for style sheet (ldoc.css) -l,--template (default !) directory for template (ldoc.ltp) - -1,--one use one-column output layout -p,--project (default ldoc) project name -t,--title (default Reference) page title -f,--format (default plain) formatting - can be markdown, discount or plain @@ -56,6 +55,8 @@ ldoc, a documentation generator for Lua, vs 1.3.8 -C,--colon use colon style -B,--boilerplate ignore first comment in source files -M,--merge allow module merging + -S,--simple no return or params, no summary + -O,--one one-column output layout --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 @@ -437,6 +438,11 @@ if type(ldoc.examples) == 'table' then end) end +if args.simple then + ldoc.no_return_or_parms=true + ldoc.no_summary=true +end + ldoc.readme = ldoc.readme or ldoc.topics if type(ldoc.readme) == 'string' then ldoc.readme = {ldoc.readme} diff --git a/ldoc/html.lua b/ldoc/html.lua index 96843845..162bc9d8 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -191,6 +191,7 @@ function html.generate_output(ldoc, args, project) 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 diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 9717c792..ddbcd368 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -50,7 +50,7 @@ return [==[ # end -# if ldoc.no_summary and module then -- bang out the functions on the side +# if ldoc.no_summary and module and not ldoc.one then -- bang out the functions on the side # for kind, items in module.kinds() do

$(kind)

    From 97538e33b965d9157cca3f1b2d626df4976c4715 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 28 Mar 2013 13:36:46 +0200 Subject: [PATCH 005/106] bump to 1.3.10 --- ldoc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldoc.lua b/ldoc.lua index 837e5ba6..2e9be7bd 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -35,7 +35,7 @@ app.require_here() --- @usage local usage = [[ -ldoc, a documentation generator for Lua, vs 1.3.9 +ldoc, a documentation generator for Lua, vs 1.3.10 -d,--dir (default docs) output directory -o,--output (default 'index') output name -v,--verbose verbose From 0ee96935a39932926a4fadb35f65b12272935f59 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Wed, 3 Apr 2013 12:20:54 +0200 Subject: [PATCH 006/106] charset functionality was borked in general case --- ldoc/html.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldoc/html.lua b/ldoc/html.lua index 162bc9d8..418ccf75 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -172,7 +172,7 @@ function html.generate_output(ldoc, args, project) local function set_charset (ldoc,m) m = m or ldoc.module - ldoc.doc_charset = m.tags.charset or ldoc.charset + ldoc.doc_charset = (m and m.tags.charset) or ldoc.charset end local module_template,err = utils.readfile (path.join(args.template,ldoc.templ)) From 6d22818ec93c9aba6bf40f3b0ba5a5705ad1b842 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 11 Apr 2013 15:37:01 +0200 Subject: [PATCH 007/106] allow -c to be used with explicit file --- ldoc.lua | 7 +++++++ tests/styles/config/config.ld | 2 ++ 2 files changed, 9 insertions(+) create mode 100644 tests/styles/config/config.ld diff --git a/ldoc.lua b/ldoc.lua index 2e9be7bd..ae5c8aa1 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -284,6 +284,13 @@ if args.file == '.' then args.file = abspath(args.file) end else + -- 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 args.file = abspath(args.file) end diff --git a/tests/styles/config/config.ld b/tests/styles/config/config.ld new file mode 100644 index 00000000..239ae21d --- /dev/null +++ b/tests/styles/config/config.ld @@ -0,0 +1,2 @@ +no_return_or_parms=true +no_summary=true From 42429e694cbf2370e5bc41ae6972a6d358ef94b1 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 13 Apr 2013 15:44:27 +0200 Subject: [PATCH 008/106] file={'.'} in config.d caused module name failure; docs->doc --- config.ld | 14 +++++++------- doc/config.ld | 7 +++++++ {docs => doc}/doc.md | 0 ldoc.lua | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 doc/config.ld rename {docs => doc}/doc.md (100%) diff --git a/config.ld b/config.ld index 4fb78c9e..796e3cf0 100644 --- a/config.ld +++ b/config.ld @@ -1,7 +1,7 @@ -project='LDoc' -title='LDoc documentation' -description='A Lua documentation tool' -format='discount' -file='ldoc.lua' -dir='out' -readme='docs/doc.md' +project='LDoc' +title='LDoc documentation' +description='A Lua documentation tool' +format='discount' +file='ldoc.lua' +dir='out' +readme='doc/doc.md' diff --git a/doc/config.ld b/doc/config.ld new file mode 100644 index 00000000..aacd86ac --- /dev/null +++ b/doc/config.ld @@ -0,0 +1,7 @@ +project='LDoc' +title='LDoc documentation' +description='A Lua documentation tool' +format='discount' +file='../ldoc.lua' +dir='../out' +readme='doc.md' diff --git a/docs/doc.md b/doc/doc.md similarity index 100% rename from docs/doc.md rename to doc/doc.md diff --git a/ldoc.lua b/ldoc.lua index ae5c8aa1..12dd5337 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -301,6 +301,7 @@ end if type(source_dir) == 'string' and path.isfile(source_dir) then source_dir = path.splitpath(source_dir) end +source_dir = source_dir:gsub('[/\\]%.$','') ---------- specifying the package for inferring module names -------- -- If you use module(...), or forget to explicitly use @module, then From 97af82c53d5f0e353ceae7207df643d8b3cb2825 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 13 Apr 2013 16:15:54 +0200 Subject: [PATCH 009/106] bumped to 1.3.11 --- ldoc.lua | 2 +- ldoc/html/ldoc_ltp.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 12dd5337..77a28097 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -35,7 +35,7 @@ app.require_here() --- @usage local usage = [[ -ldoc, a documentation generator for Lua, vs 1.3.10 +ldoc, a documentation generator for Lua, vs 1.3.11 -d,--dir (default docs) output directory -o,--output (default 'index') output name -v,--verbose verbose diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index ddbcd368..74ce34da 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -249,7 +249,7 @@ return [==[
    -generated by LDoc 1.3 +generated by LDoc 1.3.11
    From 68cd576bf4312b4d8c3ca954519de862d52a7192 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Tue, 16 Apr 2013 10:11:05 +0200 Subject: [PATCH 010/106] module deduction can blow up; UTF BOM ignored --- ldoc/doc.lua | 1 + ldoc/lexer.lua | 3 +++ ldoc/parse.lua | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index d964bc0f..2c052cfc 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -410,6 +410,7 @@ function Item:set_tag (tag,value) value = value[1] modifiers = value.modifiers end + if value == nil then self:error("Tag without value: "..tag) end local id, rest = tools.extract_identifier(value) self.tags[tag] = id self:add_to_description(rest) diff --git a/ldoc/lexer.lua b/ldoc/lexer.lua index 8f6bbe09..119ef5c5 100644 --- a/ldoc/lexer.lua +++ b/ldoc/lexer.lua @@ -172,6 +172,9 @@ function lexer.scan (s,matches,filter,options) if file then s = file:read() if not s then return nil end -- empty file + if s:match '^\239\187' then -- UTF-8 BOM Abomination + s = s:sub(4) + end s = s ..'\n' end local sz = #s diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 18293fd4..53c7e427 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -211,7 +211,7 @@ local function parse_file(fname, lang, package, args) else mod,t,v = lang:parse_module_call(tok,t,v) if mod ~= '...' then - add_module({summary='(no description)'},mod,true) + add_module(Tags.new{summary='(no description)'},mod,true) first_comment = false module_found = true end From 167a4595a5ca6d4e8560d68a38cc28a1b7faa873 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Wed, 17 Apr 2013 12:08:11 +0200 Subject: [PATCH 011/106] built-in markdown crack down on locals; still very slow --- ldoc/markdown.lua | 245 ++++++++++++++++++++++++---------------------- 1 file changed, 126 insertions(+), 119 deletions(-) diff --git a/ldoc/markdown.lua b/ldoc/markdown.lua index 5181dd8f..bfe3a1bf 100644 --- a/ldoc/markdown.lua +++ b/ldoc/markdown.lua @@ -5,7 +5,7 @@ -**Author:** Niklas Frykholm, +**Author:** Niklas Frykholm, **Date:** 31 May 2008 This is an implementation of the popular text markup language Markdown in pure Lua. @@ -47,10 +47,10 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -to whom the Software is furnished to do so, subject to the following conditions: +to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies -or substantial portions of the Software. +or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -130,31 +130,33 @@ setfenv(1, M) -- Locks table t from changes, writes an error if someone attempts to change the table. -- This is useful for detecting variables that have "accidently" been made global. Something -- I tend to do all too much. -function lock(t) - function lock_new_index(t, k, v) +function M.lock(t) + local function lock_new_index(t, k, v) error("module has been locked -- " .. k .. " must be declared local", 2) end local mt = {__newindex = lock_new_index} - if getmetatable(t) then mt.__index = getmetatable(t).__index end + if getmetatable(t) then + mt.__index = getmetatable(t).__index + end setmetatable(t, mt) end -- Returns the result of mapping the values in table t through the function f -function map(t, f) +local function map(t, f) local out = {} for k,v in pairs(t) do out[k] = f(v,k) end return out end -- The identity function, useful as a placeholder. -function identity(text) return text end +local function identity(text) return text end -- Functional style if statement. (NOTE: no short circuit evaluation) -function iff(t, a, b) if t then return a else return b end end +local function iff(t, a, b) if t then return a else return b end end -- Splits the text into an array of separate lines. -function split(text, sep) +local function split(text, sep) sep = sep or "\n" local lines = {} local pos = 1 @@ -168,7 +170,7 @@ function split(text, sep) end -- Converts tabs to spaces -function detab(text) +local function detab(text) local tab_width = 4 local function rep(match) local spaces = -match:len() @@ -180,7 +182,7 @@ function detab(text) end -- Applies string.find for every pattern in the list and returns the first match -function find_first(s, patterns, index) +local function find_first(s, patterns, index) local res = {} for _,p in ipairs(patterns) do local match = {s:find(p, index)} @@ -192,7 +194,7 @@ end -- If a replacement array is specified, the range [start, stop] in the array is replaced -- with the replacement array and the resulting array is returned. Without a replacement -- array the section of the array between start and stop is returned. -function splice(array, start, stop, replacement) +local function splice(array, start, stop, replacement) if replacement then local n = stop - start + 1 while n > 0 do @@ -213,7 +215,7 @@ function splice(array, start, stop, replacement) end -- Outdents the text one step. -function outdent(text) +local function outdent(text) text = "\n" .. text text = text:gsub("\n ? ? ?", "\n") text = text:sub(2) @@ -221,15 +223,15 @@ function outdent(text) end -- Indents the text one step. -function indent(text) +local function indent(text) text = text:gsub("\n", "\n ") return text end --- Does a simple tokenization of html data. Returns the data as a list of tokens. +-- Does a simple tokenization of html data. Returns the data as a list of tokens. -- Each token is a table with a type field (which is either "tag" or "text") and -- a text field (which contains the original token data). -function tokenize_html(html) +local function tokenize_html(html) local tokens = {} local pos = 1 while true do @@ -239,7 +241,7 @@ function tokenize_html(html) break end if start ~= pos then table.insert(tokens, {type="text", text = html:sub(pos, start-1)}) end - + local _, stop if html:match("^", start) @@ -249,7 +251,7 @@ function tokenize_html(html) _,stop = html:find("%b<>", start) end if not stop then - -- error("Could not match html tag " .. html:sub(start,start+30)) + -- error("Could not match html tag " .. html:sub(start,start+30)) table.insert(tokens, {type="text", text=html:sub(start, start)}) pos = start + 1 else @@ -272,27 +274,27 @@ end local HASH = { -- Has the hash been inited. inited = false, - + -- The unique string prepended to all hash values. This is to ensure -- that hash values do not accidently coincide with an actual existing -- string in the document. identifier = "", - + -- Counter that counts up for each new hash instance. counter = 0, - + -- Hash table. table = {} } -- Inits hashing. Creates a hash_identifier that doesn't occur anywhere -- in the text. -function init_hash(text) +local function init_hash(text) HASH.inited = true HASH.identifier = "" HASH.counter = 0 HASH.table = {} - + local s = "HASH" local counter = 0 local id @@ -305,7 +307,7 @@ function init_hash(text) end -- Returns the hashed value for s. -function hash(s) +local function hash(s) assert(HASH.inited) if not HASH.table[s] then HASH.counter = HASH.counter + 1 @@ -320,7 +322,7 @@ end ---------------------------------------------------------------------- -- The protection module is used to "protect" parts of a document --- so that they are not modified by subsequent processing steps. +-- so that they are not modified by subsequent processing steps. -- Protected parts are saved in a table for later unprotection -- Protection data @@ -342,18 +344,18 @@ local PD = { -- Nested data. -- -- -function block_pattern(tag) +local function block_pattern(tag) return "\n<" .. tag .. ".-\n[ \t]*\n" end -- Pattern for matching a block tag that begins and ends with a newline -function line_pattern(tag) +local function line_pattern(tag) return "\n<" .. tag .. ".-[ \t]*\n" end -- Protects the range of characters from start to stop in the text and -- returns the protected string. -function protect_range(text, start, stop) +local function protect_range(text, start, stop) local s = text:sub(start, stop) local h = hash(s) PD.blocks[h] = s @@ -363,7 +365,7 @@ end -- Protect every part of the text that matches any of the patterns. The first -- matching pattern is protected first, etc. -function protect_matches(text, patterns) +local function protect_matches(text, patterns) while true do local start, stop = find_first(text, patterns) if not start then break end @@ -373,7 +375,7 @@ function protect_matches(text, patterns) end -- Protects blocklevel tags in the specified text -function protect(text) +local function protect(text) -- First protect potentially nested block tags text = protect_matches(text, map(PD.tags, block_pattern)) -- Then protect block tags at the line level. @@ -385,12 +387,12 @@ function protect(text) end -- Returns true if the string s is a hash resulting from protection -function is_protected(s) +local function is_protected(s) return PD.blocks[s] end -- Unprotects the specified text by expanding all the nonces -function unprotect(text) +local function unprotect(text) for k,v in pairs(PD.blocks) do v = v:gsub("%%", "%%%%") text = text:gsub(k, v) @@ -410,22 +412,22 @@ end -- Returns true if the line is a ruler of (char) characters. -- The line must contain at least three char characters and contain only spaces and -- char characters. -function is_ruler_of(line, char) +local function is_ruler_of(line, char) if not line:match("^[ %" .. char .. "]*$") then return false end if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end return true end -- Identifies the block level formatting present in the line -function classify(line) +local function classify(line) local info = {line = line, text = line} - + if line:match("^ ") then info.type = "indented" info.outdented = line:sub(5) return info end - + for _,c in ipairs({'*', '-', '_', '='}) do if is_ruler_of(line, c) then info.type = "ruler" @@ -433,12 +435,12 @@ function classify(line) return info end end - + if line == "" then info.type = "blank" return info end - + if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") info.type = "header" @@ -446,7 +448,7 @@ function classify(line) info.text = m2 return info end - + if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") info.type = "list_item" @@ -455,7 +457,7 @@ function classify(line) info.text = text return info end - + if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") info.type = "list_item" @@ -464,29 +466,29 @@ function classify(line) info.text= text return info end - + if line:match("^>[ \t]?(.*)") then info.type = "blockquote" info.text = line:match("^>[ \t]?(.*)") return info end - + if is_protected(line) then info.type = "raw" info.html = unprotect(line) return info end - + info.type = "normal" return info end -- Find headers constisting of a normal line followed by a ruler and converts them to -- header entries. -function headers(array) +local function headers(array) local i = 1 while i <= #array - 1 do - if array[i].type == "normal" and array[i+1].type == "ruler" and + if array[i].type == "normal" and array[i+1].type == "ruler" and (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then local info = {line = array[i].line} info.text = info.line @@ -500,8 +502,10 @@ function headers(array) return array end +local block_transform, blocks_to_html, encode_code, span_transform, encode_backslash_escapes + -- Find list blocks and convert them to protected data blocks -function lists(array, sublist) +local function lists(array, sublist) local function process_list(arr) local function any_blanks(arr) for i = 1, #arr do @@ -509,7 +513,7 @@ function lists(array, sublist) end return false end - + local function split_list_items(arr) local acc = {arr[1]} local res = {} @@ -524,12 +528,12 @@ function lists(array, sublist) table.insert(res, acc) return res end - + local function process_list_item(lines, block) while lines[#lines].type == "blank" do table.remove(lines) end - + local itemtext = lines[1].text for i=2,#lines do itemtext = itemtext .. "\n" .. outdent(lines[i].line) @@ -548,7 +552,7 @@ function lists(array, sublist) return "
  • " .. itemtext .. "
  • " end end - + local block_list = any_blanks(arr) local items = split_list_items(arr) local out = "" @@ -561,7 +565,7 @@ function lists(array, sublist) return "
      \n" .. out .. "
    " end end - + -- Finds the range of lines composing the first list in the array. A list -- starts with (^ list_item) or (blank list_item) and ends with -- (blank* $) or (blank normal). @@ -586,7 +590,7 @@ function lists(array, sublist) local function find_list_end(array, start) local pos = #array for i = start, #array-1 do - if array[i].type == "blank" and array[i+1].type ~= "list_item" + if array[i].type == "blank" and array[i+1].type ~= "list_item" and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then pos = i-1 break @@ -597,12 +601,12 @@ function lists(array, sublist) end return pos end - + local start = find_list_start(array, sublist) if not start then return nil end return start, find_list_end(array, start) end - + while true do local start, stop = find_list(array, sublist) if not start then break end @@ -614,17 +618,17 @@ function lists(array, sublist) } array = splice(array, start, stop, {info}) end - + -- Convert any remaining list items to normal for _,line in ipairs(array) do if line.type == "list_item" then line.type = "normal" end end - + return array end -- Find and convert blockquote markers. -function blockquotes(lines) +local function blockquotes(lines) local function find_blockquote(lines) local start for i,line in ipairs(lines) do @@ -634,7 +638,7 @@ function blockquotes(lines) end end if not start then return nil end - + local stop = #lines for i = start+1, #lines do if lines[i].type == "blank" or lines[i].type == "blockquote" then @@ -647,7 +651,7 @@ function blockquotes(lines) while lines[stop].type == "blank" do stop = stop - 1 end return start, stop end - + local function process_blockquote(lines) local raw = lines[1].text for i = 2,#lines do @@ -658,7 +662,7 @@ function blockquotes(lines) return "
    \n " .. bt .. "\n
    " end - + while true do local start, stop = find_blockquote(lines) if not start then break end @@ -674,14 +678,14 @@ function blockquotes(lines) end -- Find and convert codeblocks. -function codeblocks(lines) +local function codeblocks(lines) local function find_codeblock(lines) local start for i,line in ipairs(lines) do if line.type == "indented" then start = i break end end if not start then return nil end - + local stop = #lines for i = start+1, #lines do if lines[i].type ~= "indented" and lines[i].type ~= "blank" then @@ -692,7 +696,7 @@ function codeblocks(lines) while lines[stop].type == "blank" do stop = stop - 1 end return start, stop end - + local function process_codeblock(lines) local raw = detab(encode_code(outdent(lines[1].line))) for i = 2,#lines do @@ -700,7 +704,7 @@ function codeblocks(lines) end return "
    " .. raw .. "\n
    " end - + while true do local start, stop = find_codeblock(lines) if not start then break end @@ -727,12 +731,12 @@ function blocks_to_html(lines, no_paragraphs) table.insert(out, line.html) elseif line.type == "normal" then local s = line.line - + while i+1 <= #lines and lines[i+1].type == "normal" do i = i + 1 s = s .. "\n" .. lines[i].line end - + if no_paragraphs then table.insert(out, span_transform(s)) else @@ -764,7 +768,7 @@ end -- Debug function for printing a line array to see the result -- of partial transforms. -function print_lines(lines) +local function print_lines(lines) for i, line in ipairs(lines) do print(i, line.type, line.text or line.line) end @@ -778,10 +782,10 @@ end -- These characters may need to be escaped because they have a special -- meaning in markdown. -escape_chars = "'\\`*_{}[]()>#+-.!'" -escape_table = {} +local escape_chars = "'\\`*_{}[]()>#+-.!'" +local escape_table = {} -function init_escape_table() +local function init_escape_table() escape_table = {} for i = 1,#escape_chars do local c = escape_chars:sub(i,i) @@ -790,17 +794,17 @@ function init_escape_table() end -- Adds a new escape to the escape table. -function add_escape(text) +local function add_escape(text) if not escape_table[text] then escape_table[text] = hash(text) end return escape_table[text] -end +end -- Escape characters that should not be disturbed by markdown. -function escape_special_chars(text) +local function escape_special_chars(text) local tokens = tokenize_html(text) - + local out = "" for _, token in ipairs(tokens) do local t = token.text @@ -826,7 +830,7 @@ function encode_backslash_escapes(t) end -- Unescape characters that have been encoded. -function unescape_special_chars(t) +local function unescape_special_chars(t) local tin = t for k,v in pairs(escape_table) do k = k:gsub("%%", "%%%%") @@ -850,7 +854,7 @@ function encode_code(s) end -- Handle backtick blocks. -function code_spans(s) +local function code_spans(s) s = s:gsub("\\\\", escape_table["\\"]) s = s:gsub("\\`", escape_table["`"]) @@ -880,7 +884,7 @@ function code_spans(s) end -- Encode alt text... enodes &, and ". -function encode_alt(s) +local function encode_alt(s) if not s then return s end s = s:gsub('&', '&') s = s:gsub('"', '"') @@ -888,8 +892,10 @@ function encode_alt(s) return s end +local link_database + -- Handle image references -function images(text) +local function images(text) local function reference_link(alt, id) alt = encode_alt(alt:match("%b[]"):sub(2,-2)) id = id:match("%[(.*)%]"):lower() @@ -902,7 +908,7 @@ function images(text) if title then title = " title=\"" .. title .. "\"" else title = "" end return add_escape ('' .. alt .. '") end - + local function inline_link(alt, link) alt = encode_alt(alt:match("%b[]"):sub(2,-2)) local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") @@ -915,14 +921,14 @@ function images(text) return add_escape('' .. alt .. '') end end - + text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) text = text:gsub("!(%b[])(%b())", inline_link) return text end -- Handle anchor references -function anchors(text) +local function anchors(text) local function reference_link(text, id) text = text:match("%b[]"):sub(2,-2) id = id:match("%b[]"):sub(2,-2):lower() @@ -935,7 +941,7 @@ function anchors(text) if title then title = " title=\"" .. title .. "\"" else title = "" end return add_escape("") .. text .. add_escape("") end - + local function inline_link(text, link) text = text:match("%b[]"):sub(2,-2) local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") @@ -948,14 +954,14 @@ function anchors(text) return add_escape("") .. text .. add_escape("") end end - + text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) text = text:gsub("(%b[])(%b())", inline_link) return text end -- Handle auto links, i.e. . -function auto_links(text) +local function auto_links(text) local function link(s) return add_escape("") .. s .. "" end @@ -969,21 +975,21 @@ function auto_links(text) local plain = {code = function(c) return c end, count = 0, rate = 0.1} local codes = {hex, dec, plain} local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end - + local out = "" for i = 1,s:len() do for _,code in ipairs(codes) do code.count = code.count + code.rate end if codes[1].count < codes[2].count then swap(codes,1,2) end if codes[2].count < codes[3].count then swap(codes,2,3) end if codes[1].count < codes[2].count then swap(codes,1,2) end - + local code = codes[1] local c = s:sub(i,i) -- Force encoding of "@" to make email address more invisible. if c == "@" and code == plain then code = codes[2] end out = out .. code.code(c) code.count = code.count - 1 - end + end return out end local function mail(s) @@ -995,7 +1001,7 @@ function auto_links(text) -- links text = text:gsub("<(https?:[^'\">%s]+)>", link) text = text:gsub("<(ftp:[^'\">%s]+)>", link) - + -- mail text = text:gsub("%s]+)>", mail) text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) @@ -1004,7 +1010,7 @@ end -- Encode free standing amps (&) and angles (<)... note that this does not -- encode free >. -function amps_and_angles(s) +local function amps_and_angles(s) -- encode amps not part of &..; expression local pos = 1 while true do @@ -1019,17 +1025,17 @@ function amps_and_angles(s) pos = amp+1 end end - + -- encode naked <'s s = s:gsub("<([^a-zA-Z/?$!])", "<%1") s = s:gsub("<$", "<") - + -- what about >, nothing done in the original markdown source to handle them return s end -- Handles emphasis markers (* and _) in the text. -function emphasis(text) +local function emphasis(text) for _, s in ipairs {"%*%*", "%_%_"} do text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "%1") text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "%1") @@ -1044,7 +1050,7 @@ function emphasis(text) end -- Handles line break markers in the text. -function line_breaks(text) +local function line_breaks(text) return text:gsub(" +\n", "
    \n") end @@ -1067,28 +1073,28 @@ end -- Cleanup the text by normalizing some possible variations to make further -- processing easier. -function cleanup(text) +local function cleanup(text) -- Standardize line endings text = text:gsub("\r\n", "\n") -- DOS to UNIX text = text:gsub("\r", "\n") -- Mac to UNIX - + -- Convert all tabs to spaces text = detab(text) - + -- Strip lines with only spaces and tabs while true do local subs text, subs = text:gsub("\n[ \t]+\n", "\n\n") if subs == 0 then break end end - + return "\n" .. text .. "\n" end -- Strips link definitions from the text and stores the data in a lookup table. -function strip_link_definitions(text) +local function strip_link_definitions(text) local linkdb = {} - + local function link_def(id, url, title) id = id:match("%[(.+)%]"):lower() linkdb[id] = linkdb[id] or {} @@ -1101,7 +1107,7 @@ function strip_link_definitions(text) local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" - + text = text:gsub(def_title1, link_def) text = text:gsub(def_title2, link_def) text = text:gsub(def_title3, link_def) @@ -1112,10 +1118,10 @@ end link_database = {} -- Main markdown processing function -function markdown(text) +local function markdown(text) init_hash(text) init_escape_table() - + text = cleanup(text) text = protect(text) text, link_database = strip_link_definitions(text) @@ -1132,7 +1138,7 @@ setfenv(1, _G) M.lock(M) -- Expose markdown function to the world -markdown = M.markdown +_G.markdown = M.markdown -- Class for parsing command-line options local OptionParser = {} @@ -1168,6 +1174,7 @@ end -- where successfully parsed and false otherwise. function OptionParser:run(args) local pos = 1 + local param while pos <= #args do local arg = args[pos] if arg == "--" then @@ -1225,7 +1232,7 @@ local function run_command_line(arg) if not options.wrap_header then return s end local header = "" if options.header then - local f = io.open(options.header) or error("Could not open file: " .. options.header) + local f = io.open(options.header) or error("Could not open file: " .. options.header) header = f:read("*a") f:close() else @@ -1239,14 +1246,14 @@ local function run_command_line(arg) ]] - local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or + local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or "Untitled" header = header:gsub("TITLE", title) if options.inline_style then local style = "" local f = io.open(options.stylesheet) - if f then - style = f:read("*a") f:close() + if f then + style = f:read("*a") f:close() else error("Could not include style sheet " .. options.stylesheet .. ": File not found") end @@ -1265,15 +1272,15 @@ local function run_command_line(arg) end return header .. s .. footer end - - -- Generate output path name from input path name given options. + + -- Generate output path name from input path name given options. local function outpath(path, options) if options.append then return path .. ".html" end local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end return path .. ".html" end - + -- Default commandline options local options = { wrap_header = true, @@ -1316,18 +1323,18 @@ Other options: op:param("s", "style", function(x) options.stylesheet = x end) op:flag("l", "inline-style", function(x) options.inline_style = true end) op:flag("a", "append", function() options.append = true end) - op:flag("t", "test", function() + op:flag("t", "test", function() local n = arg[0]:gsub("markdown.lua", "markdown-tests.lua") local f = io.open(n) - if f then - f:close() dofile(n) + if f then + f:close() dofile(n) else error("Cannot find markdown-tests.lua") end - run_stdin = false + run_stdin = false end) op:flag("h", "help", function() print(help) run_stdin = false end) - op:arg(function(path) + op:arg(function(path) local file = io.open(path) or error("Could not open file: " .. path) local s = file:read("*a") file:close() @@ -1338,7 +1345,7 @@ Other options: run_stdin = false end ) - + if not op:run(arg) then print(help) run_stdin = false @@ -1350,10 +1357,10 @@ Other options: io.write(s) end end - + -- If we are being run from the command-line, act accordingly if arg and arg[0]:find("markdown%.lua$") then run_command_line(arg) else return markdown -end \ No newline at end of file +end From a3cb09c0989c37875cabc40474b6e871a17cb2ac Mon Sep 17 00:00:00 2001 From: steve donovan Date: Mon, 6 May 2013 11:38:20 +0200 Subject: [PATCH 012/106] Issue #61 sorted - not using Tags.add consistently --- ldoc/doc.lua | 1 - ldoc/parse.lua | 6 +++--- tests/styles/three.lua | 4 ++++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 2c052cfc..cfcfa828 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -744,7 +744,6 @@ end Module.warning, Module.error = Item.warning, Item.error - -------- Resolving References ----------------- function Module:hunt_for_reference (packmod, modules) diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 53c7e427..529a64c0 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -70,6 +70,7 @@ local function parse_colon_tags (text) return preamble,tag_items end +-- Tags are stored as an ordered map local Tags = {} Tags.__index = Tags @@ -79,8 +80,7 @@ function Tags.new (t) end function Tags:add (tag,value) - self[tag] = value - --print('adding',tag,value) + rawset(self,tag,value) self._order:append(tag) end @@ -335,7 +335,7 @@ local function parse_file(fname, lang, package, args) end end if is_local or tags['local'] then - tags['local'] = true + tags:add('local',true) end if tags.name then current_item = F:new_item(tags,line) diff --git a/tests/styles/three.lua b/tests/styles/three.lua index a782358d..09c0ceb1 100644 --- a/tests/styles/three.lua +++ b/tests/styles/three.lua @@ -3,6 +3,10 @@ -- Description here ---- +--- documented, but private +local function question () +end + --- answer to everything. -- @return magic number local function answer () From 123099ca07460aa881dd5a9ddfadef05b5b033de Mon Sep 17 00:00:00 2001 From: steve donovan Date: Mon, 6 May 2013 11:52:36 +0200 Subject: [PATCH 013/106] if we find a shared master module, close its section before using it. Appears to fix issue #56 --- ldoc/doc.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index cfcfa828..dfedd1e5 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -220,6 +220,10 @@ function File:finish() if mod then print('found master module',mf) this_mod = mod + if this_mod.section then + print '***closing section from master module***' + this_mod.section = nil + end submodule = true end end From 5e795c7a1de4a4a1626441fdf9437041f4f3f232 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Mon, 6 May 2013 14:41:49 +0200 Subject: [PATCH 014/106] added scm rockspec --- ldoc-scm-2.rockspec | 61 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 ldoc-scm-2.rockspec diff --git a/ldoc-scm-2.rockspec b/ldoc-scm-2.rockspec new file mode 100644 index 00000000..0a6ecc5b --- /dev/null +++ b/ldoc-scm-2.rockspec @@ -0,0 +1,61 @@ +package = "ldoc" +version = "scm-2" + +source = { + dir="LDoc", + url = "git://github.com/stevedonovan/LDoc.git" +} + +description = { + summary = "A Lua Documentation Tool", + detailed = [[ + LDoc is a LuaDoc-compatible documentation generator which can also + process C extension source. Markdown may be optionally used to + render comments, as well as integrated readme documentation and + pretty-printed example files + ]], + homepage='http://stevedonovan.github.com/ldoc', + maintainer='steve.j.donovan@gmail.com', + license = "MIT/X11", +} + + +dependencies = { + "penlight","markdown" +} + +build = { + type = "builtin", + modules = { + ["ldoc.tools"] = "ldoc/tools.lua", + ["ldoc.lang"] = "ldoc/lang.lua", + ["ldoc.parse"] = "ldoc/parse.lua", + ["ldoc.html"] = "ldoc/html.lua", + ["ldoc.lexer"] = "ldoc/lexer.lua", + ["ldoc.markup"] = "ldoc/markup.lua", + ["ldoc.prettify"] = "ldoc/prettify.lua", + ["ldoc.doc"] = "ldoc/doc.lua", + ["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua", + ["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua", + ["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua", + ["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua", + ["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua", + ["ldoc.builtin.global"] = "ldoc/builtin/global.lua", + ["ldoc.builtin.debug"] = "ldoc/builtin/debug.lua", + ["ldoc.builtin.io"] = "ldoc/builtin/io.lua", + ["ldoc.builtin.lfs"] = "ldoc/builtin/lfs.lua", + ["ldoc.builtin.lpeg"] = "ldoc/builtin/lpeg.lua", + ["ldoc.builtin.math"] = "ldoc/builtin/math.lua", + ["ldoc.builtin.os"] = "ldoc/builtin/os.lua", + ["ldoc.builtin.package"] = "ldoc/builtin/package.lua", + ["ldoc.builtin.string"] = "ldoc/builtin/string.lua", + ["ldoc.builtin.table"] = "ldoc/builtin/table.lua", + }, + copy_directories = {'doc','tests'}, + install = { + bin = { + ldoc = "ldoc.lua" + } + } +} + From f782919f5851cdb2f79e50cdf7cfb55707ce7e92 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Mon, 6 May 2013 15:34:39 +0200 Subject: [PATCH 015/106] replaced scm rockspec with regular rockspec --- ldoc-scm-2.rockspec => ldoc-1.3.8-2.rockspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename ldoc-scm-2.rockspec => ldoc-1.3.8-2.rockspec (92%) diff --git a/ldoc-scm-2.rockspec b/ldoc-1.3.8-2.rockspec similarity index 92% rename from ldoc-scm-2.rockspec rename to ldoc-1.3.8-2.rockspec index 0a6ecc5b..096a86ee 100644 --- a/ldoc-scm-2.rockspec +++ b/ldoc-1.3.8-2.rockspec @@ -1,9 +1,9 @@ package = "ldoc" -version = "scm-2" +version = "1.3.8-2" source = { - dir="LDoc", - url = "git://github.com/stevedonovan/LDoc.git" + dir="ldoc", + url = "http://stevedonovan.github.com/files/ldoc-1.3.8.zip" } description = { @@ -51,11 +51,11 @@ build = { ["ldoc.builtin.string"] = "ldoc/builtin/string.lua", ["ldoc.builtin.table"] = "ldoc/builtin/table.lua", }, - copy_directories = {'doc','tests'}, install = { bin = { - ldoc = "ldoc.lua" + "ldoc.lua" } } } + From ac5c5f2c65294a675f88889223a74872d2b14657 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 9 May 2013 12:52:15 +0200 Subject: [PATCH 016/106] 'plain=true' when format is set but you do not want doc comments treated specially. Backticks are now expanded in usage blocks --- ldoc.lua | 2 +- ldoc/html/ldoc_ltp.lua | 3 ++- ldoc/lexer.lua | 17 +++++++++++------ ldoc/markup.lua | 11 ++++++++--- ldoc/prettify.lua | 4 ++-- tests/styles/one.lua | 4 +++- 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 77a28097..688a8a6f 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -184,7 +184,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', 'pretty', 'charset', + 'one','style','template','description','examples', 'pretty', 'charset', 'plain', 'readme','all','manual_url', 'ignore', 'colon','boilerplate','merge', 'wrap', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', } diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 74ce34da..ac63031f 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -24,7 +24,8 @@ return [==[ # local use_li = ldoc.use_li # local display_name = ldoc.display_name # local iter = ldoc.modules.iter -# local M = ldoc.markup +# ---local M = ldoc.markup +# local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end # local nowrap = ldoc.wrap and '' or 'nowrap' diff --git a/ldoc/lexer.lua b/ldoc/lexer.lua index 119ef5c5..bad9f77d 100644 --- a/ldoc/lexer.lua +++ b/ldoc/lexer.lua @@ -17,16 +17,12 @@ -- iden n -- keyword do -- --- See the Guide for further discussion
    --- @class module --- @name pl.lexer +-- +-- Based on pl.lexer from Penlight local strfind = string.find local strsub = string.sub local append = table.insert ---[[ -module ('pl.lexer',utils._module) -]] local function assert_arg(idx,val,tp) if type(val) ~= tp then @@ -70,6 +66,14 @@ local function sdump(tok,options) return "string",tok end +-- strings enclosed in back ticks +local function bdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return "backtick",tok +end + -- long Lua strings need extra work to get rid of the quotes local function sdump_l(tok,options) if options and options.string then @@ -298,6 +302,7 @@ function lexer.lua(s,filter,options) {STRING3,sdump}, {STRING1,sdump}, {STRING2,sdump}, + {'^`[^`]+`',bdump}, {'^%-%-%[(=*)%[.-%]%1%]',cdump}, {'^%-%-.-\n',cdump}, {'^%[(=*)%[.-%]%1%]',sdump_l}, diff --git a/ldoc/markup.lua b/ldoc/markup.lua index 5f2b2988..ed2959ff 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -233,7 +233,6 @@ local function get_formatter(format) end end - local function text_processor(ldoc) return function(txt,item) if txt == nil then return '' end @@ -243,10 +242,17 @@ local function text_processor(ldoc) end end +local plain_processor local function markdown_processor(ldoc, formatter) - return function (txt,item) + return function (txt,item,plain) if txt == nil then return '' end + if plain then + if not plain_processor then + plain_processor = text_processor(ldoc) + end + return plain_processor(txt,item) + end if utils.is_type(item,doc.File) then txt = process_multiline_markdown(ldoc, txt, item) else @@ -258,7 +264,6 @@ local function markdown_processor(ldoc, formatter) end end - local function get_processor(ldoc, format) if format == 'plain' then return text_processor(ldoc) end diff --git a/ldoc/prettify.lua b/ldoc/prettify.lua index 17a9edf6..d33f34ab 100644 --- a/ldoc/prettify.lua +++ b/ldoc/prettify.lua @@ -24,7 +24,7 @@ local function span(t,val) return ('%s'):format(t,val) end -local spans = {keyword=true,number=true,string=true,comment=true,global=true} +local spans = {keyword=true,number=true,string=true,comment=true,global=true,backtick=true} function prettify.lua (fname, code, initial_lineno, pre) local res = List() @@ -47,7 +47,7 @@ function prettify.lua (fname, code, initial_lineno, pre) t = 'global' end if spans[t] then - if t == 'comment' then -- may contain @{ref} + if t == 'comment' or t == 'backtick' then -- may contain @{ref} or `..` val = prettify.resolve_inline_references(val,error_reporter) end res:append(span(t,val)) diff --git a/tests/styles/one.lua b/tests/styles/one.lua index d3b1edac..30e3145f 100644 --- a/tests/styles/one.lua +++ b/tests/styles/one.lua @@ -2,7 +2,9 @@ A non-doc comment multi-line probably containing license information! -Doesn't use module(), but module name is inferred from file name +Doesn't use module(), but module name is inferred from file name. +If you have initial licence comments that look like doc comments, +then set `boilerplate=true` ]] ------------ -- Test module, From cf7b8e3a9a35400e4b7cf71242fa6fe9ffd4365b Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 9 May 2013 13:28:39 +0200 Subject: [PATCH 017/106] fixes issue #62: keep LuaDoc compatibility, unless not_luadoc is explicitly set --- ldoc.lua | 4 +++- ldoc/doc.lua | 21 ++++++++++++++++++--- tests/styles/four.lua | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 688a8a6f..594e1925 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -185,7 +185,8 @@ 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', 'pretty', 'charset', 'plain', - 'readme','all','manual_url', 'ignore', 'colon','boilerplate','merge', 'wrap', + 'readme','all','manual_url', 'ignore', 'colon', + 'boilerplate','merge', 'wrap', 'not_luadoc', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', } ldoc_contents = tablex.makeset(ldoc_contents) @@ -361,6 +362,7 @@ setup_package_base() override 'colon' override 'merge' +override 'not_luadoc' if type(args.file) == 'table' then -- this can only be set from config file so we can assume it's already read diff --git a/ldoc/doc.lua b/ldoc/doc.lua index dfedd1e5..faa91727 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -388,14 +388,21 @@ function Item:add_to_description (rest) end end +function Item:trailing_warning (kind,tag,rest) + if type(rest)=='string' and #rest > 0 then + Item.warning(self,kind.." tag: '"..tag..'" has trailing text; use no_luadoc=true\n'..rest) + end +end + function Item:set_tag (tag,value) local ttype = known_tags[tag] + local args = self.file.args if ttype == TAG_MULTI or ttype == TAG_MULTI_LINE then -- value is always a List! if getmetatable(value) ~= List then value = List{value} end - if ttype ~= TAG_MULTI_LINE then + if ttype ~= TAG_MULTI_LINE and args.not_luadoc then local last = value[#value] if type(last) == 'string' and last:match '\n' then local line,rest = last:match('([^\n]+)(.*)') @@ -417,12 +424,20 @@ function Item:set_tag (tag,value) if value == nil then self:error("Tag without value: "..tag) end local id, rest = tools.extract_identifier(value) self.tags[tag] = id - self:add_to_description(rest) + if args.not_luadoc then + self:add_to_description(rest) + else + self:trailing_warning('id',tag,rest) + end elseif ttype == TAG_SINGLE then self.tags[tag] = value elseif ttype == TAG_FLAG then self.tags[tag] = true - self:add_to_description(value) + if args.not_luadoc then + self:add_to_description(value) + else + self:trailing_warning('flag',tag,value) + end else Item.warning(self,"unknown tag: '"..tag.."' "..tostring(ttype)) end diff --git a/tests/styles/four.lua b/tests/styles/four.lua index 02b78964..492a8fab 100644 --- a/tests/styles/four.lua +++ b/tests/styles/four.lua @@ -1,8 +1,8 @@ ------------ -- Yet another module. --- @module four -- Description can continue after simple tags, if you --- like +-- like - but to keep backwards compatibility, say 'not_luadoc=true' +-- @module four -- @author bob, james -- @license MIT -- @copyright InfoReich 2013 From ff2e5ccaed5ea85e8be57ccd1e385cee38d074fc Mon Sep 17 00:00:00 2001 From: steve donovan Date: Wed, 15 May 2013 15:30:47 +0200 Subject: [PATCH 018/106] Default output directory is now 'doc'; annotations were borked and not_luadoc check could blow up --- ldoc.lua | 7 ++++--- ldoc/doc.lua | 11 ++++++----- ldoc/html/ldoc_ltp.lua | 2 +- tests/easy/easy.lua | 3 ++- tests/usage/config.ld | 2 ++ tests/usage/usage.lua | 9 ++++----- 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 594e1925..665ddf75 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -35,8 +35,8 @@ app.require_here() --- @usage local usage = [[ -ldoc, a documentation generator for Lua, vs 1.3.11 - -d,--dir (default docs) output directory +ldoc, a documentation generator for Lua, vs 1.3.12 + -d,--dir (default doc) output directory -o,--output (default 'index') output name -v,--verbose verbose -a,--all show local functions, etc, in docs @@ -63,7 +63,8 @@ ldoc, a documentation generator for Lua, vs 1.3.11 (string) source file or directory containing source `ldoc .` reads options from an `config.ld` file in same directory; - `ldoc -c path/to/myconfig.ld .` reads options from `path/to/myconfig.ld` + `ldoc -c path/to/myconfig.ld ` reads options from `path/to/myconfig.ld` + and processes if 'file' was not defined in the ld file. ]] local args = lapp(usage) local lfs = require 'lfs' diff --git a/ldoc/doc.lua b/ldoc/doc.lua index faa91727..78e76e05 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -115,10 +115,10 @@ function doc.expand_annotation_item (tags, last_item) if tags.summary ~= '' then return false end for tag, value in pairs(tags) do if known_tags._annotation_tags[tag] then - tags.class = 'annotation' - tags.summary = value + tags:add('class','annotation') + tags:add('summary',value) local item_name = last_item and last_item.tags.name or '?' - tags.name = item_name..'-'..tag..acount + tags:add('name',item_name..'-'..tag..acount) acount = acount + 1 return true end @@ -268,6 +268,7 @@ function File:finish() -- add the item to the module's item list if this_mod then -- new-style modules will have qualified names like 'mod.foo' + --require 'pl.pretty'.dump(item.tags) local mod,fname = split_dotted_name(item.name) -- warning for inferred unqualified names in new style modules -- (retired until we handle methods like Set:unset() properly) @@ -402,7 +403,7 @@ function Item:set_tag (tag,value) if getmetatable(value) ~= List then value = List{value} end - if ttype ~= TAG_MULTI_LINE and args.not_luadoc then + if ttype ~= TAG_MULTI_LINE and args and args.not_luadoc then local last = value[#value] if type(last) == 'string' and last:match '\n' then local line,rest = last:match('([^\n]+)(.*)') @@ -424,7 +425,7 @@ function Item:set_tag (tag,value) if value == nil then self:error("Tag without value: "..tag) end local id, rest = tools.extract_identifier(value) self.tags[tag] = id - if args.not_luadoc then + if args and args.not_luadoc then self:add_to_description(rest) else self:trailing_warning('id',tag,rest) diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index ac63031f..b16c9781 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -250,7 +250,7 @@ return [==[
    -generated by LDoc 1.3.11 +generated by LDoc 1.3.12
    diff --git a/tests/easy/easy.lua b/tests/easy/easy.lua index 63e39687..00dca974 100644 --- a/tests/easy/easy.lua +++ b/tests/easy/easy.lua @@ -1,4 +1,5 @@ ---- simplified LDoc style +--- simplified LDoc colon style. +-- You have to use -C flag or 'colon=true' for this one! module 'easy' --- First one. diff --git a/tests/usage/config.ld b/tests/usage/config.ld index 85ff293b..f33e4ba2 100644 --- a/tests/usage/config.ld +++ b/tests/usage/config.ld @@ -11,3 +11,5 @@ pretty='lxsh' -- suppress @params and the summary at the top no_return_or_parms=true no_summary=true +not_luadoc=true + diff --git a/tests/usage/usage.lua b/tests/usage/usage.lua index 21f6042e..ba05136d 100644 --- a/tests/usage/usage.lua +++ b/tests/usage/usage.lua @@ -70,13 +70,12 @@ end --[[----------------- @table Vector.Opts -Options table format for `Vector.options` +Options table format for `Vector:options` - autoconvert: try to convert strings to numbers + * `autoconvert`: try to convert strings to numbers + * `adder`: function used to perform addition and subtraction + * `multiplier`: function used to perform multiplication and division - adder: function used to perform addition and subtraction - - multiplier: function used to perform multiplication and division @usage v = Vector {{1},{2}} v:options {adder = function(x,y) return {x[1]+y[1]} end} From 04c4f45a61d1d312c0d545d1ec8d04ddac887aeb Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 16 May 2013 11:24:05 +0200 Subject: [PATCH 019/106] added new rockspec; updated docs; nil-description error triggered by ldoc docs --- doc/doc.md | 88 +++++++++++++++++++++++++----------------- ldoc-1.3.12-1.rockspec | 61 +++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 36 deletions(-) create mode 100644 ldoc-1.3.12-1.rockspec diff --git a/doc/doc.md b/doc/doc.md index 49edb4d6..e11097a4 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -566,9 +566,9 @@ See 'tests/examples/mylib.c' for the full example. For example, to process all files in the 'lua' directory: $ ldoc lua - output written to docs/ + output written to doc/ -Thereafter the `docs` directory will contain `index.html` which points to individual modules +Thereafter the `doc` directory will contain `index.html` which points to individual modules in the `modules` subdirectory. The `--dir` flag can specify where the output is generated, and will ensure that the directory exists. The output structure is like LuaDoc: there is an `index.html` and the individual modules are in the `modules` subdirectory. This applies to @@ -590,22 +590,33 @@ For new-style modules, that don't use `module()`, it is recommended that the mod has an explicit `@module PACKAGE.NAME`. If it does not, then `ldoc` will still attempt to deduce the module name, but may need help with `--package/-b` as above. -`format = 'markdown'` can be used in your `config.ld` and will be used to process summaries -and descriptions. This requires [markdown.lua](http://www.frykholm.se/files/markdown.lua) by -Niklas Frykholm to be installed (this can be most easily done with `luarocks install -markdown`.) A much faster alternative is -[lua-discount](http://asbradbury.org/projects/lua-discount/) which you can use by setting -`format` to 'discount' after installing using `luarocks install lua-discount`) The -[discount](http://www.pell.portland.or.us/~orc/Code/discount/) Markdown processor -additionally has more features than the pure Lua version, such as PHP-Extra style tables. -As a special case, LDoc will fall back to using `markdown.lua` if it cannot find `discount`. +A special case is if you simply say 'ldoc .'. Then there _must_ be a `config.ld` file +available in the directory, and it can specify the file: + + file = "mymod.lua" + title = "mymod documentation" + description = "mymod does some simple but useful things" + +`file` can of course point to a directory, just as with the `--file` option. This mode makes +it particularly easy for the user to build the documentation, by allowing you to specify +everything explicitly in the configuration. + +In `config.ld`, `file` may be a Lua table, containing file names or directories; if it has +an `exclude` field then that will be used to exclude files from the list, for example +`{'examples', exclude = {'examples/slow.lua'}}`. + +A particular configuration file can be specified with the `-c` flag. Configuration files don't +_have_ to contain a `file` field, but in that case LDoc does need an explicit file on the command +line. This is useful if you have some defaults you wish to apply to all of your docs. + +## Markdown Support `format = 'markdown'` can be used in your `config.ld` and will be used to process summaries -and descriptions. This requires a markdown processor. +and descriptions; you can also use the `-f` flag. This requires a markdown processor. LDoc knows how to use: - [markdown.lua](http://www.frykholm.se/files/markdown.lua) a pure Lua processor by -Niklas Frykholm (this can be installed easily with `luarocks install markdown`.) +Niklas Frykholm. For convenience, LDoc comes with a copy of markdown.lua. - [lua-discount](http://asbradbury.org/projects/lua-discount/), a faster alternative (installed with `luarocks install lua-discount`). lua-discount uses the C [discount](http://www.pell.portland.or.us/~orc/Code/discount/) Markdown processor which has @@ -615,25 +626,14 @@ markdown, and with extra features (`luarocks install lunamark`). You can request the processor you like with `format = 'markdown|discount|lunamark'`, and LDoc will attempt to use it. If it can't find it, it will look for one of the other -markdown processors. If it can't find any markdown processer, it will fall back to text -processing. +markdown processors. +Even with the default of 'plain' some minimal processing takes place, in particular empty lines +are treated as line breaks. -A special case is if you simply say 'ldoc .'. Then there _must_ be a `config.ld` file -available in the directory, and it can specify the file: - - file = "mymod.lua" - title = "mymod documentation" - description = "mymod does some simple but useful things" - -`file` can of course point to a directory, just as with the `--file` option. This mode makes -it particularly easy for the user to build the documentation, by allowing you to specify -everything explicitly in the configuration. - -In `config.ld`, `file` may be a Lua table, containing file names or directories; if it has -an `exclude` field then that will be used to exclude files from the list, for example -`{'examples', exclude = {'examples/slow.lua'}}`. - +This formatting applies to all of a project, including any readmes and so forth. You may want +Markdown for this 'narrative' documentation, but not for your code comments. `plain=true` will +switch off formatting for code. ## Processing Single Modules @@ -641,8 +641,8 @@ an `exclude` field then that will be used to exclude files from the list, for ex special case when a single module file is specified. Here an index would be redundant, so the single HTML file generated contains the module documentation. - $ ldoc mylib.lua --> results in docs/index.html - $ ldoc --output mylib mylib.lua --> results in docs/mylib.html + $ ldoc mylib.lua --> results in doc/index.html + $ ldoc --output mylib mylib.lua --> results in doc/mylib.html $ ldoc --output mylib --dir html mylib.lua --> results in html/mylib.html The default sections used by LDoc are 'Functions', 'Tables' and 'Fields', corresponding to @@ -840,6 +840,13 @@ resolves to 'examples/testu.lua.html'. Examples may link back to the API documentation, for instance the example `input.lua` has a @\{spawn_process} inline reference. +By default, LDoc uses a built-in Lua code 'prettifier'. See-references are allowed in comments, +and also in code if they're enclosed in backticks. + +[lxsh](https://github.com/xolox/lua-lxsh) +can be used (available from LuaRocks) if you want support for C as well. `pretty='lxsh'` will +cause `lxsh` to be used, if available. + ## Readme files Like all good Github projects, Winapi has a `readme.md`: @@ -858,7 +865,7 @@ the first indented line is '@plain'. (See the source for this readme to see how Another name for `readme` is `topics`, which is more descriptive. From LDoc 1.2, `readme/topics` can be a list of documents. These act as a top-level table-of-contents for your documentation. Currently, if you want them in a particular order, then use names like -`01-introduction.md` etc which sort appropriately. +`01-introduction.md` etc, which sort appropriately. The first line of a document may be a Markdown `#` title. If so, then LDoc will regard the next level as the subheadings, normally second-level `##`. But if the title is already @@ -975,24 +982,33 @@ of files and directories. - `all` show local functions, etc as well in the docs - `format` markup processor, can be 'plain' (default), 'markdown' or 'discount' - `output` output name (default 'index') - - `dir` directory for output files (default 'docs') + - `dir` directory for output files (default 'doc') + - `colon` use colon style, instead of @ tag style + - `boilerplate` ignore first comment in all source files (e.g. license comments) - `ext` extension for output (default 'html') - `one` use a one-column layout - `style`, `template`: together these specify the directories for the style and and the template. In `config.ld` they may also be `true`, meaning use the same directory as the configuration file. + - `merge` allow documentation from different files to be merged into modules without + explicit @submodule tag These only appear in `config.ld`: - `description` a project description used under the project title - `examples` a directory or file: can be a table - - `readme` name of readme file (to be processed with Markdown) + - `readme` or `topics` readme files (to be processed with Markdown) + - `pretty` code prettify 'lua' (default) or 'lxsh' + - `charset` use if you want to override the UTF-8 default (also @charset in files) - `no_return_or_parms` don't show parameters or return values in output - `backtick_references` whether references in backticks will be resolved + - `plain` set to true if `format` is set but you don't want code comments processed + - `wrap` ?? - `manual_url` point to an alternative or local location for the Lua manual, e.g. 'file:///D:/dev/lua/projects/lua-5.1.4/doc/manual.html' - - `one` use a one-column output format - `no_summary` suppress the Contents summary + - `custom_see_handler` function that filters see-references + - `not_luadoc` set to `true` if the docs break LuaDoc compatibility Available functions are: diff --git a/ldoc-1.3.12-1.rockspec b/ldoc-1.3.12-1.rockspec new file mode 100644 index 00000000..9510efb1 --- /dev/null +++ b/ldoc-1.3.12-1.rockspec @@ -0,0 +1,61 @@ +package = "ldoc" +version = "1.3.12-1" + +source = { + dir="ldoc", + url = "http://stevedonovan.github.com/files/ldoc-1.3.12.zip" +} + +description = { + summary = "A Lua Documentation Tool", + detailed = [[ + LDoc is a LuaDoc-compatible documentation generator which can also + process C extension source. Markdown may be optionally used to + render comments, as well as integrated readme documentation and + pretty-printed example files + ]], + homepage='http://stevedonovan.github.com/ldoc', + maintainer='steve.j.donovan@gmail.com', + license = "MIT/X11", +} + + +dependencies = { + "penlight","markdown" +} + +build = { + type = "builtin", + modules = { + ["ldoc.tools"] = "ldoc/tools.lua", + ["ldoc.lang"] = "ldoc/lang.lua", + ["ldoc.parse"] = "ldoc/parse.lua", + ["ldoc.html"] = "ldoc/html.lua", + ["ldoc.lexer"] = "ldoc/lexer.lua", + ["ldoc.markup"] = "ldoc/markup.lua", + ["ldoc.prettify"] = "ldoc/prettify.lua", + ["ldoc.doc"] = "ldoc/doc.lua", + ["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua", + ["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua", + ["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua", + ["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua", + ["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua", + ["ldoc.builtin.global"] = "ldoc/builtin/global.lua", + ["ldoc.builtin.debug"] = "ldoc/builtin/debug.lua", + ["ldoc.builtin.io"] = "ldoc/builtin/io.lua", + ["ldoc.builtin.lfs"] = "ldoc/builtin/lfs.lua", + ["ldoc.builtin.lpeg"] = "ldoc/builtin/lpeg.lua", + ["ldoc.builtin.math"] = "ldoc/builtin/math.lua", + ["ldoc.builtin.os"] = "ldoc/builtin/os.lua", + ["ldoc.builtin.package"] = "ldoc/builtin/package.lua", + ["ldoc.builtin.string"] = "ldoc/builtin/string.lua", + ["ldoc.builtin.table"] = "ldoc/builtin/table.lua", + }, + install = { + bin = { + ldoc = "ldoc.lua" + } + } +} + + From 2bd2b23d7dacb32d77c2c63dada9f211ddacddbf Mon Sep 17 00:00:00 2001 From: steve donovan Date: Mon, 27 May 2013 10:47:55 +0200 Subject: [PATCH 020/106] topics are added with nil description --- doc/doc.md | 11 ++++++++--- ldoc/doc.lua | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index e11097a4..ed8d5848 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -396,6 +396,8 @@ One added convenience is that it is easier to name entities: -- @class module -- @name simple +becomes: + ------------ -- a simple module. -- (LDoc) @@ -557,7 +559,7 @@ or 'lua'.) An LDoc feature which is particularly useful for C extensions is _module merging_. If several files are all marked as `@module lib` then a single module `lib` is generated, containing all -the docs from the separate files. +the docs from the separate files. For this, use `merge=true`. See 'tests/examples/mylib.c' for the full example. @@ -785,6 +787,10 @@ can suppress the contents summary with `no_summary`. ## Customizing the Page +A basic customization is to override the default UTF-8 encoding using `charset`. For instance, +Brazillian software would find it useful to put `charset='ISO-8859-1'` in `config.ld`, or use +the @charset tag for individual files. + Setting `no_return_or_parms` to `true` will suppress the display of 'param' and 'return' tags. This may appeal to programmers who dislike the traditional @tag soup xDoc style and prefer to comment functions just with a description. This is particularly useful when using @@ -811,7 +817,6 @@ to the original: no_return_or_parms = true format = 'discount' - Generally, using Markdown gives you the opportunity to structure your documentation in any way you want; particularly if using lua-discount and its [table syntax](http://michelf.com/projects/php-markdown/extra/#table); the desired result can often @@ -834,7 +839,7 @@ The line in the `config.ld` that enables this is: examples = {'examples', exclude = {'examples/slow.lua'}} That is, all files in the `examples` folder are to be pretty-printed, except for `slow.lua` -which is meant to be called from one of the examples. The see-reference to `testu.lua` +which is meant to be called from one of the examples. Now a see-reference to `testu.lua` resolves to 'examples/testu.lua.html'. Examples may link back to the API documentation, for instance the example `input.lua` has a diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 78e76e05..3b478694 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -320,7 +320,7 @@ function File:finish() end end end - section_description = this_section.summary..' '..this_section.description + section_description = this_section.summary..' '..(this_section.description or '') elseif item.tags.within then section_description = item.tags.within item.section = section_description From 4208b2123479119cdc92aacaa4cd87bb15f7fbe9 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Mon, 27 May 2013 14:47:26 +0200 Subject: [PATCH 021/106] issue #65; see references no longer have multiple option, but can now allow custom formats --- ldoc/doc.lua | 5 +---- ldoc/tools.lua | 16 ---------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 3b478694..3f19a852 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -524,10 +524,7 @@ function Item:finish() self.type = read_del(tags,'class') self.modifiers = extract_tag_modifiers(tags) self.usage = read_del(tags,'usage') - -- see tags are multiple, but they may also be comma-separated - if tags.see then - tags.see = tools.expand_comma_list(read_del(tags,'see')) - end + tags.see = read_del(tags,'see') if doc.project_level(self.type) then -- we are a module, so become one! self.items = List() diff --git a/ldoc/tools.lua b/ldoc/tools.lua index 34d014e3..a57d5b96 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -151,22 +151,6 @@ function M.split_dotted_name (s) end end --- expand lists of possibly qualified identifiers --- given something like {'one , two.2','three.drei.drie)'} --- it will output {"one","two.2","three.drei.drie"} -function M.expand_comma_list (ls) - local new_ls = List() - for s in ls:iter() do - s = s:gsub('[^%.:%-%w_]*$','') - if s:find ',' then - new_ls:extend(List.split(s,'%s*,%s*')) - else - new_ls:append(s) - end - end - return new_ls -end - -- grab lines from a line iterator `iter` until the line matches the pattern. -- Returns the joined lines and the line, which may be nil if we run out of -- lines. From 7e766f9aaadaf46e36a35c13cd59f8a30b11b610 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius Date: Sun, 2 Jun 2013 17:25:20 +0200 Subject: [PATCH 022/106] Fix markdown.lua to work with LUA 5.2 The `setfenv` variable is unavailable in LUA 5.2, so call it conditionally. --- ldoc/markdown.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ldoc/markdown.lua b/ldoc/markdown.lua index bfe3a1bf..a89b38e9 100644 --- a/ldoc/markdown.lua +++ b/ldoc/markdown.lua @@ -121,7 +121,7 @@ THE SOFTWARE. local M = {} local MT = {__index = _G} setmetatable(M, MT) -setfenv(1, M) +if setfenv then setfenv(1, M) end -- if true, we are using LUA 5.1 ---------------------------------------------------------------------- -- Utility functions @@ -1134,7 +1134,7 @@ end -- End of module ---------------------------------------------------------------------- -setfenv(1, _G) +if setfenv then setfenv(1, _G) end M.lock(M) -- Expose markdown function to the world From 624cf7d742aedc6bc9f9d7152a3db194f8c6a362 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 6 Jun 2013 18:59:11 +0200 Subject: [PATCH 023/106] issue #70 crash --- ldoc/doc.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 3f19a852..b1a32e7f 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -626,6 +626,9 @@ function Item:finish() local names = List() self.subparams = {} for i,name in ipairs(original_names) do + if type(name) ~= 'string' then + self:error("declared table cannot have array entries") + end local pname,field = split_iden(name) if field then if not fields then From 320dfed2e381a726e6792a24ac365e1e44c99e96 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Fri, 14 Jun 2013 15:19:51 +0200 Subject: [PATCH 024/106] no longer any need for setfenv in markdown.lua --- ldoc/markdown.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ldoc/markdown.lua b/ldoc/markdown.lua index a89b38e9..73c656b6 100644 --- a/ldoc/markdown.lua +++ b/ldoc/markdown.lua @@ -118,10 +118,10 @@ THE SOFTWARE. -- Set up a table for holding local functions to avoid polluting the global namespace +-- Penlight 1.2 defines compatible 5.1 setfenv in utils table local M = {} local MT = {__index = _G} setmetatable(M, MT) -if setfenv then setfenv(1, M) end -- if true, we are using LUA 5.1 ---------------------------------------------------------------------- -- Utility functions @@ -1134,7 +1134,6 @@ end -- End of module ---------------------------------------------------------------------- -if setfenv then setfenv(1, _G) end M.lock(M) -- Expose markdown function to the world From 35a391d7d75e8b81ad02920a0ff8b7e9db76f42c Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 18 Jul 2013 10:12:25 +0200 Subject: [PATCH 025/106] set tag in module can be used to set rendering properties etc of an individual module, e.g 'set no_return_or_parms=true' --- ldoc/doc.lua | 2 +- ldoc/html.lua | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index b1a32e7f..68a1871b 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -21,7 +21,7 @@ local TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG,TAG_MULTI_LINE = 'M','id','S -- - '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 = 'ML', ['return'] = 'M', field = 'M', author='M'; + param = 'M', see = 'M', usage = 'ML', ['return'] = 'M', field = 'M', author='M',set='M'; 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', charset = 'S', diff --git a/ldoc/html.lua b/ldoc/html.lua index 418ccf75..efb6f570 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -18,6 +18,7 @@ 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 tools = require 'ldoc.tools' local markup = require 'ldoc.markup' local prettify = require 'ldoc.prettify' @@ -55,6 +56,19 @@ 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_ldoc () + if not original_ldoc then + original_ldoc = tablex.copy(ldoc) + end + end + + local function restore_ldoc () + if original_ldoc then + ldoc = original_ldoc + end + end function ldoc.escape(str) return (str:gsub("['&<>\"]", escape_table)) @@ -236,6 +250,14 @@ function html.generate_output(ldoc, args, project) for m in modules() do ldoc.module = m ldoc.body = m.body + if m.tags.set then + save_ldoc() + for s in m.tags.set:iter() do + local var,val = s:match('([^=]+)=(.+)') + print('setting',var,val) + ldoc[var] = val + end + end set_charset(ldoc) m.info = get_module_info(m) if ldoc.body and m.postprocess then @@ -251,6 +273,7 @@ function html.generate_output(ldoc, args, project) out = cleanup_whitespaces(out) writefile(args.dir..lkind..'/'..m.name..args.ext,out) end + restore_ldoc() end end if not args.quiet then print('output written to '..tools.abspath(args.dir)) end From eb00a499b224eb12b967cb66f4bbb21d77fa2113 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 23 Jul 2013 10:40:07 +0200 Subject: [PATCH 026/106] finally fixed issue #32 (sorry Eric). Can now dump non-modules like scripts --- ldoc.lua | 4 ++-- ldoc/doc.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 665ddf75..8d437ef7 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -604,11 +604,11 @@ if args.style == '!' or args.template == '!' then utils.writefile(path.join(tmpdir,name),require('ldoc.html.'..name:gsub('%.','_'))) end if args.style == '!' then - tmpwrite(ldoc.templ) + tmpwrite(ldoc.css) args.style = tmpdir end if args.template == '!' then - tmpwrite(ldoc.css) + tmpwrite(ldoc.templ) args.template = tmpdir end end diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 68a1871b..f1fac617 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -917,7 +917,7 @@ local function dump_tags (tags) end function Module:dump(verbose) - if self.type ~= 'module' then return end + if not doc.project_level(self.type) then return end print '----' print(self.type..':',self.name,self.summary) if self.description then print(self.description) end From 8a071fb51756d56d2914b14f6b241fd27cff94b5 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 31 Jul 2013 14:25:33 +0200 Subject: [PATCH 027/106] issue #64: use 'sort=true' in config.ld to sort items in sections --- ldoc.lua | 2 +- ldoc/html.lua | 24 +++++++++++++++++------- ldoc/tools.lua | 23 +++++++++++++++++------ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 8d437ef7..ec534537 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -186,7 +186,7 @@ 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', 'pretty', 'charset', 'plain', - 'readme','all','manual_url', 'ignore', 'colon', + 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'boilerplate','merge', 'wrap', 'not_luadoc', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', } diff --git a/ldoc/html.lua b/ldoc/html.lua index efb6f570..c224a88e 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -58,10 +58,21 @@ 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_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 () @@ -210,6 +221,8 @@ function html.generate_output(ldoc, args, project) 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,err = template.substitute(module_template,{ @@ -218,6 +231,7 @@ function html.generate_output(ldoc, args, project) }) ldoc.root = false if not out then quit("template failed: "..err) end + restore_ldoc() check_directory(args.dir) -- make sure output directory is ok @@ -250,13 +264,9 @@ function html.generate_output(ldoc, args, project) for m in modules() do ldoc.module = m ldoc.body = m.body + m.ldoc = ldoc if m.tags.set then - save_ldoc() - for s in m.tags.set:iter() do - local var,val = s:match('([^=]+)=(.+)') - print('setting',var,val) - ldoc[var] = val - end + save_and_set_ldoc(m.tags.set) end set_charset(ldoc) m.info = get_module_info(m) diff --git a/ldoc/tools.lua b/ldoc/tools.lua index a57d5b96..22f34a2f 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -22,15 +22,26 @@ local lfs = require 'lfs' -- (something rather similar exists in LuaDoc) function M.type_iterator (list,field,value) return function() - local i = 1 - return function() - local val = list[i] - while val and val[field] ~= value do + local i, fls = 1, {} + for j = 1,#list do + local val = list[j] + if val[field] == value then + fls[i] = val i = i + 1 - val = list[i] end + end + i = 0 + local mod = fls[1].module + local ldoc = mod and mod.ldoc + if ldoc and ldoc.sort then + table.sort(fls,function(ia,ib) + return ia.name < ib.name + end) + end + return function() i = i + 1 - if val then return val end + local val = fls[i] + if val ~= nil then return val end end end end From 7fe6a95544f175b291f3eb57322baddcbca4bfca Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 1 Aug 2013 12:14:57 +0200 Subject: [PATCH 028/106] a partial solution for issue #73: ldoc.module_file to pick master modules when present --- ldoc.lua | 29 ++++++++++++++++++++++++++--- ldoc/doc.lua | 6 +++--- ldoc/html.lua | 7 +++++-- ldoc/tools.lua | 25 ++++++++----------------- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index ec534537..b402810c 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -186,7 +186,7 @@ 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', 'pretty', 'charset', 'plain', - 'readme','all','manual_url', 'ignore', 'colon', 'sort', + 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file', 'boilerplate','merge', 'wrap', 'not_luadoc', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', } @@ -261,6 +261,27 @@ if args.module then end end +local function fixup_module_file (file, fullpath) + if args.module_file then + for mname, f in pairs(args.module_file) do + if f == file then + args.module_file[mname] = fullpath + args.module_file[fullpath] = true + return "master for "..mname + end + end + end + return '' +end + +-- partial sort of file list, where anything in module_file is now upfront! +local function reorder_module_file (files) + if args.module_file then + local mf = args.module_file + table.sort(files,function(x,y) return mf[x] and not mf[y] end) + end +end + local abspath = tools.abspath -- a special case: 'ldoc .' can get all its parameters from config.ld @@ -274,14 +295,16 @@ if args.file == '.' then lfs.chdir(config_path) end config_is_read = true + override 'module_file' args.file = ldoc.file or '.' if args.file == '.' then args.file = lfs.currentdir() elseif type(args.file) == 'table' then for i,f in ipairs(args.file) do args.file[i] = abspath(f) - print(args.file[i]) + fixup_module_file(f,args.file[i]) end + reorder_module_file(args.file) else args.file = abspath(args.file) end @@ -345,7 +368,7 @@ local function process_file (f, flist) local ext = path.extension(f) local ftype = file_types[ext] if ftype then - if args.verbose then print(path.basename(f)) end + if args.verbose then print(f) end local F,err = parse.file(f,ftype,args) if err then if F then diff --git a/ldoc/doc.lua b/ldoc/doc.lua index f1fac617..f97672da 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -177,7 +177,7 @@ 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) +function File:find_module_in_files (name) for f in File.list:iter() do for m in f.modules:iter() do if m.name == name then @@ -216,7 +216,7 @@ function File:finish() -- if name is 'package.mod', then mod_name is 'mod' package,mname = split_dotted_name(this_mod.name) if self.args.merge then - local mod,mf = find_module_in_files(item.name) + local mod,mf = self:find_module_in_files(item.name) if mod then print('found master module',mf) this_mod = mod @@ -230,7 +230,7 @@ function File:finish() elseif item.type == 'submodule' then local mf submodule = true - this_mod,mf = find_module_in_files(item.name) + this_mod,mf = self:find_module_in_files(item.name) if this_mod == nil then self:error("'"..item.name.."' not found for submodule") end diff --git a/ldoc/html.lua b/ldoc/html.lua index c224a88e..c9406650 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -211,6 +211,7 @@ function html.generate_output(ldoc, args, project) 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 @@ -237,14 +238,16 @@ function html.generate_output(ldoc, args, project) args.dir = args.dir .. path.sep - check_file(args.dir..css, path.join(args.style,css)) -- has CSS been copied? + if ldoc.css then -- has CSS been copied? + check_file(args.dir..css, path.join(args.style,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; - -- this step is then only for putting out any examples or topics + -- 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() diff --git a/ldoc/tools.lua b/ldoc/tools.lua index 22f34a2f..bb5c5f1a 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -18,31 +18,22 @@ local lfs = require 'lfs' -- this constructs an iterator over a list of objects which returns only -- those objects where a field has a certain value. It's used to iterate --- only over functions or tables, etc. +-- only over functions or tables, etc. If the list of item has a module +-- with a context, then use that to pre-sort the fltered items. -- (something rather similar exists in LuaDoc) function M.type_iterator (list,field,value) return function() - local i, fls = 1, {} - for j = 1,#list do - local val = list[j] - if val[field] == value then - fls[i] = val - i = i + 1 - end - end - i = 0 - local mod = fls[1].module + local fls = list:filter(function(item) + return item[field] == value + end) + local mod = fls[1] and fls[1].module local ldoc = mod and mod.ldoc if ldoc and ldoc.sort then - table.sort(fls,function(ia,ib) + fls:sort(function(ia,ib) return ia.name < ib.name end) end - return function() - i = i + 1 - local val = fls[i] - if val ~= nil then return val end - end + return fls:iter() end end From adcd9c5ede192aa23c2c08309fbd597557a5f269 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Fri, 2 Aug 2013 14:18:41 +0200 Subject: [PATCH 029/106] better soln for issue #73: partial sorting for all processed files --- ldoc.lua | 45 +++++++++++++++++++++------------------------ ldoc/tools.lua | 13 ++++++++++--- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index b402810c..60351d8c 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -261,27 +261,6 @@ if args.module then end end -local function fixup_module_file (file, fullpath) - if args.module_file then - for mname, f in pairs(args.module_file) do - if f == file then - args.module_file[mname] = fullpath - args.module_file[fullpath] = true - return "master for "..mname - end - end - end - return '' -end - --- partial sort of file list, where anything in module_file is now upfront! -local function reorder_module_file (files) - if args.module_file then - local mf = args.module_file - table.sort(files,function(x,y) return mf[x] and not mf[y] end) - end -end - local abspath = tools.abspath -- a special case: 'ldoc .' can get all its parameters from config.ld @@ -295,16 +274,13 @@ if args.file == '.' then lfs.chdir(config_path) end config_is_read = true - override 'module_file' args.file = ldoc.file or '.' if args.file == '.' then args.file = lfs.currentdir() elseif type(args.file) == 'table' then for i,f in ipairs(args.file) do args.file[i] = abspath(f) - fixup_module_file(f,args.file[i]) end - reorder_module_file(args.file) else args.file = abspath(args.file) end @@ -387,9 +363,26 @@ setup_package_base() override 'colon' override 'merge' override 'not_luadoc' +override 'module_file' + +-- 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 if type(args.file) == 'table' then -- this can only be set from config file so we can assume it's already read + args.file.sortfn = reorder_module_file() process_file_list(args.file,'*.*',process_file, file_list) if #file_list == 0 then quit "no source files specified" end elseif path.isdir(args.file) then @@ -406,9 +399,13 @@ elseif path.isdir(args.file) then end end end + -- process files, optionally in order that respects master module files + local sortfn = reorder_module_file() + if sortfn then files:sort(sortfn) end for f in files:iter() do process_file(f, file_list) end + if #file_list == 0 then quit(quote(args.file).." contained no source files") end diff --git a/ldoc/tools.lua b/ldoc/tools.lua index bb5c5f1a..2ab4a322 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -419,24 +419,31 @@ end function M.process_file_list (list, mask, operation, ...) local exclude_list = list.exclude and M.files_from_list(list.exclude, mask) + local files = List() local function process (f,...) f = M.abspath(f) if not exclude_list or exclude_list and exclude_list:index(f) == nil then - operation(f, ...) + files:append(f) end end for _,f in ipairs(list) do if path.isdir(f) then local files = List(dir.getallfiles(f,mask)) for f in files:iter() do - process(f,...) + files:append(f) end elseif path.isfile(f) then - process(f,...) + files:append(f) else quit("file or directory does not exist: "..M.quote(f)) end end + if list.sortfn then + files:sort(list.sortfn) + end + for f in files:iter() do + operation(f,...) + end end function M.files_from_list (list, mask) From 94dc198f4b6487c3b96c0be47d8939e2caa1e477 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Mon, 5 Aug 2013 19:27:42 +0200 Subject: [PATCH 030/106] support for generating Markdown; basic support for Moonscript --- ldoc.lua | 16 +++++++++++++-- ldoc/html.lua | 12 ++++++++---- ldoc/html/ldoc_mdtp.lua | 17 ++++++++++++++++ ldoc/lang.lua | 43 ++++++++++++++++++++++++++++++++++++++++- ldoc/parse.lua | 3 +++ 5 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 ldoc/html/ldoc_mdtp.lua diff --git a/ldoc.lua b/ldoc.lua index 60351d8c..cbba0e7c 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -117,7 +117,8 @@ local file_types = { ['.cpp'] = cc, ['.cxx'] = cc, ['.C'] = cc, - ['.mm'] = cc + ['.mm'] = cc, + ['.moon'] = lang.moon, } ------- ldoc external API ------------ @@ -187,7 +188,7 @@ local ldoc_contents = { 'file','project','title','package','format','output','dir','ext', 'topics', 'one','style','template','description','examples', 'pretty', 'charset', 'plain', 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file', - 'boilerplate','merge', 'wrap', 'not_luadoc', + 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', } ldoc_contents = tablex.makeset(ldoc_contents) @@ -573,9 +574,20 @@ end ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp' +if args.ext == 'md' then + ldoc.templ = 'ldoc.mdtp' + ldoc.template_escape = '>' + ldoc.style = false + args.ext = '.md' +end + local function style_dir (sname) local style = ldoc[sname] local dir + if style==false and sname == 'style' then + args.style = false + ldoc.css = false + end if style then if style == true then dir = config_dir diff --git a/ldoc/html.lua b/ldoc/html.lua index c9406650..06db178d 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -143,7 +143,7 @@ function html.generate_output(ldoc, args, project) function ldoc.display_name(item) local name = item.display_name or item.name - if item.type == 'function' or item.type == 'lfunction' then return name..' '..item.args + if item.type == 'function' or item.type == 'lfunction' then return name..' '..item.args --   else return name end end @@ -229,6 +229,7 @@ function html.generate_output(ldoc, args, project) local out,err = template.substitute(module_template,{ ldoc = ldoc, module = ldoc.module, + _escape = ldoc.template_escape }) ldoc.root = false if not out then quit("template failed: "..err) end @@ -238,7 +239,7 @@ function html.generate_output(ldoc, args, project) args.dir = args.dir .. path.sep - if ldoc.css then -- has CSS been copied? + if css then -- has CSS been copied? check_file(args.dir..css, path.join(args.style,css)) end @@ -259,7 +260,9 @@ function html.generate_output(ldoc, args, project) -- 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. - ldoc.css = '../'..css + if css then + ldoc.css = '../'..css + end for m in mods:iter() do local kind, lkind, modules = unpack(m) check_directory(args.dir..lkind) @@ -278,7 +281,8 @@ function html.generate_output(ldoc, args, project) end out,err = template.substitute(module_template,{ module=m, - ldoc = ldoc + ldoc = ldoc, + _escape = ldoc.template_escape }) if not out then quit('template failed for '..m.name..': '..err) diff --git a/ldoc/html/ldoc_mdtp.lua b/ldoc/html/ldoc_mdtp.lua new file mode 100644 index 00000000..ca39766e --- /dev/null +++ b/ldoc/html/ldoc_mdtp.lua @@ -0,0 +1,17 @@ +return [[ +> local lev = ldoc.level or 2 +> local lev1,lev2 = ('#'):rep(lev),('#'):rep(lev+1) +> for kind, items in module.kinds() do +> local kitem = module.kinds:get_item(kind) +> if kitem then +$(lev1) $(ldoc.descript(kitem)) + +> end +> for item in items() do +$(lev2) $(ldoc.display_name(item)) + +$(ldoc.descript(item)) + +> end +> end +]] diff --git a/ldoc/lang.lua b/ldoc/lang.lua index e02d02ae..4af21969 100644 --- a/ldoc/lang.lua +++ b/ldoc/lang.lua @@ -5,6 +5,7 @@ local class = require 'pl.class' local utils = require 'pl.utils' +local List = require 'pl.List' local tools = require 'ldoc.tools' local lexer = require 'ldoc.lexer' local quit = utils.quit @@ -262,4 +263,44 @@ function CC:grab_block_comment(v,tok) return 'comment',v:sub(1,-3) end -return { lua = Lua(), cc = CC() } +local Moon = class(Lua) + +function Moon:_init() + self.line_comment = '^%-%-+' -- used for stripping + self.start_comment_ = '^%s*%-%-%-+' -- used for doc comment line start + self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments + self.end_comment_ = '[^%-]%-%-+\n$' ---- exclude --- this kind of comment --- + self:finalize() +end + +function Moon:item_follows (t,v,tok) + if t == 'iden' then + local name,t,v = v, tnext(tok) --tools.get_fun_name(tok,v) + if name == 'class' then + name = v + --name,t,v = tools.get_fun_name(tok,v) + -- class! + return function(tags,tok) + tags:add('class','type') + tags:add('name',name) + end + elseif t == '=' or t == ':' then -- function/method + t,v = tnext(tok) + return function(tags,tok) + if not tags.name then + tags:add('name',name) + end + if t == '(' then + tags.formal_args = tools.get_parameters(tok) + else + tags.formal_args = List() + end + tags:add('class','function') + end + else + return nil + end + end +end + +return { lua = Lua(), cc = CC(), moon = Moon() } diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 529a64c0..5690a7e1 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -229,6 +229,9 @@ local function parse_file(fname, lang, package, args) if lang:empty_comment(v) then -- ignore rest of empty start comments t,v = tok() + if t == 'space' and not v:match '\n' then + t,v = tok() + end end while t and t == 'comment' do From b87180996d7bd169b70ed7e8df84a2947f3a7f71 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 7 Aug 2013 09:53:39 +0200 Subject: [PATCH 031/106] Moonscript: support for modules using with statement; optionally ignore colon as part of an identifier --- ldoc/lang.lua | 8 +++++--- ldoc/tools.lua | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ldoc/lang.lua b/ldoc/lang.lua index 4af21969..39b05827 100644 --- a/ldoc/lang.lua +++ b/ldoc/lang.lua @@ -274,11 +274,13 @@ function Moon:_init() end function Moon:item_follows (t,v,tok) + if t == '.' then -- enclosed in with statement + t,v = tnext(tok) + end if t == 'iden' then - local name,t,v = v, tnext(tok) --tools.get_fun_name(tok,v) + local name,t,v = tools.get_fun_name(tok,v,'') if name == 'class' then - name = v - --name,t,v = tools.get_fun_name(tok,v) + name,t,v = tools.get_fun_name(tok,v,'') -- class! return function(tags,tok) tags:add('class','type') diff --git a/ldoc/tools.lua b/ldoc/tools.lua index 2ab4a322..09ce891d 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -352,10 +352,12 @@ function M.get_parameters (tok,endtoken,delim) return args end --- parse a Lua identifier - contains names separated by . and :. -function M.get_fun_name (tok,first) +-- parse a Lua identifier - contains names separated by . and (optionally) :. +-- Set `colon` to be the secondary separator, '' for none. +function M.get_fun_name (tok,first,colon) local res = {} local t,name,sep + colon = colon or ':' if not first then t,name = tnext(tok) else @@ -363,7 +365,7 @@ function M.get_fun_name (tok,first) end if t ~= 'iden' then return nil end t,sep = tnext(tok) - while sep == '.' or sep == ':' do + while sep == '.' or sep == colon do append(res,name) append(res,sep) t,name = tnext(tok) From 5dd69b90bf5af46764155c0d409b6739123d7185 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 7 Aug 2013 11:05:55 +0200 Subject: [PATCH 032/106] support for Moonscript fat vs thin arrows; tools.get_parameters also returns last token found; lang.method_call generalization --- ldoc/doc.lua | 3 ++- ldoc/lang.lua | 12 ++++++++++-- ldoc/parse.lua | 6 +++--- ldoc/tools.lua | 13 ++++++------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index f97672da..7993fe33 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -307,8 +307,9 @@ function File:finish() if doc.class_tag(stype) then if not item.name:match '[:%.]' then -- not qualified local class = this_section.name + local lang = this_mod.file.lang local static = item.tags.constructor or item.tags.static or item.type ~= 'function' - item.name = class..(not static and ':' or '.')..item.name + item.name = class..(not static and lang.method_call or '.')..item.name end if stype == 'factory' then if item.tags.private then to_be_removed = true diff --git a/ldoc/lang.lua b/ldoc/lang.lua index 39b05827..95a261f3 100644 --- a/ldoc/lang.lua +++ b/ldoc/lang.lua @@ -75,6 +75,7 @@ function Lua:_init() self.start_comment_ = '^%-%-%-+' -- used for doc comment line start self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments self.end_comment_ = '[^%-]%-%-+\n$' ---- exclude --- this kind of comment --- + self.method_call = ':' self:finalize() end @@ -240,7 +241,8 @@ end -- note a difference here: we scan C/C++ code in full-text mode, not line by line. --- This is because we can't detect multiline comments in line mode +-- This is because we can't detect multiline comments in line mode. +-- Note: this applies to C/C++ code used to generate _Lua_ documentation! local CC = class(Lang) @@ -248,6 +250,7 @@ function CC:_init() self.line_comment = '^//+' self.start_comment_ = '^///+' self.block_comment = '^/%*%*+' + self.method_call = ':' self:finalize() end @@ -270,6 +273,7 @@ function Moon:_init() self.start_comment_ = '^%s*%-%-%-+' -- used for doc comment line start self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments self.end_comment_ = '[^%-]%-%-+\n$' ---- exclude --- this kind of comment --- + self.method_call = '.' self:finalize() end @@ -293,11 +297,15 @@ function Moon:item_follows (t,v,tok) tags:add('name',name) end if t == '(' then - tags.formal_args = tools.get_parameters(tok) + tags.formal_args,t,v = tools.get_parameters(tok) else tags.formal_args = List() end tags:add('class','function') + if t == '=' then + tags.formal_args:insert(1,'self') + tags.formal_args.comments = {self=''} + end end else return nil diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 5690a7e1..f223327e 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -154,15 +154,15 @@ local function parse_file(fname, lang, package, args) local current_item, module_item F.args = args - + F.lang = lang F.base = package local tok,f = lang.lexer(fname) if not tok then return nil end - local function lineno () + local function lineno () return tok:lineno() - end + end local function filename () return fname end diff --git a/ldoc/tools.lua b/ldoc/tools.lua index 09ce891d..a92732c9 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -275,7 +275,7 @@ function M.get_parameters (tok,endtoken,delim) tok = M.space_skip_getter(tok) local args = List() args.comments = {} - local ltl = lexer.get_separated_list(tok,endtoken,delim) + local ltl,tt = lexer.get_separated_list(tok,endtoken,delim) if not ltl or not ltl[1] or #ltl[1] == 0 then return args end -- no arguments @@ -330,7 +330,6 @@ function M.get_parameters (tok,endtoken,delim) end end - ----[[ -- we had argument comments -- but the last one may be outside the parens! (Geoff style) -- (only try this stunt if it's a function parameter list!) @@ -339,17 +338,17 @@ function M.get_parameters (tok,endtoken,delim) local last_arg = args[n] if not args.comments[last_arg] then while true do - local t = {tok()} - if type_of(t) == 'comment' then - set_comment(n,t) + tt = {tok()} + if type_of(tt) == 'comment' then + set_comment(n,tt) else break end end end end - --]] - return args + -- return what token we ended on as well - can be token _past_ ')' + return args,tt[1],tt[2] end -- parse a Lua identifier - contains names separated by . and (optionally) :. From 3e78c4704b92f23bf5b4161f8f43676537d9c274 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 7 Aug 2013 13:33:38 +0200 Subject: [PATCH 033/106] partial file list sorting refactor was borked --- ldoc/tools.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ldoc/tools.lua b/ldoc/tools.lua index a92732c9..de7480af 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -421,7 +421,7 @@ end function M.process_file_list (list, mask, operation, ...) local exclude_list = list.exclude and M.files_from_list(list.exclude, mask) local files = List() - local function process (f,...) + local function process (f) f = M.abspath(f) if not exclude_list or exclude_list and exclude_list:index(f) == nil then files:append(f) @@ -429,19 +429,21 @@ function M.process_file_list (list, mask, operation, ...) end for _,f in ipairs(list) do if path.isdir(f) then - local files = List(dir.getallfiles(f,mask)) - for f in files:iter() do - files:append(f) + local dfiles = List(dir.getallfiles(f,mask)) + for f in dfiles:iter() do + process(f) end elseif path.isfile(f) then - files:append(f) + process(f) else quit("file or directory does not exist: "..M.quote(f)) end end + if list.sortfn then files:sort(list.sortfn) end + for f in files:iter() do operation(f,...) end From 52e9b6f32c06f10f8a8c4353fa53161e95ac141f Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 7 Aug 2013 15:19:46 +0200 Subject: [PATCH 034/106] new classmod tag for defining modules that just export one class (experimental) --- ldoc.lua | 1 + ldoc/doc.lua | 50 ++++++++++++++++++++++------ ldoc/html.lua | 6 ++-- ldoc/html/ldoc_ltp.lua | 2 +- ldoc/parse.lua | 19 +++++++++-- tests/moonscript/List.moon | 67 ++++++++++++++++++++++++++++++++++++++ tests/moonscript/config.ld | 5 +++ 7 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 tests/moonscript/List.moon create mode 100644 tests/moonscript/config.ld diff --git a/ldoc.lua b/ldoc.lua index cbba0e7c..f1e2c70a 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -104,6 +104,7 @@ end ProjectMap:add_kind('module','Modules') ProjectMap:add_kind('script','Scripts') +ProjectMap:add_kind('classmod','Classes') ProjectMap:add_kind('topic','Topics') ProjectMap:add_kind('example','Examples') diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 7993fe33..e0495ac7 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -27,7 +27,7 @@ local known_tags = { fixme = 'S', todo = 'S', warning = 'S', raise = 'S', charset = 'S', ['local'] = 'N', export = 'N', private = 'N', constructor = 'N', static = 'N'; -- project-level - module = 'T', script = 'T', example = 'T', topic = 'T', submodule='T', + module = 'T', script = 'T', example = 'T', topic = 'T', submodule='T', classmod='T', -- module-level ['function'] = 'T', lfunction = 'T', table = 'T', section = 'T', type = 'T', annotation = 'T', factory = 'T'; @@ -39,12 +39,18 @@ known_tags._project_level = { script = true, example = true, topic = true, - submodule = true; + submodule = true, + classmod = true, } known_tags._code_types = { module = true, - script = true + script = true, + classmod = true, +} + +known_tags._presentation_names = { + classmod = 'Class', } known_tags._module_info = { @@ -99,6 +105,18 @@ function doc.class_tag (tag) return tag == 'type' or tag == 'factory' end +-- how the type wants to be formally presented; e.g. 'module' becomes 'Module' +-- but 'classmod' will become 'Class' +function doc.presentation_name (tag) + local name = known_tags._presentation_names[tag] + if not name then + name = tag:gsub('(%a)(%a*)',function(f,r) + return f:upper()..r + end) + end + return name +end + function doc.module_info_tags () return List.iter(known_tags._module_info) end @@ -268,7 +286,6 @@ function File:finish() -- add the item to the module's item list if this_mod then -- new-style modules will have qualified names like 'mod.foo' - --require 'pl.pretty'.dump(item.tags) local mod,fname = split_dotted_name(item.name) -- warning for inferred unqualified names in new style modules -- (retired until we handle methods like Set:unset() properly) @@ -298,15 +315,19 @@ function File:finish() -- right, this item was within a section or a 'class' local section_description - if this_mod.section then + local classmod = this_mod.type == 'classmod' + if this_mod.section or classmod then + local stype local this_section = this_mod.section - item.section = this_section.display_name + if this_section then + item.section = this_section.display_name + stype = this_section.type + end -- if it was a class, then if the name is unqualified then it becomes -- 'Class:foo' (unless flagged as being a constructor, static or not a function) - local stype = this_section.type - if doc.class_tag(stype) then + if doc.class_tag(stype) or classmod then if not item.name:match '[:%.]' then -- not qualified - local class = this_section.name + local class = classmod and this_mod.name or this_section.name local lang = this_mod.file.lang local static = item.tags.constructor or item.tags.static or item.type ~= 'function' item.name = class..(not static and lang.method_call or '.')..item.name @@ -321,7 +342,16 @@ function File:finish() end end end - section_description = this_section.summary..' '..(this_section.description or '') + if this_section then + section_description = this_section.summary..' '..(this_section.description or '') + this_section.summary = '' + elseif item.tags.within then + section_description = item.tags.within + item.section = section_description + else + section_description = "Methods" + item.section = item.type + end elseif item.tags.within then section_description = item.tags.within item.section = section_description diff --git a/ldoc/html.lua b/ldoc/html.lua index 06db178d..dcec02e4 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -152,10 +152,8 @@ function html.generate_output(ldoc, args, project) return (s:gsub('%W','_')) end - function ldoc.titlecase(s) - return (s:gsub('(%a)(%a*)',function(f,r) - return f:upper()..r - end)) + function ldoc.module_typename(m) + return doc.presentation_name(m.type) end function ldoc.is_list (t) diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index b16c9781..a730db5c 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -85,7 +85,7 @@ return [==[
    #if module then -

    $(ldoc.titlecase(module.type)) $(module.name)

    +

    $(ldoc.module_typename(module)) $(module.name)

    # end # if ldoc.body then -- verbatim HTML as contents; 'non-code' entries diff --git a/ldoc/parse.lua b/ldoc/parse.lua index f223327e..bb573daa 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -74,9 +74,19 @@ end local Tags = {} Tags.__index = Tags -function Tags.new (t) +function Tags.new (t,name) + local class + if name then + class = t + t = {} + end t._order = List() - return setmetatable(t,Tags) + local tags = setmetatable(t,Tags) + if name then + tags:add('class',class) + tags:add('name',name) + end + return tags end function Tags:add (tag,value) @@ -347,6 +357,11 @@ local function parse_file(fname, lang, package, args) if module_item then F:error("Module already declared!") end + if tags.class == 'classmod' then + tags = tags.new('section','methods') + tags:add('summary','Methods') + F:new_item(tags,line) + end module_item = current_item end end diff --git a/tests/moonscript/List.moon b/tests/moonscript/List.moon new file mode 100644 index 00000000..c70ce39d --- /dev/null +++ b/tests/moonscript/List.moon @@ -0,0 +1,67 @@ +---- +-- A list class that wraps a table +-- @classmod List +import insert,concat,remove from table + +class List + --- constructor passed a table `t`, which can be `nil`. + new: (t) => + @ls = t or {} + + --- append to list. + add: (item) => + insert @ls,item + + --- insert `item` at `idx` + insert: (idx,item) => + insert @ls,idx,item + + --- remove item at `idx` + remove: (idx) => remove @ls,idx + + --- length of list + len: => #@ls + + --- string representation + -- @within Metamethods + __tostring: => '['..(concat @ls,',')..']' + + --- return idx of first occurence of `item` + find: (item) => + for i = 1,#@ls + if @ls[i] == item then return i + + --- remove item by value + remove_value: (item) => + idx = self\find item + self\remove idx if idx + + --- remove a list of items + remove_values: (items) => + for item in *items do self\remove_value item + + --- create a sublist of items indexed by a table `indexes` + index_by: (indexes) => + List [@ls[idx] for idx in *indexes] + + --- make a copy of this list + copy: => List [v for v in *@ls] + + --- append items from the table or list `list` + extend: (list) => + other = if list.__class == List then list.ls else list + for v in *other do self\add v + self + + --- concatenate two lists, giving a new list + -- @within Metamethods + __concat: (l1,l2) -> l1\copy!\extend l2 + + --- an iterator over all items + iter: => + i,t,n = 0,@ls,#@ls + -> + i += 1 + if i <= n then t[i] + +return List diff --git a/tests/moonscript/config.ld b/tests/moonscript/config.ld new file mode 100644 index 00000000..e92d36ae --- /dev/null +++ b/tests/moonscript/config.ld @@ -0,0 +1,5 @@ +file = 'list.moon' +no_return_or_parms=true +no_summary=true +format = 'markdown' +--sort = true From 80a109e022c91686f71547a221de97d397de5e6f Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 20 Aug 2013 14:57:41 +0200 Subject: [PATCH 035/106] issue #66: better error message for non-luadoc behaviour; now controlling backticks in the docs better --- doc/config.ld | 1 + ldoc/doc.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/config.ld b/doc/config.ld index aacd86ac..f0ac5f38 100644 --- a/doc/config.ld +++ b/doc/config.ld @@ -2,6 +2,7 @@ project='LDoc' title='LDoc documentation' description='A Lua documentation tool' format='discount' +backtick_references=false file='../ldoc.lua' dir='../out' readme='doc.md' diff --git a/ldoc/doc.lua b/ldoc/doc.lua index e0495ac7..884b8ccc 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -422,7 +422,7 @@ end function Item:trailing_warning (kind,tag,rest) if type(rest)=='string' and #rest > 0 then - Item.warning(self,kind.." tag: '"..tag..'" has trailing text; use no_luadoc=true\n'..rest) + Item.warning(self,kind.." tag: '"..tag..'" has trailing text; use not_luadoc=true if you want description to continue between tags\n'..rest) end end From 5699d002efcf25b37ec95b338170f7c297cb5237 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 20 Aug 2013 14:59:13 +0200 Subject: [PATCH 036/106] issue #77: 'See also' in template --- ldoc/html/ldoc_ltp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index a730db5c..5336bdd5 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -201,7 +201,7 @@ return [==[ # if item.see then # local li,il = use_li(item.see) -

    see also:

    +

    See also:

      # for see in iter(item.see) do $(li)$(see.label)$(il) From dfdac3f977ba3dc43e18d4b7196dc72677083d3b Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 20 Aug 2013 15:26:49 +0200 Subject: [PATCH 037/106] fix #84; also no_space_before_args=false to stop ldoc putting space between name and arglist of functions --- ldoc.lua | 1 + ldoc/doc.lua | 2 +- ldoc/html.lua | 10 ++++++++-- tests/styles/four.lua | 6 +++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index f1e2c70a..8817e4d1 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -191,6 +191,7 @@ local ldoc_contents = { 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file', 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', + 'no_space_before_args', } ldoc_contents = tablex.makeset(ldoc_contents) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 884b8ccc..bacad6e7 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -737,7 +737,7 @@ function build_arg_list (names,pmods) end opt = m.optchain or m.opt if opt then - acc(' [') + acc('[') npending=npending+1 end end diff --git a/ldoc/html.lua b/ldoc/html.lua index dcec02e4..abd664b5 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -143,8 +143,14 @@ function html.generate_output(ldoc, args, project) function ldoc.display_name(item) local name = item.display_name or item.name - if item.type == 'function' or item.type == 'lfunction' then return name..' '..item.args --   - else return name end + 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.no_spaces(s) diff --git a/tests/styles/four.lua b/tests/styles/four.lua index 492a8fab..a1787263 100644 --- a/tests/styles/four.lua +++ b/tests/styles/four.lua @@ -27,7 +27,6 @@ end function two (one,two,three,four) end - --- third useless function. -- Can always put comments inline, may -- be multiple. @@ -38,6 +37,11 @@ function three ( -- person: -- not less than zero! ) +---- function with single optional arg +-- @param[opt] one +function four (one) +end + --- an implicit table. -- Again, we can use the comments person = { From d9d749fa376d6c4217f5b710f3924ca6662a9aa0 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 21 Aug 2013 14:52:09 +0200 Subject: [PATCH 038/106] fix issue #79: will not allow a module as a type. Plus, add reference lookup for lfs and lpeg --- ldoc/builtin/globals.lua | 49 +++++++++++++++++++++++++++++----------- ldoc/doc.lua | 15 ++++++++---- ldoc/html.lua | 2 +- ldoc/markup.lua | 8 +++---- 4 files changed, 52 insertions(+), 22 deletions(-) diff --git a/ldoc/builtin/globals.lua b/ldoc/builtin/globals.lua index 06dbdd5e..ed51f023 100644 --- a/ldoc/builtin/globals.lua +++ b/ldoc/builtin/globals.lua @@ -73,29 +73,52 @@ else globals.set_manual_url 'http://www.lua.org/manual/5.1/manual.html' end +-- external libs tracked by LDoc using LDoc style +local xlibs = { + lfs='lfs.html', lpeg='lpeg.html', +} +local xlib_url = 'http://stevedonovan.github.io/lua-stdlibs/modules/' + local tables = globals.tables -local function function_ref (name) - return {href = fun_ref..name, label = name} +local function function_ref (name,tbl) + local href + tbl = tbl or '' + if tables[tbl] then + name = tbl..'.'..name + href = fun_ref..name + elseif xlibs[tbl] then + href = xlib_url..xlibs[tbl]..'#'..name + name = tbl..'.'..name + else + return nil + end + return {href = href, label = name} end -local function module_ref (name) - return {href = manual..tables[name], label = name} +local function module_ref (tbl) + local href + if tables[tbl] ~= nil then + href = manual..tables[tbl] + elseif xlibs[tbl] then + href = xlib_url..xlibs[tbl] + else + return nil + end + return {href = href, label = tbl} end function globals.lua_manual_ref (name) local tbl,fname = tools.split_dotted_name(name) + local ref if not tbl then -- plain symbol - if functions[name] then - return function_ref(name) - end - if tables[name] then - return module_ref(name) - end + ref = function_ref(name) + if ref then return ref end + ref = module_ref(name) + if ref then return ref end else - if tables[tbl] then - return function_ref(name) - end + ref = function_ref(fname,tbl) + if ref then return ref end end return nil end diff --git a/ldoc/doc.lua b/ldoc/doc.lua index bacad6e7..290aee07 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -830,19 +830,26 @@ local function reference (s, mod_ref, item_ref) return {mod = mod_ref, name = name, label=s} end -function Module:process_see_reference (s,modules) +function Module:process_see_reference (s,modules,istype) local mod_ref,fun_ref,name,packmod local ref = custom_see_references(s) if ref then return ref end if not s:match '^[%w_%.%:%-]+$' or not s:match '[%w_]$' then return nil, "malformed see reference: '"..s..'"' end + local function ismod(item) + if item == nil then return false end + if not istype then return true + else + return item.type == 'classmod' + end + end -- is this a fully qualified module name? local mod_ref = modules.by_name[s] - if mod_ref then return reference(s, mod_ref,nil) end + if ismod(mod_ref) then return reference(s, mod_ref,nil) end -- module reference? mod_ref = self:hunt_for_reference(s, modules) - if mod_ref then return mod_ref end + if ismod(mod_ref) then return mod_ref end -- method reference? (These are of form CLASS.NAME) fun_ref = self.items.by_name[s] if fun_ref then return reference(s,self,fun_ref) end @@ -871,7 +878,7 @@ function Module:process_see_reference (s,modules) end else -- plain jane name; module in this package, function in this module mod_ref = modules.by_name[self.package..'.'..s] - if mod_ref then return reference(s, mod_ref,nil) end + if ismod(mod_ref) then return reference(s, mod_ref,nil) end fun_ref = self.items.by_name[s] if fun_ref then return reference(s, self,fun_ref) else diff --git a/ldoc/html.lua b/ldoc/html.lua index abd664b5..6fe21d85 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -180,7 +180,7 @@ function html.generate_output(ldoc, args, project) end local types = {} for name in tp:gmatch("[^|]+") do - local ref,err = markup.process_reference(name) + local ref,err = markup.process_reference(name,true) if ref then types[#types+1] = ('%s'):format(ldoc.href(ref),ref.label or name) else diff --git a/ldoc/markup.lua b/ldoc/markup.lua index ed2959ff..8be16205 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -285,21 +285,21 @@ function markup.create (ldoc, format, pretty) global_context = ldoc.package and ldoc.package .. '.' prettify.set_prettifier(pretty) - markup.process_reference = function(name) + markup.process_reference = function(name,istype) if local_context == 'none.' and not name:match '%.' then return nil,'not found' end local mod = ldoc.single or ldoc.module or ldoc.modules[1] - local ref,err = mod:process_see_reference(name, ldoc.modules) + local ref,err = mod:process_see_reference(name, ldoc.modules, istype) if ref then return ref end if global_context then local qname = global_context .. name - ref = mod:process_see_reference(qname, ldoc.modules) + ref = mod:process_see_reference(qname, ldoc.modules, istype) if ref then return ref end end if local_context then local qname = local_context .. name - ref = mod:process_see_reference(qname, ldoc.modules) + ref = mod:process_see_reference(qname, ldoc.modules, istype) if ref then return ref end end -- note that we'll return the original error! From a162c4b9e96a8d91fd1fb45bc77db780a37c1e7d Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 21 Aug 2013 15:52:53 +0200 Subject: [PATCH 039/106] a list of comma-separated items can appear after a see tag; fixed problem with references to Lua global functions from last commit --- ldoc/builtin/globals.lua | 16 +++++++++++----- ldoc/doc.lua | 3 +++ ldoc/tools.lua | 13 +++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/ldoc/builtin/globals.lua b/ldoc/builtin/globals.lua index ed51f023..deb0c450 100644 --- a/ldoc/builtin/globals.lua +++ b/ldoc/builtin/globals.lua @@ -83,11 +83,17 @@ local tables = globals.tables local function function_ref (name,tbl) local href - tbl = tbl or '' - if tables[tbl] then + if not tbl then -- can only be a standard Lua global function + if globals.functions[name] then + return {href = fun_ref..name, label = name} + else + return nil + end + end + if tables[tbl] then -- function inside standard Lua table name = tbl..'.'..name href = fun_ref..name - elseif xlibs[tbl] then + elseif xlibs[tbl] then -- in external libs, use LDoc style href = xlib_url..xlibs[tbl]..'#'..name name = tbl..'.'..name else @@ -98,9 +104,9 @@ end local function module_ref (tbl) local href - if tables[tbl] ~= nil then + if tables[tbl] ~= nil then -- standard Lua table href = manual..tables[tbl] - elseif xlibs[tbl] then + elseif xlibs[tbl] then -- external lib href = xlib_url..xlibs[tbl] else return nil diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 290aee07..220f210c 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -556,6 +556,9 @@ function Item:finish() self.modifiers = extract_tag_modifiers(tags) self.usage = read_del(tags,'usage') tags.see = read_del(tags,'see') + if tags.see then + tags.see = tools.identifier_list(tags.see) + end if doc.project_level(self.type) then -- we are a module, so become one! self.items = List() diff --git a/ldoc/tools.lua b/ldoc/tools.lua index de7480af..9aeb0b9f 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -172,6 +172,19 @@ function M.extract_identifier (value) return value:match('([%.:%-_%w]+)(.*)$') end +function M.identifier_list (ls) + local ns = List() + if type(ls) == 'string' then ls = List{ns} end + for s in ls:iter() do + if s:match ',' then + ns:extend(List.split(s,'[,%s]+')) + else + ns:append(s) + end + end + return ns +end + function M.strip (s) return s:gsub('^%s+',''):gsub('%s+$','') end From 5e18d2ad4c4ec94a1f12bb8855c55efa668cedcc Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 22 Aug 2013 09:15:16 +0200 Subject: [PATCH 040/106] check builtin references. Builtin files are now all requireable (without module) --- ldoc/builtin/coroutine.lua | 4 +++- ldoc/builtin/debug.lua | 5 +++-- ldoc/builtin/globals.lua | 8 ++++++++ ldoc/builtin/io.lua | 4 +++- ldoc/builtin/lfs.lua | 7 +++++-- ldoc/builtin/lpeg.lua | 5 ++++- ldoc/builtin/math.lua | 4 +++- ldoc/builtin/os.lua | 4 +++- ldoc/builtin/package.lua | 4 +++- ldoc/builtin/string.lua | 5 ++++- ldoc/builtin/table.lua | 5 ++++- 11 files changed, 43 insertions(+), 12 deletions(-) diff --git a/ldoc/builtin/coroutine.lua b/ldoc/builtin/coroutine.lua index 6992e20f..b6ea4630 100644 --- a/ldoc/builtin/coroutine.lua +++ b/ldoc/builtin/coroutine.lua @@ -1,6 +1,7 @@ --- creating and controlling coroutines. +-- @module coroutine -module 'coroutine' +local coroutine = {} --- -- Creates a new coroutine, with body `f`. `f` must be a Lua @@ -46,3 +47,4 @@ function coroutine.wrap(f) end -- `yield` are passed as extra results to `resume`. function coroutine.yield(...) end +return coroutine diff --git a/ldoc/builtin/debug.lua b/ldoc/builtin/debug.lua index a5dd0be7..981a638f 100644 --- a/ldoc/builtin/debug.lua +++ b/ldoc/builtin/debug.lua @@ -1,7 +1,7 @@ --- getting runtime debug information. +-- @module debug -module 'debug' - +local debug = {} --- -- Enters an interactive mode with the user, running each string that -- the user enters. Using simple commands and other debug facilities, @@ -121,3 +121,4 @@ function debug.setmetatable(object, table) end -- with the given index. Otherwise, it returns the name of the upvalue. function debug.setupvalue(func, up, value) end +return debug diff --git a/ldoc/builtin/globals.lua b/ldoc/builtin/globals.lua index deb0c450..df6a903a 100644 --- a/ldoc/builtin/globals.lua +++ b/ldoc/builtin/globals.lua @@ -91,9 +91,17 @@ local function function_ref (name,tbl) end end if tables[tbl] then -- function inside standard Lua table + local t = rawget(_G,tbl) -- do a quick sanity check + if not rawget(t,name) then + return nil + end name = tbl..'.'..name href = fun_ref..name elseif xlibs[tbl] then -- in external libs, use LDoc style + local t = require('ldoc.builtin.'..tbl) + if not rawget(t,name) then + return nil + end href = xlib_url..xlibs[tbl]..'#'..name name = tbl..'.'..name else diff --git a/ldoc/builtin/io.lua b/ldoc/builtin/io.lua index f8145a11..28253dfa 100644 --- a/ldoc/builtin/io.lua +++ b/ldoc/builtin/io.lua @@ -1,6 +1,7 @@ --- Reading and Writing Files. +-- @module io -module 'io' +local io = {} --- -- Equivalent to `file:close()`. Without a `file`, closes the default @@ -155,3 +156,4 @@ function file:setvbuf(mode , size) end -- `string.format` before `write`. function file:write(...) end +return io diff --git a/ldoc/builtin/lfs.lua b/ldoc/builtin/lfs.lua index 772fbb04..ed37bc1d 100644 --- a/ldoc/builtin/lfs.lua +++ b/ldoc/builtin/lfs.lua @@ -1,6 +1,7 @@ --- File and Directory manipulation +-- @module lfs -module 'lfs' +local lfs = {} --- -- Returns a table with the file attributes corresponding to filepath (or nil @@ -73,7 +74,7 @@ function lfs.dir(path) end -- and its length; both should be numbers. -- Returns true if the operation was successful; in case of error, it returns -- nil plus an error string. -function lfs.lock(filehandle, mode, start, length) +function lfs.lock(filehandle, mode, start, length) end --- -- Creates a new directory. The argument is the name of the new directory. @@ -120,3 +121,5 @@ function lfs.touch(filepath , atime , mtime) end -- Returns true if the operation was successful; in case of error, it returns -- nil plus an error string. function lfs.unlock(filehandle, start, length) end + +return lfs diff --git a/ldoc/builtin/lpeg.lua b/ldoc/builtin/lpeg.lua index 97053a74..b2c020e0 100644 --- a/ldoc/builtin/lpeg.lua +++ b/ldoc/builtin/lpeg.lua @@ -1,6 +1,7 @@ --- LPeg PEG pattern matching. +-- @module lpeg -module 'lpeg' +local lpeg = {} --- -- The matching function. It attempts to match the given pattern against the @@ -209,3 +210,5 @@ function lpeg.Ct(patt) end -- Any extra values returned by the function become the values produced by the -- capture. function lpeg.Cmt(patt, function) end + +return lpeg diff --git a/ldoc/builtin/math.lua b/ldoc/builtin/math.lua index d3e9e79e..9e97544c 100644 --- a/ldoc/builtin/math.lua +++ b/ldoc/builtin/math.lua @@ -1,6 +1,7 @@ --- standard mathematical functions. +-- @module math -module 'math' +local math = {} --- -- Returns the absolute value of `x`. @@ -140,3 +141,4 @@ function math.tan(x) end -- Returns the hyperbolic tangent of `x`. function math.tanh(x) end +return math diff --git a/ldoc/builtin/os.lua b/ldoc/builtin/os.lua index 87a4a3fe..a8b36656 100644 --- a/ldoc/builtin/os.lua +++ b/ldoc/builtin/os.lua @@ -1,6 +1,7 @@ --- Operating System facilities like date, time and program execution. +-- @module os -module 'os' +local os = {} --- -- Returns an approximation of the amount in seconds of CPU time used by @@ -108,3 +109,4 @@ function os.time(table) end -- removes the file when the program ends. function os.tmpname() end +return os diff --git a/ldoc/builtin/package.lua b/ldoc/builtin/package.lua index 45e9b5da..61a60c59 100644 --- a/ldoc/builtin/package.lua +++ b/ldoc/builtin/package.lua @@ -1,6 +1,7 @@ --- controlling how `require` finds packages. +-- @module package -module 'package' +local package = {} --- -- The path used by `require` to search for a C loader. @@ -93,3 +94,4 @@ function package.loadlib(libname, funcname) end -- environment. To be used as an option to function `module`. function package.seeall(module) end +return package diff --git a/ldoc/builtin/string.lua b/ldoc/builtin/string.lua index e69ccb1f..af7448f6 100644 --- a/ldoc/builtin/string.lua +++ b/ldoc/builtin/string.lua @@ -1,6 +1,7 @@ --- string operations like searching and matching. +-- @module string -module 'string' +local string = {} --- -- Returns the internal numerical codes of the characters `s[i]`, `s[i+1]`, @@ -170,3 +171,5 @@ function string.sub(s, i , j) end -- definition of what a lowercase letter is depends on the current locale. function string.upper(s) end +return string + diff --git a/ldoc/builtin/table.lua b/ldoc/builtin/table.lua index a386a302..7221535a 100644 --- a/ldoc/builtin/table.lua +++ b/ldoc/builtin/table.lua @@ -1,6 +1,7 @@ --- manipulating Lua tables. +-- @module table -module 'table' +local table = {} --- -- Given an array where all elements are strings or numbers, returns @@ -39,3 +40,5 @@ function table.remove(table , pos) end -- (so that `not comp(a[i+1],a[i])` will be true after the sort). If `comp` -- is not given, then the '<' operator will be used. function table.sort(table , comp) end + +return table From 3c72e9e4d1da6fc42f3ec66b34e63da74e63338c Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 22 Aug 2013 09:43:26 +0200 Subject: [PATCH 041/106] fix issue #80, backticks expand in copyright and other 'info' tags. format='backtick' (-f backtick) is like format='plain' but with implicit 'backtick_references=true'. -X is short for 'not_luadoc' --- ldoc.lua | 1 + ldoc/html/ldoc_ltp.lua | 2 +- ldoc/markup.lua | 6 +++++- tests/styles/priority_queue.lua | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 8817e4d1..9910f5bc 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -51,6 +51,7 @@ ldoc, a documentation generator for Lua, vs 1.3.12 -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 + -X,--not_luadoc break LuaDoc compatibility. Descriptions may continue after tags. -D,--define (default none) set a flag to be used in config.ld -C,--colon use colon style -B,--boilerplate ignore first comment in source files diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 5336bdd5..0943f3c1 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -106,7 +106,7 @@ return [==[

      Info:

        # for tag, value in ldoc.pairs(module.info) do -
      • $(tag): $(value)
      • +
      • $(tag): $(M(value,module))
      • # end
      # end -- if module.info diff --git a/ldoc/markup.lua b/ldoc/markup.lua index 8be16205..b24e2255 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -45,7 +45,7 @@ local function resolve_inline_references (ldoc, txt, item, plain) if ref then return ('%s '):format(ldoc.href(ref),name) else - return '`'..name..'`' + return ''..name..'' end end) end @@ -281,6 +281,10 @@ end function markup.create (ldoc, format, pretty) local processor markup.plain = true + if format == 'backtick' then + ldoc.backtick_references = true + format = 'plain' + end backtick_references = ldoc.backtick_references global_context = ldoc.package and ldoc.package .. '.' prettify.set_prettifier(pretty) diff --git a/tests/styles/priority_queue.lua b/tests/styles/priority_queue.lua index cd8c70b7..5991f673 100644 --- a/tests/styles/priority_queue.lua +++ b/tests/styles/priority_queue.lua @@ -77,7 +77,7 @@ do return front_elem[PRIORITY_KEY], front_elem[VALUE_KEY] end - --- construct a @{priority_queue}. + --- construct a `priority_queue`. -- @constructor make_priority_queue = function() --- @export From 7bb95e5e7dffdc7e537cab7d65e239009a2558ba Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 22 Aug 2013 10:12:04 +0200 Subject: [PATCH 042/106] issue #78 default parm value now in documentation --- ldoc/doc.lua | 16 ++++++++++++++-- ldoc/html/ldoc_ltp.lua | 10 +++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 220f210c..4038c4fb 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -752,13 +752,25 @@ function build_arg_list (names,pmods) return '('..table.concat(buffer)..')' end -function Item:type_of_param(p) +function Item:param_modifiers (p) local mods = self.modifiers[self.parameter] if not mods then return '' end - local mparam = rawget(mods,p) + return rawget(mods,p) +end + +function Item:type_of_param(p) + local mparam = self:param_modifiers(p) return mparam and mparam.type or '' end +function Item:default_of_param(p) + local m = self:param_modifiers(p) + if not m then return nil end + local opt = m.optchain or m.opt + if opt == true then return nil end + return opt +end + function Item:type_of_ret(idx) local rparam = self.modifiers['return'][idx] return rparam and rparam.type or '' diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 0943f3c1..08e03d68 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -165,12 +165,16 @@ return [==[
        # end # for p in iter(param) do -# local name,tp = item:display_name_of(p), ldoc.typename(item:type_of_param(p)) +# local name,tp,def = item:display_name_of(p), ldoc.typename(item:type_of_param(p)), item:default_of_param(p)
      • $(name) # if tp ~= '' then $(tp) -# end - $(M(item.params[p],item))
      • +# end + $(M(item.params[p],item)) +# if def then + (default $(def)) +# end + # end # if sublist then
      From 70eaf2be6fafa14ef2b29d11e36152ea01d5acb8 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Fri, 23 Aug 2013 13:50:34 +0200 Subject: [PATCH 043/106] bumped to 1.4.0; doc updates and formatting --- doc/config.ld | 2 + doc/doc.md | 98 +++++++++++++++++++++++++----------------- ldoc.lua | 6 +-- ldoc/doc.lua | 2 +- ldoc/html/ldoc_ltp.lua | 2 +- ldoc/markup.lua | 1 + 6 files changed, 66 insertions(+), 45 deletions(-) diff --git a/doc/config.ld b/doc/config.ld index f0ac5f38..708fc736 100644 --- a/doc/config.ld +++ b/doc/config.ld @@ -6,3 +6,5 @@ backtick_references=false file='../ldoc.lua' dir='../out' readme='doc.md' +examples = {'../tests/styles/colon.lua','../tests/styles/four.lua','../tests/example/mylib.c'} + diff --git a/doc/doc.md b/doc/doc.md index ed8d5848..98bfc13a 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -214,7 +214,13 @@ If only one module or script is documented for a project, then the `index.html` contains the documentation for that module, since an index pointing to one module would be redundant. -(If you want to document a script, there is a project-level type 'script' for that.) +LDoc has a two-layer hierarchy; underneath the project, there are modules, scripts, classes +(containing code) and examples and 'topics' (containing documentation). These then contain +items like functions, tables, sections, and so forth. + +If you want to document scripts, then use `@script` instead of `@module`. New with 1.4 is +`@classmod` which is a module which exports a single class. + ## See References @@ -257,29 +263,28 @@ If a reference is not found within the project, LDoc checks to see if it is a re Lua standard function or table, and links to the online Lua manual. So references like 'table.concat' are handled sensibly. -References may be made inline using the @\{ref} syntax. This may appear anywhere in the -text, and is more flexible than @see. In particular, it provides one way to document the +References may be made inline using the `@{\ref}` syntax. This may appear anywhere in the +text, and is more flexible than `@see`. In particular, it provides one way to document the type of a parameter or return value when that type has a particular structure: ------ -- extract standard variables. -- @param s the string - -- @return @\{stdvars} + -- @return @{\stdvars} function extract_std(s) ... end ------ -- standard variables. - -- Use @\{extract_std} to parse a string containing variables, - -- and @\{pack_std} to make such a string. + -- Use @{\extract_std} to parse a string containing variables, + -- and @{\pack_std} to make such a string. -- @field length -- @field duration -- @field viscosity -- @table stdvars -@\{ref} is very useful for referencing your API from code samples and readme text. (I've had -to throw in a spurious backspace to stop expansion in this example.) +`@{\ref}` is very useful for referencing your API from code samples and readme text. -The link text can be changed from the default by the extended syntax @\{ref|text}. +The link text can be changed from the default by the extended syntax `@{\ref|text}. You can also put references in backticks, like `\`stdvars\``. This is commonly used in Markdown to indicate code, so it comes naturally when writing documents. It is controlled by @@ -311,20 +316,20 @@ online references to the Linux manpages. So in `config.ld` we have: return name, url end) -'^(%a+)%((%d)%)$' both matches the pattern and extracts the name and its section. THen it's +'^(%a+)%((%d)%)$' both matches the pattern and extracts the name and its section. Then it's a simple matter of building up the appropriate URL. The function is expected to return _link text_ and _link source_ and the patterns are checked before LDoc tries to resolve project references. So it is best to make them match as exactly as possible. ## Sections -LDoc supports _explicit_ sections. By default, the sections correspond to the pre-existing +LDoc supports _explicit_ sections. By default, the implicit sections correspond to the pre-existing types in a module: 'Functions', 'Tables' and 'Fields' (There is another default section 'Local Functions' which only appears if LDoc is invoked with the `--all` flag.) But new -sections can be added; the first mechanism is when you define a new type (say 'macro') a new -section ('Macros') is created to contain these types. There is also a way to declare ad-hoc -sections using the `@section` tag. +sections can be added; the first mechanism is when you define a new type (say 'macro'). Then a new +section ('Macros') is created to contain these types. +There is also a way to declare ad-hoc sections using the `@section` tag. The need occurs when a module has a lot of functions that need to be put into logical sections. @@ -442,7 +447,7 @@ However, you must either use the `--colon` flag or set `colon=true` in your `con In this style, types may be used directly if prefixed with '!' or '?' (for type-or-nil) -(see `tests/styles/colon.lua`) +(see @{colon.lua}) ## Adding new Tags @@ -561,7 +566,7 @@ An LDoc feature which is particularly useful for C extensions is _module merging files are all marked as `@module lib` then a single module `lib` is generated, containing all the docs from the separate files. For this, use `merge=true`. -See 'tests/examples/mylib.c' for the full example. +See @{mylib.c} for the full example. ## Basic Usage @@ -628,10 +633,13 @@ markdown, and with extra features (`luarocks install lunamark`). You can request the processor you like with `format = 'markdown|discount|lunamark'`, and LDoc will attempt to use it. If it can't find it, it will look for one of the other -markdown processors. +markdown processors; the original `markdown.lua` ships with LDoc, although it's slow +for larger documents. Even with the default of 'plain' some minimal processing takes place, in particular empty lines -are treated as line breaks. +are treated as line breaks. If 'process_backticks=true` then backticks will be +expanded into documentation links like `@{\ref}` and converted into `ref` +otherwise. This formatting applies to all of a project, including any readmes and so forth. You may want Markdown for this 'narrative' documentation, but not for your code comments. `plain=true` will @@ -843,7 +851,7 @@ which is meant to be called from one of the examples. Now a see-reference to `te resolves to 'examples/testu.lua.html'. Examples may link back to the API documentation, for instance the example `input.lua` has a -@\{spawn_process} inline reference. +`@{\spawn_process}` inline reference. By default, LDoc uses a built-in Lua code 'prettifier'. See-references are allowed in comments, and also in code if they're enclosed in backticks. @@ -861,10 +869,10 @@ Like all good Github projects, Winapi has a `readme.md`: This goes under the 'Topics' global section; the 'Contents' of this document is generated from the second-level (##) headings of the readme. -Readme files are always processed with the current Markdown processor, but may also contain @\{} references back +Readme files are always processed with the current Markdown processor, but may also contain `@{\ref}` references back to the documentation and to example files. Any symbols within backticks will be expanded as references, if possible. As with doc comments, a link to a standard Lua function like -@\{os.execute} will work as well. Any code sections will be pretty-printed as Lua, unless +`@{\os.execute}` will work as well. Any code sections will be pretty-printed as Lua, unless the first indented line is '@plain'. (See the source for this readme to see how it's used.) Another name for `readme` is `topics`, which is more descriptive. From LDoc 1.2, @@ -878,15 +886,15 @@ second-level, then third-level headings will be used `###`, and so forth. The im that the first heading must be top-level relative to the headings that follow, and must start at the first line. -A reference like @\{string.upper} is unambiguous, and will refer to the online Lua manual. +A reference like `@{\string.upper}` is unambiguous, and will refer to the online Lua manual. In a project like Penlight, it can get tedious to have to write out fully qualified names -like @\{pl.utils.printf}. The first simplification is to use the `package` field to resolve +like `@{\pl.utils.printf}`. The first simplification is to use the `package` field to resolve unknown references, which in this case is 'pl'. (Previously we discussed how `package` is used to tell LDoc where the base package is in cases where the module author wishes to remain vague, but it does double-duty here.) A further level of simplification comes from -the @lookup directive in documents, which must start at the first column on its own line. -For instance, if I am talking about `pl.utils`, then I can say "@lookup utils" and -thereafter references like @\{printf} will resolve correctly. +the `@lookup` directive in documents, which must start at the first column on its own line. +For instance, if I am talking about `pl.utils`, then I can say `@lookup utils` and +thereafter references like `@{\printf}` will resolve correctly. If you look at the source for this document, you will see a `@lookup doc.md` which allows direct references to sections like @{Readme_files|this}. @@ -895,16 +903,16 @@ Remember that the default is for references in backticks to be resolved; unlike references, it is not an error if the reference cannot be found. The _sections_ of a document (the second-level headings) are also references. This -particular section can be refered to as @\{doc.md.Resolving_References_in_Documents} - the +particular section can be refered to as `@{\doc.md.Resolving_References_in_Documents}` - the rule is that any non-alphabetic character is replaced by an underscore. ## Tag Modifiers Ay tag may have _tag modifiers_. For instance, you may say -@\param[type=number] and this associates the modifier `type` with value `number` with this -particular param tag. A shorthand can be introduced for this common case, which is "@tparam - "; in the same way @\treturn is defined. +`@param[type=number]` and this associates the modifier `type` with value `number` with this +particular param tag. A shorthand can be introduced for this common case, which is `@tparam + `; in the same way `@treturn` is defined. This is useful for larger projects where you want to provide the argument and return value types for your API, in a structured way that can be easily extracted later. There is a @@ -973,11 +981,11 @@ then LDoc can present this as the default value for this optional argument. end ----> displayed as: one (name, age [, calender='gregorian' [, offset=0]]) -(See `tests/styles/four.lua`) +(See @{four.lua}) ## Fields allowed in `config.ld` -These mostly have the same meaning as the corresponding parameters: +_Same meaning as the corresponding parameters:_ - `file` a file or directory containing sources. In `config.ld` this can also be a table of files and directories. @@ -996,17 +1004,20 @@ of files and directories. template. In `config.ld` they may also be `true`, meaning use the same directory as the configuration file. - `merge` allow documentation from different files to be merged into modules without - explicit @submodule tag +explicit @submodule tag -These only appear in `config.ld`: +_These only appear in `config.ld`:_ - - `description` a project description used under the project title + - `description` a short project description used under the project title + - `full_description` when you _really_ need a longer project description - `examples` a directory or file: can be a table - `readme` or `topics` readme files (to be processed with Markdown) - `pretty` code prettify 'lua' (default) or 'lxsh' - `charset` use if you want to override the UTF-8 default (also @charset in files) + - `sort` set if you want all items in alphabetical order - `no_return_or_parms` don't show parameters or return values in output - - `backtick_references` whether references in backticks will be resolved + - `backtick_references` whether references in backticks will be resolved. Happens by default +when using Markdown. When explicit will expand non-references in backticks into `` elements - `plain` set to true if `format` is set but you don't want code comments processed - `wrap` ?? - `manual_url` point to an alternative or local location for the Lua manual, e.g. @@ -1014,8 +1025,10 @@ These only appear in `config.ld`: - `no_summary` suppress the Contents summary - `custom_see_handler` function that filters see-references - `not_luadoc` set to `true` if the docs break LuaDoc compatibility + - `no_space_before_args` set to `true` if you do not want a space between a function's name and its arguments. + - `template_escape` overrides the usual '#' used for Lua code in templates. This needs to be changed if the output format is Markdown, for instance. -Available functions are: +_Available functions are:_ - `alias(a,tag)` provide an alias `a` for the tag `tag`, for instance `p` as short for `param` @@ -1024,8 +1037,8 @@ an extension to be recognized as this language - `add_section` - `new_type(tag,header,project_level)` used to add new tags, which are put in their own section `header`. They may be 'project level'. - - `tparam_alias(name,type)` for instance, you may wish that `Object` means `@\tparam -Object`. + - `tparam_alias(name,type)` for instance, you may wish that `Object` becomes a new tag alias +that means `@tparam Object`. - `custom_see_handler(pattern,handler)`. If a reference matches `pattern`, then the extracted values will be passed to `handler`. It is expected to return link text and a suitable URI. (This match will happen before default processing.) @@ -1059,9 +1072,10 @@ Although not currently rendered by the template as HTML, they can be extracted b ## Generating HTML -LDoc, like LuaDoc, generates output HTML using a template, in this case `ldoc_ltp.lua`. This +LDoc, like LuaDoc, generates output HTML using a template, in this case `ldoc/html/ldoc_ltp.lua`. This is expanded by the powerful but simple preprocessor devised originally by [Rici Lake](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor) which is now part of +Lake](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor) which is now part of Penlight. There are two rules - any line starting with '#' is Lua code, which can also be embedded with '$(...)'. @@ -1093,6 +1107,10 @@ extension to use; this can also be set in the configuration file. So it's possib a template that converts LDoc output to LaTex, for instance. The separation of processing and presentation makes this kind of new application possible with LDoc. +From 1.4, LDoc has some limited support for generating Markdown output, although only +for single files currently. Use `--ext md` for this. 'ldoc/html/ldoc_mdtp.lua' defines +the template for Markdown. + ## Internal Data Representation The `--dump` flag gives a rough text output on the console. But there is a more diff --git a/ldoc.lua b/ldoc.lua index 9910f5bc..c06986c5 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -35,7 +35,7 @@ app.require_here() --- @usage local usage = [[ -ldoc, a documentation generator for Lua, vs 1.3.12 +ldoc, a documentation generator for Lua, vs 1.4.0 -d,--dir (default doc) output directory -o,--output (default 'index') output name -v,--verbose verbose @@ -178,11 +178,11 @@ function ldoc.new_type (tag, header, project_level,subfield) end function ldoc.manual_url (url) - global.set_manual_url(url) + global.set_manual_url(url) end function ldoc.custom_see_handler(pat, handler) - doc.add_custom_see_handler(pat, handler) + doc.add_custom_see_handler(pat, handler) end local ldoc_contents = { diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 4038c4fb..37252754 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -122,7 +122,7 @@ function doc.module_info_tags () end --- annotation tags can appear anywhere in the code and may contain of these tags: +-- annotation tags can appear anywhere in the code and may contain any of these tags: known_tags._annotation_tags = { fixme = true, todo = true, warning = true } diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 08e03d68..85b4f4c6 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -254,7 +254,7 @@ return [==[
    -generated by LDoc 1.3.12 +generated by LDoc 1.4.0
    diff --git a/ldoc/markup.lua b/ldoc/markup.lua index b24e2255..43b6b91f 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -15,6 +15,7 @@ local backtick_references -- inline use same lookup as @see local function resolve_inline_references (ldoc, txt, item, plain) local res = (txt:gsub('@{([^}]-)}',function (name) + if name:match '^\\' then return '@{'..name:sub(2)..'}' end local qname,label = utils.splitv(name,'%s*|') if not qname then qname = name From 1bf3461917a545727b0111e4cea99089ebfb12c4 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Fri, 23 Aug 2013 15:59:35 +0200 Subject: [PATCH 044/106] doc updates --- doc/doc.md | 48 ++++++++++++++++++++++++------------------ tests/styles/colon.lua | 11 ++++++++-- tests/styles/four.lua | 2 +- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 98bfc13a..29b7b0fa 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -225,7 +225,7 @@ If you want to document scripts, then use `@script` instead of `@module`. New wi ## See References The tag 'see' is used to reference other parts of the documentation, and 'usage' can provide -examples of use: +examples of use; there can be multiple such tags: --------- -- split a string in two. @@ -897,7 +897,7 @@ For instance, if I am talking about `pl.utils`, then I can say `@lookup utils` a thereafter references like `@{\printf}` will resolve correctly. If you look at the source for this document, you will see a `@lookup doc.md` which allows -direct references to sections like @{Readme_files|this}. +direct references to sections like @{Readme_files|this} with `@{\Readme_files|this}`. Remember that the default is for references in backticks to be resolved; unlike @ references, it is not an error if the reference cannot be found. @@ -906,21 +906,26 @@ The _sections_ of a document (the second-level headings) are also references. Th particular section can be refered to as `@{\doc.md.Resolving_References_in_Documents}` - the rule is that any non-alphabetic character is replaced by an underscore. - ## Tag Modifiers Ay tag may have _tag modifiers_. For instance, you may say `@param[type=number]` and this associates the modifier `type` with value `number` with this -particular param tag. A shorthand can be introduced for this common case, which is `@tparam +particular param tag. A shorthand has been introduced for this common case, which is `@tparam `; in the same way `@treturn` is defined. This is useful for larger projects where you want to provide the argument and return value -types for your API, in a structured way that can be easily extracted later. There is a -useful function for creating new tags that can be used in `config.ld`: +types for your API, in a structured way that can be easily extracted later. + +These types can be combined, so that "?string|number" means "ether a string or a number"; +"?string" is short for "?|nil|string". However, for this last case you should usually use the +`opt` modifier discussed below. + +There is a useful function for creating new tags that can be used in `config.ld`: tparam_alias('string','string') -That is, "@string" will now have the same meaning as "@tparam string". +That is, "@string" will now have the same meaning as "@tparam string"; this also applies +to the optional type syntax "?|T1|T2". From 1.3, the following standard type aliases are predefined: @@ -932,19 +937,18 @@ From 1.3, the following standard type aliases are predefined: * `tab` 'table' * `thread` -The exact form of `` is not defined, but here is a suggested scheme: +When using 'colon-style' (@{colon.lua}) it's possible to directly use types by prepending +them with '!'; '?' is also naturally understood. - number -- a plain type - Bonzo -- a known type; a reference link will be generated - {string,number} -- a 'list' tuple, built from type expressions - {A=string,N=number} -- a 'struct' tuple, ditto - {Bonzo,...} -- an array of Bonzo objects - {[string]=Bonzo,...} -- a map of Bonzo objects with string keys - Array(Bonzo) -- (assuming that Array is a container) +The exact form of `` is not defined, but here is one suggested scheme: -Currently the `type` modifier is the only one known and used by LDoc when generating HTML -output. However, any other modifiers are allowed and are available for use with your own -templates or for extraction by your own tools. + * `number` -- a plain type + * `Bonzo` -- a known type; a reference link will be generated + * `{string,number}` -- a 'list' tuple of two values, built from type expressions + * `{A=string,N=number}` -- a 'struct', ditto (But it's often better to create a named table and refer to it) + * `{Bonzo,...}` -- an array of Bonzo objects + * `{[string]=Bonzo,...}` -- a map of Bonzo objects with string keys + * `Array(Bonzo)` -- (assuming that Array is a container type) The `alias` function within configuration files has been extended so that alias tags can be defined as a tag plus a set of modifiers. So `tparam` is defined as: @@ -966,7 +970,7 @@ Another modifier understood by LDoc is `opt`. For instance, end ----> displayed as: two (one [, two], three [, four]) -This modifier can also be used with type aliases. If a value is given for the modifier +This modifier can also be used with type aliases. If a value is given for `opt` then LDoc can present this as the default value for this optional argument. --- a function with typed args. @@ -981,6 +985,10 @@ then LDoc can present this as the default value for this optional argument. end ----> displayed as: one (name, age [, calender='gregorian' [, offset=0]]) +Currently the `type` and `opt` modifiers are the only ones known and used by LDoc when generating HTML +output. However, any other modifiers are allowed and are available for use with your own +templates or for extraction by your own tools. + (See @{four.lua}) ## Fields allowed in `config.ld` @@ -1006,7 +1014,7 @@ configuration file. - `merge` allow documentation from different files to be merged into modules without explicit @submodule tag -_These only appear in `config.ld`:_ +_These only appear in config.ld:_ - `description` a short project description used under the project title - `full_description` when you _really_ need a longer project description diff --git a/tests/styles/colon.lua b/tests/styles/colon.lua index ea72ead6..17912576 100644 --- a/tests/styles/colon.lua +++ b/tests/styles/colon.lua @@ -11,15 +11,22 @@ --- first useless function. -- Optional type specifiers are allowed in this format. --- As an extension, '?' is short for '?|'. +-- As an extension, '?T' is short for '?nil|T'. -- Note how these types are rendered! -- string: name -- int: age --- ?person2: options +-- ?person3: options -- treturn: ?table|string function one (name,age,options) end +--- implicit table can always use colon notation. +person2 = { + id=true, -- string: official ID number + sex=true, -- string: one of 'M', 'F' or 'N' + spouse=true, -- ?person3: wife or husband +} + --- explicit table in colon format. -- Note how '!' lets you use a type name directly. -- string: surname diff --git a/tests/styles/four.lua b/tests/styles/four.lua index a1787263..4c3f2906 100644 --- a/tests/styles/four.lua +++ b/tests/styles/four.lua @@ -11,7 +11,7 @@ -- 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 +-- @tparam ?string|Person name person's name -- @int age -- @string[opt='gregorian'] calender optional calendar -- @int[opt=0] offset optional offset From 9af4bae066567b09bd9bae4a5d7fb478477bfb3f Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 24 Aug 2013 13:21:41 +0200 Subject: [PATCH 045/106] return groups; experimental 'error' tag --- ldoc.lua | 12 ++++++++++++ ldoc/doc.lua | 40 ++++++++++++++++++++++++++++++++++------ ldoc/html/ldoc_ltp.lua | 19 +++++++++++-------- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index c06986c5..bd7fd2a5 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -149,6 +149,18 @@ function ldoc.tparam_alias (name,type) ldoc.alias(name,{'param',modifiers={type=type}}) end +ldoc.alias ('error',function(tags,value,modifiers) + local t = List{'nil','error message'} + local oret = tags['return'] + if not oret or type(oret) == 'string' then + if oret then t:insert(1,oret) end + tags:add('return',t) + else + tags['return']:extend(t) + end +end) + + ldoc.tparam_alias 'string' ldoc.tparam_alias 'number' ldoc.tparam_alias 'int' diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 37252754..e398d35d 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -405,10 +405,7 @@ function Item:_init(tags,file,line) self.tags = {} self.formal_args = tags.formal_args tags.formal_args = nil - local iter = tags.iter - if not iter then - iter = Map.iter - end + local iter = tags.iter or Map.Iter for tag in iter(tags) do self:set_tag(tag,tags[tag]) end @@ -482,7 +479,7 @@ function Item.check_tag(tags,tag, value, modifiers) if alias then if type(alias) == 'string' then tag = alias - else + elseif type(alias) == 'table' then --{ tag, value=, modifiers = } local avalue,amod tag, avalue, amod = alias[1],alias.value,alias.modifiers if avalue then value = avalue..' '..value end @@ -496,6 +493,8 @@ function Item.check_tag(tags,tag, value, modifiers) modifiers[m] = v end end + else -- has to be a function + alias(tags,value,modifiers) end end local ttype = known_tags[tag] @@ -684,6 +683,9 @@ function Item:finish() self.params = params self.args = build_arg_list (names,pmods) end + if self.ret then + self:build_return_groups() + end end -- ldoc allows comments in the formal arg list to be used, if they aren't specified with @param @@ -776,6 +778,33 @@ function Item:type_of_ret(idx) return rparam and rparam.type or '' end +local function integer_keys(t) + for k in pairs(t) do + local num = tonumber(k) + if num then return num end + end + return 0 +end + +function Item:build_return_groups() + local retmod = self.modifiers['return'] + local groups = List() + local lastg, group + for i,ret in ipairs(self.ret) do + local mods = retmod[i] + local g = integer_keys(mods) + print(g,lastg) + if g ~= lastg then + group = List() + groups:append(group) + lastg = g + end + group:append({text=ret, type = mods.type or ''}) + end + print(groups) + self.retgroups = groups +end + function Item:subparam(p) local subp = rawget(self.subparams,p) if subp then @@ -794,7 +823,6 @@ function Item:display_name_of(p) end end - function Item:warning(msg) local file = self.file and self.file.filename if type(file) == 'table' then require 'pl.pretty'.dump(file); file = '?' end diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 85b4f4c6..5eaa2454 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -183,19 +183,22 @@ return [==[
# end -- if params -# if show_return and item.ret then -# local li,il = use_li(item.ret) +# if show_return and item.retgroups then local groups = item.retgroups

Returns:

+# for i,group in ldoc.ipairs(groups) do local li,il = use_li(group)
    -# for i,r in ldoc.ipairs(item.ret) do +# for r in group:iter() do $(li) -# local tp = ldoc.typename(item:type_of_ret(i)) -# if tp ~= '' then +# local tp = ldoc.typename(r.type); if tp ~= '' then $(tp) -# end - $(M(r,item))$(il) -# end -- for +# end + $(M(r.text,item))$(il) +# end -- for r
+# if i < #groups then +

Or

+# end +# end -- for group # end -- if returns # if show_return and item.raise then From 3c72ea112e211130a5d1710973dfbba9cfb7b091 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 24 Aug 2013 15:19:45 +0200 Subject: [PATCH 046/106] ldoc.alias can specify a function which must return tag, value, modifiers like Item:check_tag. The alias error works with return groups --- ldoc.lua | 14 ++++---------- ldoc/doc.lua | 6 ++---- ldoc/parse.lua | 33 +++++++++++++++++++-------------- tests/styles/multiple.lua | 23 +++++++++++++++++++++++ 4 files changed, 48 insertions(+), 28 deletions(-) create mode 100644 tests/styles/multiple.lua diff --git a/ldoc.lua b/ldoc.lua index bd7fd2a5..892c7d05 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -149,18 +149,12 @@ function ldoc.tparam_alias (name,type) ldoc.alias(name,{'param',modifiers={type=type}}) end -ldoc.alias ('error',function(tags,value,modifiers) - local t = List{'nil','error message'} - local oret = tags['return'] - if not oret or type(oret) == 'string' then - if oret then t:insert(1,oret) end - tags:add('return',t) - else - tags['return']:extend(t) - end +ldoc.alias ('error',function(tags,value) + local g = '2' + tags:add('return','',{[g]=true,type='nil'}) + return 'return', value, {[g]=true,type='string'} end) - ldoc.tparam_alias 'string' ldoc.tparam_alias 'number' ldoc.tparam_alias 'int' diff --git a/ldoc/doc.lua b/ldoc/doc.lua index e398d35d..7f3c4506 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -405,7 +405,7 @@ function Item:_init(tags,file,line) self.tags = {} self.formal_args = tags.formal_args tags.formal_args = nil - local iter = tags.iter or Map.Iter + local iter = tags.iter or Map.iter for tag in iter(tags) do self:set_tag(tag,tags[tag]) end @@ -494,7 +494,7 @@ function Item.check_tag(tags,tag, value, modifiers) end end else -- has to be a function - alias(tags,value,modifiers) + return alias(tags,value,modifiers) end end local ttype = known_tags[tag] @@ -793,7 +793,6 @@ function Item:build_return_groups() for i,ret in ipairs(self.ret) do local mods = retmod[i] local g = integer_keys(mods) - print(g,lastg) if g ~= lastg then group = List() groups:append(group) @@ -801,7 +800,6 @@ function Item:build_return_groups() end group:append({text=ret, type = mods.type or ''}) end - print(groups) self.retgroups = groups end diff --git a/ldoc/parse.lua b/ldoc/parse.lua index bb573daa..5bbc1661 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -70,7 +70,8 @@ local function parse_colon_tags (text) return preamble,tag_items end --- Tags are stored as an ordered map +-- Tags are stored as an ordered multi map from strings to strings +-- If the same key is used, then the value becomes a list local Tags = {} Tags.__index = Tags @@ -89,9 +90,22 @@ function Tags.new (t,name) return tags end -function Tags:add (tag,value) +function Tags:add (tag,value,modifiers) + if modifiers then -- how modifiers are encoded + value = {value,modifiers=modifiers} + end + local ovalue = rawget(self,tag) + if ovalue then -- previous value? + if getmetatable(ovalue) ~= List then + ovalue = List{ovalue} + end + ovalue:append(value) + value = ovalue + end rawset(self,tag,value) - self._order:append(tag) + if not ovalue then + self._order:append(tag) + end end function Tags:iter () @@ -128,17 +142,8 @@ local function extract_tags (s,args) if not value:match '\n[^\n]+\n' then value = strip(value) end - - if modifiers then value = { value, modifiers=modifiers } end - local old_value = tags[tag] - - if not old_value then -- first element - tags:add(tag,value) - elseif type(old_value)=='table' and old_value.append then -- append to existing list - old_value :append (value) - else -- upgrade string->list - tags:add(tag,List{old_value, value}) - end + + tags:add(tag,value,modifiers) end return tags --Map(tags) end diff --git a/tests/styles/multiple.lua b/tests/styles/multiple.lua new file mode 100644 index 00000000..54c72cfe --- /dev/null +++ b/tests/styles/multiple.lua @@ -0,0 +1,23 @@ +------ +-- Various ways of indicating errors +-- @module multiple + +----- +-- function with return groups. +-- @treturn[1] string result +-- @return[2] nil +-- @return[2] error message +function mul1 () end + +----- +-- function with return and error tag +-- @return result +-- @error message +function mul2 () end + +----- +-- function that raises an error. +-- @string filename +-- @treturn string result +-- @raise 'file not found' +function mul3(filename) end From 45cb21bcc1421fa98e3ccbd18452516bc9f5649e Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 24 Aug 2013 15:39:41 +0200 Subject: [PATCH 047/106] try harder with -m to resolve methods --- ldoc.lua | 8 ++++++-- ldoc/doc.lua | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 892c7d05..ef9652d6 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -549,8 +549,12 @@ if args.module then if args.module == true then file_list[1]:dump(args.verbose) else - local fun = module_list[1].items.by_name[args.module] - if not fun then quit(quote(args.module).." is not part of "..quote(args.file)) end + 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 fun:dump(true) end return diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 7f3c4506..5c41e005 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -121,7 +121,6 @@ function doc.module_info_tags () return List.iter(known_tags._module_info) end - -- annotation tags can appear anywhere in the code and may contain any of these tags: known_tags._annotation_tags = { fixme = true, todo = true, warning = true From 149ded81fcf7ef3a4e70514c6e6fe81cef6c2ffd Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sun, 25 Aug 2013 10:47:45 +0200 Subject: [PATCH 048/106] composite return types experiment --- ldoc-scm-2.rockspec | 60 +++++++++++++++++++++++++++++++++ ldoc/doc.lua | 31 +++++++++++++++-- ldoc/html/ldoc_ltp.lua | 17 +++++++--- tests/styles/priority_queue.lua | 9 +++-- tests/styles/struct.lua | 12 +++++++ 5 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 ldoc-scm-2.rockspec create mode 100644 tests/styles/struct.lua diff --git a/ldoc-scm-2.rockspec b/ldoc-scm-2.rockspec new file mode 100644 index 00000000..39c06359 --- /dev/null +++ b/ldoc-scm-2.rockspec @@ -0,0 +1,60 @@ +package = "ldoc" +version = "scm-2" + +source = { + dir="LDoc", + url = "git://github.com/stevedonovan/LDoc.git" +} + +description = { + summary = "A Lua Documentation Tool", + detailed = [[ + LDoc is a LuaDoc-compatible documentation generator which can also + process C extension source. Markdown may be optionally used to + render comments, as well as integrated readme documentation and + pretty-printed example files + ]], + homepage='http://stevedonovan.github.com/ldoc', + maintainer='steve.j.donovan@gmail.com', + license = "MIT/X11", +} + +dependencies = { + "penlight","markdown" +} + +build = { + type = "builtin", + modules = { + ["ldoc.tools"] = "ldoc/tools.lua", + ["ldoc.lang"] = "ldoc/lang.lua", + ["ldoc.parse"] = "ldoc/parse.lua", + ["ldoc.html"] = "ldoc/html.lua", + ["ldoc.lexer"] = "ldoc/lexer.lua", + ["ldoc.markup"] = "ldoc/markup.lua", + ["ldoc.prettify"] = "ldoc/prettify.lua", + ["ldoc.doc"] = "ldoc/doc.lua", + ["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua", + ["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua", + ["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua", + ["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua", + ["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua", + ["ldoc.builtin.global"] = "ldoc/builtin/global.lua", + ["ldoc.builtin.debug"] = "ldoc/builtin/debug.lua", + ["ldoc.builtin.io"] = "ldoc/builtin/io.lua", + ["ldoc.builtin.lfs"] = "ldoc/builtin/lfs.lua", + ["ldoc.builtin.lpeg"] = "ldoc/builtin/lpeg.lua", + ["ldoc.builtin.math"] = "ldoc/builtin/math.lua", + ["ldoc.builtin.os"] = "ldoc/builtin/os.lua", + ["ldoc.builtin.package"] = "ldoc/builtin/package.lua", + ["ldoc.builtin.string"] = "ldoc/builtin/string.lua", + ["ldoc.builtin.table"] = "ldoc/builtin/table.lua", + }, + copy_directories = {'doc','tests'}, + install = { + bin = { + ldoc = "ldoc.lua" + } + } +} + diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 5c41e005..e754e5cb 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -778,6 +778,7 @@ function Item:type_of_ret(idx) end local function integer_keys(t) + if not t then return 0 end for k in pairs(t) do local num = tonumber(k) if num then return num end @@ -785,8 +786,14 @@ local function integer_keys(t) return 0 end +function Item:return_type(r) + if not r.type then return '' end + return r.type, r.ctypes +end + function Item:build_return_groups() - local retmod = self.modifiers['return'] + local modifiers = self.modifiers + local retmod = modifiers['return'] local groups = List() local lastg, group for i,ret in ipairs(self.ret) do @@ -797,9 +804,29 @@ function Item:build_return_groups() groups:append(group) lastg = g end - group:append({text=ret, type = mods.type or ''}) + group:append({text=ret, type = mods.type or '',mods = mods}) end self.retgroups = groups + -- cool, now see if there are any treturns that have tfields to associate with + local fields = self.tags.field + if fields then + local fcomments = List() + for i,f in ipairs(fields) do + local name, comment = f:match('%s*([%w_%.:]+)(.*)') + fields[i] = name + fcomments[i] = coment + end + local fmods = modifiers.field + for group in groups:iter() do for r in group:iter() do + if r.mods and r.mods.type == '*' then + local ctypes = List() + for i,f in ipairs(fields) do + ctypes:append {name=f,type=fmods[i].type,comment=fcomments[i]} + end + r.ctypes = ctypes + end + end end + end end function Item:subparam(p) diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 5eaa2454..450e1994 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -187,12 +187,21 @@ return [==[

Returns:

# for i,group in ldoc.ipairs(groups) do local li,il = use_li(group)
    -# for r in group:iter() do +# for r in group:iter() do local type, ctypes = item:return_type(r) $(li) -# local tp = ldoc.typename(r.type); if tp ~= '' then - $(tp) -# end +# if type ~= '' then + $(ldoc.typename(type)) +# end $(M(r.text,item))$(il) +# if ctypes then +
      +# for c in ctypes:iter() do +
    • $(c.name) + $(ldoc.typename(c.type)) + $(M(c.comment,item))
    • +# end +
    +# end -- if ctypes # end -- for r
# if i < #groups then diff --git a/tests/styles/priority_queue.lua b/tests/styles/priority_queue.lua index 5991f673..9dcc5ad5 100644 --- a/tests/styles/priority_queue.lua +++ b/tests/styles/priority_queue.lua @@ -1,7 +1,10 @@ -------------------------------------------------------------------------------- ---- Queue of objects sorted by priority +--- Queue of objects sorted by priority. -- @module lua-nucleo.priority_queue --- This file is a part of lua-nucleo library +-- This file is a part of lua-nucleo library. Note that if you wish to spread +-- the description after tags, then invoke with `not_luadoc=true`. +-- The flags here are `ldoc -X -f backtick priority_queue.lua`, which +-- also expands backticks. -- @copyright lua-nucleo authors (see file `COPYRIGHT` for the license) -------------------------------------------------------------------------------- @@ -34,7 +37,7 @@ do local insert = function(self, priority, value) method_arguments( - self, + s "number", priority ) assert(value ~= nil, "value can't be nil") -- value may be of any type, except nil diff --git a/tests/styles/struct.lua b/tests/styles/struct.lua new file mode 100644 index 00000000..9d454a21 --- /dev/null +++ b/tests/styles/struct.lua @@ -0,0 +1,12 @@ +------ +-- functions returning compound types +-- @module struct + +----- +-- returns a 'struct'. +-- @string name your name dammit +-- @treturn * details of person +-- @tfield string name of person +-- @tfield int age of person +function struct(name) end + From 89cb20d7528411e49e5192153b9471889aa979ac Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sun, 25 Aug 2013 14:28:57 +0200 Subject: [PATCH 049/106] doc updates --- config.ld | 7 --- doc/config.ld | 8 ++- doc/doc.md | 159 ++++++++++++++++++++++++++++++++------------------ 3 files changed, 108 insertions(+), 66 deletions(-) delete mode 100644 config.ld diff --git a/config.ld b/config.ld deleted file mode 100644 index 796e3cf0..00000000 --- a/config.ld +++ /dev/null @@ -1,7 +0,0 @@ -project='LDoc' -title='LDoc documentation' -description='A Lua documentation tool' -format='discount' -file='ldoc.lua' -dir='out' -readme='doc/doc.md' diff --git a/doc/config.ld b/doc/config.ld index 708fc736..1d01883c 100644 --- a/doc/config.ld +++ b/doc/config.ld @@ -6,5 +6,11 @@ backtick_references=false file='../ldoc.lua' dir='../out' readme='doc.md' -examples = {'../tests/styles/colon.lua','../tests/styles/four.lua','../tests/example/mylib.c'} +examples = { + '../tests/styles/colon.lua', + '../tests/styles/four.lua', + '../tests/styles/three.lua', + '../tests/example/mylib.c', + '../tests/moonscript/List.moon', +} diff --git a/doc/doc.md b/doc/doc.md index 29b7b0fa..b6177b32 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -205,6 +205,21 @@ and 'return' can be specified multiple times, whereas a type tag like 'function' occur once in a comment. The basic rule is that a single doc comment can only document one entity. +Since 1.3, LDoc allows the use of _colons_ instead of @. + + --- a simple function. + -- string name person's name + -- int: age age of person + -- !person: person object + -- treturn: ?string + -- function check(name,age) + +However, you must either use the `--colon` flag or set `colon=true` in your `config.ld`. + +In this style, types may be used directly if prefixed with '!' or '?' (for type-or-nil) + +(see @{colon.lua}) + By default, LDoc will process any file ending in '.lua' or '.luadoc' in a specified directory; you may point it to a single file as well. A 'project' usually consists of many modules in one or more _packages_. The generated `index.html` will point to the generated @@ -288,10 +303,13 @@ The link text can be changed from the default by the extended syntax `@{\ref|tex You can also put references in backticks, like `\`stdvars\``. This is commonly used in Markdown to indicate code, so it comes naturally when writing documents. It is controlled by -the configuration variable `backtick_references`; the default is `true` if you use Markdown -in your project, but can be specified explicitly in your `config.ld`. +the configuration variable `backtick_references` or the `backtick` format; +the default is `true` if you use Markdown in your project, but can be specified explicitly +in `config.ld`. -### Custom @see References +To quote such references so they won't be expanded, say @{\\ref}. + +#### Custom @see References It's useful to define how to handle references external to a project. For instance, in the [luaposix](https://github.com/luaposix/luaposix) project we wanted to have `man` references @@ -321,13 +339,46 @@ a simple matter of building up the appropriate URL. The function is expected to return _link text_ and _link source_ and the patterns are checked before LDoc tries to resolve project references. So it is best to make them match as exactly as possible. +## Module Tags + +LDoc requires you to have a module doc comment. If your code style requires +license blocks that might look like doc comments, then set `boilerplate=true` in your +configuration and they will be skipped. + +This comment does not have to have an explicit `@module` tag and LDoc continues to +respect the use of `module()`. + +There are three types of 'modules' (i.e. 'project-level'); `module`, a library +loadable with `require()`, `script`, a program, and `classmod` which is a class +implemented in a single module. + +There are some tags which are only useful in module comments: `author`,`copyright`, +`license` and `release`. These are presented in a special **Info** section in the +default HTML output. + +The `@usage` tag has a somewhat different presentation when used in modules; the text +is presented formatted as-is in a code font. If you look at the script `ldoc` in +this documentation, you can see how the command-line usage is shown. Since coding +is all about avoiding repetition and the out-of-sync issues that arise, +the `@usage` tag can appear later in the module, before a long string. For instance, +the main script of LDoc is [ldoc.lua](https://github.com/stevedonovan/LDoc/blob/master/ldoc.lua) +and you will see that the usage tag appears on line 36 before the usage string +presented as help. + +`@export` is another module tag that is usually 'detached'. It is for supporting +modules that wish to explicitly export their functions @{three.lua|at the end}. +In that example, both `question` and `answer` are local and therefore private to +the module, but `answer` has been explicitly exported. (If you invoke LDoc with +the `-a` flag on this file, you will see the documentation for the unexported +function as well.) + ## Sections LDoc supports _explicit_ sections. By default, the implicit sections correspond to the pre-existing types in a module: 'Functions', 'Tables' and 'Fields' (There is another default section 'Local Functions' which only appears if LDoc is invoked with the `--all` flag.) But new -sections can be added; the first mechanism is when you define a new type (say 'macro'). Then a new -section ('Macros') is created to contain these types. +sections can be added; the first mechanism is when you @{Adding_new_Tags|define a new type} +(say 'macro'). Then a new section ('Macros') is created to contain these types. There is also a way to declare ad-hoc sections using the `@section` tag. The need occurs when a module has a lot of functions that need to be put into logical @@ -351,9 +402,9 @@ sections. A section doc-comment has the same structure as a normal doc-comment; the summary is used as the new section title, and the description will be output at the start of the function -details for that section. +details for that section; the name is not used, but must be unique. -In any case, sections appear under 'Contents' on the left-hand side. See the +Sections appear under 'Contents' on the left-hand side. See the [winapi](http://stevedonovan.github.com/winapi/api.html) documentation for an example of how this looks. @@ -374,7 +425,7 @@ A specialized kind of section is `type`: it is used for documenting classes. The end (In an ideal world, we would use the word 'class' instead of 'type', but this would conflict -with the LuaDoc usage.) +with the LuaDoc `class` tag.) A section continues until the next section is found, `@section end`, or end of file. @@ -382,7 +433,12 @@ You can put items into an implicit section using the @within tag. This allows yo adjacent functions in different sections, so that you are not forced to order your code in a particular way. -Sometimes a module may logically span several files. There will be a master module with name +With 1.4, there is another option for documenting classes, which is the top-level type +`classmod`. It is intended for larger classes which are implemented within one module, +and the advantage that methods can be put into sections. + +Sometimes a module may logically span several files, which can easily happen with large +There will be a master module with name 'foo' and other files which when required add functions to that module. If these files have a @submodule tag, their contents will be placed in the master module documentation. However, a current limitation is that the master module must be processed before the submodules. @@ -416,38 +472,10 @@ can define an alias for it, such as 'p'. This can also be specified in the confi LDoc will also work with C/C++ files, since extension writers clearly have the same documentation needs as Lua module writers. -LDoc allows you to attach a _type_ to a parameter or return value - - --- better mangler. - -- @tparam string name - -- @int max length - -- @treturn string mangled name - function strmangler(name,max) - ... - end - -`int` here is short for `tparam int` (see @{Tag_Modifiers}) - -It's common for types to be optional, or have different types, so the type can be like -'?int|string' which renders as '(int or string)', or '?int', which renders as -'(optional int)'. - -LDoc gives the documenter the option to use Markdown to parse the contents of comments. - -Since 1.3, LDoc allows the use of _colons_ instead of @. - - --- a simple function. - -- string name person's name - -- int: age age of person - -- !person: person object - -- treturn: ?string - -- function check(name,age) - -However, you must either use the `--colon` flag or set `colon=true` in your `config.ld`. - -In this style, types may be used directly if prefixed with '!' or '?' (for type-or-nil) - -(see @{colon.lua}) +LDoc allows you to attach a _type_ to a parameter or return value with `tparam` or `treturn`, +and gives the documenter the option to use Markdown to parse the contents of comments. +You may also include code examples which will be prettified, and readme files which will be +rendered with Markdown and contain prettified code blocks. ## Adding new Tags @@ -542,16 +570,17 @@ As always, explicit tags can override this behaviour if it is inappropriate. LDoc can process C/C++ files: - @plain - /*** - Create a table with given array and hash slots. - @function createtable - @param narr initial array slots, default 0 - @param nrec initial hash slots, default 0 - @return the new table - */ - static int l_createtable (lua_State *L) { - .... +```c +/*** +Create a table with given array and hash slots. +@function createtable +@param narr initial array slots, default 0 +@param nrec initial hash slots, default 0 +@return the new table +*/ +static int l_createtable (lua_State *L) { +.... +``` Both `/**` and `///` are recognized as starting a comment block. Otherwise, the tags are processed in exactly the same way. It is necessary to specify that this is a function with a @@ -568,6 +597,13 @@ the docs from the separate files. For this, use `merge=true`. See @{mylib.c} for the full example. +## Moonscript Support + +1.4 introduces basic support for [Moonscript](http://moonscript.org). Moonscript module +conventions are just the same as Lua, except for an explicit class construct. +@{list.moon} shows how `@classmod` can declare modules that export one class, with metamethods +put explicitly into a separate section. + ## Basic Usage For example, to process all files in the 'lua' directory: @@ -631,13 +667,14 @@ more features than the pure Lua version, such as PHP-Extra style tables. - [lunamark](http://jgm.github.com/lunamark/), another pure Lua processor, faster than markdown, and with extra features (`luarocks install lunamark`). -You can request the processor you like with `format = 'markdown|discount|lunamark'`, and +You can request the processor you like with `format = 'markdown|discount|lunamark|plain|backticks'`, and LDoc will attempt to use it. If it can't find it, it will look for one of the other markdown processors; the original `markdown.lua` ships with LDoc, although it's slow for larger documents. Even with the default of 'plain' some minimal processing takes place, in particular empty lines -are treated as line breaks. If 'process_backticks=true` then backticks will be +are treated as line breaks. If the 'backticks' formatter is used, then it's equivalent to +using 'process_backticks=true` in `config.ld` and backticks will be expanded into documentation links like `@{\ref}` and converted into `ref` otherwise. @@ -847,17 +884,18 @@ The line in the `config.ld` that enables this is: examples = {'examples', exclude = {'examples/slow.lua'}} That is, all files in the `examples` folder are to be pretty-printed, except for `slow.lua` -which is meant to be called from one of the examples. Now a see-reference to `testu.lua` -resolves to 'examples/testu.lua.html'. +which is meant to be called from one of the examples. +To link to an example, use a reference like `@{\testu.lua}` +which resolves to 'examples/testu.lua.html'. Examples may link back to the API documentation, for instance the example `input.lua` has a `@{\spawn_process}` inline reference. -By default, LDoc uses a built-in Lua code 'prettifier'. See-references are allowed in comments, -and also in code if they're enclosed in backticks. +By default, LDoc uses a built-in Lua code 'prettifier'. Reference links are allowed in comments, +and also in code if they're enclosed in backticks. Lua and C are known languages. [lxsh](https://github.com/xolox/lua-lxsh) -can be used (available from LuaRocks) if you want support for C as well. `pretty='lxsh'` will +can be used (available from LuaRocks) if you want something more powerful. `pretty='lxsh'` will cause `lxsh` to be used, if available. ## Readme files @@ -906,6 +944,11 @@ The _sections_ of a document (the second-level headings) are also references. Th particular section can be refered to as `@{\doc.md.Resolving_References_in_Documents}` - the rule is that any non-alphabetic character is replaced by an underscore. +Any indented blocks are assumed to be Lua, unless their first line is `@plain`. New +with 1.4 is github-markdown-style fenced code blocks, which start with three backticks +optionally followed by a language. The code continues until another three backticks +is found: the language can be `c`,'cpp' or `cxx` for C/C++, anything else is Lua. + ## Tag Modifiers Ay tag may have _tag modifiers_. For instance, you may say From ad909d683bdff0e68ca142334bd23b0e9bcc0153 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sun, 25 Aug 2013 14:29:30 +0200 Subject: [PATCH 050/106] Can prettify C files as well as Lua with built-in prettifier --- ldoc.lua | 5 +++-- ldoc/prettify.lua | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index ef9652d6..9c589744 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -7,7 +7,7 @@ -- -- C/C++ support for Lua extensions is provided. -- --- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.3.9.zip) +-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.4.0.zip) -- -- [Github Page](https://github.com/stevedonovan/ldoc) -- @@ -475,7 +475,8 @@ if type(ldoc.examples) == 'table' then }) -- wrap prettify for this example so it knows which file to blame -- if there's a problem - item.postprocess = function(code) return prettify.lua(f,code,0,true) end + local ext = path.extension(f):sub(2) + item.postprocess = function(code) return prettify.lua(ext,f,code,0,true) end end) end diff --git a/ldoc/prettify.lua b/ldoc/prettify.lua index d33f34ab..8013dc5a 100644 --- a/ldoc/prettify.lua +++ b/ldoc/prettify.lua @@ -4,9 +4,7 @@ -- A module reference to an example `test-fun.lua` would look like -- `@{example:test-fun}`. local List = require 'pl.List' -local lexer = require 'ldoc.lexer' local globals = require 'ldoc.builtin.globals' -local tnext = lexer.skipws local prettify = {} local escaped_chars = { @@ -26,14 +24,23 @@ end local spans = {keyword=true,number=true,string=true,comment=true,global=true,backtick=true} -function prettify.lua (fname, code, initial_lineno, pre) - local res = List() +local cpp_lang = {c = true, cpp = true, cxx = true, h = true} + +function prettify.lua (lang, fname, code, initial_lineno, pre) + local res, lexer, tokenizer = List(), require 'ldoc.lexer' + local tnext = lexer.skipws + if not cpp_lang[lang] then + tokenizer = lexer.lua + else + tokenizer = lexer.cpp + end + if pre then res:append '
\n'
    end
    initial_lineno = initial_lineno or 0
 
-   local tok = lexer.lua(code,{},{})
+   local tok = tokenizer(code,{},{})
    local error_reporter = {
       warning = function (self,msg)
          io.stderr:write(fname..':'..tok:lineno()+initial_lineno..': '..msg,'\n')
@@ -72,7 +79,7 @@ local lxsh_highlighers = {bib=true,c=true,lua=true,sh=true}
 
 function prettify.code (lang,fname,code,initial_lineno,pre)
    if not lxsh then
-      return prettify.lua (fname, code, initial_lineno, pre)
+      return prettify.lua (lang,fname, code, initial_lineno, pre)
    else
       if not lxsh_highlighers[lang] then
          lang = 'lua'

From 7da46268dc528431228440f52a17146dc3a29dde Mon Sep 17 00:00:00 2001
From: steve donovan 
Date: Sun, 25 Aug 2013 19:38:01 +0200
Subject: [PATCH 051/106] 'pale' template added; interpretation of --style and
 --template extended

---
 doc/config.ld                                |   1 +
 doc/doc.md                                   |  58 +++-
 ldoc.lua                                     |  56 +++-
 ldoc/html/{ldoc_mdtp.lua => ldoc_md_ltp.lua} |   0
 ldoc/html/ldoc_pale_css.lua                  | 297 +++++++++++++++++++
 5 files changed, 390 insertions(+), 22 deletions(-)
 rename ldoc/html/{ldoc_mdtp.lua => ldoc_md_ltp.lua} (100%)
 create mode 100644 ldoc/html/ldoc_pale_css.lua

diff --git a/doc/config.ld b/doc/config.ld
index 1d01883c..b37025dd 100644
--- a/doc/config.ld
+++ b/doc/config.ld
@@ -10,6 +10,7 @@ examples = {
     '../tests/styles/colon.lua',
     '../tests/styles/four.lua',
     '../tests/styles/three.lua',
+    '../tests/styles/multiple.lua',
     '../tests/example/mylib.c',
     '../tests/moonscript/List.moon',
 }
diff --git a/doc/doc.md b/doc/doc.md
index b6177b32..77bbe8b1 100644
--- a/doc/doc.md
+++ b/doc/doc.md
@@ -218,7 +218,7 @@ However, you must either use the `--colon` flag or set `colon=true` in your `con
 
 In this style, types may be used directly if prefixed with '!' or '?' (for type-or-nil)
 
-(see @{colon.lua})
+(see @{colon.lua}, rendered [here](http://stevedonovan.github.io/ldoc/examples/colon))
 
 By default, LDoc will process any file ending in '.lua' or '.luadoc' in a specified
 directory; you may point it to a single file as well. A 'project' usually consists of many
@@ -372,6 +372,11 @@ the module, but `answer` has been explicitly exported. (If you invoke LDoc with
 the `-a` flag on this file, you will see the documentation for the unexported
 function as well.)
 
+`@set` is a powerful tag which assigns a configuration variable to a value _just for this module_.
+Saying `@set no_summary=true` in a module comment will temporarily disable summary generation when
+the template is expanded. Generally configuration variables that effect template expansion
+are modifiable in this way.
+
 ## Sections
 
 LDoc supports _explicit_ sections. By default, the implicit sections correspond to the pre-existing
@@ -1028,11 +1033,37 @@ then LDoc can present this as the default value for this optional argument.
     end
     ----> displayed as: one (name, age [, calender='gregorian' [, offset=0]])
 
-Currently the `type` and `opt` modifiers are the only ones known and used by LDoc when generating HTML
+
+(See @{four.lua}, rendered [here](http://stevedonovan.github.io/ldoc/examples/four))
+
+An experimental feature in 1.4 allows different 'return groups' to be defined. There may be
+multiple `@return` tags, and the meaning of this is well-defined, since Lua functions may
+return multiple values.  However, being a dynamic language it may return a single value if
+successful and two values (`nil`,an error message) if there is an error. This is in fact the
+convention for returning 'normal' errors (like 'file not found') as opposed to parameter errors
+(like 'file must be a string') that are often raised as errors.
+
+Return groups allow a documenter to specify the various possible return values of a function,
+by specifying _number_ modifiers. All `return` tags with the same digit modifier belong together
+as a group:
+
+    -----
+    -- function with return groups.
+    -- @return[1] result
+    -- @return[2] nil
+    -- @return[2] error message
+    function mul1() ... end
+
+This is the first function in @{multiple.lua}, and the [output](http://stevedonovan.github.io/ldoc/examples/multiple)
+shows how return groups are presented, with an **Or** between the groups.
+
+This is rather clumsy, and so there is a shortcut, the `@error` tag which achieves the same result,
+with helpful type information.
+
+Currently the `type`,`opt` and `` modifiers are the only ones known and used by LDoc when generating HTML
 output. However, any other modifiers are allowed and are available for use with your own
 templates or for extraction by your own tools.
 
-(See @{four.lua})
 
 ## Fields allowed in `config.ld`
 
@@ -1139,7 +1170,11 @@ embedded with '$(...)'.
 
 This is then styled with `ldoc.css`. Currently the template and stylesheet is very much
 based on LuaDoc, so the results are mostly equivalent; the main change that the template has
-been more generalized. The default location (indicated by '!') is the directory of `ldoc.lua`.
+been more generalized. The default location (indicated by '!') is the directory of `ldoc_ltp.lua`.
+
+You will notice that the built-in templates and stylesheets end in `.lua`; this is simply to
+make it easier for LDoc to find them.  Where you are customizing one or both of the template
+and stylesheet, they will have their usual extensions.
 
 You may customize how you generate your documentation by specifying an alternative style
 sheet and/or template, which can be deployed with your project. The parameters are `--style`
@@ -1151,7 +1186,14 @@ be a valid directory relative to the ldoc invocation.
 An example of fully customized documentation is `tests/example/style`: this is what you
 could call 'minimal Markdown style' where there is no attempt to tag things (except
 emphasizing parameter names). The narrative alone _can_ to be sufficient, if it is written
-appropriately.
+well.
+
+There are two other stylesheets available in LDoc since 1.4; the first is `ldoc_one.css` which is what
+you get from `one=true` and the second is `ldoc_pale.css`. This is a lighter theme which
+might give some relief from the heavier colours of the default. You can use this style with
+`style="!pale"` or `-s !pale`.
+See the [Lake](http://stevedonovan.github.io/lake/modules/lakelibs.html) documentation
+as an example of its use.
 
 Of course, there's no reason why LDoc must always generate HTML. `--ext` defines what output
 extension to use; this can also be set in the configuration file. So it's possible to write
@@ -1159,8 +1201,10 @@ a template that converts LDoc output to LaTex, for instance. The separation of p
 and presentation makes this kind of new application possible with LDoc.
 
 From 1.4, LDoc has some limited support for generating Markdown output, although only
-for single files currently. Use `--ext md` for this.  'ldoc/html/ldoc_mdtp.lua' defines
-the template for Markdown.
+for single files currently. Use `--ext md` for this.  'ldoc/html/ldoc_md_ltp.lua' defines
+the template for Markdown, but this can be overriden with `template` as above. It's another
+example of minimal structure, and provides a better place to learn about these templates than the
+rather elaborate default HTML template.
 
 ## Internal Data Representation
 
diff --git a/ldoc.lua b/ldoc.lua
index 9c589744..e00d42e3 100644
--- a/ldoc.lua
+++ b/ldoc.lua
@@ -586,15 +586,36 @@ if args.filter ~= 'none' then
    os.exit()
 end
 
+-- can specify format, output, dir and ext in config.ld
+override 'output'
+override 'dir'
+override 'ext'
+override 'one'
+override 'boilerplate'
+
+-- handling styling and templates --
 ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp'
 
+-- special case: user wants to generate a .md file from a .lua file
 if args.ext == 'md' then
-   ldoc.templ = 'ldoc.mdtp'
+   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 = '.'
    ldoc.template_escape = '>'
    ldoc.style = false
    args.ext = '.md'
 end
 
+local function match_bang (s)
+   if type(s) ~= 'string' then return end
+   return s:match '^!(.*)'
+end
+
 local function style_dir (sname)
    local style = ldoc[sname]
    local dir
@@ -605,7 +626,7 @@ local function style_dir (sname)
    if style then
       if style == true then
          dir = config_dir
-      elseif type(style) == 'string' and path.isdir(style) then
+      elseif type(style) == 'string' and (path.isdir(style) or match_bang(style)) then
          dir = style
       else
          quit(quote(tostring(style)).." is not a directory")
@@ -614,7 +635,6 @@ local function style_dir (sname)
    end
 end
 
-
 -- 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.
@@ -625,35 +645,41 @@ end
 style_dir 'style'
 style_dir 'template'
 
--- can specify format, output, dir and ext in config.ld
-override 'output'
-override 'dir'
-override 'ext'
-override 'one'
-override 'boilerplate'
-
 if not args.ext:find '^%.' then
    args.ext = '.'..args.ext
 end
 
 if args.one then
-   ldoc.css = 'ldoc_one.css'
+   ldoc.style = '!one'
 end
 
-if args.style == '!' or args.template == '!' then
+local builtin_style, builtin_template = match_bang(args.style),match_bang(args.template)
+if builtin_style or builtin_template then
    -- '!' 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)
-      utils.writefile(path.join(tmpdir,name),require('ldoc.html.'..name:gsub('%.','_')))
+      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
    end
-   if args.style == '!' then
+   if builtin_style then
+      if builtin_style ~= '' then
+         ldoc.css = 'ldoc_'..builtin_style..'.css'
+      end
       tmpwrite(ldoc.css)
       args.style = tmpdir
    end
-   if args.template == '!' then
+   if builtin_template then
+      if builtin_template ~= '' then
+         ldoc.templ = 'ldoc_'..builtin_template..'.ltp'
+      end
       tmpwrite(ldoc.templ)
       args.template = tmpdir
    end
diff --git a/ldoc/html/ldoc_mdtp.lua b/ldoc/html/ldoc_md_ltp.lua
similarity index 100%
rename from ldoc/html/ldoc_mdtp.lua
rename to ldoc/html/ldoc_md_ltp.lua
diff --git a/ldoc/html/ldoc_pale_css.lua b/ldoc/html/ldoc_pale_css.lua
new file mode 100644
index 00000000..fe720120
--- /dev/null
+++ b/ldoc/html/ldoc_pale_css.lua
@@ -0,0 +1,297 @@
+return [[/* BEGIN RESET
+
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.2r1
+*/
+html {
+    color: #000;
+    background: #FFF;
+}
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
+    margin: 0;
+    padding: 0;
+}
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+}
+fieldset,img {
+    border: 0;
+}
+address,caption,cite,code,dfn,em,strong,th,var,optgroup {
+    font-style: inherit;
+    font-weight: inherit;
+}
+del,ins {
+    text-decoration: none;
+}
+li {
+    list-style: bullet;
+    margin-left: 20px;
+}
+caption,th {
+    text-align: left;
+}
+h1,h2,h3,h4,h5,h6 {
+    font-size: 100%;
+    font-weight: bold;
+}
+q:before,q:after {
+    content: '';
+}
+abbr,acronym {
+    border: 0;
+    font-variant: normal;
+}
+sup {
+    vertical-align: baseline;
+}
+sub {
+    vertical-align: baseline;
+}
+legend {
+    color: #000;
+}
+input,button,textarea,select,optgroup,option {
+    font-family: inherit;
+    font-size: inherit;
+    font-style: inherit;
+    font-weight: inherit;
+}
+input,button,textarea,select {*font-size:100%;
+}
+/* END RESET */
+
+body {
+    margin-left: 1em;
+    margin-right: 1em;
+    font-family: arial, helvetica, geneva, sans-serif;
+    background-color: #ffffff; margin: 0px;
+}
+
+code, tt { font-family: monospace; }
+span.parameter { font-family:monospace; }
+span.parameter:after { content:":"; }
+span.types:before { content:"("; }
+span.types:after { content:")"; }
+.type { font-weight: bold; font-style:italic }
+
+body, p, td, th { font-size: .95em; line-height: 1.2em;}
+
+p, ul { margin: 10px 0 0 0px;}
+
+strong { font-weight: bold;}
+
+em { font-style: italic;}
+
+h1 {
+    font-size: 1.5em;
+    margin: 0 0 20px 0;
+}
+h2, h3, h4 { margin: 15px 0 10px 0; }
+h2 { font-size: 1.25em; }
+h3 { font-size: 1.15em; }
+h4 { font-size: 1.06em; }
+
+a:link { font-weight: bold; color: #004080; text-decoration: none; }
+a:visited { font-weight: bold; color: #006699; text-decoration: none; }
+a:link:hover { text-decoration: underline; }
+
+hr {
+    color:#cccccc;
+    background: #00007f;
+    height: 1px;
+}
+
+blockquote { margin-left: 3em; }
+
+ul { list-style-type: disc; }
+
+p.name {
+    font-family: "Andale Mono", monospace;
+    padding-top: 1em;
+}
+
+pre.example {
+    background-color: rgb(245, 245, 245);
+    border: 1px solid silver;
+    padding: 10px;
+    margin: 10px 0 10px 0;
+    font-family: "Andale Mono", monospace;
+    font-size: .85em;
+}
+
+pre {
+    background-color: rgb(245,245,255); // rgb(245, 245, 245);
+    border: 1px solid #cccccc; //silver;
+    padding: 10px;
+    margin: 10px 0 10px 0;
+    overflow: auto;
+    font-family: "Andale Mono", monospace;
+}
+
+
+table.index { border: 1px #00007f; }
+table.index td { text-align: left; vertical-align: top; }
+
+#container {
+    margin-left: 1em;
+    margin-right: 1em;
+    background-color: #f0f0f0;
+}
+
+#product {
+    text-align: center;
+    border-bottom: 1px solid #cccccc;
+    background-color: #ffffff;
+}
+
+#product big {
+    font-size: 2em;
+}
+
+#main {
+    background-color:#FFFFFF; // #f0f0f0;
+    //border-left: 2px solid #cccccc;
+}
+
+#navigation {
+    float: left;
+    width: 14em;
+    vertical-align: top;
+    background-color:#FFFFFF; // #f0f0f0;
+    overflow: visible;
+}
+
+#navigation h2 {
+    background-color:#FFFFFF;//:#e7e7e7;
+    font-size:1.1em;
+    color:#000000;
+    text-align: left;
+    padding:0.2em;
+    //border-top:1px solid #dddddd;
+    border-bottom:1px solid #dddddd;
+}
+
+#navigation ul
+{
+    font-size:1em;
+    list-style-type: none;
+    margin: 1px 1px 10px 1px;
+}
+
+#navigation li {
+    text-indent: -1em;
+    display: block;
+    margin: 3px 0px 0px 22px;
+}
+
+#navigation li li a {
+    margin: 0px 3px 0px -1em;
+}
+
+#content {
+    margin-left: 14em;
+    padding: 1em;
+    width: 700px;
+    border-left: 2px solid #cccccc;
+   // border-right: 2px solid #cccccc;
+    background-color: #ffffff;
+}
+
+#about {
+    clear: both;
+    padding: 5px;
+    border-top: 2px solid #cccccc;
+    background-color: #ffffff;
+}
+
+@media print {
+    body {
+        font: 12pt "Times New Roman", "TimeNR", Times, serif;
+    }
+    a { font-weight: bold; color: #004080; text-decoration: underline; }
+
+    #main {
+        background-color: #ffffff;
+        border-left: 0px;
+    }
+
+    #container {
+        margin-left: 2%;
+        margin-right: 2%;
+        background-color: #ffffff;
+    }
+
+    #content {
+        padding: 1em;
+        background-color: #ffffff;
+    }
+
+    #navigation {
+        display: none;
+    }
+    pre.example {
+        font-family: "Andale Mono", monospace;
+        font-size: 10pt;
+        page-break-inside: avoid;
+    }
+}
+
+table.module_list {
+    border-width: 1px;
+    border-style: solid;
+    border-color: #cccccc;
+    border-collapse: collapse;
+}
+table.module_list td {
+    border-width: 1px;
+    padding: 3px;
+    border-style: solid;
+    border-color: #cccccc;
+}
+table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; }
+table.module_list td.summary { width: 100%; }
+
+table.function_list {
+    border-width: 1px;
+    border-style: solid;
+    border-color: #cccccc;
+    border-collapse: collapse;
+}
+table.function_list td {
+    border-width: 1px;
+    padding: 3px;
+    border-style: solid;
+    border-color: #cccccc;
+}
+table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; }
+table.function_list td.summary { width: 100%; }
+
+dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
+dl.table h3, dl.function h3 {font-size: .95em;}
+
+/* stop sublists from having initial vertical space */
+ul ul { margin-top: 0px; }
+ol ul { margin-top: 0px; }
+ol ol { margin-top: 0px; }
+ul ol { margin-top: 0px; }
+
+/* styles for prettification of source */
+pre .comment { color: #558817; }
+pre .constant { color: #a8660d; }
+pre .escape { color: #844631; }
+pre .keyword { color: #2239a8; font-weight: bold; }
+pre .library { color: #0e7c6b; }
+pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
+pre .string { color: #a8660d; }
+pre .number { color: #f8660d; }
+pre .operator { color: #2239a8; font-weight: bold; }
+pre .preprocessor, pre .prepro { color: #a33243; }
+pre .global { color: #800080; }
+pre .prompt { color: #558817; }
+pre .url { color: #272fc2; text-decoration: underline; }
+]]

From 85dbd3d73149300ac20d965bb3f2f5e39f5fd4ff Mon Sep 17 00:00:00 2001
From: steve donovan 
Date: Sun, 25 Aug 2013 21:01:30 +0200
Subject: [PATCH 052/106] no-compat 5.2/5.3 compatible; line endings for
 markdown.lua

---
 ldoc/html.lua     |    1 +
 ldoc/markdown.lua | 2730 ++++++++++++++++++++++-----------------------
 ldoc/parse.lua    |    4 +-
 3 files changed, 1369 insertions(+), 1366 deletions(-)

diff --git a/ldoc/html.lua b/ldoc/html.lua
index 6fe21d85..cdc77fa2 100644
--- a/ldoc/html.lua
+++ b/ldoc/html.lua
@@ -23,6 +23,7 @@ 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 = {}
 
 
diff --git a/ldoc/markdown.lua b/ldoc/markdown.lua
index 73c656b6..96d9b31a 100644
--- a/ldoc/markdown.lua
+++ b/ldoc/markdown.lua
@@ -1,1365 +1,1365 @@
-#!/usr/bin/env lua
-
---[[
-# markdown.lua -- version 0.32
-
-
-
-**Author:** Niklas Frykholm, 
-**Date:** 31 May 2008
-
-This is an implementation of the popular text markup language Markdown in pure Lua.
-Markdown can convert documents written in a simple and easy to read text format
-to well-formatted HTML. For a more thourough description of Markdown and the Markdown
-syntax, see .
-
-The original Markdown source is written in Perl and makes heavy use of advanced
-regular expression techniques (such as negative look-ahead, etc) which are not available
-in Lua's simple regex engine. Therefore this Lua port has been rewritten from the ground
-up. It is probably not completely bug free. If you notice any bugs, please report them to
-me. A unit test that exposes the error is helpful.
-
-## Usage
-
-    require "markdown"
-    markdown(source)
-
-``markdown.lua`` exposes a single global function named ``markdown(s)`` which applies the
-Markdown transformation to the specified string.
-
-``markdown.lua`` can also be used directly from the command line:
-
-	lua markdown.lua test.md
-
-Creates a file ``test.html`` with the converted content of ``test.md``. Run:
-
-    lua markdown.lua -h
-
-For a description of the command-line options.
-
-``markdown.lua`` uses the same license as Lua, the MIT license.
-
-## License
-
-Copyright © 2008 Niklas Frykholm.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this
-software and associated documentation files (the "Software"), to deal in the Software
-without restriction, including without limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
-to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies
-or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-## Version history
-
--	**0.32** -- 31 May 2008
-	- Fix for links containing brackets
--	**0.31** -- 1 Mar 2008
-	-	Fix for link definitions followed by spaces
--	**0.30** -- 25 Feb 2008
-	-	Consistent behavior with Markdown when the same link reference is reused
--	**0.29** -- 24 Feb 2008
-	-	Fix for 
 blocks with spaces in them
--	**0.28** -- 18 Feb 2008
-	-	Fix for link encoding
--	**0.27** -- 14 Feb 2008
-	-	Fix for link database links with ()
--	**0.26** -- 06 Feb 2008
-	-	Fix for nested italic and bold markers
--	**0.25** -- 24 Jan 2008
-	-	Fix for encoding of naked <
--	**0.24** -- 21 Jan 2008
-	-	Fix for link behavior.
--	**0.23** -- 10 Jan 2008
-	-	Fix for a regression bug in longer expressions in italic or bold.
--	**0.22** -- 27 Dec 2007
-	-	Fix for crash when processing blocks with a percent sign in them.
--	**0.21** -- 27 Dec 2007
-	- 	Fix for combined strong and emphasis tags
--	**0.20** -- 13 Oct 2007
-	-	Fix for < as well in image titles, now matches Dingus behavior
--	**0.19** -- 28 Sep 2007
-	-	Fix for quotation marks " and ampersands & in link and image titles.
--	**0.18** -- 28 Jul 2007
-	-	Does not crash on unmatched tags (behaves like standard markdown)
--	**0.17** -- 12 Apr 2007
-	-	Fix for links with %20 in them.
--	**0.16** -- 12 Apr 2007
-	-	Do not require arg global to exist.
--	**0.15** -- 28 Aug 2006
-	-	Better handling of links with underscores in them.
--	**0.14** -- 22 Aug 2006
-	-	Bug for *`foo()`*
--	**0.13** -- 12 Aug 2006
-	-	Added -l option for including stylesheet inline in document.
-	-	Fixed bug in -s flag.
-	-	Fixed emphasis bug.
--	**0.12** -- 15 May 2006
-	-	Fixed several bugs to comply with MarkdownTest 1.0 
--	**0.11** -- 12 May 2006
-	-	Fixed bug for escaping `*` and `_` inside code spans.
-	-	Added license terms.
-	-	Changed join() to table.concat().
--	**0.10** -- 3 May 2006
-	-	Initial public release.
-
-// Niklas
-]]
-
-
--- Set up a table for holding local functions to avoid polluting the global namespace
--- Penlight 1.2 defines compatible 5.1 setfenv in utils table
-local M = {}
-local MT = {__index = _G}
-setmetatable(M, MT)
-
-----------------------------------------------------------------------
--- Utility functions
-----------------------------------------------------------------------
-
--- Locks table t from changes, writes an error if someone attempts to change the table.
--- This is useful for detecting variables that have "accidently" been made global. Something
--- I tend to do all too much.
-function M.lock(t)
-	local function lock_new_index(t, k, v)
-		error("module has been locked -- " .. k .. " must be declared local", 2)
-	end
-
-	local mt = {__newindex = lock_new_index}
-	if getmetatable(t) then
-      mt.__index = getmetatable(t).__index
-   end
-	setmetatable(t, mt)
-end
-
--- Returns the result of mapping the values in table t through the function f
-local function map(t, f)
-	local out = {}
-	for k,v in pairs(t) do out[k] = f(v,k) end
-	return out
-end
-
--- The identity function, useful as a placeholder.
-local function identity(text) return text end
-
--- Functional style if statement. (NOTE: no short circuit evaluation)
-local function iff(t, a, b) if t then return a else return b end end
-
--- Splits the text into an array of separate lines.
-local function split(text, sep)
-	sep = sep or "\n"
-	local lines = {}
-	local pos = 1
-	while true do
-		local b,e = text:find(sep, pos)
-		if not b then table.insert(lines, text:sub(pos)) break end
-		table.insert(lines, text:sub(pos, b-1))
-		pos = e + 1
-	end
-	return lines
-end
-
--- Converts tabs to spaces
-local function detab(text)
-	local tab_width = 4
-	local function rep(match)
-		local spaces = -match:len()
-		while spaces<1 do spaces = spaces + tab_width end
-		return match .. string.rep(" ", spaces)
-	end
-	text = text:gsub("([^\n]-)\t", rep)
-	return text
-end
-
--- Applies string.find for every pattern in the list and returns the first match
-local function find_first(s, patterns, index)
-	local res = {}
-	for _,p in ipairs(patterns) do
-		local match = {s:find(p, index)}
-		if #match>0 and (#res==0 or match[1] < res[1]) then res = match end
-	end
-	return unpack(res)
-end
-
--- If a replacement array is specified, the range [start, stop] in the array is replaced
--- with the replacement array and the resulting array is returned. Without a replacement
--- array the section of the array between start and stop is returned.
-local function splice(array, start, stop, replacement)
-	if replacement then
-		local n = stop - start + 1
-		while n > 0 do
-			table.remove(array, start)
-			n = n - 1
-		end
-		for i,v in ipairs(replacement) do
-			table.insert(array, start, v)
-		end
-		return array
-	else
-		local res = {}
-		for i = start,stop do
-			table.insert(res, array[i])
-		end
-		return res
-	end
-end
-
--- Outdents the text one step.
-local function outdent(text)
-	text = "\n" .. text
-	text = text:gsub("\n  ? ? ?", "\n")
-	text = text:sub(2)
-	return text
-end
-
--- Indents the text one step.
-local function indent(text)
-	text = text:gsub("\n", "\n    ")
-	return text
-end
-
--- Does a simple tokenization of html data. Returns the data as a list of tokens.
--- Each token is a table with a type field (which is either "tag" or "text") and
--- a text field (which contains the original token data).
-local function tokenize_html(html)
-	local tokens = {}
-	local pos = 1
-	while true do
-		local start = find_first(html, {"", start)
-		elseif html:match("^<%?", start) then
-			_,stop = html:find("?>", start)
-		else
-			_,stop = html:find("%b<>", start)
-		end
-		if not stop then
-			-- error("Could not match html tag " .. html:sub(start,start+30))
-		 	table.insert(tokens, {type="text", text=html:sub(start, start)})
-			pos = start + 1
-		else
-			table.insert(tokens, {type="tag", text=html:sub(start, stop)})
-			pos = stop + 1
-		end
-	end
-	return tokens
-end
-
-----------------------------------------------------------------------
--- Hash
-----------------------------------------------------------------------
-
--- This is used to "hash" data into alphanumeric strings that are unique
--- in the document. (Note that this is not cryptographic hash, the hash
--- function is not one-way.) The hash procedure is used to protect parts
--- of the document from further processing.
-
-local HASH = {
-	-- Has the hash been inited.
-	inited = false,
-
-	-- The unique string prepended to all hash values. This is to ensure
-	-- that hash values do not accidently coincide with an actual existing
-	-- string in the document.
-	identifier = "",
-
-	-- Counter that counts up for each new hash instance.
-	counter = 0,
-
-	-- Hash table.
-	table = {}
-}
-
--- Inits hashing. Creates a hash_identifier that doesn't occur anywhere
--- in the text.
-local function init_hash(text)
-	HASH.inited = true
-	HASH.identifier = ""
-	HASH.counter = 0
-	HASH.table = {}
-
-	local s = "HASH"
-	local counter = 0
-	local id
-	while true do
-		id  = s .. counter
-		if not text:find(id, 1, true) then break end
-		counter = counter + 1
-	end
-	HASH.identifier = id
-end
-
--- Returns the hashed value for s.
-local function hash(s)
-	assert(HASH.inited)
-	if not HASH.table[s] then
-		HASH.counter = HASH.counter + 1
-		local id = HASH.identifier .. HASH.counter .. "X"
-		HASH.table[s] = id
-	end
-	return HASH.table[s]
-end
-
-----------------------------------------------------------------------
--- Protection
-----------------------------------------------------------------------
-
--- The protection module is used to "protect" parts of a document
--- so that they are not modified by subsequent processing steps.
--- Protected parts are saved in a table for later unprotection
-
--- Protection data
-local PD = {
-	-- Saved blocks that have been converted
-	blocks = {},
-
-	-- Block level tags that will be protected
-	tags = {"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote",
-	"pre", "table", "dl", "ol", "ul", "script", "noscript", "form", "fieldset",
-	"iframe", "math", "ins", "del"}
-}
-
--- Pattern for matching a block tag that begins and ends in the leftmost
--- column and may contain indented subtags, i.e.
--- 
--- A nested block. ---
--- Nested data. ---
---
-local function block_pattern(tag) - return "\n<" .. tag .. ".-\n[ \t]*\n" -end - --- Pattern for matching a block tag that begins and ends with a newline -local function line_pattern(tag) - return "\n<" .. tag .. ".-[ \t]*\n" -end - --- Protects the range of characters from start to stop in the text and --- returns the protected string. -local function protect_range(text, start, stop) - local s = text:sub(start, stop) - local h = hash(s) - PD.blocks[h] = s - text = text:sub(1,start) .. h .. text:sub(stop) - return text -end - --- Protect every part of the text that matches any of the patterns. The first --- matching pattern is protected first, etc. -local function protect_matches(text, patterns) - while true do - local start, stop = find_first(text, patterns) - if not start then break end - text = protect_range(text, start, stop) - end - return text -end - --- Protects blocklevel tags in the specified text -local function protect(text) - -- First protect potentially nested block tags - text = protect_matches(text, map(PD.tags, block_pattern)) - -- Then protect block tags at the line level. - text = protect_matches(text, map(PD.tags, line_pattern)) - -- Protect
and comment tags - text = protect_matches(text, {"\n]->[ \t]*\n"}) - text = protect_matches(text, {"\n[ \t]*\n"}) - return text -end - --- Returns true if the string s is a hash resulting from protection -local function is_protected(s) - return PD.blocks[s] -end - --- Unprotects the specified text by expanding all the nonces -local function unprotect(text) - for k,v in pairs(PD.blocks) do - v = v:gsub("%%", "%%%%") - text = text:gsub(k, v) - end - return text -end - - ----------------------------------------------------------------------- --- Block transform ----------------------------------------------------------------------- - --- The block transform functions transform the text on the block level. --- They work with the text as an array of lines rather than as individual --- characters. - --- Returns true if the line is a ruler of (char) characters. --- The line must contain at least three char characters and contain only spaces and --- char characters. -local function is_ruler_of(line, char) - if not line:match("^[ %" .. char .. "]*$") then return false end - if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end - return true -end - --- Identifies the block level formatting present in the line -local function classify(line) - local info = {line = line, text = line} - - if line:match("^ ") then - info.type = "indented" - info.outdented = line:sub(5) - return info - end - - for _,c in ipairs({'*', '-', '_', '='}) do - if is_ruler_of(line, c) then - info.type = "ruler" - info.ruler_char = c - return info - end - end - - if line == "" then - info.type = "blank" - return info - end - - if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then - local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") - info.type = "header" - info.level = m1:len() - info.text = m2 - return info - end - - if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then - local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") - info.type = "list_item" - info.list_type = "numeric" - info.number = 0 + number - info.text = text - return info - end - - if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then - local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") - info.type = "list_item" - info.list_type = "bullet" - info.bullet = bullet - info.text= text - return info - end - - if line:match("^>[ \t]?(.*)") then - info.type = "blockquote" - info.text = line:match("^>[ \t]?(.*)") - return info - end - - if is_protected(line) then - info.type = "raw" - info.html = unprotect(line) - return info - end - - info.type = "normal" - return info -end - --- Find headers constisting of a normal line followed by a ruler and converts them to --- header entries. -local function headers(array) - local i = 1 - while i <= #array - 1 do - if array[i].type == "normal" and array[i+1].type == "ruler" and - (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then - local info = {line = array[i].line} - info.text = info.line - info.type = "header" - info.level = iff(array[i+1].ruler_char == "=", 1, 2) - table.remove(array, i+1) - array[i] = info - end - i = i + 1 - end - return array -end - -local block_transform, blocks_to_html, encode_code, span_transform, encode_backslash_escapes - --- Find list blocks and convert them to protected data blocks -local function lists(array, sublist) - local function process_list(arr) - local function any_blanks(arr) - for i = 1, #arr do - if arr[i].type == "blank" then return true end - end - return false - end - - local function split_list_items(arr) - local acc = {arr[1]} - local res = {} - for i=2,#arr do - if arr[i].type == "list_item" then - table.insert(res, acc) - acc = {arr[i]} - else - table.insert(acc, arr[i]) - end - end - table.insert(res, acc) - return res - end - - local function process_list_item(lines, block) - while lines[#lines].type == "blank" do - table.remove(lines) - end - - local itemtext = lines[1].text - for i=2,#lines do - itemtext = itemtext .. "\n" .. outdent(lines[i].line) - end - if block then - itemtext = block_transform(itemtext, true) - if not itemtext:find("
") then itemtext = indent(itemtext) end
-				return "    
  • " .. itemtext .. "
  • " - else - local lines = split(itemtext) - lines = map(lines, classify) - lines = lists(lines, true) - lines = blocks_to_html(lines, true) - itemtext = table.concat(lines, "\n") - if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    -				return "    
  • " .. itemtext .. "
  • " - end - end - - local block_list = any_blanks(arr) - local items = split_list_items(arr) - local out = "" - for _, item in ipairs(items) do - out = out .. process_list_item(item, block_list) .. "\n" - end - if arr[1].list_type == "numeric" then - return "
      \n" .. out .. "
    " - else - return "
      \n" .. out .. "
    " - end - end - - -- Finds the range of lines composing the first list in the array. A list - -- starts with (^ list_item) or (blank list_item) and ends with - -- (blank* $) or (blank normal). - -- - -- A sublist can start with just (list_item) does not need a blank... - local function find_list(array, sublist) - local function find_list_start(array, sublist) - if array[1].type == "list_item" then return 1 end - if sublist then - for i = 1,#array do - if array[i].type == "list_item" then return i end - end - else - for i = 1, #array-1 do - if array[i].type == "blank" and array[i+1].type == "list_item" then - return i+1 - end - end - end - return nil - end - local function find_list_end(array, start) - local pos = #array - for i = start, #array-1 do - if array[i].type == "blank" and array[i+1].type ~= "list_item" - and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then - pos = i-1 - break - end - end - while pos > start and array[pos].type == "blank" do - pos = pos - 1 - end - return pos - end - - local start = find_list_start(array, sublist) - if not start then return nil end - return start, find_list_end(array, start) - end - - while true do - local start, stop = find_list(array, sublist) - if not start then break end - local text = process_list(splice(array, start, stop)) - local info = { - line = text, - type = "raw", - html = text - } - array = splice(array, start, stop, {info}) - end - - -- Convert any remaining list items to normal - for _,line in ipairs(array) do - if line.type == "list_item" then line.type = "normal" end - end - - return array -end - --- Find and convert blockquote markers. -local function blockquotes(lines) - local function find_blockquote(lines) - local start - for i,line in ipairs(lines) do - if line.type == "blockquote" then - start = i - break - end - end - if not start then return nil end - - local stop = #lines - for i = start+1, #lines do - if lines[i].type == "blank" or lines[i].type == "blockquote" then - elseif lines[i].type == "normal" then - if lines[i-1].type == "blank" then stop = i-1 break end - else - stop = i-1 break - end - end - while lines[stop].type == "blank" do stop = stop - 1 end - return start, stop - end - - local function process_blockquote(lines) - local raw = lines[1].text - for i = 2,#lines do - raw = raw .. "\n" .. lines[i].text - end - local bt = block_transform(raw) - if not bt:find("
    ") then bt = indent(bt) end
    -		return "
    \n " .. bt .. - "\n
    " - end - - while true do - local start, stop = find_blockquote(lines) - if not start then break end - local text = process_blockquote(splice(lines, start, stop)) - local info = { - line = text, - type = "raw", - html = text - } - lines = splice(lines, start, stop, {info}) - end - return lines -end - --- Find and convert codeblocks. -local function codeblocks(lines) - local function find_codeblock(lines) - local start - for i,line in ipairs(lines) do - if line.type == "indented" then start = i break end - end - if not start then return nil end - - local stop = #lines - for i = start+1, #lines do - if lines[i].type ~= "indented" and lines[i].type ~= "blank" then - stop = i-1 - break - end - end - while lines[stop].type == "blank" do stop = stop - 1 end - return start, stop - end - - local function process_codeblock(lines) - local raw = detab(encode_code(outdent(lines[1].line))) - for i = 2,#lines do - raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) - end - return "
    " .. raw .. "\n
    " - end - - while true do - local start, stop = find_codeblock(lines) - if not start then break end - local text = process_codeblock(splice(lines, start, stop)) - local info = { - line = text, - type = "raw", - html = text - } - lines = splice(lines, start, stop, {info}) - end - return lines -end - --- Convert lines to html code -function blocks_to_html(lines, no_paragraphs) - local out = {} - local i = 1 - while i <= #lines do - local line = lines[i] - if line.type == "ruler" then - table.insert(out, "
    ") - elseif line.type == "raw" then - table.insert(out, line.html) - elseif line.type == "normal" then - local s = line.line - - while i+1 <= #lines and lines[i+1].type == "normal" do - i = i + 1 - s = s .. "\n" .. lines[i].line - end - - if no_paragraphs then - table.insert(out, span_transform(s)) - else - table.insert(out, "

    " .. span_transform(s) .. "

    ") - end - elseif line.type == "header" then - local s = "" .. span_transform(line.text) .. "" - table.insert(out, s) - else - table.insert(out, line.line) - end - i = i + 1 - end - return out -end - --- Perform all the block level transforms -function block_transform(text, sublist) - local lines = split(text) - lines = map(lines, classify) - lines = headers(lines) - lines = lists(lines, sublist) - lines = codeblocks(lines) - lines = blockquotes(lines) - lines = blocks_to_html(lines) - local text = table.concat(lines, "\n") - return text -end - --- Debug function for printing a line array to see the result --- of partial transforms. -local function print_lines(lines) - for i, line in ipairs(lines) do - print(i, line.type, line.text or line.line) - end -end - ----------------------------------------------------------------------- --- Span transform ----------------------------------------------------------------------- - --- Functions for transforming the text at the span level. - --- These characters may need to be escaped because they have a special --- meaning in markdown. -local escape_chars = "'\\`*_{}[]()>#+-.!'" -local escape_table = {} - -local function init_escape_table() - escape_table = {} - for i = 1,#escape_chars do - local c = escape_chars:sub(i,i) - escape_table[c] = hash(c) - end -end - --- Adds a new escape to the escape table. -local function add_escape(text) - if not escape_table[text] then - escape_table[text] = hash(text) - end - return escape_table[text] -end - --- Escape characters that should not be disturbed by markdown. -local function escape_special_chars(text) - local tokens = tokenize_html(text) - - local out = "" - for _, token in ipairs(tokens) do - local t = token.text - if token.type == "tag" then - -- In tags, encode * and _ so they don't conflict with their use in markdown. - t = t:gsub("%*", escape_table["*"]) - t = t:gsub("%_", escape_table["_"]) - else - t = encode_backslash_escapes(t) - end - out = out .. t - end - return out -end - --- Encode backspace-escaped characters in the markdown source. -function encode_backslash_escapes(t) - for i=1,escape_chars:len() do - local c = escape_chars:sub(i,i) - t = t:gsub("\\%" .. c, escape_table[c]) - end - return t -end - --- Unescape characters that have been encoded. -local function unescape_special_chars(t) - local tin = t - for k,v in pairs(escape_table) do - k = k:gsub("%%", "%%%%") - t = t:gsub(v,k) - end - if t ~= tin then t = unescape_special_chars(t) end - return t -end - --- Encode/escape certain characters inside Markdown code runs. --- The point is that in code, these characters are literals, --- and lose their special Markdown meanings. -function encode_code(s) - s = s:gsub("%&", "&") - s = s:gsub("<", "<") - s = s:gsub(">", ">") - for k,v in pairs(escape_table) do - s = s:gsub("%"..k, v) - end - return s -end - --- Handle backtick blocks. -local function code_spans(s) - s = s:gsub("\\\\", escape_table["\\"]) - s = s:gsub("\\`", escape_table["`"]) - - local pos = 1 - while true do - local start, stop = s:find("`+", pos) - if not start then return s end - local count = stop - start + 1 - -- Find a matching numbert of backticks - local estart, estop = s:find(string.rep("`", count), stop+1) - local brstart = s:find("\n", stop+1) - if estart and (not brstart or estart < brstart) then - local code = s:sub(stop+1, estart-1) - code = code:gsub("^[ \t]+", "") - code = code:gsub("[ \t]+$", "") - code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) - code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) - code = "" .. encode_code(code) .. "" - code = add_escape(code) - s = s:sub(1, start-1) .. code .. s:sub(estop+1) - pos = start + code:len() - else - pos = stop + 1 - end - end - return s -end - --- Encode alt text... enodes &, and ". -local function encode_alt(s) - if not s then return s end - s = s:gsub('&', '&') - s = s:gsub('"', '"') - s = s:gsub('<', '<') - return s -end - -local link_database - --- Handle image references -local function images(text) - local function reference_link(alt, id) - alt = encode_alt(alt:match("%b[]"):sub(2,-2)) - id = id:match("%[(.*)%]"):lower() - if id == "" then id = text:lower() end - link_database[id] = link_database[id] or {} - if not link_database[id].url then return nil end - local url = link_database[id].url or id - url = encode_alt(url) - local title = encode_alt(link_database[id].title) - if title then title = " title=\"" .. title .. "\"" else title = "" end - return add_escape ('' .. alt .. '") - end - - local function inline_link(alt, link) - alt = encode_alt(alt:match("%b[]"):sub(2,-2)) - local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") - url = url or link:match("%(?%)") - url = encode_alt(url) - title = encode_alt(title) - if title then - return add_escape('' .. alt .. '') - else - return add_escape('' .. alt .. '') - end - end - - text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) - text = text:gsub("!(%b[])(%b())", inline_link) - return text -end - --- Handle anchor references -local function anchors(text) - local function reference_link(text, id) - text = text:match("%b[]"):sub(2,-2) - id = id:match("%b[]"):sub(2,-2):lower() - if id == "" then id = text:lower() end - link_database[id] = link_database[id] or {} - if not link_database[id].url then return nil end - local url = link_database[id].url or id - url = encode_alt(url) - local title = encode_alt(link_database[id].title) - if title then title = " title=\"" .. title .. "\"" else title = "" end - return add_escape("") .. text .. add_escape("") - end - - local function inline_link(text, link) - text = text:match("%b[]"):sub(2,-2) - local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") - title = encode_alt(title) - url = url or link:match("%(?%)") or "" - url = encode_alt(url) - if title then - return add_escape("") .. text .. "" - else - return add_escape("") .. text .. add_escape("") - end - end - - text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) - text = text:gsub("(%b[])(%b())", inline_link) - return text -end - --- Handle auto links, i.e. . -local function auto_links(text) - local function link(s) - return add_escape("") .. s .. "" - end - -- Encode chars as a mix of dec and hex entitites to (perhaps) fool - -- spambots. - local function encode_email_address(s) - -- Use a deterministic encoding to make unit testing possible. - -- Code 45% hex, 45% dec, 10% plain. - local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} - local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} - local plain = {code = function(c) return c end, count = 0, rate = 0.1} - local codes = {hex, dec, plain} - local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end - - local out = "" - for i = 1,s:len() do - for _,code in ipairs(codes) do code.count = code.count + code.rate end - if codes[1].count < codes[2].count then swap(codes,1,2) end - if codes[2].count < codes[3].count then swap(codes,2,3) end - if codes[1].count < codes[2].count then swap(codes,1,2) end - - local code = codes[1] - local c = s:sub(i,i) - -- Force encoding of "@" to make email address more invisible. - if c == "@" and code == plain then code = codes[2] end - out = out .. code.code(c) - code.count = code.count - 1 - end - return out - end - local function mail(s) - s = unescape_special_chars(s) - local address = encode_email_address("mailto:" .. s) - local text = encode_email_address(s) - return add_escape("") .. text .. "" - end - -- links - text = text:gsub("<(https?:[^'\">%s]+)>", link) - text = text:gsub("<(ftp:[^'\">%s]+)>", link) - - -- mail - text = text:gsub("%s]+)>", mail) - text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) - return text -end - --- Encode free standing amps (&) and angles (<)... note that this does not --- encode free >. -local function amps_and_angles(s) - -- encode amps not part of &..; expression - local pos = 1 - while true do - local amp = s:find("&", pos) - if not amp then break end - local semi = s:find(";", amp+1) - local stop = s:find("[ \t\n&]", amp+1) - if not semi or (stop and stop < semi) or (semi - amp) > 15 then - s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) - pos = amp+1 - else - pos = amp+1 - end - end - - -- encode naked <'s - s = s:gsub("<([^a-zA-Z/?$!])", "<%1") - s = s:gsub("<$", "<") - - -- what about >, nothing done in the original markdown source to handle them - return s -end - --- Handles emphasis markers (* and _) in the text. -local function emphasis(text) - for _, s in ipairs {"%*%*", "%_%_"} do - text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "%1") - text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "%1") - end - for _, s in ipairs {"%*", "%_"} do - text = text:gsub(s .. "([^%s_])" .. s, "%1") - text = text:gsub(s .. "([^%s_])" .. s, "%1") - text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "%1") - text = text:gsub(s .. "([^<>_]-[^<>_]-[^<>_]-)" .. s, "%1") - end - return text -end - --- Handles line break markers in the text. -local function line_breaks(text) - return text:gsub(" +\n", "
    \n") -end - --- Perform all span level transforms. -function span_transform(text) - text = code_spans(text) - text = escape_special_chars(text) - text = images(text) - text = anchors(text) - text = auto_links(text) - text = amps_and_angles(text) - text = emphasis(text) - text = line_breaks(text) - return text -end - ----------------------------------------------------------------------- --- Markdown ----------------------------------------------------------------------- - --- Cleanup the text by normalizing some possible variations to make further --- processing easier. -local function cleanup(text) - -- Standardize line endings - text = text:gsub("\r\n", "\n") -- DOS to UNIX - text = text:gsub("\r", "\n") -- Mac to UNIX - - -- Convert all tabs to spaces - text = detab(text) - - -- Strip lines with only spaces and tabs - while true do - local subs - text, subs = text:gsub("\n[ \t]+\n", "\n\n") - if subs == 0 then break end - end - - return "\n" .. text .. "\n" -end - --- Strips link definitions from the text and stores the data in a lookup table. -local function strip_link_definitions(text) - local linkdb = {} - - local function link_def(id, url, title) - id = id:match("%[(.+)%]"):lower() - linkdb[id] = linkdb[id] or {} - linkdb[id].url = url or linkdb[id].url - linkdb[id].title = title or linkdb[id].title - return "" - end - - local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*]+)>?[ \t]*" - local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" - local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" - local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" - - text = text:gsub(def_title1, link_def) - text = text:gsub(def_title2, link_def) - text = text:gsub(def_title3, link_def) - text = text:gsub(def_no_title, link_def) - return text, linkdb -end - -link_database = {} - --- Main markdown processing function -local function markdown(text) - init_hash(text) - init_escape_table() - - text = cleanup(text) - text = protect(text) - text, link_database = strip_link_definitions(text) - text = block_transform(text) - text = unescape_special_chars(text) - return text -end - ----------------------------------------------------------------------- --- End of module ----------------------------------------------------------------------- - -M.lock(M) - --- Expose markdown function to the world -_G.markdown = M.markdown - --- Class for parsing command-line options -local OptionParser = {} -OptionParser.__index = OptionParser - --- Creates a new option parser -function OptionParser:new() - local o = {short = {}, long = {}} - setmetatable(o, self) - return o -end - --- Calls f() whenever a flag with specified short and long name is encountered -function OptionParser:flag(short, long, f) - local info = {type = "flag", f = f} - if short then self.short[short] = info end - if long then self.long[long] = info end -end - --- Calls f(param) whenever a parameter flag with specified short and long name is encountered -function OptionParser:param(short, long, f) - local info = {type = "param", f = f} - if short then self.short[short] = info end - if long then self.long[long] = info end -end - --- Calls f(v) for each non-flag argument -function OptionParser:arg(f) - self.arg = f -end - --- Runs the option parser for the specified set of arguments. Returns true if all arguments --- where successfully parsed and false otherwise. -function OptionParser:run(args) - local pos = 1 - local param - while pos <= #args do - local arg = args[pos] - if arg == "--" then - for i=pos+1,#args do - if self.arg then self.arg(args[i]) end - return true - end - end - if arg:match("^%-%-") then - local info = self.long[arg:sub(3)] - if not info then print("Unknown flag: " .. arg) return false end - if info.type == "flag" then - info.f() - pos = pos + 1 - else - param = args[pos+1] - if not param then print("No parameter for flag: " .. arg) return false end - info.f(param) - pos = pos+2 - end - elseif arg:match("^%-") then - for i=2,arg:len() do - local c = arg:sub(i,i) - local info = self.short[c] - if not info then print("Unknown flag: -" .. c) return false end - if info.type == "flag" then - info.f() - else - if i == arg:len() then - param = args[pos+1] - if not param then print("No parameter for flag: -" .. c) return false end - info.f(param) - pos = pos + 1 - else - param = arg:sub(i+1) - info.f(param) - end - break - end - end - pos = pos + 1 - else - if self.arg then self.arg(arg) end - pos = pos + 1 - end - end - return true -end - --- Handles the case when markdown is run from the command line -local function run_command_line(arg) - -- Generate output for input s given options - local function run(s, options) - s = markdown(s) - if not options.wrap_header then return s end - local header = "" - if options.header then - local f = io.open(options.header) or error("Could not open file: " .. options.header) - header = f:read("*a") - f:close() - else - header = [[ - - - - - TITLE - - - -]] - local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or - s:match("

    (.-)

    ") or "Untitled" - header = header:gsub("TITLE", title) - if options.inline_style then - local style = "" - local f = io.open(options.stylesheet) - if f then - style = f:read("*a") f:close() - else - error("Could not include style sheet " .. options.stylesheet .. ": File not found") - end - header = header:gsub('', - "") - else - header = header:gsub("STYLESHEET", options.stylesheet) - end - header = header:gsub("CHARSET", options.charset) - end - local footer = "" - if options.footer then - local f = io.open(options.footer) or error("Could not open file: " .. options.footer) - footer = f:read("*a") - f:close() - end - return header .. s .. footer - end - - -- Generate output path name from input path name given options. - local function outpath(path, options) - if options.append then return path .. ".html" end - local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end - m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end - return path .. ".html" - end - - -- Default commandline options - local options = { - wrap_header = true, - header = nil, - footer = nil, - charset = "utf-8", - title = nil, - stylesheet = "default.css", - inline_style = false - } - local help = [[ -Usage: markdown.lua [OPTION] [FILE] -Runs the markdown text markup to HTML converter on each file specified on the -command line. If no files are specified, runs on standard input. - -No header: - -n, --no-wrap Don't wrap the output in ... tags. -Custom header: - -e, --header FILE Use content of FILE for header. - -f, --footer FILE Use content of FILE for footer. -Generated header: - -c, --charset SET Specifies charset (default utf-8). - -i, --title TITLE Specifies title (default from first

    tag). - -s, --style STYLE Specifies style sheet file (default default.css). - -l, --inline-style Include the style sheet file inline in the header. -Generated files: - -a, --append Append .html extension (instead of replacing). -Other options: - -h, --help Print this help text. - -t, --test Run the unit tests. -]] - - local run_stdin = true - local op = OptionParser:new() - op:flag("n", "no-wrap", function () options.wrap_header = false end) - op:param("e", "header", function (x) options.header = x end) - op:param("f", "footer", function (x) options.footer = x end) - op:param("c", "charset", function (x) options.charset = x end) - op:param("i", "title", function(x) options.title = x end) - op:param("s", "style", function(x) options.stylesheet = x end) - op:flag("l", "inline-style", function(x) options.inline_style = true end) - op:flag("a", "append", function() options.append = true end) - op:flag("t", "test", function() - local n = arg[0]:gsub("markdown.lua", "markdown-tests.lua") - local f = io.open(n) - if f then - f:close() dofile(n) - else - error("Cannot find markdown-tests.lua") - end - run_stdin = false - end) - op:flag("h", "help", function() print(help) run_stdin = false end) - op:arg(function(path) - local file = io.open(path) or error("Could not open file: " .. path) - local s = file:read("*a") - file:close() - s = run(s, options) - file = io.open(outpath(path, options), "w") or error("Could not open output file: " .. outpath(path, options)) - file:write(s) - file:close() - run_stdin = false - end - ) - - if not op:run(arg) then - print(help) - run_stdin = false - end - - if run_stdin then - local s = io.read("*a") - s = run(s, options) - io.write(s) - end -end - --- If we are being run from the command-line, act accordingly -if arg and arg[0]:find("markdown%.lua$") then - run_command_line(arg) -else - return markdown -end +#!/usr/bin/env lua + +--[[ +# markdown.lua -- version 0.32 + + + +**Author:** Niklas Frykholm, +**Date:** 31 May 2008 + +This is an implementation of the popular text markup language Markdown in pure Lua. +Markdown can convert documents written in a simple and easy to read text format +to well-formatted HTML. For a more thourough description of Markdown and the Markdown +syntax, see . + +The original Markdown source is written in Perl and makes heavy use of advanced +regular expression techniques (such as negative look-ahead, etc) which are not available +in Lua's simple regex engine. Therefore this Lua port has been rewritten from the ground +up. It is probably not completely bug free. If you notice any bugs, please report them to +me. A unit test that exposes the error is helpful. + +## Usage + + require "markdown" + markdown(source) + +``markdown.lua`` exposes a single global function named ``markdown(s)`` which applies the +Markdown transformation to the specified string. + +``markdown.lua`` can also be used directly from the command line: + + lua markdown.lua test.md + +Creates a file ``test.html`` with the converted content of ``test.md``. Run: + + lua markdown.lua -h + +For a description of the command-line options. + +``markdown.lua`` uses the same license as Lua, the MIT license. + +## License + +Copyright © 2008 Niklas Frykholm. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## Version history + +- **0.32** -- 31 May 2008 + - Fix for links containing brackets +- **0.31** -- 1 Mar 2008 + - Fix for link definitions followed by spaces +- **0.30** -- 25 Feb 2008 + - Consistent behavior with Markdown when the same link reference is reused +- **0.29** -- 24 Feb 2008 + - Fix for
     blocks with spaces in them
    +-	**0.28** -- 18 Feb 2008
    +	-	Fix for link encoding
    +-	**0.27** -- 14 Feb 2008
    +	-	Fix for link database links with ()
    +-	**0.26** -- 06 Feb 2008
    +	-	Fix for nested italic and bold markers
    +-	**0.25** -- 24 Jan 2008
    +	-	Fix for encoding of naked <
    +-	**0.24** -- 21 Jan 2008
    +	-	Fix for link behavior.
    +-	**0.23** -- 10 Jan 2008
    +	-	Fix for a regression bug in longer expressions in italic or bold.
    +-	**0.22** -- 27 Dec 2007
    +	-	Fix for crash when processing blocks with a percent sign in them.
    +-	**0.21** -- 27 Dec 2007
    +	- 	Fix for combined strong and emphasis tags
    +-	**0.20** -- 13 Oct 2007
    +	-	Fix for < as well in image titles, now matches Dingus behavior
    +-	**0.19** -- 28 Sep 2007
    +	-	Fix for quotation marks " and ampersands & in link and image titles.
    +-	**0.18** -- 28 Jul 2007
    +	-	Does not crash on unmatched tags (behaves like standard markdown)
    +-	**0.17** -- 12 Apr 2007
    +	-	Fix for links with %20 in them.
    +-	**0.16** -- 12 Apr 2007
    +	-	Do not require arg global to exist.
    +-	**0.15** -- 28 Aug 2006
    +	-	Better handling of links with underscores in them.
    +-	**0.14** -- 22 Aug 2006
    +	-	Bug for *`foo()`*
    +-	**0.13** -- 12 Aug 2006
    +	-	Added -l option for including stylesheet inline in document.
    +	-	Fixed bug in -s flag.
    +	-	Fixed emphasis bug.
    +-	**0.12** -- 15 May 2006
    +	-	Fixed several bugs to comply with MarkdownTest 1.0 
    +-	**0.11** -- 12 May 2006
    +	-	Fixed bug for escaping `*` and `_` inside code spans.
    +	-	Added license terms.
    +	-	Changed join() to table.concat().
    +-	**0.10** -- 3 May 2006
    +	-	Initial public release.
    +
    +// Niklas
    +]]
    +
    +
    +-- Set up a table for holding local functions to avoid polluting the global namespace
    +local M = {}
    +local unpack = unpack or table.unpack
    +local MT = {__index = _G}
    +setmetatable(M, MT)
    +
    +----------------------------------------------------------------------
    +-- Utility functions
    +----------------------------------------------------------------------
    +
    +-- Locks table t from changes, writes an error if someone attempts to change the table.
    +-- This is useful for detecting variables that have "accidently" been made global. Something
    +-- I tend to do all too much.
    +function M.lock(t)
    +	local function lock_new_index(t, k, v)
    +		error("module has been locked -- " .. k .. " must be declared local", 2)
    +	end
    +
    +	local mt = {__newindex = lock_new_index}
    +	if getmetatable(t) then
    +      mt.__index = getmetatable(t).__index
    +   end
    +	setmetatable(t, mt)
    +end
    +
    +-- Returns the result of mapping the values in table t through the function f
    +local function map(t, f)
    +	local out = {}
    +	for k,v in pairs(t) do out[k] = f(v,k) end
    +	return out
    +end
    +
    +-- The identity function, useful as a placeholder.
    +local function identity(text) return text end
    +
    +-- Functional style if statement. (NOTE: no short circuit evaluation)
    +local function iff(t, a, b) if t then return a else return b end end
    +
    +-- Splits the text into an array of separate lines.
    +local function split(text, sep)
    +	sep = sep or "\n"
    +	local lines = {}
    +	local pos = 1
    +	while true do
    +		local b,e = text:find(sep, pos)
    +		if not b then table.insert(lines, text:sub(pos)) break end
    +		table.insert(lines, text:sub(pos, b-1))
    +		pos = e + 1
    +	end
    +	return lines
    +end
    +
    +-- Converts tabs to spaces
    +local function detab(text)
    +	local tab_width = 4
    +	local function rep(match)
    +		local spaces = -match:len()
    +		while spaces<1 do spaces = spaces + tab_width end
    +		return match .. string.rep(" ", spaces)
    +	end
    +	text = text:gsub("([^\n]-)\t", rep)
    +	return text
    +end
    +
    +-- Applies string.find for every pattern in the list and returns the first match
    +local function find_first(s, patterns, index)
    +	local res = {}
    +	for _,p in ipairs(patterns) do
    +		local match = {s:find(p, index)}
    +		if #match>0 and (#res==0 or match[1] < res[1]) then res = match end
    +	end
    +	return unpack(res)
    +end
    +
    +-- If a replacement array is specified, the range [start, stop] in the array is replaced
    +-- with the replacement array and the resulting array is returned. Without a replacement
    +-- array the section of the array between start and stop is returned.
    +local function splice(array, start, stop, replacement)
    +	if replacement then
    +		local n = stop - start + 1
    +		while n > 0 do
    +			table.remove(array, start)
    +			n = n - 1
    +		end
    +		for i,v in ipairs(replacement) do
    +			table.insert(array, start, v)
    +		end
    +		return array
    +	else
    +		local res = {}
    +		for i = start,stop do
    +			table.insert(res, array[i])
    +		end
    +		return res
    +	end
    +end
    +
    +-- Outdents the text one step.
    +local function outdent(text)
    +	text = "\n" .. text
    +	text = text:gsub("\n  ? ? ?", "\n")
    +	text = text:sub(2)
    +	return text
    +end
    +
    +-- Indents the text one step.
    +local function indent(text)
    +	text = text:gsub("\n", "\n    ")
    +	return text
    +end
    +
    +-- Does a simple tokenization of html data. Returns the data as a list of tokens.
    +-- Each token is a table with a type field (which is either "tag" or "text") and
    +-- a text field (which contains the original token data).
    +local function tokenize_html(html)
    +	local tokens = {}
    +	local pos = 1
    +	while true do
    +		local start = find_first(html, {"", start)
    +		elseif html:match("^<%?", start) then
    +			_,stop = html:find("?>", start)
    +		else
    +			_,stop = html:find("%b<>", start)
    +		end
    +		if not stop then
    +			-- error("Could not match html tag " .. html:sub(start,start+30))
    +		 	table.insert(tokens, {type="text", text=html:sub(start, start)})
    +			pos = start + 1
    +		else
    +			table.insert(tokens, {type="tag", text=html:sub(start, stop)})
    +			pos = stop + 1
    +		end
    +	end
    +	return tokens
    +end
    +
    +----------------------------------------------------------------------
    +-- Hash
    +----------------------------------------------------------------------
    +
    +-- This is used to "hash" data into alphanumeric strings that are unique
    +-- in the document. (Note that this is not cryptographic hash, the hash
    +-- function is not one-way.) The hash procedure is used to protect parts
    +-- of the document from further processing.
    +
    +local HASH = {
    +	-- Has the hash been inited.
    +	inited = false,
    +
    +	-- The unique string prepended to all hash values. This is to ensure
    +	-- that hash values do not accidently coincide with an actual existing
    +	-- string in the document.
    +	identifier = "",
    +
    +	-- Counter that counts up for each new hash instance.
    +	counter = 0,
    +
    +	-- Hash table.
    +	table = {}
    +}
    +
    +-- Inits hashing. Creates a hash_identifier that doesn't occur anywhere
    +-- in the text.
    +local function init_hash(text)
    +	HASH.inited = true
    +	HASH.identifier = ""
    +	HASH.counter = 0
    +	HASH.table = {}
    +
    +	local s = "HASH"
    +	local counter = 0
    +	local id
    +	while true do
    +		id  = s .. counter
    +		if not text:find(id, 1, true) then break end
    +		counter = counter + 1
    +	end
    +	HASH.identifier = id
    +end
    +
    +-- Returns the hashed value for s.
    +local function hash(s)
    +	assert(HASH.inited)
    +	if not HASH.table[s] then
    +		HASH.counter = HASH.counter + 1
    +		local id = HASH.identifier .. HASH.counter .. "X"
    +		HASH.table[s] = id
    +	end
    +	return HASH.table[s]
    +end
    +
    +----------------------------------------------------------------------
    +-- Protection
    +----------------------------------------------------------------------
    +
    +-- The protection module is used to "protect" parts of a document
    +-- so that they are not modified by subsequent processing steps.
    +-- Protected parts are saved in a table for later unprotection
    +
    +-- Protection data
    +local PD = {
    +	-- Saved blocks that have been converted
    +	blocks = {},
    +
    +	-- Block level tags that will be protected
    +	tags = {"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote",
    +	"pre", "table", "dl", "ol", "ul", "script", "noscript", "form", "fieldset",
    +	"iframe", "math", "ins", "del"}
    +}
    +
    +-- Pattern for matching a block tag that begins and ends in the leftmost
    +-- column and may contain indented subtags, i.e.
    +-- 
    +-- A nested block. +--
    +-- Nested data. +--
    +--
    +local function block_pattern(tag) + return "\n<" .. tag .. ".-\n[ \t]*\n" +end + +-- Pattern for matching a block tag that begins and ends with a newline +local function line_pattern(tag) + return "\n<" .. tag .. ".-[ \t]*\n" +end + +-- Protects the range of characters from start to stop in the text and +-- returns the protected string. +local function protect_range(text, start, stop) + local s = text:sub(start, stop) + local h = hash(s) + PD.blocks[h] = s + text = text:sub(1,start) .. h .. text:sub(stop) + return text +end + +-- Protect every part of the text that matches any of the patterns. The first +-- matching pattern is protected first, etc. +local function protect_matches(text, patterns) + while true do + local start, stop = find_first(text, patterns) + if not start then break end + text = protect_range(text, start, stop) + end + return text +end + +-- Protects blocklevel tags in the specified text +local function protect(text) + -- First protect potentially nested block tags + text = protect_matches(text, map(PD.tags, block_pattern)) + -- Then protect block tags at the line level. + text = protect_matches(text, map(PD.tags, line_pattern)) + -- Protect
    and comment tags + text = protect_matches(text, {"\n]->[ \t]*\n"}) + text = protect_matches(text, {"\n[ \t]*\n"}) + return text +end + +-- Returns true if the string s is a hash resulting from protection +local function is_protected(s) + return PD.blocks[s] +end + +-- Unprotects the specified text by expanding all the nonces +local function unprotect(text) + for k,v in pairs(PD.blocks) do + v = v:gsub("%%", "%%%%") + text = text:gsub(k, v) + end + return text +end + + +---------------------------------------------------------------------- +-- Block transform +---------------------------------------------------------------------- + +-- The block transform functions transform the text on the block level. +-- They work with the text as an array of lines rather than as individual +-- characters. + +-- Returns true if the line is a ruler of (char) characters. +-- The line must contain at least three char characters and contain only spaces and +-- char characters. +local function is_ruler_of(line, char) + if not line:match("^[ %" .. char .. "]*$") then return false end + if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end + return true +end + +-- Identifies the block level formatting present in the line +local function classify(line) + local info = {line = line, text = line} + + if line:match("^ ") then + info.type = "indented" + info.outdented = line:sub(5) + return info + end + + for _,c in ipairs({'*', '-', '_', '='}) do + if is_ruler_of(line, c) then + info.type = "ruler" + info.ruler_char = c + return info + end + end + + if line == "" then + info.type = "blank" + return info + end + + if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then + local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") + info.type = "header" + info.level = m1:len() + info.text = m2 + return info + end + + if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then + local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") + info.type = "list_item" + info.list_type = "numeric" + info.number = 0 + number + info.text = text + return info + end + + if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then + local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") + info.type = "list_item" + info.list_type = "bullet" + info.bullet = bullet + info.text= text + return info + end + + if line:match("^>[ \t]?(.*)") then + info.type = "blockquote" + info.text = line:match("^>[ \t]?(.*)") + return info + end + + if is_protected(line) then + info.type = "raw" + info.html = unprotect(line) + return info + end + + info.type = "normal" + return info +end + +-- Find headers constisting of a normal line followed by a ruler and converts them to +-- header entries. +local function headers(array) + local i = 1 + while i <= #array - 1 do + if array[i].type == "normal" and array[i+1].type == "ruler" and + (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then + local info = {line = array[i].line} + info.text = info.line + info.type = "header" + info.level = iff(array[i+1].ruler_char == "=", 1, 2) + table.remove(array, i+1) + array[i] = info + end + i = i + 1 + end + return array +end + +local block_transform, blocks_to_html, encode_code, span_transform, encode_backslash_escapes + +-- Find list blocks and convert them to protected data blocks +local function lists(array, sublist) + local function process_list(arr) + local function any_blanks(arr) + for i = 1, #arr do + if arr[i].type == "blank" then return true end + end + return false + end + + local function split_list_items(arr) + local acc = {arr[1]} + local res = {} + for i=2,#arr do + if arr[i].type == "list_item" then + table.insert(res, acc) + acc = {arr[i]} + else + table.insert(acc, arr[i]) + end + end + table.insert(res, acc) + return res + end + + local function process_list_item(lines, block) + while lines[#lines].type == "blank" do + table.remove(lines) + end + + local itemtext = lines[1].text + for i=2,#lines do + itemtext = itemtext .. "\n" .. outdent(lines[i].line) + end + if block then + itemtext = block_transform(itemtext, true) + if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    +				return "    
  • " .. itemtext .. "
  • " + else + local lines = split(itemtext) + lines = map(lines, classify) + lines = lists(lines, true) + lines = blocks_to_html(lines, true) + itemtext = table.concat(lines, "\n") + if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    +				return "    
  • " .. itemtext .. "
  • " + end + end + + local block_list = any_blanks(arr) + local items = split_list_items(arr) + local out = "" + for _, item in ipairs(items) do + out = out .. process_list_item(item, block_list) .. "\n" + end + if arr[1].list_type == "numeric" then + return "
      \n" .. out .. "
    " + else + return "
      \n" .. out .. "
    " + end + end + + -- Finds the range of lines composing the first list in the array. A list + -- starts with (^ list_item) or (blank list_item) and ends with + -- (blank* $) or (blank normal). + -- + -- A sublist can start with just (list_item) does not need a blank... + local function find_list(array, sublist) + local function find_list_start(array, sublist) + if array[1].type == "list_item" then return 1 end + if sublist then + for i = 1,#array do + if array[i].type == "list_item" then return i end + end + else + for i = 1, #array-1 do + if array[i].type == "blank" and array[i+1].type == "list_item" then + return i+1 + end + end + end + return nil + end + local function find_list_end(array, start) + local pos = #array + for i = start, #array-1 do + if array[i].type == "blank" and array[i+1].type ~= "list_item" + and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then + pos = i-1 + break + end + end + while pos > start and array[pos].type == "blank" do + pos = pos - 1 + end + return pos + end + + local start = find_list_start(array, sublist) + if not start then return nil end + return start, find_list_end(array, start) + end + + while true do + local start, stop = find_list(array, sublist) + if not start then break end + local text = process_list(splice(array, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + array = splice(array, start, stop, {info}) + end + + -- Convert any remaining list items to normal + for _,line in ipairs(array) do + if line.type == "list_item" then line.type = "normal" end + end + + return array +end + +-- Find and convert blockquote markers. +local function blockquotes(lines) + local function find_blockquote(lines) + local start + for i,line in ipairs(lines) do + if line.type == "blockquote" then + start = i + break + end + end + if not start then return nil end + + local stop = #lines + for i = start+1, #lines do + if lines[i].type == "blank" or lines[i].type == "blockquote" then + elseif lines[i].type == "normal" then + if lines[i-1].type == "blank" then stop = i-1 break end + else + stop = i-1 break + end + end + while lines[stop].type == "blank" do stop = stop - 1 end + return start, stop + end + + local function process_blockquote(lines) + local raw = lines[1].text + for i = 2,#lines do + raw = raw .. "\n" .. lines[i].text + end + local bt = block_transform(raw) + if not bt:find("
    ") then bt = indent(bt) end
    +		return "
    \n " .. bt .. + "\n
    " + end + + while true do + local start, stop = find_blockquote(lines) + if not start then break end + local text = process_blockquote(splice(lines, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + lines = splice(lines, start, stop, {info}) + end + return lines +end + +-- Find and convert codeblocks. +local function codeblocks(lines) + local function find_codeblock(lines) + local start + for i,line in ipairs(lines) do + if line.type == "indented" then start = i break end + end + if not start then return nil end + + local stop = #lines + for i = start+1, #lines do + if lines[i].type ~= "indented" and lines[i].type ~= "blank" then + stop = i-1 + break + end + end + while lines[stop].type == "blank" do stop = stop - 1 end + return start, stop + end + + local function process_codeblock(lines) + local raw = detab(encode_code(outdent(lines[1].line))) + for i = 2,#lines do + raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) + end + return "
    " .. raw .. "\n
    " + end + + while true do + local start, stop = find_codeblock(lines) + if not start then break end + local text = process_codeblock(splice(lines, start, stop)) + local info = { + line = text, + type = "raw", + html = text + } + lines = splice(lines, start, stop, {info}) + end + return lines +end + +-- Convert lines to html code +function blocks_to_html(lines, no_paragraphs) + local out = {} + local i = 1 + while i <= #lines do + local line = lines[i] + if line.type == "ruler" then + table.insert(out, "
    ") + elseif line.type == "raw" then + table.insert(out, line.html) + elseif line.type == "normal" then + local s = line.line + + while i+1 <= #lines and lines[i+1].type == "normal" do + i = i + 1 + s = s .. "\n" .. lines[i].line + end + + if no_paragraphs then + table.insert(out, span_transform(s)) + else + table.insert(out, "

    " .. span_transform(s) .. "

    ") + end + elseif line.type == "header" then + local s = "" .. span_transform(line.text) .. "" + table.insert(out, s) + else + table.insert(out, line.line) + end + i = i + 1 + end + return out +end + +-- Perform all the block level transforms +function block_transform(text, sublist) + local lines = split(text) + lines = map(lines, classify) + lines = headers(lines) + lines = lists(lines, sublist) + lines = codeblocks(lines) + lines = blockquotes(lines) + lines = blocks_to_html(lines) + local text = table.concat(lines, "\n") + return text +end + +-- Debug function for printing a line array to see the result +-- of partial transforms. +local function print_lines(lines) + for i, line in ipairs(lines) do + print(i, line.type, line.text or line.line) + end +end + +---------------------------------------------------------------------- +-- Span transform +---------------------------------------------------------------------- + +-- Functions for transforming the text at the span level. + +-- These characters may need to be escaped because they have a special +-- meaning in markdown. +local escape_chars = "'\\`*_{}[]()>#+-.!'" +local escape_table = {} + +local function init_escape_table() + escape_table = {} + for i = 1,#escape_chars do + local c = escape_chars:sub(i,i) + escape_table[c] = hash(c) + end +end + +-- Adds a new escape to the escape table. +local function add_escape(text) + if not escape_table[text] then + escape_table[text] = hash(text) + end + return escape_table[text] +end + +-- Escape characters that should not be disturbed by markdown. +local function escape_special_chars(text) + local tokens = tokenize_html(text) + + local out = "" + for _, token in ipairs(tokens) do + local t = token.text + if token.type == "tag" then + -- In tags, encode * and _ so they don't conflict with their use in markdown. + t = t:gsub("%*", escape_table["*"]) + t = t:gsub("%_", escape_table["_"]) + else + t = encode_backslash_escapes(t) + end + out = out .. t + end + return out +end + +-- Encode backspace-escaped characters in the markdown source. +function encode_backslash_escapes(t) + for i=1,escape_chars:len() do + local c = escape_chars:sub(i,i) + t = t:gsub("\\%" .. c, escape_table[c]) + end + return t +end + +-- Unescape characters that have been encoded. +local function unescape_special_chars(t) + local tin = t + for k,v in pairs(escape_table) do + k = k:gsub("%%", "%%%%") + t = t:gsub(v,k) + end + if t ~= tin then t = unescape_special_chars(t) end + return t +end + +-- Encode/escape certain characters inside Markdown code runs. +-- The point is that in code, these characters are literals, +-- and lose their special Markdown meanings. +function encode_code(s) + s = s:gsub("%&", "&") + s = s:gsub("<", "<") + s = s:gsub(">", ">") + for k,v in pairs(escape_table) do + s = s:gsub("%"..k, v) + end + return s +end + +-- Handle backtick blocks. +local function code_spans(s) + s = s:gsub("\\\\", escape_table["\\"]) + s = s:gsub("\\`", escape_table["`"]) + + local pos = 1 + while true do + local start, stop = s:find("`+", pos) + if not start then return s end + local count = stop - start + 1 + -- Find a matching numbert of backticks + local estart, estop = s:find(string.rep("`", count), stop+1) + local brstart = s:find("\n", stop+1) + if estart and (not brstart or estart < brstart) then + local code = s:sub(stop+1, estart-1) + code = code:gsub("^[ \t]+", "") + code = code:gsub("[ \t]+$", "") + code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) + code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) + code = "" .. encode_code(code) .. "" + code = add_escape(code) + s = s:sub(1, start-1) .. code .. s:sub(estop+1) + pos = start + code:len() + else + pos = stop + 1 + end + end + return s +end + +-- Encode alt text... enodes &, and ". +local function encode_alt(s) + if not s then return s end + s = s:gsub('&', '&') + s = s:gsub('"', '"') + s = s:gsub('<', '<') + return s +end + +local link_database + +-- Handle image references +local function images(text) + local function reference_link(alt, id) + alt = encode_alt(alt:match("%b[]"):sub(2,-2)) + id = id:match("%[(.*)%]"):lower() + if id == "" then id = text:lower() end + link_database[id] = link_database[id] or {} + if not link_database[id].url then return nil end + local url = link_database[id].url or id + url = encode_alt(url) + local title = encode_alt(link_database[id].title) + if title then title = " title=\"" .. title .. "\"" else title = "" end + return add_escape ('' .. alt .. '") + end + + local function inline_link(alt, link) + alt = encode_alt(alt:match("%b[]"):sub(2,-2)) + local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") + url = url or link:match("%(?%)") + url = encode_alt(url) + title = encode_alt(title) + if title then + return add_escape('' .. alt .. '') + else + return add_escape('' .. alt .. '') + end + end + + text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) + text = text:gsub("!(%b[])(%b())", inline_link) + return text +end + +-- Handle anchor references +local function anchors(text) + local function reference_link(text, id) + text = text:match("%b[]"):sub(2,-2) + id = id:match("%b[]"):sub(2,-2):lower() + if id == "" then id = text:lower() end + link_database[id] = link_database[id] or {} + if not link_database[id].url then return nil end + local url = link_database[id].url or id + url = encode_alt(url) + local title = encode_alt(link_database[id].title) + if title then title = " title=\"" .. title .. "\"" else title = "" end + return add_escape("") .. text .. add_escape("") + end + + local function inline_link(text, link) + text = text:match("%b[]"):sub(2,-2) + local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") + title = encode_alt(title) + url = url or link:match("%(?%)") or "" + url = encode_alt(url) + if title then + return add_escape("") .. text .. "" + else + return add_escape("") .. text .. add_escape("") + end + end + + text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) + text = text:gsub("(%b[])(%b())", inline_link) + return text +end + +-- Handle auto links, i.e. . +local function auto_links(text) + local function link(s) + return add_escape("") .. s .. "" + end + -- Encode chars as a mix of dec and hex entitites to (perhaps) fool + -- spambots. + local function encode_email_address(s) + -- Use a deterministic encoding to make unit testing possible. + -- Code 45% hex, 45% dec, 10% plain. + local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} + local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} + local plain = {code = function(c) return c end, count = 0, rate = 0.1} + local codes = {hex, dec, plain} + local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end + + local out = "" + for i = 1,s:len() do + for _,code in ipairs(codes) do code.count = code.count + code.rate end + if codes[1].count < codes[2].count then swap(codes,1,2) end + if codes[2].count < codes[3].count then swap(codes,2,3) end + if codes[1].count < codes[2].count then swap(codes,1,2) end + + local code = codes[1] + local c = s:sub(i,i) + -- Force encoding of "@" to make email address more invisible. + if c == "@" and code == plain then code = codes[2] end + out = out .. code.code(c) + code.count = code.count - 1 + end + return out + end + local function mail(s) + s = unescape_special_chars(s) + local address = encode_email_address("mailto:" .. s) + local text = encode_email_address(s) + return add_escape("") .. text .. "" + end + -- links + text = text:gsub("<(https?:[^'\">%s]+)>", link) + text = text:gsub("<(ftp:[^'\">%s]+)>", link) + + -- mail + text = text:gsub("%s]+)>", mail) + text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) + return text +end + +-- Encode free standing amps (&) and angles (<)... note that this does not +-- encode free >. +local function amps_and_angles(s) + -- encode amps not part of &..; expression + local pos = 1 + while true do + local amp = s:find("&", pos) + if not amp then break end + local semi = s:find(";", amp+1) + local stop = s:find("[ \t\n&]", amp+1) + if not semi or (stop and stop < semi) or (semi - amp) > 15 then + s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) + pos = amp+1 + else + pos = amp+1 + end + end + + -- encode naked <'s + s = s:gsub("<([^a-zA-Z/?$!])", "<%1") + s = s:gsub("<$", "<") + + -- what about >, nothing done in the original markdown source to handle them + return s +end + +-- Handles emphasis markers (* and _) in the text. +local function emphasis(text) + for _, s in ipairs {"%*%*", "%_%_"} do + text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "%1") + text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "%1") + end + for _, s in ipairs {"%*", "%_"} do + text = text:gsub(s .. "([^%s_])" .. s, "%1") + text = text:gsub(s .. "([^%s_])" .. s, "%1") + text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "%1") + text = text:gsub(s .. "([^<>_]-[^<>_]-[^<>_]-)" .. s, "%1") + end + return text +end + +-- Handles line break markers in the text. +local function line_breaks(text) + return text:gsub(" +\n", "
    \n") +end + +-- Perform all span level transforms. +function span_transform(text) + text = code_spans(text) + text = escape_special_chars(text) + text = images(text) + text = anchors(text) + text = auto_links(text) + text = amps_and_angles(text) + text = emphasis(text) + text = line_breaks(text) + return text +end + +---------------------------------------------------------------------- +-- Markdown +---------------------------------------------------------------------- + +-- Cleanup the text by normalizing some possible variations to make further +-- processing easier. +local function cleanup(text) + -- Standardize line endings + text = text:gsub("\r\n", "\n") -- DOS to UNIX + text = text:gsub("\r", "\n") -- Mac to UNIX + + -- Convert all tabs to spaces + text = detab(text) + + -- Strip lines with only spaces and tabs + while true do + local subs + text, subs = text:gsub("\n[ \t]+\n", "\n\n") + if subs == 0 then break end + end + + return "\n" .. text .. "\n" +end + +-- Strips link definitions from the text and stores the data in a lookup table. +local function strip_link_definitions(text) + local linkdb = {} + + local function link_def(id, url, title) + id = id:match("%[(.+)%]"):lower() + linkdb[id] = linkdb[id] or {} + linkdb[id].url = url or linkdb[id].url + linkdb[id].title = title or linkdb[id].title + return "" + end + + local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*]+)>?[ \t]*" + local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" + local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" + local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" + + text = text:gsub(def_title1, link_def) + text = text:gsub(def_title2, link_def) + text = text:gsub(def_title3, link_def) + text = text:gsub(def_no_title, link_def) + return text, linkdb +end + +link_database = {} + +-- Main markdown processing function +local function markdown(text) + init_hash(text) + init_escape_table() + + text = cleanup(text) + text = protect(text) + text, link_database = strip_link_definitions(text) + text = block_transform(text) + text = unescape_special_chars(text) + return text +end + +---------------------------------------------------------------------- +-- End of module +---------------------------------------------------------------------- + +M.lock(M) + +-- Expose markdown function to the world +_G.markdown = M.markdown + +-- Class for parsing command-line options +local OptionParser = {} +OptionParser.__index = OptionParser + +-- Creates a new option parser +function OptionParser:new() + local o = {short = {}, long = {}} + setmetatable(o, self) + return o +end + +-- Calls f() whenever a flag with specified short and long name is encountered +function OptionParser:flag(short, long, f) + local info = {type = "flag", f = f} + if short then self.short[short] = info end + if long then self.long[long] = info end +end + +-- Calls f(param) whenever a parameter flag with specified short and long name is encountered +function OptionParser:param(short, long, f) + local info = {type = "param", f = f} + if short then self.short[short] = info end + if long then self.long[long] = info end +end + +-- Calls f(v) for each non-flag argument +function OptionParser:arg(f) + self.arg = f +end + +-- Runs the option parser for the specified set of arguments. Returns true if all arguments +-- where successfully parsed and false otherwise. +function OptionParser:run(args) + local pos = 1 + local param + while pos <= #args do + local arg = args[pos] + if arg == "--" then + for i=pos+1,#args do + if self.arg then self.arg(args[i]) end + return true + end + end + if arg:match("^%-%-") then + local info = self.long[arg:sub(3)] + if not info then print("Unknown flag: " .. arg) return false end + if info.type == "flag" then + info.f() + pos = pos + 1 + else + param = args[pos+1] + if not param then print("No parameter for flag: " .. arg) return false end + info.f(param) + pos = pos+2 + end + elseif arg:match("^%-") then + for i=2,arg:len() do + local c = arg:sub(i,i) + local info = self.short[c] + if not info then print("Unknown flag: -" .. c) return false end + if info.type == "flag" then + info.f() + else + if i == arg:len() then + param = args[pos+1] + if not param then print("No parameter for flag: -" .. c) return false end + info.f(param) + pos = pos + 1 + else + param = arg:sub(i+1) + info.f(param) + end + break + end + end + pos = pos + 1 + else + if self.arg then self.arg(arg) end + pos = pos + 1 + end + end + return true +end + +-- Handles the case when markdown is run from the command line +local function run_command_line(arg) + -- Generate output for input s given options + local function run(s, options) + s = markdown(s) + if not options.wrap_header then return s end + local header = "" + if options.header then + local f = io.open(options.header) or error("Could not open file: " .. options.header) + header = f:read("*a") + f:close() + else + header = [[ + + + + + TITLE + + + +]] + local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or + s:match("

    (.-)

    ") or "Untitled" + header = header:gsub("TITLE", title) + if options.inline_style then + local style = "" + local f = io.open(options.stylesheet) + if f then + style = f:read("*a") f:close() + else + error("Could not include style sheet " .. options.stylesheet .. ": File not found") + end + header = header:gsub('', + "") + else + header = header:gsub("STYLESHEET", options.stylesheet) + end + header = header:gsub("CHARSET", options.charset) + end + local footer = "" + if options.footer then + local f = io.open(options.footer) or error("Could not open file: " .. options.footer) + footer = f:read("*a") + f:close() + end + return header .. s .. footer + end + + -- Generate output path name from input path name given options. + local function outpath(path, options) + if options.append then return path .. ".html" end + local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end + m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end + return path .. ".html" + end + + -- Default commandline options + local options = { + wrap_header = true, + header = nil, + footer = nil, + charset = "utf-8", + title = nil, + stylesheet = "default.css", + inline_style = false + } + local help = [[ +Usage: markdown.lua [OPTION] [FILE] +Runs the markdown text markup to HTML converter on each file specified on the +command line. If no files are specified, runs on standard input. + +No header: + -n, --no-wrap Don't wrap the output in ... tags. +Custom header: + -e, --header FILE Use content of FILE for header. + -f, --footer FILE Use content of FILE for footer. +Generated header: + -c, --charset SET Specifies charset (default utf-8). + -i, --title TITLE Specifies title (default from first

    tag). + -s, --style STYLE Specifies style sheet file (default default.css). + -l, --inline-style Include the style sheet file inline in the header. +Generated files: + -a, --append Append .html extension (instead of replacing). +Other options: + -h, --help Print this help text. + -t, --test Run the unit tests. +]] + + local run_stdin = true + local op = OptionParser:new() + op:flag("n", "no-wrap", function () options.wrap_header = false end) + op:param("e", "header", function (x) options.header = x end) + op:param("f", "footer", function (x) options.footer = x end) + op:param("c", "charset", function (x) options.charset = x end) + op:param("i", "title", function(x) options.title = x end) + op:param("s", "style", function(x) options.stylesheet = x end) + op:flag("l", "inline-style", function(x) options.inline_style = true end) + op:flag("a", "append", function() options.append = true end) + op:flag("t", "test", function() + local n = arg[0]:gsub("markdown.lua", "markdown-tests.lua") + local f = io.open(n) + if f then + f:close() dofile(n) + else + error("Cannot find markdown-tests.lua") + end + run_stdin = false + end) + op:flag("h", "help", function() print(help) run_stdin = false end) + op:arg(function(path) + local file = io.open(path) or error("Could not open file: " .. path) + local s = file:read("*a") + file:close() + s = run(s, options) + file = io.open(outpath(path, options), "w") or error("Could not open output file: " .. outpath(path, options)) + file:write(s) + file:close() + run_stdin = false + end + ) + + if not op:run(arg) then + print(help) + run_stdin = false + end + + if run_stdin then + local s = io.read("*a") + s = run(s, options) + io.write(s) + end +end + +-- If we are being run from the command-line, act accordingly +if arg and arg[0]:find("markdown%.lua$") then + run_command_line(arg) +else + return markdown +end diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 5bbc1661..671f2ca4 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -1,5 +1,6 @@ -- parsing code for doc comments +local utils = require 'pl.utils' local List = require 'pl.List' local Map = require 'pl.Map' local stringio = require 'pl.stringio' @@ -7,6 +8,7 @@ local lexer = require 'ldoc.lexer' local tools = require 'ldoc.tools' local doc = require 'ldoc.doc' local Item,File = doc.Item,doc.File +local unpack = utils.unpack ------ Parsing the Source -------------- -- This uses the lexer from PL, but it should be possible to use Peter Odding's @@ -142,7 +144,7 @@ local function extract_tags (s,args) if not value:match '\n[^\n]+\n' then value = strip(value) end - + tags:add(tag,value,modifiers) end return tags --Map(tags) From 0d79e16706819fa8cba96e7e20ec80728dbe624c Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Mon, 26 Aug 2013 09:59:10 +0200 Subject: [PATCH 053/106] PL utils.unpack compatibility; refactoring error 'tag' --- doc/doc.md | 8 ++++++-- ldoc.lua | 8 +++----- ldoc/doc.lua | 42 +++++++++++++++++++++++++-------------- tests/styles/multiple.lua | 9 ++++++++- 4 files changed, 44 insertions(+), 23 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 77bbe8b1..398767dc 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -375,7 +375,8 @@ function as well.) `@set` is a powerful tag which assigns a configuration variable to a value _just for this module_. Saying `@set no_summary=true` in a module comment will temporarily disable summary generation when the template is expanded. Generally configuration variables that effect template expansion -are modifiable in this way. +are modifiable in this way. For instance, if you wish that the contents of a particular module +be sorted, then `@set sort=true` will do it _just_ for that module. ## Sections @@ -830,8 +831,11 @@ description. There are then sections for the following tags: 'param', 'usage', ' 'see' in that order. (For tables, 'Fields' is used instead of 'Parameters' but internally fields of a table are stored as the 'param' tag.) +By default, the items appear in the order of declaration within their section. If `sort=true` +then they will be sorted alphabetically. (This can be set per-module with @{Module_Tags|@set}.) + You can of course customize the default template, but there are some parameters that can -control what the template will generate. Setting `one` to `true` in your configuration file +control what the template will generate. Setting `one=true` in your configuration file will give a _one-column_ layout, which can be easier to use as a programming reference. You can suppress the contents summary with `no_summary`. diff --git a/ldoc.lua b/ldoc.lua index e00d42e3..8481ebb3 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -25,6 +25,8 @@ local List = require 'pl.List' local stringx = require 'pl.stringx' local tablex = require 'pl.tablex' +-- Penlight compatibility +utils.unpack = utils.unpack or unpack or table.unpack local append = table.insert @@ -149,11 +151,7 @@ function ldoc.tparam_alias (name,type) ldoc.alias(name,{'param',modifiers={type=type}}) end -ldoc.alias ('error',function(tags,value) - local g = '2' - tags:add('return','',{[g]=true,type='nil'}) - return 'return', value, {[g]=true,type='string'} -end) +ldoc.alias ('error',doc.error_macro) ldoc.tparam_alias 'string' ldoc.tparam_alias 'number' diff --git a/ldoc/doc.lua b/ldoc/doc.lua index e754e5cb..d8757675 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -772,6 +772,26 @@ function Item:default_of_param(p) return opt end +function Item:subparam(p) + local subp = rawget(self.subparams,p) + if subp then + return subp,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 + +-------- return values and types ------- + function Item:type_of_ret(idx) local rparam = self.modifiers['return'][idx] return rparam and rparam.type or '' @@ -829,23 +849,15 @@ function Item:build_return_groups() end end -function Item:subparam(p) - local subp = rawget(self.subparams,p) - if subp then - return subp,p - else - return {p},nil - end +-- this alias macro implements @error. +-- Alias macros need to return the same results as Item:check_tags... +function doc.error_macro(tags,value,modifiers) + local key = integer_keys(modifiers) + local g = key > 0 and tostring(key) or '2' + tags:add('return','',{[g]=true,type='nil'}) + return 'return', value, {[g]=true,type='string'} 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 diff --git a/tests/styles/multiple.lua b/tests/styles/multiple.lua index 54c72cfe..4800a949 100644 --- a/tests/styles/multiple.lua +++ b/tests/styles/multiple.lua @@ -15,9 +15,16 @@ function mul1 () end -- @error message function mul2 () end +----- +-- function with multiple error tags +-- @return result +-- @error[1] not found +-- @error[2] bad format +function mul3 () end + ----- -- function that raises an error. -- @string filename -- @treturn string result -- @raise 'file not found' -function mul3(filename) end +function mul4(filename) end From a825a316a17cf1b8c579ba3b7609b61dcf1183e5 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Mon, 26 Aug 2013 13:37:41 +0200 Subject: [PATCH 054/106] error tag now auto-groups; refactorings --- ldoc/doc.lua | 85 +++++++++++++++++++++++++++------------ ldoc/parse.lua | 17 +++++--- tests/styles/multiple.lua | 4 +- 3 files changed, 73 insertions(+), 33 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index d8757675..32e902b9 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -492,7 +492,7 @@ function Item.check_tag(tags,tag, value, modifiers) modifiers[m] = v end end - else -- has to be a function + else -- has to be a function that at least returns tag, value return alias(tags,value,modifiers) end end @@ -545,6 +545,13 @@ end local build_arg_list, split_iden -- forward declaration +function Item:split_param (line) + 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 + return name, comment +end function Item:finish() local tags = self.tags @@ -593,10 +600,7 @@ function Item:finish() local param_names, comments = List(), List() if params then for line in params:iter() do - 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 + local name, comment = self:split_param(line) param_names:append(name) comments:append(comment) end @@ -612,16 +616,10 @@ function Item:finish() 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 - 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 ret + formal,comments,ret = self:parse_formal_arguments(fargs) + if ret and not self.ret then self.ret = ret end + elseif #fargs > 0 then -- consistency check! local varargs = fargs[#fargs] == '...' if varargs then table.remove(fargs) end local k = 0 @@ -639,14 +637,12 @@ function Item:finish() end end if k < #fargs then - for i = k+1,#fargs do - if fargs[i] ~= '...' then - self:warning("undocumented formal argument: "..quote(fargs[i])) - end - end + for i = k+1,#fargs do if fargs[i] ~= '...' then + self:warning("undocumented formal argument: "..quote(fargs[i])) + end end end - end - end + end -- #fargs > 0 + end -- fargs -- the comments are associated with each parameter by -- adding name-value pairs to the params list (this is @@ -702,6 +698,19 @@ function Item:parse_argument_comment (comment,field) return comment or '' end +function Item:parse_formal_arguments (fargs) + local formal, comments, ret = List(), List() + if fargs.return_comment then + local retc = self:parse_argument_comment(fargs.return_comment,'return') + ret = List{retc} + end + for i, name in ipairs(fargs) do + formal:append(name) + comments:append(self:parse_argument_comment(fargs.comments[name],self.parameter)) + end + return formal, comments, ret +end + function split_iden (name) if name == '...' then return name end local pname,field = name:match('(.-)%.(.+)') @@ -753,6 +762,9 @@ function build_arg_list (names,pmods) return '('..table.concat(buffer)..')' end +------ retrieving information about parameters ----- +-- The template leans on these guys heavily.... + function Item:param_modifiers (p) local mods = self.modifiers[self.parameter] if not mods then return '' end @@ -798,7 +810,7 @@ function Item:type_of_ret(idx) end local function integer_keys(t) - if not t then return 0 end + if type(t) ~= 'table' then return 0 end for k in pairs(t) do local num = tonumber(k) if num then return num end @@ -832,7 +844,7 @@ function Item:build_return_groups() if fields then local fcomments = List() for i,f in ipairs(fields) do - local name, comment = f:match('%s*([%w_%.:]+)(.*)') + local name, comment = self:split_param(f) fields[i] = name fcomments[i] = coment end @@ -852,12 +864,33 @@ end -- this alias macro implements @error. -- Alias macros need to return the same results as Item:check_tags... function doc.error_macro(tags,value,modifiers) + local g = '2' -- our default group id + -- Were we given an explicit group modifier? local key = integer_keys(modifiers) - local g = key > 0 and tostring(key) or '2' + if key > 0 then + g = tostring(key) + else + local l = tags:get 'return' + if l then -- there were returns already...... + -- maximum group of _existing_ error return + local grp, text = 0, List() + for r in l:iter() do if type(r) == 'table' then + grp = math.max(grp,r.modifiers._err or 0) + text:append(r[1]) + end end + if grp > 0 then -- cool, create new group + g = tostring(grp+1) + text:append(value) + print(text) + end + end + end tags:add('return','',{[g]=true,type='nil'}) - return 'return', value, {[g]=true,type='string'} + -- note that this 'return' is tagged with _err! + return 'return', value, {[g]=true,_err=tonumber(g),type='string'} end +---------- bothering the user -------------------- function Item:warning(msg) local file = self.file and self.file.filename diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 671f2ca4..6889dfe8 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -96,11 +96,8 @@ function Tags:add (tag,value,modifiers) if modifiers then -- how modifiers are encoded value = {value,modifiers=modifiers} end - local ovalue = rawget(self,tag) - if ovalue then -- previous value? - if getmetatable(ovalue) ~= List then - ovalue = List{ovalue} - end + local ovalue = self:get(tag) + if ovalue then ovalue:append(value) value = ovalue end @@ -110,6 +107,16 @@ function Tags:add (tag,value,modifiers) end end +function Tags:get (tag) + local ovalue = rawget(self,tag) + if ovalue then -- previous value? + if getmetatable(ovalue) ~= List then + ovalue = List{ovalue} + end + return ovalue + end +end + function Tags:iter () return self._order:iter() end diff --git a/tests/styles/multiple.lua b/tests/styles/multiple.lua index 4800a949..355d788a 100644 --- a/tests/styles/multiple.lua +++ b/tests/styles/multiple.lua @@ -18,8 +18,8 @@ function mul2 () end ----- -- function with multiple error tags -- @return result --- @error[1] not found --- @error[2] bad format +-- @error not found +-- @error bad format function mul3 () end ----- From 8395d6d9d7db624e0794d72aaa701cf23baa12eb Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Mon, 26 Aug 2013 14:22:52 +0200 Subject: [PATCH 055/106] inline error comments starting to work --- ldoc/doc.lua | 18 ++++++++++++++++-- tests/styles/multiple.lua | 13 ++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 32e902b9..656b5f7c 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -138,8 +138,11 @@ function doc.expand_annotation_item (tags, last_item) tags:add('name',item_name..'-'..tag..acount) acount = acount + 1 return true + elseif tag == 'return' then + last_item:set_tag(tag,value) end end + return false end -- we process each file, resulting in a File object, which has a list of Item objects. @@ -422,12 +425,21 @@ function Item:trailing_warning (kind,tag,rest) end end +local function is_list (l) + return getmetatable(l) == List +end + function Item:set_tag (tag,value) local ttype = known_tags[tag] local args = self.file.args if ttype == TAG_MULTI or ttype == TAG_MULTI_LINE then -- value is always a List! - if getmetatable(value) ~= List then + local ovalue = self.tags[tag] + if is_list(ovalue) then + ovalue:append(value) + value = ovalue + end + if not is_list(value) then value = List{value} end if ttype ~= TAG_MULTI_LINE and args and args.not_luadoc then @@ -833,11 +845,14 @@ function Item:build_return_groups() local g = integer_keys(mods) if g ~= lastg then group = List() + group.g = g groups:append(group) lastg = g end group:append({text=ret, type = mods.type or '',mods = mods}) end + -- order by groups to force error groups to the end + table.sort(groups,function(g1,g2) return g1.g < g2.g end) self.retgroups = groups -- cool, now see if there are any treturns that have tfields to associate with local fields = self.tags.field @@ -881,7 +896,6 @@ function doc.error_macro(tags,value,modifiers) if grp > 0 then -- cool, create new group g = tostring(grp+1) text:append(value) - print(text) end end end diff --git a/tests/styles/multiple.lua b/tests/styles/multiple.lua index 355d788a..bd935fb9 100644 --- a/tests/styles/multiple.lua +++ b/tests/styles/multiple.lua @@ -22,9 +22,20 @@ function mul2 () end -- @error bad format function mul3 () end +---- +-- function with inline return and errors +-- @string name +function mul4 (name) + if type(name) ~= 'string' then + --- @error not a string + return nil, 'not a string' + end + --- @treturn string converted to uppercase + return name:upper() +end ----- -- function that raises an error. -- @string filename -- @treturn string result -- @raise 'file not found' -function mul4(filename) end +function mul5(filename) end From f33a09e4d31414f1e390183264408a4acf88b695 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 27 Aug 2013 12:44:04 +0200 Subject: [PATCH 056/106] issue #76 auto-scroll to avoid breaking identifiers --- ldoc/html/ldoc_css.lua | 5 +++++ ldoc/html/ldoc_ltp.lua | 11 +++++------ ldoc/html/ldoc_pale_css.lua | 5 +++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ldoc/html/ldoc_css.lua b/ldoc/html/ldoc_css.lua index 44700453..76756b1b 100644 --- a/ldoc/html/ldoc_css.lua +++ b/ldoc/html/ldoc_css.lua @@ -272,6 +272,11 @@ table.function_list td { table.function_list td.name { background-color: #f0f0f0; ; min-width: 200px; } table.function_list td.summary { width: 100%; } +ul.nowrap { + overflow:auto; + whitespace:nowrap; +} + dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} dl.table h3, dl.function h3 {font-size: .95em;} diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 450e1994..eee62775 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -24,7 +24,6 @@ return [==[ # local use_li = ldoc.use_li # local display_name = ldoc.display_name # local iter = ldoc.modules.iter -# ---local M = ldoc.markup # local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end # local nowrap = ldoc.wrap and '' or 'nowrap' @@ -54,7 +53,7 @@ return [==[ # if ldoc.no_summary and module and not ldoc.one then -- bang out the functions on the side # for kind, items in module.kinds() do

    $(kind)

    -
      +
        # for item in items() do
      • $(display_name(item))
      • # end @@ -67,7 +66,7 @@ return [==[ # for kind, mods, type in ldoc.kinds() do # if not ldoc.kinds_allowed or ldoc.kinds_allowed[type] then

        $(kind)

        -
          +
            # for mod in mods() do # if mod.name == this_mod then -- highlight current module, link to others
          • $(mod.name)
          • @@ -187,10 +186,10 @@ return [==[

            Returns:

            # for i,group in ldoc.ipairs(groups) do local li,il = use_li(group)
              -# for r in group:iter() do local type, ctypes = item:return_type(r) +# for r in group:iter() do local type, ctypes = item:return_type(r); local rt = ldoc.typename(type) $(li) -# if type ~= '' then - $(ldoc.typename(type)) +# if rt ~= '' then + $(rt) # end $(M(r.text,item))$(il) # if ctypes then diff --git a/ldoc/html/ldoc_pale_css.lua b/ldoc/html/ldoc_pale_css.lua index fe720120..75abb320 100644 --- a/ldoc/html/ldoc_pale_css.lua +++ b/ldoc/html/ldoc_pale_css.lua @@ -274,6 +274,11 @@ dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} dl.table h3, dl.function h3 {font-size: .95em;} +ul.nowrap { + overflow:auto; + whitespace:nowrap; +} + /* stop sublists from having initial vertical space */ ul ul { margin-top: 0px; } ol ul { margin-top: 0px; } From df72613e571c3502ceaa6ae30b502e7e36ec8d7e Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 27 Aug 2013 12:47:47 +0200 Subject: [PATCH 057/106] structure return needs 'typename'; by default merge_error_groups will combine different error tag texts --- ldoc.lua | 10 ++++-- ldoc/doc.lua | 64 ++++++++++++++++++++++++++++++--------- ldoc/html.lua | 2 +- tests/styles/multiple.lua | 6 +++- tests/styles/struct.lua | 7 +++-- 5 files changed, 68 insertions(+), 21 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 8481ebb3..838caeea 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -130,6 +130,8 @@ local file_types = { -- the ldoc table represents the API available in `config.ld`. local ldoc = { charset = 'UTF-8' } local add_language_extension +-- hacky way for doc module to be passed options... +doc.ldoc = ldoc local function override (field) if ldoc[field] ~= nil then args[field] = ldoc[field] end @@ -193,8 +195,8 @@ 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', 'pretty', 'charset', 'plain', - 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file', - 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape', + 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars', + 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', 'no_space_before_args', } @@ -373,6 +375,10 @@ override 'merge' override 'not_luadoc' override 'module_file' +if ldoc.merge_error_groups == nil then + ldoc.merge_error_groups = 'Error Message' +end + -- ldoc.module_file establishes a partial ordering where the -- master module files are processed first. local function reorder_module_file () diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 656b5f7c..615abb8e 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -21,7 +21,7 @@ local TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG,TAG_MULTI_LINE = 'M','id','S -- - '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 = 'ML', ['return'] = 'M', field = 'M', author='M',set='M'; + param = 'M', see = 'M', comment = 'M', usage = 'ML', ['return'] = 'M', field = 'M', author='M',set='M'; 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', charset = 'S', @@ -130,11 +130,11 @@ local acount = 1 function doc.expand_annotation_item (tags, last_item) if tags.summary ~= '' then return false end + local item_name = last_item and last_item.tags.name or '?' for tag, value in pairs(tags) do if known_tags._annotation_tags[tag] then tags:add('class','annotation') tags:add('summary',value) - local item_name = last_item and last_item.tags.name or '?' tags:add('name',item_name..'-'..tag..acount) acount = acount + 1 return true @@ -435,10 +435,16 @@ function Item:set_tag (tag,value) if ttype == TAG_MULTI or ttype == TAG_MULTI_LINE then -- value is always a List! local ovalue = self.tags[tag] - if is_list(ovalue) then - ovalue:append(value) + if ovalue then -- already defined, must be a list + --print(tag,ovalue,value) + if is_list(value) then + ovalue:extend(value) + else + ovalue:append(value) + end value = ovalue end + -- these multiple values are always represented as lists if not is_list(value) then value = List{value} end @@ -835,6 +841,8 @@ function Item:return_type(r) return r.type, r.ctypes end +local struct_return_type = '*' + function Item:build_return_groups() local modifiers = self.modifiers local retmod = modifiers['return'] @@ -849,11 +857,13 @@ function Item:build_return_groups() groups:append(group) lastg = g end + --require 'pl.pretty'.dump(ret) group:append({text=ret, type = mods.type or '',mods = mods}) end -- order by groups to force error groups to the end table.sort(groups,function(g1,g2) return g1.g < g2.g end) self.retgroups = groups + --require 'pl.pretty'.dump(groups) -- cool, now see if there are any treturns that have tfields to associate with local fields = self.tags.field if fields then @@ -861,24 +871,28 @@ function Item:build_return_groups() for i,f in ipairs(fields) do local name, comment = self:split_param(f) fields[i] = name - fcomments[i] = coment + fcomments[i] = comment end local fmods = modifiers.field for group in groups:iter() do for r in group:iter() do - if r.mods and r.mods.type == '*' then - local ctypes = List() - for i,f in ipairs(fields) do + if r.mods and r.mods.type then + local ctypes, T = List(), r.mods.type + for i,f in ipairs(fields) do if fmods[i][T] then ctypes:append {name=f,type=fmods[i].type,comment=fcomments[i]} - end + end end r.ctypes = ctypes + --require 'pl.pretty'.dump(ctypes) end end end end end +local ecount = 0 + -- this alias macro implements @error. -- Alias macros need to return the same results as Item:check_tags... function doc.error_macro(tags,value,modifiers) + local merge_groups = doc.ldoc.merge_error_groups local g = '2' -- our default group id -- Were we given an explicit group modifier? local key = integer_keys(modifiers) @@ -888,14 +902,36 @@ function doc.error_macro(tags,value,modifiers) local l = tags:get 'return' if l then -- there were returns already...... -- maximum group of _existing_ error return - local grp, text = 0, List() + local grp, lastr = 0 for r in l:iter() do if type(r) == 'table' then - grp = math.max(grp,r.modifiers._err or 0) - text:append(r[1]) + local rg = r.modifiers._err + if rg then + lastr = r + grp = math.max(grp,rg) + end end end if grp > 0 then -- cool, create new group - g = tostring(grp+1) - text:append(value) + if not merge_groups then + g = tostring(grp+1) + else + local mods, text, T = lastr.modifiers + local new = function(text) + return mods._collected..' '..text,{type='string',[T]=true} + end + if not mods._collected then + text = lastr[1] + lastr[1] = merge_groups + T = '@'..ecount + mods.type = T + mods._collected = 1 + ecount = ecount + 1 + tags:add('field',new(text)) + else + T = mods.type + end + mods._collected = mods._collected + 1 + return 'field',new(value) + end end end end diff --git a/ldoc/html.lua b/ldoc/html.lua index cdc77fa2..9a92ec20 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -168,7 +168,7 @@ function html.generate_output(ldoc, args, project) end function ldoc.typename (tp) - if not tp or tp == '' then return '' end + if not tp or tp == '' or tp:match '^@' then return '' end local optional -- ? is short for ?nil| if tp:match("^%?") and not tp:match '|' then diff --git a/tests/styles/multiple.lua b/tests/styles/multiple.lua index bd935fb9..85e12b99 100644 --- a/tests/styles/multiple.lua +++ b/tests/styles/multiple.lua @@ -27,9 +27,13 @@ function mul3 () end -- @string name function mul4 (name) if type(name) ~= 'string' then - --- @error not a string + --- @error[1] not a string return nil, 'not a string' end + if #name == 0 then + --- @error[2] zero-length string + return nil, 'zero-length string' + end --- @treturn string converted to uppercase return name:upper() end diff --git a/tests/styles/struct.lua b/tests/styles/struct.lua index 9d454a21..c28689a1 100644 --- a/tests/styles/struct.lua +++ b/tests/styles/struct.lua @@ -5,8 +5,9 @@ ----- -- returns a 'struct'. -- @string name your name dammit --- @treturn * details of person --- @tfield string name of person --- @tfield int age of person +-- @tfield string arb stuff +-- @treturn st details of person +-- @tfield[st] string name of person +-- @tfield[st] int age of person function struct(name) end From 1d1ed6d0f07f87aade54ac180085b8290ed65e53 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 27 Aug 2013 13:17:11 +0200 Subject: [PATCH 058/106] on left-hand side all module names go through doc.module_name. This strips off package part currently --- ldoc/html.lua | 8 ++++++++ ldoc/html/ldoc_ltp.lua | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ldoc/html.lua b/ldoc/html.lua index 9a92ec20..a7401db9 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -95,6 +95,14 @@ function html.generate_output(ldoc, args, project) return (item.summary or '?')..' '..(item.description or '') end + function ldoc.module_name (mod) + local name = mod.name + if mod.type == 'module' then + name = name:gsub('^.-%.','') + 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 diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index eee62775..3a433620 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -71,7 +71,7 @@ return [==[ # if mod.name == this_mod then -- highlight current module, link to others
            1. $(mod.name)
            2. # else -
            3. $(mod.name)
            4. +
            5. $(ldoc.module_name(mod))
            6. # end # end # end From 8e3e2117163d5d64417c7a99cb9f3cb710c4461b Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 27 Aug 2013 14:16:21 +0200 Subject: [PATCH 059/106] fixup selected module item --- doc/config.ld | 1 + doc/doc.md | 2 +- ldoc/html/ldoc_ltp.lua | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/config.ld b/doc/config.ld index b37025dd..54e40523 100644 --- a/doc/config.ld +++ b/doc/config.ld @@ -6,6 +6,7 @@ backtick_references=false file='../ldoc.lua' dir='../out' readme='doc.md' +style='!pale' examples = { '../tests/styles/colon.lua', '../tests/styles/four.lua', diff --git a/doc/doc.md b/doc/doc.md index 398767dc..8d402c50 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -1092,7 +1092,7 @@ configuration file. - `merge` allow documentation from different files to be merged into modules without explicit @submodule tag -_These only appear in config.ld:_ +_These only appear in the configuration file:_ - `description` a short project description used under the project title - `full_description` when you _really_ need a longer project description diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 3a433620..957aae37 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -67,11 +67,11 @@ return [==[ # if not ldoc.kinds_allowed or ldoc.kinds_allowed[type] then

              $(kind)

                -# for mod in mods() do -# if mod.name == this_mod then -- highlight current module, link to others -
              • $(mod.name)
              • +# for mod in mods() do local name = ldoc.module_name(mod) +# if mod.name == this_mod then +
              • $(name)
              • # else -
              • $(ldoc.module_name(mod))
              • +
              • $(name)
              • # end # end # end From 47e6a2094e4c5463ef3266e4cd0243d68c75f96f Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Tue, 27 Aug 2013 15:16:10 +0200 Subject: [PATCH 060/106] CSS errors fixed --- ldoc/html/ldoc_css.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ldoc/html/ldoc_css.lua b/ldoc/html/ldoc_css.lua index 76756b1b..1724909a 100644 --- a/ldoc/html/ldoc_css.lua +++ b/ldoc/html/ldoc_css.lua @@ -29,7 +29,7 @@ del,ins { text-decoration: none; } li { - list-style: bullet; + list-style: disc; margin-left: 20px; } caption,th { @@ -253,7 +253,7 @@ table.module_list td { border-style: solid; border-color: #cccccc; } -table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; } +table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } table.module_list td.summary { width: 100%; } @@ -269,12 +269,12 @@ table.function_list td { border-style: solid; border-color: #cccccc; } -table.function_list td.name { background-color: #f0f0f0; ; min-width: 200px; } +table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } table.function_list td.summary { width: 100%; } ul.nowrap { overflow:auto; - whitespace:nowrap; + white-space:nowrap; } dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} From d2e40e2f2fd66a1be4e608be01ef6810bbf3843c Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Fri, 20 Sep 2013 13:33:06 +0200 Subject: [PATCH 061/106] basic support for documenting C. If you put 'parse_extra={C=true}' in config.ld, then it will attempt to extract function name, arguments and types from the source --- ldoc.lua | 4 +++- ldoc/doc.lua | 21 +++++++++++++++++- ldoc/lang.lua | 48 ++++++++++++++++++++++++++++++++++++++++++ ldoc/lexer.lua | 1 + ldoc/project.ldoc.mode | 2 ++ ldoc/tools.lua | 29 ++++++++++++++++++++----- 6 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 ldoc/project.ldoc.mode diff --git a/ldoc.lua b/ldoc.lua index 838caeea..69d6f119 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -198,7 +198,7 @@ local ldoc_contents = { 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars', 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', - 'no_space_before_args', + 'no_space_before_args','parse_extra', } ldoc_contents = tablex.makeset(ldoc_contents) @@ -355,6 +355,7 @@ local function process_file (f, flist) local ftype = file_types[ext] if ftype then if args.verbose then print(f) end + ftype.extra = ldoc.parse_extra or {} local F,err = parse.file(f,ftype,args) if err then if F then @@ -544,6 +545,7 @@ end) ldoc.single = modcount == 1 and first_module or nil +--do return end -------- three ways to dump the object graph after processing ----- diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 615abb8e..b184c7e6 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -659,6 +659,21 @@ function Item:finish() self:warning("undocumented formal argument: "..quote(fargs[i])) end end end + -- formal arguments may come with types, inferred by the + -- appropriate code in ldoc.lang + if fargs.types then + self.modifiers[field] = List() + for t in fargs.types:iter() do + self:add_type(field,t) + end + if fargs.return_type then + if not self.ret then -- type, but no comment; no worries + self.ret = List{''} + end + self.modifiers['return'] = List() + self:add_type('return',fargs.return_type) + end + end end -- #fargs > 0 end -- fargs @@ -701,6 +716,10 @@ function Item:finish() end end +function Item:add_type(field,type) + self.modifiers[field]:append {type = type} +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 @@ -709,7 +728,7 @@ function Item:parse_argument_comment (comment,field) comment = comment:gsub('^%-+%s*','') local type,rest = comment:match '([^:]+):(.*)' if type then - self.modifiers[field]:append {type = type} + self:add_type(field,type) comment = rest end end diff --git a/ldoc/lang.lua b/ldoc/lang.lua index 95a261f3..da750023 100644 --- a/ldoc/lang.lua +++ b/ldoc/lang.lua @@ -266,6 +266,54 @@ function CC:grab_block_comment(v,tok) return 'comment',v:sub(1,-3) end +--- here the argument name is always last, and the type is composed of any tokens before +function CC:extract_arg (tl,idx) + idx = idx or 1 + local res = List() + for i = idx,#tl-1 do + res:append(tl[i][2]) + end + local type = res:join ' ' + return tl[#tl][2], type +end + +function CC:item_follows (t,v,tok) + if not self.extra.C then + return false + end + if t == 'iden' or t == 'keyword' then -- + if v == self.extra.export then -- this is not part of the return type! + t,v = tnext(tok) + end + -- TBD collecting types which are not single tokens (may contain '*') + local return_type = v + t,v = tnext(tok) + if t == 'iden' or t=='keyword' then + local name = v + t,v = tnext(tok) + if t ~= '(' then + return_type = return_type .. ' ' .. name + name = v + t,v = tnext(tok) + end + print ('got',name,t,v,return_type) + return function(tags,tok) + if not tags.name then + tags:add('name',name) + end + tags:add('class','function') + if t == '(' then + tags.formal_args,t,v = tools.get_parameters(tok,')',',',self) + if return_type ~= 'void' then + tags.formal_args.return_type = return_type + end + end + end + end + end + return false +end + local Moon = class(Lua) function Moon:_init() diff --git a/ldoc/lexer.lua b/ldoc/lexer.lua index bad9f77d..fa50ac87 100644 --- a/ldoc/lexer.lua +++ b/ldoc/lexer.lua @@ -371,6 +371,7 @@ function lexer.cpp(s,filter,options) {'^|=',tdump}, {'^%^=',tdump}, {'^::',tdump}, + {'^%.%.%.',tdump}, {'^.',tdump} } end diff --git a/ldoc/project.ldoc.mode b/ldoc/project.ldoc.mode new file mode 100644 index 00000000..dae53bfc --- /dev/null +++ b/ldoc/project.ldoc.mode @@ -0,0 +1,2 @@ +mode=lua +tabs=use=false,size=3 diff --git a/ldoc/tools.lua b/ldoc/tools.lua index 9aeb0b9f..cc434323 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -284,7 +284,7 @@ local function value_of (tok) return tok[2] end -- following the arguments. ldoc will use these in addition to explicit -- param tags. -function M.get_parameters (tok,endtoken,delim) +function M.get_parameters (tok,endtoken,delim,lang) tok = M.space_skip_getter(tok) local args = List() args.comments = {} @@ -292,8 +292,18 @@ function M.get_parameters (tok,endtoken,delim) if not ltl or not ltl[1] or #ltl[1] == 0 then return args end -- no arguments - local function strip_comment (text) - return text:match("%s*%-%-+%s*(.*)") + local strip_comment, extract_arg + + if lang then + strip_comment = utils.bind1(lang.trim_comment,lang) + extract_arg = utils.bind1(lang.extract_arg,lang) + else + strip_comment = function(text) + return text:match("%s*%-%-+%s*(.*)") + end + extract_arg = function(tl,idx) + return value_of(tl[idx or 1]) + end end local function set_comment (idx,tok) @@ -307,6 +317,15 @@ function M.get_parameters (tok,endtoken,delim) args.comments[arg] = text end + local function add_arg (tl,idx) + local name, type = extract_arg(tl,idx) + args:append(name) + if type then + if not args.types then args.types = List() end + args.types:append(type) + end + end + for i = 1,#ltl do local tl = ltl[i] -- token list for argument if #tl > 0 then @@ -323,10 +342,10 @@ function M.get_parameters (tok,endtoken,delim) j = j + 1 end if #tl > 1 then - args:append(value_of(tl[j])) + add_arg(tl,j) end else - args:append(value_of(tl[1])) + add_arg(tl,1) end if i == #ltl and #tl > 1 then while j <= #tl and type_of(tl[j]) ~= 'comment' do From 20ff72cd352f3995b5da8c73a1f379d6dea16a30 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Fri, 20 Sep 2013 13:52:18 +0200 Subject: [PATCH 062/106] add changes document --- changes.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 changes.md diff --git a/changes.md b/changes.md new file mode 100644 index 00000000..9370e36e --- /dev/null +++ b/changes.md @@ -0,0 +1,29 @@ +## Version 1.4.0 + +### Features + + * `sort=true` to sort items within sections alphabetically + * `@set` tag in module comments; e.g, can say `@set sort=true` + * `@classmod` tag for defining modules that export one class + * can generate Markdown output + * Can prettify C as well as Lua code with built-in prettifier + * lfs and lpeg references understood + * 'pale' template available + * multiple return groups + * experimental `@error` tag + * Moonscript and plain C support + + +### Fixes + + * works with non-compatibily Lua 5.2, including `markdown.lua` + * module names can not be types + * all `builtin` Lua files are requirable without `module` + * backticks expand in copyright and other 'info' tabs + * `-m` tries harder to resolve methods + * auto-scroll in navigation area to avoid breaking identifiers + * better error message for non-luadoc-compatible behaviour + * custom see references fixed + + + From 531a7d34dea732a098ff0a474051a9f57a5e8ea4 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 26 Sep 2013 15:34:53 +0200 Subject: [PATCH 063/106] Issue 81 'example' tag raises more explicit error, exclude more 'accidental' doc comments --- ldoc/doc.lua | 11 +++++++++-- ldoc/html.lua | 1 + ldoc/lang.lua | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index b184c7e6..1b8f83a9 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -462,7 +462,11 @@ function Item:set_tag (tag,value) if type(value) == 'table' then if value.append then -- it was a List! -- such tags are _not_ multiple, e.g. name - self:error("'"..tag.."' cannot have multiple values") + if tag == 'class' and value:contains 'example' then + self:error("cannot use 'example' tag for functions or tables. Use 'usage'") + else + self:error("'"..tag.."' cannot have multiple values; "..tostring(value)) + end end value = value[1] modifiers = value.modifiers @@ -877,7 +881,10 @@ function Item:build_return_groups() lastg = g end --require 'pl.pretty'.dump(ret) - group:append({text=ret, type = mods.type or '',mods = mods}) + if not mods then + self:error(quote(self.name)..' had no return?') + end + group:append({text=ret, type = mods and (mods.type or '') or '',mods = mods}) end -- order by groups to force error groups to the end table.sort(groups,function(g1,g2) return g1.g < g2.g end) diff --git a/ldoc/html.lua b/ldoc/html.lua index a7401db9..c0fd0f09 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -187,6 +187,7 @@ function html.generate_output(ldoc, args, project) optional = true tp = tp2 end + local types = {} for name in tp:gmatch("[^|]+") do local ref,err = markup.process_reference(name,true) diff --git a/ldoc/lang.lua b/ldoc/lang.lua index da750023..07836cf2 100644 --- a/ldoc/lang.lua +++ b/ldoc/lang.lua @@ -74,7 +74,7 @@ function Lua:_init() self.line_comment = '^%-%-+' -- used for stripping self.start_comment_ = '^%-%-%-+' -- used for doc comment line start self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments - self.end_comment_ = '[^%-]%-%-+\n$' ---- exclude --- this kind of comment --- + self.end_comment_ = '[^%-]%-%-+[^-]*\n$' ---- exclude --- this kind of comment --- self.method_call = ':' self:finalize() end From 7a7865f300cfaed0f3888f7da820dea370e6040c Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 26 Sep 2013 16:05:45 +0200 Subject: [PATCH 064/106] Issue 95: strip leading stars in C block comments; improvements in identifying typenames within type expressions --- ldoc/doc.lua | 28 ++++++++++++++-------------- ldoc/html.lua | 9 +++++++-- ldoc/lang.lua | 4 ++-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 1b8f83a9..2eafe77a 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -663,22 +663,22 @@ function Item:finish() self:warning("undocumented formal argument: "..quote(fargs[i])) end end end - -- formal arguments may come with types, inferred by the - -- appropriate code in ldoc.lang - if fargs.types then - self.modifiers[field] = List() - for t in fargs.types:iter() do - self:add_type(field,t) - end - if fargs.return_type then - if not self.ret then -- type, but no comment; no worries - self.ret = List{''} - end - self.modifiers['return'] = List() - self:add_type('return',fargs.return_type) + end -- #fargs > 0 + -- formal arguments may come with types, inferred by the + -- appropriate code in ldoc.lang + if fargs.types then + self.modifiers[field] = List() + for t in fargs.types:iter() do + self:add_type(field,t) + end + if fargs.return_type then + if not self.ret then -- type, but no comment; no worries + self.ret = List{''} end + self.modifiers['return'] = List() + self:add_type('return',fargs.return_type) end - end -- #fargs > 0 + end end -- fargs -- the comments are associated with each parameter by diff --git a/ldoc/html.lua b/ldoc/html.lua index c0fd0f09..3f10726c 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -190,9 +190,14 @@ function html.generate_output(ldoc, args, project) local types = {} for name in tp:gmatch("[^|]+") do - local ref,err = markup.process_reference(name,true) + --local sym, rest = name:match('(%S+)%s*([%*%&])') + local sym = name:match '([%w%.%:]+)' + local ref,err = markup.process_reference(sym,true) if ref then - types[#types+1] = ('%s'):format(ldoc.href(ref),ref.label or name) + if ref.label and sym == name then + name = ref.label + end + types[#types+1] = ('%s'):format(ldoc.href(ref),name) else types[#types+1] = ''..name..'' end diff --git a/ldoc/lang.lua b/ldoc/lang.lua index 07836cf2..756fef5d 100644 --- a/ldoc/lang.lua +++ b/ldoc/lang.lua @@ -262,7 +262,7 @@ function CC.lexer(f) end function CC:grab_block_comment(v,tok) - v = v:gsub(self.block_comment,'') + v = v:gsub(self.block_comment,''):gsub('\n%s*%*','\n') return 'comment',v:sub(1,-3) end @@ -296,7 +296,7 @@ function CC:item_follows (t,v,tok) name = v t,v = tnext(tok) end - print ('got',name,t,v,return_type) + --print ('got',name,t,v,return_type) return function(tags,tok) if not tags.name then tags:add('name',name) From 7e2e6e975c66cae925bad8ef18b787337cfea229 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Fri, 27 Sep 2013 15:37:46 +0200 Subject: [PATCH 065/106] Issue #96: was expecting a space after ## in markdown documents; better support for C types --- ldoc/doc.lua | 1 + ldoc/html.lua | 1 - ldoc/lang.lua | 36 +++++++++++++++++------------------- ldoc/markup.lua | 5 +++-- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 2eafe77a..38b391c0 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -1019,6 +1019,7 @@ local function reference (s, mod_ref, item_ref) end function Module:process_see_reference (s,modules,istype) + if s == nil then return nil end local mod_ref,fun_ref,name,packmod local ref = custom_see_references(s) if ref then return ref end diff --git a/ldoc/html.lua b/ldoc/html.lua index 3f10726c..1d9fee47 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -190,7 +190,6 @@ function html.generate_output(ldoc, args, project) local types = {} for name in tp:gmatch("[^|]+") do - --local sym, rest = name:match('(%S+)%s*([%*%&])') local sym = name:match '([%w%.%:]+)' local ref,err = markup.process_reference(sym,true) if ref then diff --git a/ldoc/lang.lua b/ldoc/lang.lua index 756fef5d..a0704d95 100644 --- a/ldoc/lang.lua +++ b/ldoc/lang.lua @@ -285,28 +285,26 @@ function CC:item_follows (t,v,tok) if v == self.extra.export then -- this is not part of the return type! t,v = tnext(tok) end - -- TBD collecting types which are not single tokens (may contain '*') - local return_type = v + -- types may have multiple tokens: example, const char *bonzo(...) + local return_type, name = v t,v = tnext(tok) - if t == 'iden' or t=='keyword' then - local name = v + name = v + t,v = tnext(tok) + while t ~= '(' do + return_type = return_type .. ' ' .. name + name = v t,v = tnext(tok) - if t ~= '(' then - return_type = return_type .. ' ' .. name - name = v - t,v = tnext(tok) + end + --print ('got',name,t,v,return_type) + return function(tags,tok) + if not tags.name then + tags:add('name',name) end - --print ('got',name,t,v,return_type) - return function(tags,tok) - if not tags.name then - tags:add('name',name) - end - tags:add('class','function') - if t == '(' then - tags.formal_args,t,v = tools.get_parameters(tok,')',',',self) - if return_type ~= 'void' then - tags.formal_args.return_type = return_type - end + tags:add('class','function') + if t == '(' then + tags.formal_args,t,v = tools.get_parameters(tok,')',',',self) + if return_type ~= 'void' then + tags.formal_args.return_type = return_type end end end diff --git a/ldoc/markup.lua b/ldoc/markup.lua index 43b6b91f..060e7071 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -57,7 +57,7 @@ end -- they can appear in the contents list as a ToC. function markup.add_sections(F, txt) local sections, L, first = {}, 1, true - local title_pat_end, title_pat = '[^#]%s*(.+)' + local title_pat for line in stringx.lines(txt) do if first then local level,header = line:match '^(#+)%s*(.+)' @@ -66,7 +66,8 @@ function markup.add_sections(F, txt) else level = '##' end - title_pat = '^'..level..title_pat_end + title_pat = '^'..level..'([^#]%s*.+)' + title_pat = stringx.lstrip(title_pat) first = false end local title = line:match (title_pat) From 955802e07bedb09900d54f1773aaf947a4de3a1b Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 16 Nov 2013 19:21:50 +0200 Subject: [PATCH 066/106] Issue #93: can define fields/properties of objects; the 'readonly' modifier is now special. See tests/styles/type.lua --- ldoc/doc.lua | 11 ++++++++++- ldoc/html/ldoc_ltp.lua | 8 +++++++- ldoc/parse.lua | 11 +++++++++++ tests/styles/type.lua | 28 ++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 tests/styles/type.lua diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 38b391c0..9c01b9b8 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -288,6 +288,9 @@ function File:finish() -- add the item to the module's item list if this_mod then -- new-style modules will have qualified names like 'mod.foo' + if item.name == nil then + self:error("item's name is nil") + end local mod,fname = split_dotted_name(item.name) -- warning for inferred unqualified names in new style modules -- (retired until we handle methods like Set:unset() properly) @@ -421,7 +424,7 @@ end function Item:trailing_warning (kind,tag,rest) if type(rest)=='string' and #rest > 0 then - Item.warning(self,kind.." tag: '"..tag..'" has trailing text; use not_luadoc=true if you want description to continue between tags\n'..rest) + Item.warning(self,kind.." tag: '"..tag..'" has trailing text ; use not_luadoc=true if you want description to continue between tags\n"'..rest..'"') end end @@ -825,6 +828,12 @@ function Item:default_of_param(p) return opt end +function Item:readonly(p) + local m = self:param_modifiers(p) + if not m then return nil end + return m.readonly +end + function Item:subparam(p) local subp = rawget(self.subparams,p) if subp then diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 957aae37..53204de0 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -155,7 +155,10 @@ return [==[ $(M(ldoc.descript(item),item)) # if show_parms and item.params and #item.params > 0 then -

                $(module.kinds:type_of(item).subnames):

                +# local subnames = module.kinds:type_of(item).subnames +# if subnames then +

                $(subnames):

                +# end
                  # for parm in iter(item.params) do # local param,sublist = item:subparam(parm) @@ -172,6 +175,9 @@ return [==[ $(M(item.params[p],item)) # if def then (default $(def)) +# end +# if item:readonly(p) then + readonly # end # end diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 6889dfe8..90d6e264 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -364,6 +364,17 @@ local function parse_file(fname, lang, package, args) if is_local or tags['local'] then tags:add('local',true) end + -- support for standalone fields/properties of classes/modules + if (tags.field or tags.param) and not tags.class then + -- the hack is to take a subfield and pull out its name, + -- (see Tag:add above) but let the subfield itself go through + -- with any modifiers. + local fp = tags.field or tags.param + if type(fp) == 'table' then fp = fp[1] end + fp = tools.extract_identifier(fp) + tags:add('name',fp) + tags:add('class','field') + end if tags.name then current_item = F:new_item(tags,line) current_item.inferred = item_follows ~= nil diff --git a/tests/styles/type.lua b/tests/styles/type.lua new file mode 100644 index 00000000..8ce217a3 --- /dev/null +++ b/tests/styles/type.lua @@ -0,0 +1,28 @@ +----- +-- module containing a class +-- @module type + +---- +-- Our class. +-- @type Bonzo + +---- +-- make a new Bonzo +-- @string s name of Bonzo +function Bonzo.new(s) +end + +----- +-- get a string representation. +function Bonzo.__tostring() +end + +---- +-- A subtable with fields. +-- @table Details +-- @string[readonly] name +-- @int[readonly] age + +--- +-- This is a simple field/property of the class. +-- @string[opt="Bilbo",readonly] frodo direct access to text From fd323aa2861379e203f4b52fcab7dd1b69d713d1 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 16 Nov 2013 20:36:53 +0200 Subject: [PATCH 067/106] Issue #105; can switch off auto-linking to Lua manual with 'no_lua_ref' variable. This also happens if plain C documentation is being generated --- ldoc.lua | 7 ++++++- ldoc/doc.lua | 17 +++++++++++++++-- ldoc/tools.lua | 10 ++++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 69d6f119..42319891 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -198,7 +198,7 @@ local ldoc_contents = { 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars', 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', - 'no_space_before_args','parse_extra', + 'no_space_before_args','parse_extra','no_lua_ref', } ldoc_contents = tablex.makeset(ldoc_contents) @@ -376,6 +376,11 @@ override 'merge' override 'not_luadoc' override 'module_file' +-- LDoc is doing plain ole C, don't want random Lua references! +if ldoc.parse_extra and ldoc.parse_extra.C then + ldoc.no_lua_ref = true +end + if ldoc.merge_error_groups == nil then ldoc.merge_error_groups = 'Error Message' end diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 9c01b9b8..6f7c5d9a 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -1035,6 +1035,9 @@ function Module:process_see_reference (s,modules,istype) if not s:match '^[%w_%.%:%-]+$' or not s:match '[%w_]$' then return nil, "malformed see reference: '"..s..'"' end + + -- `istype` means that we are looking up strictly in a _type_ context, so then only + -- allow `classmod` module references. local function ismod(item) if item == nil then return false end if not istype then return true @@ -1042,6 +1045,16 @@ function Module:process_see_reference (s,modules,istype) return item.type == 'classmod' end end + + -- it is _entirely_ possible that someone does not want auto references for standard Lua libraries! + local lua_manual_ref + local ldoc = tools.item_ldoc(self) + if ldoc and ldoc.no_lua_ref then + lua_manual_ref = function(s) return false end + else + lua_manual_ref = global.lua_manual_ref + end + -- is this a fully qualified module name? local mod_ref = modules.by_name[s] if ismod(mod_ref) then return reference(s, mod_ref,nil) end @@ -1058,7 +1071,7 @@ function Module:process_see_reference (s,modules,istype) if not mod_ref then mod_ref = self:hunt_for_reference(packmod, modules) if not mod_ref then - local ref = global.lua_manual_ref(s) + local ref = lua_manual_ref(s) if ref then return ref end return nil,"module not found: "..packmod end @@ -1080,7 +1093,7 @@ function Module:process_see_reference (s,modules,istype) fun_ref = self.items.by_name[s] if fun_ref then return reference(s, self,fun_ref) else - local ref = global.lua_manual_ref (s) + local ref = lua_manual_ref (s) if ref then return ref end return nil, "function not found: "..s.." in this module" end diff --git a/ldoc/tools.lua b/ldoc/tools.lua index cc434323..3dc74790 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -16,6 +16,13 @@ local lexer = require 'ldoc.lexer' local quit = utils.quit local lfs = require 'lfs' +-- at rendering time, can access the ldoc table from any module item, +-- or the item itself if it's a module +function M.item_ldoc (item) + local mod = item and (item.module or item) + return mod and mod.ldoc +end + -- this constructs an iterator over a list of objects which returns only -- those objects where a field has a certain value. It's used to iterate -- only over functions or tables, etc. If the list of item has a module @@ -26,8 +33,7 @@ function M.type_iterator (list,field,value) local fls = list:filter(function(item) return item[field] == value end) - local mod = fls[1] and fls[1].module - local ldoc = mod and mod.ldoc + local ldoc = M.item_ldoc(fls[1]) if ldoc and ldoc.sort then fls:sort(function(ia,ib) return ia.name < ib.name From 4666e464cceaf42ba104463565c07dc72208dd45 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 16 Nov 2013 21:03:43 +0200 Subject: [PATCH 068/106] no more fooling with globals in LDoc; the class ModuleMap is no longer global, but inserted into doc module so it can use it --- ldoc.lua | 6 +++--- ldoc/doc.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 42319891..f6b5d5d8 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -82,8 +82,8 @@ local Item,File,Module = doc.Item,doc.File,doc.Module local quit = utils.quit -class.ModuleMap(KindMap) -local ModuleMap = ModuleMap +local ModuleMap = class(KindMap) +doc.ModuleMap = ModuleMap function ModuleMap:_init () self.klass = ModuleMap @@ -97,7 +97,7 @@ ModuleMap:add_kind('lfunction','Local Functions','Parameters') ModuleMap:add_kind('annotation','Issues') -class.ProjectMap(KindMap) +local ProjectMap = class(KindMap) ProjectMap.project_level = true function ProjectMap:_init () diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 6f7c5d9a..dc2fafe6 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -264,7 +264,7 @@ function File:finish() if not submodule then this_mod.package = package this_mod.mod_name = mname - this_mod.kinds = ModuleMap() -- the iterator over the module contents + this_mod.kinds = doc.ModuleMap() -- the iterator over the module contents self.modules:append(this_mod) end elseif doc.section_tag(item.type) then From 9fc5697cffdeb9fa8e98a24fc6506ec36bf6a2ef Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sun, 17 Nov 2013 10:38:24 +0200 Subject: [PATCH 069/106] Issue #108: sorting modules etc is off by default, switch back on using 'sort_modules=true'. Section lookup in docs was borked, fixed by stripping any extra whitespace before section titles --- ldoc.lua | 10 ++++++---- ldoc/markup.lua | 7 ++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index f6b5d5d8..ef73107f 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -198,7 +198,7 @@ local ldoc_contents = { 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars', 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', - 'no_space_before_args','parse_extra','no_lua_ref', + 'no_space_before_args','parse_extra','no_lua_ref','sort_modules', } ldoc_contents = tablex.makeset(ldoc_contents) @@ -544,9 +544,11 @@ if not args.all and not ldoc.all then end end -table.sort(module_list,function(m1,m2) - return m1.name < m2.name -end) +if ldoc.sort_modules then + table.sort(module_list,function(m1,m2) + return m1.name < m2.name + end) +end ldoc.single = modcount == 1 and first_module or nil diff --git a/ldoc/markup.lua b/ldoc/markup.lua index 060e7071..6a953997 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -57,7 +57,8 @@ end -- they can appear in the contents list as a ToC. function markup.add_sections(F, txt) local sections, L, first = {}, 1, true - local title_pat + local title_pat + local lstrip = stringx.lstrip for line in stringx.lines(txt) do if first then local level,header = line:match '^(#+)%s*(.+)' @@ -67,14 +68,14 @@ function markup.add_sections(F, txt) level = '##' end title_pat = '^'..level..'([^#]%s*.+)' - title_pat = stringx.lstrip(title_pat) + title_pat = lstrip(title_pat) first = false end local title = line:match (title_pat) if title then -- Markdown does allow this pattern title = title:gsub('%s*#+$','') - sections[L] = F:add_document_section(title) + sections[L] = F:add_document_section(lstrip(title)) end L = L + 1 end From 9ef1f08784dba3d3730dc6edeb00a60450e393bd Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sun, 17 Nov 2013 19:48:35 +0200 Subject: [PATCH 070/106] can resolve references to local class; no longer have 'Topic: ' for readme documents --- ldoc/doc.lua | 33 ++++++++++++++++++++++++--------- ldoc/html/ldoc_ltp.lua | 5 +---- tests/styles/type.lua | 12 ++++++++++-- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index dc2fafe6..77b8431f 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -1009,13 +1009,13 @@ end local err = io.stderr local function custom_see_references (s) - for pat, action in pairs(see_reference_handlers) do - if s:match(pat) then - local label, href = action(s:match(pat)) - if not label then print('custom rule failed',s,pat,href) end - return {href = href, label = label} - end - end + for pat, action in pairs(see_reference_handlers) do + if s:match(pat) then + local label, href = action(s:match(pat)) + if not label then print('custom rule failed',s,pat,href) end + return {href = href, label = label} + end + end end local function reference (s, mod_ref, item_ref) @@ -1027,6 +1027,19 @@ local function reference (s, mod_ref, item_ref) return {mod = mod_ref, name = name, label=s} end +function Module:lookup_class_item (packmod, s) + local section = "Class_"..packmod + if self.sections.by_name[section] then + for item in self.items:iter() do + --print('item',item.name,item.section) + if item.section == section and s == item.name then + return reference(s,self,item) + end + end + end + return nil +end + function Module:process_see_reference (s,modules,istype) if s == nil then return nil end local mod_ref,fun_ref,name,packmod @@ -1071,7 +1084,9 @@ function Module:process_see_reference (s,modules,istype) if not mod_ref then mod_ref = self:hunt_for_reference(packmod, modules) if not mod_ref then - local ref = lua_manual_ref(s) + local ref = self:lookup_class_item(packmod,s) + if ref then return ref end + ref = lua_manual_ref(s) if ref then return ref end return nil,"module not found: "..packmod end @@ -1091,7 +1106,7 @@ function Module:process_see_reference (s,modules,istype) mod_ref = modules.by_name[self.package..'.'..s] if ismod(mod_ref) then return reference(s, mod_ref,nil) end fun_ref = self.items.by_name[s] - if fun_ref then return reference(s, self,fun_ref) + if fun_ref then return reference(s,self,fun_ref) else local ref = lua_manual_ref (s) if ref then return ref end diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 53204de0..54adb3ba 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -83,13 +83,10 @@ return [==[
                  -#if module then -

                  $(ldoc.module_typename(module)) $(module.name)

                  -# end - # if ldoc.body then -- verbatim HTML as contents; 'non-code' entries $(ldoc.body) # elseif module then -- module documentation +

                  $(ldoc.module_typename(module)) $(module.name)

                  $(M(module.summary,module))

                  $(M(module.description,module))

                  # if module.usage then diff --git a/tests/styles/type.lua b/tests/styles/type.lua index 8ce217a3..6f2158d8 100644 --- a/tests/styles/type.lua +++ b/tests/styles/type.lua @@ -7,14 +7,22 @@ -- @type Bonzo ---- --- make a new Bonzo +-- make a new Bonzo. +-- @see Bonzo:dog -- @string s name of Bonzo function Bonzo.new(s) end ----- -- get a string representation. -function Bonzo.__tostring() +-- works with `tostring` +function Bonzo:__tostring() +end + +---- +-- Another method. +function Bonzo:dog () + end ---- From 8081e5b43ca8397d71cfb47200418b250255390c Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Mon, 18 Nov 2013 10:26:40 +0200 Subject: [PATCH 071/106] option to use Markdown titles instead of file names with 'use_markdown_titles' (otherwise drop the .md extension on display) --- ldoc.lua | 5 ++++- ldoc/html.lua | 8 +++++++- ldoc/html/ldoc_ltp.lua | 4 +--- ldoc/markup.lua | 3 ++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index ef73107f..18e109eb 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -198,7 +198,7 @@ local ldoc_contents = { 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars', 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', - 'no_space_before_args','parse_extra','no_lua_ref','sort_modules', + 'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles', } ldoc_contents = tablex.makeset(ldoc_contents) @@ -508,6 +508,9 @@ if type(ldoc.readme) == 'table' then -- 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. + if ldoc.use_markdown_titles then + item.display_name = F.display_name + end item.postprocess = function(txt) return ldoc.markup(txt,F) end end) end diff --git a/ldoc/html.lua b/ldoc/html.lua index 1d9fee47..1422ca5d 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -97,8 +97,14 @@ function html.generate_output(ldoc, args, project) function ldoc.module_name (mod) local name = mod.name - if mod.type == 'module' then + if mod.type == 'module' then -- leave out package (also for 'classmod'??) 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 diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 54adb3ba..e173fa48 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -61,12 +61,11 @@ return [==[ # end # end # -------- contents of project ---------- -# -- if not ldoc.no_summary then # local this_mod = module and module.name # for kind, mods, type in ldoc.kinds() do # if not ldoc.kinds_allowed or ldoc.kinds_allowed[type] then

                  $(kind)

                  -
                    +
                      # for mod in mods() do local name = ldoc.module_name(mod) # if mod.name == this_mod then
                    • $(name)
                    • @@ -75,7 +74,6 @@ return [==[ # end # end # end -# -- end
                    # end diff --git a/ldoc/markup.lua b/ldoc/markup.lua index 6a953997..f5694efe 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -70,10 +70,11 @@ function markup.add_sections(F, txt) title_pat = '^'..level..'([^#]%s*.+)' title_pat = lstrip(title_pat) first = false + F.display_name = header end local title = line:match (title_pat) if title then - -- Markdown does allow this pattern + -- Markdown allows trailing '#'... title = title:gsub('%s*#+$','') sections[L] = F:add_document_section(lstrip(title)) end From feabf5c91a0e96190d90a189664881dc02a32b23 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Mon, 18 Nov 2013 14:54:28 +0200 Subject: [PATCH 072/106] when using export tag, decide whether function is meant to be a class method from 'self' formal arg, otherwise it becomes static. --- ldoc/doc.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 77b8431f..7e800701 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -176,6 +176,7 @@ function File:export_item (name) for item in self.items:iter() do local tags = item.tags if tags.name == name then + tags.export = true if tags['local'] then tags['local'] = nil end @@ -647,6 +648,13 @@ function Item:finish() elseif #fargs > 0 then -- consistency check! local varargs = fargs[#fargs] == '...' if varargs then table.remove(fargs) end + if tags.export then + if fargs[1] == 'self' then + table.remove(fargs,1) + else + tags.static = true + end + end local k = 0 for _,pname in ipairs(param_names) do local _,field = split_iden(pname) From 4fac99406c1761b4010b0584ec7179d88e799c82 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 21 Nov 2013 16:06:34 +0200 Subject: [PATCH 073/106] try match single names against fully qualified names of module funtions --- ldoc/doc.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 7e800701..2073bef3 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -1114,6 +1114,16 @@ function Module:process_see_reference (s,modules,istype) mod_ref = modules.by_name[self.package..'.'..s] if ismod(mod_ref) then return reference(s, mod_ref,nil) end fun_ref = self.items.by_name[s] + -- did not get an exact match, so try to match by the unqualified fun name + if not fun_ref then + local patt = '[.:]'..s..'$' + for qname,ref in pairs(self.items.by_name) do + if qname:match(patt) then + fun_ref = ref + break + end + end + end if fun_ref then return reference(s,self,fun_ref) else local ref = lua_manual_ref (s) From 46515b2411741be65af77dcb11963f27137a5431 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 27 Nov 2013 09:46:37 +0200 Subject: [PATCH 074/106] Issue #113, borked classmod: now custom sections are always respected. Automatic sections are generated for 'Metamethods' and 'Methods' --- ldoc/doc.lua | 51 ++++++++++++++++++++++++++++---------- ldoc/parse.lua | 5 ---- tests/moonscript/List.moon | 2 -- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 2073bef3..cb4148a4 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -208,6 +208,13 @@ function File:find_module_in_files (name) end end +local function init_within_section (mod,name) + mod.kinds:add_kind(name, name) + mod.enclosing_section = mod.section + mod.section = nil + return name +end + function File:finish() local this_mod local items = self.items @@ -282,7 +289,11 @@ function File:finish() display_name = summary end item.display_name = display_name - add_section(item) +--~ add_section(item) + this_mod.section = item + this_mod.kinds:add_kind(display_name,display_name..' ',nil,item) + this_mod.sections:append(item) + this_mod.sections.by_name[display_name:gsub('%A','_')] = item end else local to_be_removed @@ -308,15 +319,11 @@ function File:finish() item.name = fname end - local enclosing_section if tagged_inside then item.tags.within = tagged_inside end if item.tags.within then - local name = item.tags.within - this_mod.kinds:add_kind(name, name) - enclosing_section = this_mod.section - this_mod.section = nil + init_within_section(this_mod,item.tags.within) end -- right, this item was within a section or a 'class' @@ -332,11 +339,23 @@ function File:finish() -- if it was a class, then if the name is unqualified then it becomes -- 'Class:foo' (unless flagged as being a constructor, static or not a function) if doc.class_tag(stype) or classmod then - if not item.name:match '[:%.]' then -- not qualified + if not item.name:match '[:%.]' then -- not qualified name! + -- a class is either a @type section or a @classmod module. Is this a _method_? local class = classmod and this_mod.name or this_section.name - local lang = this_mod.file.lang local static = item.tags.constructor or item.tags.static or item.type ~= 'function' - item.name = class..(not static and lang.method_call or '.')..item.name + if classmod then -- methods and metamethods go into their own special sections... + local inferred_section + if item.name:match '^__' then + inferred_section = 'Metamethods' + elseif not static then + inferred_section = 'Methods' + end + if inferred_section then print('name',item.name,inferred_section) + item.tags.within = init_within_section(this_mod,inferred_section) + end + end + -- Whether to use '.' or the language's version of ':' (e.g. \ for Moonscript) + item.name = class..(not static and this_mod.file.lang.method_call or '.')..item.name end if stype == 'factory' then if item.tags.private then to_be_removed = true @@ -355,14 +374,16 @@ function File:finish() section_description = item.tags.within item.section = section_description else - section_description = "Methods" + if item.type == 'function' or item.type == 'lfunction' then + section_description = "Methods" + end item.section = item.type end - elseif item.tags.within then + elseif item.tags.within then -- ad-hoc section... section_description = item.tags.within item.section = section_description else -- otherwise, just goes into the default sections (Functions,Tables,etc) - item.section = item.type + item.section = item.type; end item.module = this_mod @@ -370,11 +391,15 @@ function File:finish() local these_items = this_mod.items these_items.by_name[item.name] = item these_items:append(item) +--~ print(item.name,section_description,item.type) this_mod.kinds:add(item,these_items,section_description) end -- restore current section after a 'within' - if enclosing_section then this_mod.section = enclosing_section end + if this_mod.enclosing_section then + this_mod.section = this_mod.enclosing_section + this_mod.enclosing_section = nil + end else -- must be a free-standing function (sometimes a problem...) diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 90d6e264..5709b249 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -382,11 +382,6 @@ local function parse_file(fname, lang, package, args) if module_item then F:error("Module already declared!") end - if tags.class == 'classmod' then - tags = tags.new('section','methods') - tags:add('summary','Methods') - F:new_item(tags,line) - end module_item = current_item end end diff --git a/tests/moonscript/List.moon b/tests/moonscript/List.moon index c70ce39d..bd1272e9 100644 --- a/tests/moonscript/List.moon +++ b/tests/moonscript/List.moon @@ -23,7 +23,6 @@ class List len: => #@ls --- string representation - -- @within Metamethods __tostring: => '['..(concat @ls,',')..']' --- return idx of first occurence of `item` @@ -54,7 +53,6 @@ class List self --- concatenate two lists, giving a new list - -- @within Metamethods __concat: (l1,l2) -> l1\copy!\extend l2 --- an iterator over all items From 63e3618c0511c99ca1ded3cd60498de65c0c4877 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Wed, 27 Nov 2013 09:50:38 +0200 Subject: [PATCH 075/106] Issue #110; use '-u' or 'unqualified' explicitly to strip package names on sidebar (thanks to abadc0de) --- ldoc.lua | 2 ++ ldoc/html.lua | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ldoc.lua b/ldoc.lua index 18e109eb..31db3c47 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -52,6 +52,7 @@ ldoc, a documentation generator for Lua, vs 1.4.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 + -u,--unqualified don't show package name in sidebar links -i,--ignore ignore any 'no doc comment or no module' warnings -X,--not_luadoc break LuaDoc compatibility. Descriptions may continue after tags. -D,--define (default none) set a flag to be used in config.ld @@ -199,6 +200,7 @@ local ldoc_contents = { 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', 'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles', + 'unqualified', } ldoc_contents = tablex.makeset(ldoc_contents) diff --git a/ldoc/html.lua b/ldoc/html.lua index 1422ca5d..bfc67cda 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -97,7 +97,7 @@ function html.generate_output(ldoc, args, project) function ldoc.module_name (mod) local name = mod.name - if mod.type == 'module' then -- leave out package (also for 'classmod'??) + if args.unqualified and mod.type == 'module' then -- leave out package (also for 'classmod'??) name = name:gsub('^.-%.','') elseif mod.type == 'topic' then if mod.display_name then From 4294b2e2da774acd8da5db257bce298a1c805f77 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 28 Nov 2013 11:57:13 +0200 Subject: [PATCH 076/106] Issue #114: trying harder to resolve references by unqualified method name --- ldoc/doc.lua | 44 +++++++++++++++++++++----------------------- ldoc/html.lua | 2 +- ldoc/tools.lua | 16 +++++++++++----- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index cb4148a4..4e08dbcf 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -219,13 +219,6 @@ 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..' ',nil,item) - 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..'}' @@ -289,7 +282,6 @@ function File:finish() display_name = summary end item.display_name = display_name ---~ add_section(item) this_mod.section = item this_mod.kinds:add_kind(display_name,display_name..' ',nil,item) this_mod.sections:append(item) @@ -343,14 +335,15 @@ function File:finish() -- a class is either a @type section or a @classmod module. Is this a _method_? local class = classmod and this_mod.name or this_section.name local static = item.tags.constructor or item.tags.static or item.type ~= 'function' - if classmod then -- methods and metamethods go into their own special sections... + -- methods and metamethods go into their own special sections... + if classmod and item.type == 'function' then local inferred_section if item.name:match '^__' then inferred_section = 'Metamethods' elseif not static then inferred_section = 'Methods' end - if inferred_section then print('name',item.name,inferred_section) + if inferred_section then item.tags.within = init_within_section(this_mod,inferred_section) end end @@ -391,7 +384,6 @@ function File:finish() local these_items = this_mod.items these_items.by_name[item.name] = item these_items:append(item) ---~ print(item.name,section_description,item.type) this_mod.kinds:add(item,these_items,section_description) end @@ -1124,7 +1116,7 @@ function Module:process_see_reference (s,modules,istype) return nil,"module not found: "..packmod end end - fun_ref = mod_ref.items.by_name[name] + fun_ref = mod_ref:get_fun_ref(name) if fun_ref then return reference(s,mod_ref,fun_ref) else @@ -1138,17 +1130,7 @@ function Module:process_see_reference (s,modules,istype) else -- plain jane name; module in this package, function in this module mod_ref = modules.by_name[self.package..'.'..s] if ismod(mod_ref) then return reference(s, mod_ref,nil) end - fun_ref = self.items.by_name[s] - -- did not get an exact match, so try to match by the unqualified fun name - if not fun_ref then - local patt = '[.:]'..s..'$' - for qname,ref in pairs(self.items.by_name) do - if qname:match(patt) then - fun_ref = ref - break - end - end - end + fun_ref = self:get_fun_ref(s) if fun_ref then return reference(s,self,fun_ref) else local ref = lua_manual_ref (s) @@ -1158,6 +1140,22 @@ function Module:process_see_reference (s,modules,istype) end end +function Module:get_fun_ref(s) + local fun_ref = self.items.by_name[s] + -- did not get an exact match, so try to match by the unqualified fun name + if not fun_ref then + local patt = '[.:]'..s..'$' + for qname,ref in pairs(self.items.by_name) do + if qname:match(patt) then + fun_ref = ref + break + end + end + end + return fun_ref +end + + -- resolving @see references. A word may be either a function in this module, -- or a module in this package. A MOD.NAME reference is within this package. -- Otherwise, the full qualified name must be used. diff --git a/ldoc/html.lua b/ldoc/html.lua index bfc67cda..9d76eacd 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -97,7 +97,7 @@ function html.generate_output(ldoc, args, project) function ldoc.module_name (mod) local name = mod.name - if args.unqualified and mod.type == 'module' then -- leave out package (also for 'classmod'??) + 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 diff --git a/ldoc/tools.lua b/ldoc/tools.lua index 3dc74790..0de2672e 100644 --- a/ldoc/tools.lua +++ b/ldoc/tools.lua @@ -137,7 +137,6 @@ function KindMap.add_kind (klass,tag,kind,subnames,item) end end - ----- some useful utility functions ------ function M.module_basepath() @@ -151,12 +150,19 @@ function M.module_basepath() end -- split a qualified name into the module part and the name part, --- e.g 'pl.utils.split' becomes 'pl.utils' and 'split' +-- e.g 'pl.utils.split' becomes 'pl.utils' and 'split'. Also +-- must understand colon notation! function M.split_dotted_name (s) - local s1,s2 = path.splitext(s) - if s2=='' then return nil - else return s1,s2:sub(2) + local s1,s2 = s:match '^(.+)[%.:](.+)$' + if s1 then -- we can split + return s1,s2 + else + return nil end +--~ local s1,s2 = path.splitext(s) +--~ if s2=='' then return nil +--~ else return s1,s2:sub(2) +--~ end end -- grab lines from a line iterator `iter` until the line matches the pattern. From 89854e153731af4a6cb1c6178433846266d62bf4 Mon Sep 17 00:00:00 2001 From: abadc0de Date: Wed, 27 Nov 2013 19:47:44 -0500 Subject: [PATCH 077/106] Custom tags Custom tags --- ldoc.lua | 12 +++++++++++- ldoc/html/ldoc_ltp.lua | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ldoc.lua b/ldoc.lua index 31db3c47..a46bcb26 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -193,7 +193,7 @@ function ldoc.custom_see_handler(pat, handler) end local ldoc_contents = { - 'alias','add_language_extension','new_type','add_section', 'tparam_alias', + 'alias','add_language_extension','custom_tags','new_type','add_section', 'tparam_alias', 'file','project','title','package','format','output','dir','ext', 'topics', 'one','style','template','description','examples', 'pretty', 'charset', 'plain', 'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars', @@ -307,6 +307,16 @@ else args.file = abspath(args.file) end +if type(ldoc.custom_tags) == 'table' then -- custom tags + for i, custom in ipairs(ldoc.custom_tags) do + if type(custom) == 'string' then + custom = {custom} + ldoc.custom_tags[i] = custom + end + doc.add_tag(custom[1], 'ML') + end +end -- custom tags + local source_dir = args.file if type(source_dir) == 'table' then source_dir = source_dir[1] diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index e173fa48..07ddadb8 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -148,6 +148,21 @@ return [==[
                    $(M(ldoc.descript(item),item)) + +# if ldoc.custom_tags then +# for custom in iter(ldoc.custom_tags) do +# local tag = item.tags[custom[1]] +# if tag then +# local li,il = use_li(tag) +

                    $(custom.title or custom[1]):

                    +
                      +# for value in iter(tag) do + $(li)$(custom.format and custom.format(value) or M(value))$(il) +# end -- for +# end -- if tag +
                    +# end -- iter tags +# end # if show_parms and item.params and #item.params > 0 then # local subnames = module.kinds:type_of(item).subnames From 3053079fee4a01be193447afad77c961e9905ac2 Mon Sep 17 00:00:00 2001 From: Mooffie Date: Mon, 9 Dec 2013 12:13:06 +0200 Subject: [PATCH 078/106] Custom display_name handler. --- doc/doc.md | 14 ++++++++++++++ ldoc.lua | 2 +- ldoc/html.lua | 10 +++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 8d402c50..3248c2eb 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -1110,6 +1110,20 @@ when using Markdown. When explicit will expand non-references in backticks into 'file:///D:/dev/lua/projects/lua-5.1.4/doc/manual.html' - `no_summary` suppress the Contents summary - `custom_see_handler` function that filters see-references + - `custom_display_name_handler` function that formats an item's name. The arguments are the item +and the default function used to format the name. For example, to show an icon or label beside any +function tagged with a certain tag: + -- define a @callback tag: + custom_tags = { { 'callback', hidden = true } } + + -- show a label beside functions tagged with @callback. + custom_display_name_handler = function(item, default_handler) + if item.type == 'function' and item.tags.callback then + return item.name .. ' [callback]' + end + return default_handler(item) + end + - `not_luadoc` set to `true` if the docs break LuaDoc compatibility - `no_space_before_args` set to `true` if you do not want a space between a function's name and its arguments. - `template_escape` overrides the usual '#' used for Lua code in templates. This needs to be changed if the output format is Markdown, for instance. diff --git a/ldoc.lua b/ldoc.lua index a46bcb26..c92b328c 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -200,7 +200,7 @@ local ldoc_contents = { 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', 'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles', - 'unqualified', + 'unqualified', 'custom_display_name_handler', } ldoc_contents = tablex.makeset(ldoc_contents) diff --git a/ldoc/html.lua b/ldoc/html.lua index 9d76eacd..08f31ca0 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -156,7 +156,7 @@ function html.generate_output(ldoc, args, project) if #ls > 1 then return '
                  • ','
                  • ' else return '','' end end - function ldoc.display_name(item) + function ldoc.default_display_name(item) 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 @@ -168,6 +168,14 @@ function html.generate_output(ldoc, args, project) 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','_')) From f60e6d419733826e869b1ed845a23a21e17aa7e0 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Mon, 9 Dec 2013 15:13:00 +0200 Subject: [PATCH 079/106] Pull #121 applied manually - custom tags may be hidden --- ldoc/html/ldoc_ltp.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 07ddadb8..1f01fb08 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -148,11 +148,11 @@ return [==[
                    $(M(ldoc.descript(item),item)) - + # if ldoc.custom_tags then # for custom in iter(ldoc.custom_tags) do # local tag = item.tags[custom[1]] -# if tag then +# if tag and not custom.hidden then # local li,il = use_li(tag)

                    $(custom.title or custom[1]):

                      From d5e623e8d73d927af7aad0590f2af67a7e6a9abe Mon Sep 17 00:00:00 2001 From: "Gary V. Vaughan" Date: Thu, 12 Dec 2013 09:42:44 +1300 Subject: [PATCH 080/106] doc: improve match pattern for manual page references with custom_see_handler. * doc/doc.md: Use '[%w_]+' instead of '%a+' so that references to, say, _exit(2) and dup2(2) work too. Signed-off-by: Gary V. Vaughan --- doc/doc.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 3248c2eb..9bfc50ac 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -328,13 +328,13 @@ online references to the Linux manpages. So in `config.ld` we have: local upat = "http://www.kernel.org/doc/man-pages/online/pages/man%s/%s.%s.html" - custom_see_handler('^(%a+)%((%d)%)$',function(name,section) + custom_see_handler('^([%w_]+)%((%d)%)$',function(name,section) local url = upat:format(section,name,section) local name = name .. '(' ..section..')' return name, url end) -'^(%a+)%((%d)%)$' both matches the pattern and extracts the name and its section. Then it's +'^([%w_]+)%((%d)%)$' both matches the pattern and extracts the name and its section. Then it's a simple matter of building up the appropriate URL. The function is expected to return _link text_ and _link source_ and the patterns are checked before LDoc tries to resolve project references. So it is best to make them match as exactly as possible. From 67a78ee0a5c89fcd4f8445a34c2e12fd4e6b2371 Mon Sep 17 00:00:00 2001 From: Mooffie Date: Thu, 12 Dec 2013 17:26:38 +0200 Subject: [PATCH 081/106] Fix minor issues with the "prettify" facility. --- ldoc/markup.lua | 14 +++++++++----- ldoc/prettify.lua | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ldoc/markup.lua b/ldoc/markup.lua index f5694efe..ab0b5f3b 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -90,8 +90,8 @@ local function indent_line (line) return indent,line end -local function non_blank (line) - return line:find '%S' +local function blank (line) + return not line:find '%S' end local global_context, local_context @@ -119,6 +119,8 @@ local function process_multiline_markdown(ldoc, txt, F) code = concat(code,'\n') if code ~= '' then local err + -- If we omit the following '\n', a '--' (or '//') comment on the + -- last line won't be recognized. code, err = prettify.code(lang,filename,code..'\n',L,false) append(res,'
                      ')
                                append(res, code)
                      @@ -156,7 +158,7 @@ local function process_multiline_markdown(ldoc, txt, F)
                             if indent >= 4 then -- indented code block
                                local code = {}
                                local plain
                      -         while indent >= 4 or not non_blank(line) do
                      +         while indent >= 4 or blank(line) do
                                   if not start_indent then
                                      start_indent = indent
                                      if line:match '^%s*@plain%s*$' then
                      @@ -165,7 +167,7 @@ local function process_multiline_markdown(ldoc, txt, F)
                                      end
                                   end
                                   if not plain then
                      -               append(code,line:sub(start_indent))
                      +               append(code,line:sub(start_indent + 1))
                                   else
                                      append(res,line)
                                   end
                      @@ -174,7 +176,9 @@ local function process_multiline_markdown(ldoc, txt, F)
                                   indent, line = indent_line(line)
                                end
                                start_indent = nil
                      -         if #code > 1 then table.remove(code) end
                      +         while #code > 1 and blank(code[#code]) do  -- trim blank lines.
                      +           table.remove(code)
                      +         end
                                pretty_code (code,'lua')
                             else
                                local section = F.sections[L]
                      diff --git a/ldoc/prettify.lua b/ldoc/prettify.lua
                      index 8013dc5a..c3ea3d5c 100644
                      --- a/ldoc/prettify.lua
                      +++ b/ldoc/prettify.lua
                      @@ -89,7 +89,7 @@ function prettify.code (lang,fname,code,initial_lineno,pre)
                                external = true
                             })
                             if not pre then
                      -         code = code:gsub("^(.*)
                      $", '%1') + code = code:gsub("^(.-)%s*

    $", '%1') end return code end From 41a0d9b00906a7c123afcaa9a661ee47f8fb32d9 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 21 Dec 2013 00:41:24 -0200 Subject: [PATCH 082/106] Add entries missing in rockspec. --- ldoc-scm-2.rockspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ldoc-scm-2.rockspec b/ldoc-scm-2.rockspec index 39c06359..fb86bae0 100644 --- a/ldoc-scm-2.rockspec +++ b/ldoc-scm-2.rockspec @@ -33,10 +33,12 @@ build = { ["ldoc.lexer"] = "ldoc/lexer.lua", ["ldoc.markup"] = "ldoc/markup.lua", ["ldoc.prettify"] = "ldoc/prettify.lua", + ["ldoc.markdown"] = "ldoc/markdown.lua", ["ldoc.doc"] = "ldoc/doc.lua", ["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua", ["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua", ["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua", + ["ldoc.html.ldoc_pale_css"] = "ldoc/html/ldoc_pale_css.lua", ["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua", ["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua", ["ldoc.builtin.global"] = "ldoc/builtin/global.lua", From 9b40e03e3ae5229ebe31c51ef061932f33df9fee Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 21 Dec 2013 00:41:41 -0200 Subject: [PATCH 083/106] Command-line flags that were explicitly passed by the user should take precedence over config file defaults. --- ldoc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldoc.lua b/ldoc.lua index c92b328c..6db58ccc 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -135,7 +135,7 @@ local add_language_extension doc.ldoc = ldoc local function override (field) - if ldoc[field] ~= nil then args[field] = ldoc[field] end + if args[field] == nil and ldoc[field] ~= nil then args[field] = ldoc[field] end end -- aliases to existing tags can be defined. E.g. just 'p' for 'param' From 8b48e1d25675c294e450693f2b90d3a12ce098cc Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 21 Dec 2013 00:50:07 -0200 Subject: [PATCH 084/106] Don't call function when missing. Tweak needed for ldoc to build its own documentation files. --- ldoc/markup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldoc/markup.lua b/ldoc/markup.lua index ab0b5f3b..5451f623 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -23,7 +23,7 @@ local function resolve_inline_references (ldoc, txt, item, plain) local ref,err = markup.process_reference(qname) if not ref then err = err .. ' ' .. qname - if item then item:warning(err) + if item and item.warning then item:warning(err) else io.stderr:write('nofile error: ',err,'\n') end From b16e85af7df0a137c47d107edde068622ee221c8 Mon Sep 17 00:00:00 2001 From: Dirk Feytons Date: Tue, 24 Dec 2013 11:30:56 +0100 Subject: [PATCH 085/106] Fix issue #123: override 'boilerplate' was done too late --- ldoc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldoc.lua b/ldoc.lua index 6db58ccc..8b7aaadb 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -387,6 +387,7 @@ override 'colon' override 'merge' override 'not_luadoc' override 'module_file' +override 'boilerplate' -- LDoc is doing plain ole C, don't want random Lua references! if ldoc.parse_extra and ldoc.parse_extra.C then @@ -619,7 +620,6 @@ override 'output' override 'dir' override 'ext' override 'one' -override 'boilerplate' -- handling styling and templates -- ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp' From a870f91e59ccab161825cc1aa732af8684404d9d Mon Sep 17 00:00:00 2001 From: JonasT Date: Tue, 31 Dec 2013 08:07:41 +0100 Subject: [PATCH 086/106] Being more direct on how tags look like when introducing them first --- doc/doc.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 3248c2eb..b7fc4dcf 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -59,8 +59,8 @@ All doc comments start with a summary sentence, that ends with a period or a que An optional description may follow. Normally the summary sentence will appear in the module contents. -After this descriptive text, there will typically be _tags_. These follow the convention -established by Javadoc and widely used in tools for other languages. +After this descriptive text, there will typically be _tags_ which are introduced with an @. +These follow the convention established by Javadoc and widely used in tools for other languages. --- foo explodes text. -- It is a specialized splitting operation on a string. From 721d626b7008fc2094f9fff3d049f5dcda2ed442 Mon Sep 17 00:00:00 2001 From: JonasT Date: Tue, 31 Dec 2013 08:18:33 +0100 Subject: [PATCH 087/106] Rewrite of first introductory paragraph A suggested rewrite of first introductory paragraph. It does drop some information (e.g. second generation) in favor of being more beginner's friendly and more basic in what this is actually about. The followup LDoc-referring paragraphs seem sufficient to cater the needs of advanced doc tool users, while the first paragraph possibly stumps and scares away beginners in its current form. --- doc/doc.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 3248c2eb..aeeb14d3 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -4,12 +4,13 @@ ## Introduction -LDoc is a second-generation documentation tool that can be used as a replacement for -[LuaDoc](http://keplerproject.github.com/luadoc/). It arose out of my need to document my -own projects and only depends on the [Penlight](https://github.com/stevedonovan/Penlight) -libraries. +LDoc is a software documentation tool which automatically generates API documentation +out of source code comments (doc comments). It is mainly targeted at Lua and documenting +Lua APIs, but it can also parse C with according doc comments for documenting Lua modules +implemented in C. -It is mostly compatible with LuaDoc, except that certain workarounds are no longer needed. +It is mostly compatible with [LuaDoc](http://keplerproject.github.com/luadoc/), +except that certain workarounds are no longer needed. For instance, it is not so married to the idea that Lua modules should be defined using the `module` function; this is not only a matter of taste since this has been deprecated in Lua 5.2. From 2bd0c3f760294affa152c9a26a9136db0a6dc958 Mon Sep 17 00:00:00 2001 From: JonasT Date: Tue, 31 Dec 2013 08:40:50 +0100 Subject: [PATCH 088/106] Getting all the sections in a more beginner's friendly, logical order --- doc/doc.md | 92 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 3248c2eb..f5e82801 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -32,7 +32,10 @@ end-users to build your documentation using this simple command. ## Commenting Conventions -LDoc follows the conventions established by Javadoc and later by LuaDoc. +LDoc follows the conventions established by Javadoc and later by LuaDoc to document the +modules, functions, types (=classes) and so forth of your API. + +### Doc comments Only 'doc comments' are parsed; these can be started with at least 3 hyphens, or by a empty comment line with at least 3 hypens: @@ -55,6 +58,9 @@ Any module or script must start with a doc comment; any other files are ignored warning issued. The only exception is if the module starts with an explicit `module` statement. + +### Summary and tags of a doc comment + All doc comments start with a summary sentence, that ends with a period or a question mark. An optional description may follow. Normally the summary sentence will appear in the module contents. @@ -62,6 +68,34 @@ contents. After this descriptive text, there will typically be _tags_. These follow the convention established by Javadoc and widely used in tools for other languages. +The first important tag to know is the module tag: + +### The module tag, naming and describing your API module + +The first thing in your API module should be a name and a description. +This is how a module is commonly done in Lua 5.2 with a @module tag at the top +which introduces the name: + + --- a test module + -- @module test + + local test = {} + + function test.my_module_function_1() + ... + end + + ... + + return test + +This sets up a module named 'test' with the description 'a test module'. + +### Describing functions with tags: + +The next thing to describe are the functions your module has. +This is a simple example of a documented function: + --- foo explodes text. -- It is a specialized splitting operation on a string. -- @param text the string @@ -70,17 +104,27 @@ established by Javadoc and widely used in tools for other languages. .... end -There are also 'tparam' and 'treturn' which let you [specify a type](#Tag_Modifiers): +The tags basically add all the detail that cannot be derived from the source code +automatically. + +### Most common tags - -- @tparam string text the string +Common tags are the 'param' tag which takes a parameter name followed by a parameter +description separated by a space, and the 'return' tag which is simply followed by +a description for a return value: + + -- @param name_of_parameter the description of this parameter as verbose text + -- @return the description of the return value + +If you want to [specify a type](#Tag_Modifiers) for a parameter or a return value, +there are also 'tparam' and 'treturn': + + -- @tparam string text this parameter is named 'text' and has the fixed type 'string' -- @treturn {string,...} a table of substrings There may be multiple 'param' tags, which should document each formal parameter of the function. For Lua, there can also be multiple 'return' tags - --- solvers for common equations. - module("solvers", package.seeall) - --- solve a quadratic equation. -- @param a first coeff -- @param b second coeff @@ -99,29 +143,19 @@ function. For Lua, there can also be multiple 'return' tags end ... +Of course there is also the 'module' tag which you have already seen. -This is the common module style used in Lua 5.1, but it's increasingly common to see less -'magic' ways of creating modules in Lua. Since `module` is deprecated in Lua 5.2, any -future-proof documentation tool needs to handle these styles gracefully: - - --- a test module - -- @module test - - local test = {} - - --- first test. - function test.one() - ... - end +As an alternative to using the 'module' tag as described before, you +can still start your modules the Lua 5.1 way: - ... + --- solvers for common equations. + module("solvers", package.seeall) - return test +However, the 'module' function is deprecated in Lua 5.2 and it is increasingly +common to see less 'magic' ways of creating modules, as seen in the description +of the 'module' tag previously with the explicitely returned module table. -Here the name of the module is explicitly given using the 'module' tag. If you leave this -out, then LDoc will infer the name of the module from the name of the file and its relative -location in the filesystem; this logic is also used for the `module(...)` idiom. (How this -works and when you need to provide extra information is discussed later.) +### Local module name It is common to use a local name for a module when declaring its contents. In this case the 'alias' tag can tell LDoc that these functions do belong to the module: @@ -149,6 +183,8 @@ explicit assignment to a variable: --- second test. M.two = function(...) ... end +### Local functions + Apart from exported functions, a module usually contains local functions. By default, LDoc does not include these in the documentation, but they can be enabled using the `--all` flag. They can be documented just like 'public' functions: @@ -162,6 +198,8 @@ They can be documented just like 'public' functions: -- @local here function foo(...) .. end +### Tables and other constant values + Modules can of course export tables and other values. The classic way to document a table looks like this: @@ -205,6 +243,8 @@ and 'return' can be specified multiple times, whereas a type tag like 'function' occur once in a comment. The basic rule is that a single doc comment can only document one entity. +### Alternative way of specifying tags + Since 1.3, LDoc allows the use of _colons_ instead of @. --- a simple function. @@ -220,6 +260,8 @@ In this style, types may be used directly if prefixed with '!' or '?' (for type- (see @{colon.lua}, rendered [here](http://stevedonovan.github.io/ldoc/examples/colon)) +### Which files are processed + By default, LDoc will process any file ending in '.lua' or '.luadoc' in a specified directory; you may point it to a single file as well. A 'project' usually consists of many modules in one or more _packages_. The generated `index.html` will point to the generated From 2061b5fc1160078679dfe2d7c1fdf1f1ed9b3bc8 Mon Sep 17 00:00:00 2001 From: JonasT Date: Tue, 31 Dec 2013 08:53:59 +0100 Subject: [PATCH 089/106] Some more reordering, introducing tags more explicitly --- doc/doc.md | 115 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 49 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index f5e82801..895de07a 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -59,7 +59,7 @@ warning issued. The only exception is if the module starts with an explicit `mod statement. -### Summary and tags of a doc comment +### Tags All doc comments start with a summary sentence, that ends with a period or a question mark. An optional description may follow. Normally the summary sentence will appear in the module @@ -68,9 +68,15 @@ contents. After this descriptive text, there will typically be _tags_. These follow the convention established by Javadoc and widely used in tools for other languages. +--- Some doc comment +-- @tag1 parameters for first tag +-- @tag2 parameters for the second tag + +The order of tags is not important, but as always, consistency is useful. + The first important tag to know is the module tag: -### The module tag, naming and describing your API module +#### The module tag, naming and describing your API module The first thing in your API module should be a name and a description. This is how a module is commonly done in Lua 5.2 with a @module tag at the top @@ -91,7 +97,7 @@ which introduces the name: This sets up a module named 'test' with the description 'a test module'. -### Describing functions with tags: +#### Describing functions with tags: The next thing to describe are the functions your module has. This is a simple example of a documented function: @@ -107,7 +113,7 @@ This is a simple example of a documented function: The tags basically add all the detail that cannot be derived from the source code automatically. -### Most common tags +#### Most common tags: param, return, tparam, treturn Common tags are the 'param' tag which takes a parameter name followed by a parameter description separated by a space, and the 'return' tag which is simply followed by @@ -145,6 +151,55 @@ function. For Lua, there can also be multiple 'return' tags ... Of course there is also the 'module' tag which you have already seen. +#### Describing tables and constants: field, table + +Modules can of course export tables and other values. The classic way to document a table +looks like this: + + --- a useful table of constants + -- @field alpha first correction + -- @field beta second correction + -- @field gamma fudge factor + -- @table constants + +Here the kind of item is made explicit by the 'table' tag; tables have 'fields' in the same +way as functions have parameters. + +This can get tedious, so LDoc will attempt to extract table documentation from code: + + --- a useful table of constants + M.constants = { + alpha = 0.23, -- first correction + beta = 0.443, -- second correction + gamma = 0.01 -- fudge factor + } + +The rule followed here is `NAME = `. If LDoc can't work out the name and +type from the following code, then a warning will be issued, pointing to the file and +location. + +Another kind of module-level type is 'field', such as follows: + + --- module version. + M._VERSION = '0.5' + +That is, a module may contain exported functions, local functions, tables and fields. + +#### Explicitly specifying a function or fields +When the code analysis would lead to the wrong type, you can always be explicit. + + --- module contents with explicitly documented field _CONTENTS. + -- @field _CONTENTS + M._CONTENTS = {constants=true,one=true,...} + + --- an explicitly named function. + -- @function my_function + function my_function() + ... + end + +### Doing modules the Lua 5.1 way + As an alternative to using the 'module' tag as described before, you can still start your modules the Lua 5.1 way: @@ -155,6 +210,13 @@ However, the 'module' function is deprecated in Lua 5.2 and it is increasingly common to see less 'magic' ways of creating modules, as seen in the description of the 'module' tag previously with the explicitely returned module table. +#### Repeating tags + +Tags like 'param' and 'return' can be specified multiple times, whereas a type +tag like 'function' can only occur once in a comment. + +The basic rule is that a single doc comment can only document one entity. + ### Local module name It is common to use a local name for a module when declaring its contents. In this case the @@ -198,51 +260,6 @@ They can be documented just like 'public' functions: -- @local here function foo(...) .. end -### Tables and other constant values - -Modules can of course export tables and other values. The classic way to document a table -looks like this: - - --- a useful table of constants - -- @field alpha first correction - -- @field beta second correction - -- @field gamma fudge factor - -- @table constants - -Here the kind of item is made explicit by the 'table' tag; tables have 'fields' in the same -way as functions have parameters. - -This can get tedious, so LDoc will attempt to extract table documentation from code: - - --- a useful table of constants - M.constants = { - alpha = 0.23, -- first correction - beta = 0.443, -- second correction - gamma = 0.01 -- fudge factor - } - -The rule followed here is `NAME = `. If LDoc can't work out the name and -type from the following code, then a warning will be issued, pointing to the file and -location. - -Another kind of module-level type is 'field', such as follows: - - --- module version. - M._VERSION = '0.5' - -That is, a module may contain exported functions, local functions, tables and fields. - -When the code analysis would lead to the wrong type, you can always be explicit. - - --- module contents. - -- @field _CONTENTS - M._CONTENTS = {constants=true,one=true,...} - -The order of tags is not important, but as always, consistency is useful. Tags like 'param' -and 'return' can be specified multiple times, whereas a type tag like 'function' can only -occur once in a comment. The basic rule is that a single doc comment can only document one -entity. - ### Alternative way of specifying tags Since 1.3, LDoc allows the use of _colons_ instead of @. From e3c9f5336a7d1be02de5c5dc9b863330b42c7c27 Mon Sep 17 00:00:00 2001 From: JonasT Date: Tue, 31 Dec 2013 08:54:56 +0100 Subject: [PATCH 090/106] Fixing tag introduction example (hopefully?) --- doc/doc.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 895de07a..42b1c6b6 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -68,9 +68,9 @@ contents. After this descriptive text, there will typically be _tags_. These follow the convention established by Javadoc and widely used in tools for other languages. ---- Some doc comment --- @tag1 parameters for first tag --- @tag2 parameters for the second tag + --- Some doc comment + -- @tag1 parameters for first tag + -- @tag2 parameters for the second tag The order of tags is not important, but as always, consistency is useful. From e38a1b140209b8adc9dde55f9c2178b2acdefcba Mon Sep 17 00:00:00 2001 From: JonasT Date: Tue, 31 Dec 2013 09:00:30 +0100 Subject: [PATCH 091/106] Better section naming, some hints for C code --- doc/doc.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 42b1c6b6..83b9f15d 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -76,7 +76,7 @@ The order of tags is not important, but as always, consistency is useful. The first important tag to know is the module tag: -#### The module tag, naming and describing your API module +#### Modules: naming and describing your API module The first thing in your API module should be a name and a description. This is how a module is commonly done in Lua 5.2 with a @module tag at the top @@ -97,7 +97,7 @@ which introduces the name: This sets up a module named 'test' with the description 'a test module'. -#### Describing functions with tags: +#### Functions: The next thing to describe are the functions your module has. This is a simple example of a documented function: @@ -110,10 +110,19 @@ This is a simple example of a documented function: .... end +You can also give the function name itself as an explicit tag, +which is especially useful when documenting a Lua api exported by C code: + /// A C function which is exported to Lua with another name, + // because the ways of C can be mysterious! + // @function our_nice_function + int _some_function_for_lua(lua_State* l) { + .... + } + The tags basically add all the detail that cannot be derived from the source code automatically. -#### Most common tags: param, return, tparam, treturn +#### Function parameters and return values Common tags are the 'param' tag which takes a parameter name followed by a parameter description separated by a space, and the 'return' tag which is simply followed by @@ -151,7 +160,7 @@ function. For Lua, there can also be multiple 'return' tags ... Of course there is also the 'module' tag which you have already seen. -#### Describing tables and constants: field, table +#### Tables and constant values (fields) Modules can of course export tables and other values. The classic way to document a table looks like this: @@ -197,6 +206,9 @@ When the code analysis would lead to the wrong type, you can always be explicit. function my_function() ... end +As mentioned before, this is often especially useful in C where things +may look different in the C code than they will in the final Lua api which +you want to document. ### Doing modules the Lua 5.1 way From ffec76bd5671218c65762459c4b50f1cdc3abd5d Mon Sep 17 00:00:00 2001 From: JonasT Date: Tue, 31 Dec 2013 09:01:14 +0100 Subject: [PATCH 092/106] Removing inconsistent header colon --- doc/doc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/doc.md b/doc/doc.md index 83b9f15d..0eb99758 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -97,7 +97,7 @@ which introduces the name: This sets up a module named 'test' with the description 'a test module'. -#### Functions: +#### Functions The next thing to describe are the functions your module has. This is a simple example of a documented function: From f507e36720a3172131d2796b63d567da47d54af4 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Thu, 2 Jan 2014 18:45:04 +0200 Subject: [PATCH 093/106] more general method of controlling visibility of local functions or items marked with @local --- ldoc.lua | 7 +------ ldoc/doc.lua | 13 ++++--------- tests/styles/type.lua | 9 +++++++++ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 8b7aaadb..9ae13e86 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -553,12 +553,7 @@ for mod in module_list:iter() do project:add(mod,module_list) end --- the default is not to show local functions in the documentation. -if not args.all and not ldoc.all then - for mod in module_list:iter() do - mod:mask_locals() - end -end +override 'all' if ldoc.sort_modules then table.sort(module_list,function(m1,m2) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 4e08dbcf..7be979fb 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -230,7 +230,10 @@ function File:finish() end end item:finish() - if doc.project_level(item.type) then + -- the default is not to show local functions in the documentation. + if not self.args.all and (item.type=='lfunction' or (item.tags and item.tags['local'])) then + -- don't add to the module -- + elseif doc.project_level(item.type) then this_mod = item local package,mname,submodule if item.type == 'module' then @@ -1187,14 +1190,6 @@ function Module:resolve_references(modules) end end --- suppress the display of local functions and annotations. --- This is just a placeholder hack until we have a more general scheme --- for indicating 'private' content of a module. -function Module:mask_locals () - self.kinds['Local Functions'] = nil - self.kinds['Annotations'] = nil -end - function Item:dump_tags (taglist) for tag, value in pairs(self.tags) do if not taglist or taglist[tag] then diff --git a/tests/styles/type.lua b/tests/styles/type.lua index 6f2158d8..8499dc8e 100644 --- a/tests/styles/type.lua +++ b/tests/styles/type.lua @@ -25,6 +25,15 @@ function Bonzo:dog () end +---- +-- Private method. +-- You need -a flag or 'all=true' to see these +-- @local +function Bonzo:cat () + +end + + ---- -- A subtable with fields. -- @table Details From 86a3fde07e7c0ac9478d2c10577f9d4d3acfde92 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sun, 5 Jan 2014 14:55:20 +0200 Subject: [PATCH 094/106] issue #139; improved lookup for class methods; args override now only kicks if the argument does not have its default value --- ldoc.lua | 16 ++++++++++------ ldoc/doc.lua | 33 ++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 9ae13e86..5aa3b6c4 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -134,8 +134,10 @@ local add_language_extension -- hacky way for doc module to be passed options... doc.ldoc = ldoc -local function override (field) - if args[field] == nil and ldoc[field] ~= nil then args[field] = ldoc[field] end +-- if the corresponding argument was the default, then any ldoc field overrides +local function override (field,defval) + defval = defval or false + if args[field] == defval and ldoc[field] ~= nil then args[field] = ldoc[field] end end -- aliases to existing tags can be defined. E.g. just 'p' for 'param' @@ -336,6 +338,8 @@ source_dir = source_dir:gsub('[/\\]%.$','') -- * 'NAME' explicitly give the base module package name -- +override ('package','.') + local function setup_package_base() if ldoc.package then args.package = ldoc.package end if args.package == '.' then @@ -460,7 +464,7 @@ end -- create the function that renders text (descriptions and summaries) -- (this also will initialize the code prettifier used) -override 'format' +override ('format','plain') override 'pretty' ldoc.markup = markup.create(ldoc, args.format,args.pretty) @@ -611,9 +615,9 @@ if args.filter ~= 'none' then end -- can specify format, output, dir and ext in config.ld -override 'output' -override 'dir' -override 'ext' +override ('output','index') +override ('dir','doc') +override ('ext','html') override 'one' -- handling styling and templates -- diff --git a/ldoc/doc.lua b/ldoc/doc.lua index 7be979fb..ef8aa319 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -219,6 +219,7 @@ function File:finish() local this_mod local items = self.items local tagged_inside + self.args = self.args or {} for item in items:iter() do if mod_section_type(this_mod) == 'factory' and item.tags then local klass = '@{'..this_mod.section.name..'}' @@ -277,18 +278,21 @@ function File:finish() this_mod.section = nil else local summary = item.summary:gsub('%.$','') + local lookup_name if doc.class_tag(item.type) then display_name = 'Class '..item.name + lookup_name = item.name item.module = this_mod this_mod.items.by_name[item.name] = item else display_name = summary + lookup_name = summary end item.display_name = display_name this_mod.section = item this_mod.kinds:add_kind(display_name,display_name..' ',nil,item) this_mod.sections:append(item) - this_mod.sections.by_name[display_name:gsub('%A','_')] = item + this_mod.sections.by_name[lookup_name:gsub('%A','_')] = item end else local to_be_removed @@ -1049,20 +1053,21 @@ end local function reference (s, mod_ref, item_ref) local name = item_ref and item_ref.name or '' -- this is deeply hacky; classes have 'Class ' prepended. - if item_ref and doc.class_tag(item_ref.type) then - name = 'Class_'..name - end +--~ if item_ref and doc.class_tag(item_ref.type) then +--~ name = 'Class_'..name +--~ end return {mod = mod_ref, name = name, label=s} end function Module:lookup_class_item (packmod, s) - local section = "Class_"..packmod - if self.sections.by_name[section] then - for item in self.items:iter() do - --print('item',item.name,item.section) - if item.section == section and s == item.name then - return reference(s,self,item) - end + local klass = packmod --"Class_"..packmod + local qs = klass..':'..s + local klass_section = self.sections.by_name[klass] + if not klass_section then return nil end -- no such class + for item in self.items:iter() do + --print('item',qs,item.name) + if s == item.name or qs == item.name then + return reference(s,self,item) end end return nil @@ -1114,6 +1119,12 @@ function Module:process_see_reference (s,modules,istype) if not mod_ref then local ref = self:lookup_class_item(packmod,s) if ref then return ref end + local mod, klass = split_dotted_name(packmod) + mod_ref = modules.by_name[mod] + if mod_ref then + ref = mod_ref:lookup_class_item(klass,name) + if ref then return ref end + end ref = lua_manual_ref(s) if ref then return ref end return nil,"module not found: "..packmod From 60c6e79dd982478737c0c37e4d7419e321b8b709 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Mon, 6 Jan 2014 13:35:12 +0200 Subject: [PATCH 095/106] Issue #137 boilerplate Lua block comment --- ldoc/lang.lua | 7 ++++--- ldoc/parse.lua | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ldoc/lang.lua b/ldoc/lang.lua index a0704d95..888dc352 100644 --- a/ldoc/lang.lua +++ b/ldoc/lang.lua @@ -87,6 +87,7 @@ end function Lua:grab_block_comment(v,tok) local equals = v:match('^%-%-%[(=*)%[') + if not equals then return v end v = v:gsub(self.block_comment,'') return tools.grab_block_comment(v,tok,'%]'..equals..'%]') end @@ -286,11 +287,11 @@ function CC:item_follows (t,v,tok) t,v = tnext(tok) end -- types may have multiple tokens: example, const char *bonzo(...) - local return_type, name = v + local return_type, name = v t,v = tnext(tok) - name = v + name = v t,v = tnext(tok) - while t ~= '(' do + while t ~= '(' do return_type = return_type .. ' ' .. name name = v t,v = tnext(tok) diff --git a/ldoc/parse.lua b/ldoc/parse.lua index 5709b249..30ecdb13 100644 --- a/ldoc/parse.lua +++ b/ldoc/parse.lua @@ -214,6 +214,8 @@ local function parse_file(fname, lang, package, args) local t,v = tnext(tok) -- with some coding styles first comment is standard boilerplate; option to ignore this. if args.boilerplate and t == 'comment' then + -- hack to deal with boilerplate inside Lua block comments + if v:match '%s*%-%-%[%[' then lang:grab_block_comment(v,tok) end t,v = tnext(tok) end if t == '#' then -- skip Lua shebang line, if present From 04dc148df606c79e1931d322ccd730eb151b8e78 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Mon, 6 Jan 2014 15:03:09 +0200 Subject: [PATCH 096/106] Issue #145: do not expand annotations if there's no previous item --- ldoc/doc.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ldoc/doc.lua b/ldoc/doc.lua index ef8aa319..ea25ad0d 100644 --- a/ldoc/doc.lua +++ b/ldoc/doc.lua @@ -129,8 +129,8 @@ known_tags._annotation_tags = { local acount = 1 function doc.expand_annotation_item (tags, last_item) - if tags.summary ~= '' then return false end - local item_name = last_item and last_item.tags.name or '?' + if tags.summary ~= '' or last_item == nil then return false end + local item_name = last_item.tags.name for tag, value in pairs(tags) do if known_tags._annotation_tags[tag] then tags:add('class','annotation') From 7c49337402b5a3aacc5ed09ab792d757f2e95f08 Mon Sep 17 00:00:00 2001 From: vanzomerenc Date: Mon, 6 Jan 2014 16:10:12 -0500 Subject: [PATCH 097/106] Resolve issue #22 'In-line links with underscores are broken' --- ldoc/markup.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ldoc/markup.lua b/ldoc/markup.lua index 5451f623..c7af788f 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -44,6 +44,9 @@ local function resolve_inline_references (ldoc, txt, item, plain) res = res:gsub('`([^`]+)`',function(name) local ref,err = markup.process_reference(name) if ref then + if not plain and name then + name = name:gsub('_', '\\_') + end return ('%s '):format(ldoc.href(ref),name) else return ''..name..'' From d367390c685081f67efd589983df6f269255e968 Mon Sep 17 00:00:00 2001 From: steve donovan Date: Tue, 7 Jan 2014 17:48:50 +0200 Subject: [PATCH 098/106] Issue #150: 'info' section ordering is now consistent --- ldoc/html.lua | 7 ++++--- ldoc/html/ldoc_ltp.lua | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ldoc/html.lua b/ldoc/html.lua index 08f31ca0..c6f89672 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -19,6 +19,7 @@ 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' @@ -39,16 +40,16 @@ local function cleanup_whitespaces(text) end local function get_module_info(m) - local info = {} + 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[tag] = val + info:set(tag,val) end - if next(info) then + if #info:keys() > 0 then return info end end diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 1f01fb08..63910e34 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -99,7 +99,7 @@ return [==[ # if module.info then

    Info:

      -# for tag, value in ldoc.pairs(module.info) do +# for tag, value in module.info:iter() do
    • $(tag): $(M(value,module))
    • # end
    From fc0e6099fa683c365d26834405367d6256571fbb Mon Sep 17 00:00:00 2001 From: "Gary V. Vaughan" Date: Wed, 8 Jan 2014 16:55:32 +1300 Subject: [PATCH 099/106] html: case sensitive file systems require matching require args. * ldoc/html.lua: Make sure to `require 'pl.OrderedMap'` (with a capital 'O') to match the filename installed by penlight. Signed-off-by: Gary V. Vaughan --- ldoc/html.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldoc/html.lua b/ldoc/html.lua index c6f89672..f138da81 100644 --- a/ldoc/html.lua +++ b/ldoc/html.lua @@ -19,7 +19,7 @@ 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 OrderedMap = require 'pl.OrderedMap' local tools = require 'ldoc.tools' local markup = require 'ldoc.markup' local prettify = require 'ldoc.prettify' From 94439e8e64fa99e403e820d239c9f2de347d2faf Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Fri, 10 Jan 2014 15:50:44 +0200 Subject: [PATCH 100/106] updated changes.md - preparing for release --- changes.md | 27 ++++++++++++++++++++++ doc/doc.md | 66 +++++++++++++++++++++++++++++------------------------- 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/changes.md b/changes.md index 9370e36e..b6e06f87 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,30 @@ +## Version 1.4.2 + +### Features + + * Can define fields/properties of objects; `readonly` modifier supported (#93) + * Can switch off auto-linking to Lua manual with `no_lua_ref` + * Module sorting is off by default, use `sort_modules=true` + * References to 'classes' now work properly + * Option to use first Markdown title instead of file names with `use_markdown_titles` + * Automatic `Metamethods` and `Methods` sections generated for `classmod` classes + * `unqualified=true` to strip package names on sidebar (#110) + * Custom tags (which may be hidden) + * Custom Display Name handlers + +### Fixes + + * stricter about doc comments, now excludes common '----- XXXXX ----' pattern + * no longer expects space after `##` in Markdown (#96) + * Section lookup was broken + * With `export` tag, decide whether method is static or not + * `classmod` classes now respect custom sections (#113) + * Minor issues with prettification + * Command-line flags set explicitly take precendence over configuration file values. + * Boilerplate Lua block comment ignored properly (#137) + * Inline links with underscores sorted (#22) + * Info section ordering is now consistent (#150) + ## Version 1.4.0 ### Features diff --git a/doc/doc.md b/doc/doc.md index a6d71656..163fd78a 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -59,6 +59,9 @@ Any module or script must start with a doc comment; any other files are ignored warning issued. The only exception is if the module starts with an explicit `module` statement. +If your coding standards require a boilerplate copyright notice, then the `-B` flag or +`boilerplate=true` will make LDoc ignore the first comment of each module. + ### Tags @@ -80,7 +83,7 @@ The first important tag to know is the module tag: #### Modules: naming and describing your API module The first thing in your API module should be a name and a description. -This is how a module is commonly done in Lua 5.2 with a @module tag at the top +This is how a module is commonly done in Lua 5.2 with a **@module** tag at the top which introduces the name: --- a test module @@ -113,6 +116,7 @@ This is a simple example of a documented function: You can also give the function name itself as an explicit tag, which is especially useful when documenting a Lua api exported by C code: + /// A C function which is exported to Lua with another name, // because the ways of C can be mysterious! // @function our_nice_function @@ -196,6 +200,7 @@ Another kind of module-level type is 'field', such as follows: That is, a module may contain exported functions, local functions, tables and fields. #### Explicitly specifying a function or fields + When the code analysis would lead to the wrong type, you can always be explicit. --- module contents with explicitly documented field _CONTENTS. @@ -207,6 +212,7 @@ When the code analysis would lead to the wrong type, you can always be explicit. function my_function() ... end + As mentioned before, this is often especially useful in C where things may look different in the C code than they will in the final Lua api which you want to document. @@ -305,8 +311,8 @@ LDoc has a two-layer hierarchy; underneath the project, there are modules, scrip (containing code) and examples and 'topics' (containing documentation). These then contain items like functions, tables, sections, and so forth. -If you want to document scripts, then use `@script` instead of `@module`. New with 1.4 is -`@classmod` which is a module which exports a single class. +If you want to document scripts, then use **@script** instead of **@module**. New with 1.4 is +**@classmod** which is a module which exports a single class. ## See References @@ -328,9 +334,9 @@ Here it's assumed that 'split' is a function defined in the same module. If you to a function in another module, then the reference has to be qualified. References to methods use a colon: `myclass:method`; this is for instance how you would -refer to members of a `@type` section. +refer to members of a **@type** section. -The example at `tests/complex` shows how @see references are interpreted: +The example at `tests/complex` shows how **@see** references are interpreted: complex.util.parse complex.convert.basic @@ -351,7 +357,7 @@ Lua standard function or table, and links to the online Lua manual. So reference 'table.concat' are handled sensibly. References may be made inline using the `@{\ref}` syntax. This may appear anywhere in the -text, and is more flexible than `@see`. In particular, it provides one way to document the +text, and is more flexible than **@see**. In particular, it provides one way to document the type of a parameter or return value when that type has a particular structure: ------ @@ -406,7 +412,7 @@ online references to the Linux manpages. So in `config.ld` we have: return name, url end) -'^([%w_]+)%((%d)%)$' both matches the pattern and extracts the name and its section. Then it's +`^([%w_]+)%((%d)%)$` both matches the pattern and extracts the name and its section. Then it's a simple matter of building up the appropriate URL. The function is expected to return _link text_ and _link source_ and the patterns are checked before LDoc tries to resolve project references. So it is best to make them match as exactly as possible. @@ -417,7 +423,7 @@ LDoc requires you to have a module doc comment. If your code style requires license blocks that might look like doc comments, then set `boilerplate=true` in your configuration and they will be skipped. -This comment does not have to have an explicit `@module` tag and LDoc continues to +This comment does not have to have an explicit **@module** tag and LDoc continues to respect the use of `module()`. There are three types of 'modules' (i.e. 'project-level'); `module`, a library @@ -428,23 +434,23 @@ There are some tags which are only useful in module comments: `author`,`copyrigh `license` and `release`. These are presented in a special **Info** section in the default HTML output. -The `@usage` tag has a somewhat different presentation when used in modules; the text +The **@usage** tag has a somewhat different presentation when used in modules; the text is presented formatted as-is in a code font. If you look at the script `ldoc` in this documentation, you can see how the command-line usage is shown. Since coding is all about avoiding repetition and the out-of-sync issues that arise, -the `@usage` tag can appear later in the module, before a long string. For instance, +the **@usage** tag can appear later in the module, before a long string. For instance, the main script of LDoc is [ldoc.lua](https://github.com/stevedonovan/LDoc/blob/master/ldoc.lua) and you will see that the usage tag appears on line 36 before the usage string presented as help. -`@export` is another module tag that is usually 'detached'. It is for supporting +**@export** is another module tag that is usually 'detached'. It is for supporting modules that wish to explicitly export their functions @{three.lua|at the end}. In that example, both `question` and `answer` are local and therefore private to the module, but `answer` has been explicitly exported. (If you invoke LDoc with the `-a` flag on this file, you will see the documentation for the unexported function as well.) -`@set` is a powerful tag which assigns a configuration variable to a value _just for this module_. +**@set** is a powerful tag which assigns a configuration variable to a value _just for this module_. Saying `@set no_summary=true` in a module comment will temporarily disable summary generation when the template is expanded. Generally configuration variables that effect template expansion are modifiable in this way. For instance, if you wish that the contents of a particular module @@ -458,7 +464,7 @@ types in a module: 'Functions', 'Tables' and 'Fields' (There is another default sections can be added; the first mechanism is when you @{Adding_new_Tags|define a new type} (say 'macro'). Then a new section ('Macros') is created to contain these types. -There is also a way to declare ad-hoc sections using the `@section` tag. +There is also a way to declare ad-hoc sections using the **@section** tag. The need occurs when a module has a lot of functions that need to be put into logical sections. @@ -507,7 +513,7 @@ with the LuaDoc `class` tag.) A section continues until the next section is found, `@section end`, or end of file. -You can put items into an implicit section using the @within tag. This allows you to put +You can put items into an implicit section using the **@within tag**. This allows you to put adjacent functions in different sections, so that you are not forced to order your code in a particular way. @@ -518,7 +524,7 @@ and the advantage that methods can be put into sections. Sometimes a module may logically span several files, which can easily happen with large There will be a master module with name 'foo' and other files which when required add functions to that module. If these files have -a @submodule tag, their contents will be placed in the master module documentation. However, +a **@submodule** tag, their contents will be placed in the master module documentation. However, a current limitation is that the master module must be processed before the submodules. See the `tests/submodule` example for how this works in practice. @@ -679,8 +685,8 @@ See @{mylib.c} for the full example. 1.4 introduces basic support for [Moonscript](http://moonscript.org). Moonscript module conventions are just the same as Lua, except for an explicit class construct. -@{list.moon} shows how `@classmod` can declare modules that export one class, with metamethods -put explicitly into a separate section. +@{list.moon} shows how **@classmod** can declare modules that export one class, with metamethods +and methods put implicitly into a separate section. ## Basic Usage @@ -915,7 +921,7 @@ can suppress the contents summary with `no_summary`. A basic customization is to override the default UTF-8 encoding using `charset`. For instance, Brazillian software would find it useful to put `charset='ISO-8859-1'` in `config.ld`, or use -the @charset tag for individual files. +the **@charset** tag for individual files. Setting `no_return_or_parms` to `true` will suppress the display of 'param' and 'return' tags. This may appeal to programmers who dislike the traditional @tag soup xDoc style and @@ -957,7 +963,7 @@ and writer-generated documentation using different tools, and having to match th LDoc allows for source examples to be included in the documentation. For example, see the online documentation for [winapi](http://stevedonovan.github.com/winapi/api.html). The -function `utf8_expand` has a `@see` reference to 'testu.lua' and following that link gives +function `utf8_expand` has a **@see** reference to 'testu.lua' and following that link gives you a pretty-printed version of the code. The line in the `config.ld` that enables this is: @@ -1048,7 +1054,7 @@ There is a useful function for creating new tags that can be used in `config.ld` tparam_alias('string','string') -That is, "@string" will now have the same meaning as "@tparam string"; this also applies +That is, **@string** will now have the same meaning as "@tparam string"; this also applies to the optional type syntax "?|T1|T2". From 1.3, the following standard type aliases are predefined: @@ -1079,8 +1085,8 @@ defined as a tag plus a set of modifiers. So `tparam` is defined as: alias('tparam',{'param',modifiers={type="$1"}}) -As an extension, you're allowed to use '@param' tags in table definitions. This makes it -possible to use type alias like '@string' to describe fields, since they will expand to +As an extension, you're allowed to use **@param** tags in table definitions. This makes it +possible to use type aliases like **@string** to describe fields, since they will expand to 'param'. Another modifier understood by LDoc is `opt`. For instance, @@ -1113,7 +1119,7 @@ then LDoc can present this as the default value for this optional argument. (See @{four.lua}, rendered [here](http://stevedonovan.github.io/ldoc/examples/four)) An experimental feature in 1.4 allows different 'return groups' to be defined. There may be -multiple `@return` tags, and the meaning of this is well-defined, since Lua functions may +multiple **@return** tags, and the meaning of this is well-defined, since Lua functions may return multiple values. However, being a dynamic language it may return a single value if successful and two values (`nil`,an error message) if there is an error. This is in fact the convention for returning 'normal' errors (like 'file not found') as opposed to parameter errors @@ -1133,7 +1139,7 @@ as a group: This is the first function in @{multiple.lua}, and the [output](http://stevedonovan.github.io/ldoc/examples/multiple) shows how return groups are presented, with an **Or** between the groups. -This is rather clumsy, and so there is a shortcut, the `@error` tag which achieves the same result, +This is rather clumsy, and so there is a shortcut, the **@error** tag which achieves the same result, with helpful type information. Currently the `type`,`opt` and `` modifiers are the only ones known and used by LDoc when generating HTML @@ -1162,7 +1168,7 @@ of files and directories. template. In `config.ld` they may also be `true`, meaning use the same directory as the configuration file. - `merge` allow documentation from different files to be merged into modules without -explicit @submodule tag +explicit **@submodule** tag _These only appear in the configuration file:_ @@ -1171,7 +1177,7 @@ _These only appear in the configuration file:_ - `examples` a directory or file: can be a table - `readme` or `topics` readme files (to be processed with Markdown) - `pretty` code prettify 'lua' (default) or 'lxsh' - - `charset` use if you want to override the UTF-8 default (also @charset in files) + - `charset` use if you want to override the UTF-8 default (also **@charset** in files) - `sort` set if you want all items in alphabetical order - `no_return_or_parms` don't show parameters or return values in output - `backtick_references` whether references in backticks will be resolved. Happens by default @@ -1195,7 +1201,7 @@ function tagged with a certain tag: end return default_handler(item) end - + - `not_luadoc` set to `true` if the docs break LuaDoc compatibility - `no_space_before_args` set to `true` if you do not want a space between a function's name and its arguments. - `template_escape` overrides the usual '#' used for Lua code in templates. This needs to be changed if the output format is Markdown, for instance. @@ -1328,7 +1334,7 @@ The basic data structure is straightforward: it is an array of 'modules' (projec entities, including scripts) which each contain an `item` array (functions, tables and so forth). -For instance, to find all functions which don't have a @return tag: +For instance, to find all functions which don't have a **@return** tag: return { filter = function (t) @@ -1342,8 +1348,8 @@ For instance, to find all functions which don't have a @return tag: end } -The internal naming is not always so consistent; `ret` corresponds to @return, and `params` -corresponds to @param. `item.params` is an array of the function parameters, in order; it +The internal naming is not always so consistent; `ret` corresponds to **@return**, and `params` +corresponds to **@param**. `item.params` is an array of the function parameters, in order; it is also a map from these names to the individual descriptions of the parameters. `item.modifiers` is a table where the keys are the tags and the values are arrays of From 0c6bd7d8b6785f7988aeacce9f14fad9b2697993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Moreno?= Date: Tue, 14 Jan 2014 18:58:41 -0200 Subject: [PATCH 101/106] Updated LuaDoc url --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 3d6c378c..bd720b42 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ with LuaDoc) and depends on Penlight itself.(This allowed me to _not_ write a lo The [API documentation](http://stevedonovan.github.com/Penlight/api/index.html) of Penlight is an example of a project using plain LuaDoc markup processed using LDoc. -LDoc is intended to be compatible with [LuaDoc](http://luadoc.luaforge.net/manual.htm) and +LDoc is intended to be compatible with [LuaDoc](http://luadoc.luaforge.net/manual.html) and thus follows the pattern set by the various *Doc tools: --- Summary ends with a period. From b2bf47ac02939a48d233c86115e0e2631861790a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Moreno?= Date: Wed, 15 Jan 2014 12:16:58 -0200 Subject: [PATCH 102/106] Updated LuaDoc canonical URL --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index bd720b42..35e9c2b5 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ with LuaDoc) and depends on Penlight itself.(This allowed me to _not_ write a lo The [API documentation](http://stevedonovan.github.com/Penlight/api/index.html) of Penlight is an example of a project using plain LuaDoc markup processed using LDoc. -LDoc is intended to be compatible with [LuaDoc](http://luadoc.luaforge.net/manual.html) and +LDoc is intended to be compatible with [LuaDoc](http://keplerproject.github.io/luadoc/) and thus follows the pattern set by the various *Doc tools: --- Summary ends with a period. From 2c0d459a433cbc5e33621d849602286e919593f6 Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Thu, 16 Jan 2014 09:53:29 +0200 Subject: [PATCH 103/106] bumped version to 1.4.2 --- ldoc.lua | 4 ++-- ldoc/html/ldoc_ltp.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index 5aa3b6c4..a1ec0b4b 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -7,7 +7,7 @@ -- -- C/C++ support for Lua extensions is provided. -- --- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.4.0.zip) +-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.4.2.zip) -- -- [Github Page](https://github.com/stevedonovan/ldoc) -- @@ -37,7 +37,7 @@ app.require_here() --- @usage local usage = [[ -ldoc, a documentation generator for Lua, vs 1.4.0 +ldoc, a documentation generator for Lua, vs 1.4.2 -d,--dir (default doc) output directory -o,--output (default 'index') output name -v,--verbose verbose diff --git a/ldoc/html/ldoc_ltp.lua b/ldoc/html/ldoc_ltp.lua index 63910e34..b7de5f6c 100644 --- a/ldoc/html/ldoc_ltp.lua +++ b/ldoc/html/ldoc_ltp.lua @@ -281,7 +281,7 @@ return [==[
    -generated by LDoc 1.4.0 +generated by LDoc 1.4.2
    From e5605a4a94f66b433aa96c581dbff8df5d512bac Mon Sep 17 00:00:00 2001 From: Steve Donovan Date: Fri, 17 Jan 2014 15:59:39 +0200 Subject: [PATCH 104/106] kind_names variable can override names used in sidebar, e.g. 'kind_names={topic='Manual'}' will override default of 'Topics' --- ldoc.lua | 52 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/ldoc.lua b/ldoc.lua index a1ec0b4b..afb424c7 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -91,13 +91,6 @@ function ModuleMap:_init () self.fieldname = 'section' end -ModuleMap:add_kind('function','Functions','Parameters') -ModuleMap:add_kind('table','Tables','Fields') -ModuleMap:add_kind('field','Fields') -ModuleMap:add_kind('lfunction','Local Functions','Parameters') -ModuleMap:add_kind('annotation','Issues') - - local ProjectMap = class(KindMap) ProjectMap.project_level = true @@ -106,11 +99,7 @@ function ProjectMap:_init () self.fieldname = 'type' end -ProjectMap:add_kind('module','Modules') -ProjectMap:add_kind('script','Scripts') -ProjectMap:add_kind('classmod','Classes') -ProjectMap:add_kind('topic','Topics') -ProjectMap:add_kind('example','Examples') + local lua, cc = lang.lua, lang.cc @@ -125,11 +114,43 @@ local file_types = { ['.mm'] = cc, ['.moon'] = lang.moon, } - ------- ldoc external API ------------ -- the ldoc table represents the API available in `config.ld`. local ldoc = { charset = 'UTF-8' } + +local kind_names + +local function lookup (itype,igroup,isubgroup) + local kn = kind_names[itype] + if kn then + if type(kn) == 'string' then + igroup = kn + else + igroup = kn[1] + isubgroup = kn[2] + end + end + return itype, igroup, isubgroup +end + +local function setup_kinds () + kind_names = ldoc.kind_names or {} + + ModuleMap:add_kind(lookup('function','Functions','Parameters')) + ModuleMap:add_kind(lookup('table','Tables','Fields')) + ModuleMap:add_kind(lookup('field','Fields')) + ModuleMap:add_kind(lookup('lfunction','Local Functions','Parameters')) + ModuleMap:add_kind(lookup('annotation','Issues')) + + ProjectMap:add_kind(lookup('module','Modules')) + ProjectMap:add_kind(lookup('script','Scripts')) + ProjectMap:add_kind(lookup('classmod','Classes')) + ProjectMap:add_kind(lookup('topic','Topics')) + ProjectMap:add_kind(lookup('example','Examples')) +end + + local add_language_extension -- hacky way for doc module to be passed options... doc.ldoc = ldoc @@ -202,7 +223,7 @@ local ldoc_contents = { 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', 'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles', - 'unqualified', 'custom_display_name_handler', + 'unqualified', 'custom_display_name_handler', 'kind_names', } ldoc_contents = tablex.makeset(ldoc_contents) @@ -393,6 +414,8 @@ override 'not_luadoc' override 'module_file' override 'boilerplate' +setup_kinds() + -- LDoc is doing plain ole C, don't want random Lua references! if ldoc.parse_extra and ldoc.parse_extra.C then ldoc.no_lua_ref = true @@ -462,6 +485,7 @@ else quit ("file or directory does not exist: "..quote(args.file)) end + -- create the function that renders text (descriptions and summaries) -- (this also will initialize the code prettifier used) override ('format','plain') From b09f7f865fb124e266764f78357796070cdb151c Mon Sep 17 00:00:00 2001 From: steve donovan Date: Sat, 18 Jan 2014 16:13:15 +0200 Subject: [PATCH 105/106] unknown typename check for kind_names; updated ldoc's config.ld --- doc/config.ld | 1 + ldoc.lua | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/config.ld b/doc/config.ld index 54e40523..f45b2935 100644 --- a/doc/config.ld +++ b/doc/config.ld @@ -7,6 +7,7 @@ file='../ldoc.lua' dir='../out' readme='doc.md' style='!pale' +kind_names={topic='Manual',script='Programs'} examples = { '../tests/styles/colon.lua', '../tests/styles/four.lua', diff --git a/ldoc.lua b/ldoc.lua index afb424c7..3ada4e5d 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -119,10 +119,11 @@ local file_types = { -- the ldoc table represents the API available in `config.ld`. local ldoc = { charset = 'UTF-8' } -local kind_names +local known_types, kind_names = {} local function lookup (itype,igroup,isubgroup) local kn = kind_names[itype] + known_types[itype] = true if kn then if type(kn) == 'string' then igroup = kn @@ -148,6 +149,12 @@ local function setup_kinds () ProjectMap:add_kind(lookup('classmod','Classes')) ProjectMap:add_kind(lookup('topic','Topics')) ProjectMap:add_kind(lookup('example','Examples')) + + for k in pairs(kind_names) do + if not known_types[k] then + quit("unknown item type "..tools.quote(k).." in kind_names") + end + end end From 511dfe7df01f92da03c639c5f56495c0c31b6e2b Mon Sep 17 00:00:00 2001 From: steve donovan Date: Tue, 21 Jan 2014 19:51:55 +0200 Subject: [PATCH 106/106] ldoc.custom_references to create new @{KIND:NAME} reference schemes; doc updates --- doc/doc.md | 53 +++++++++++++++++++++++++++++++++++++------------ ldoc.lua | 2 +- ldoc/markup.lua | 12 ++++++++++- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/doc/doc.md b/doc/doc.md index 163fd78a..9e3c15dd 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -34,7 +34,7 @@ end-users to build your documentation using this simple command. ## Commenting Conventions LDoc follows the conventions established by Javadoc and later by LuaDoc to document the -modules, functions, types (=classes) and so forth of your API. +modules, functions, tables and types ("classes") of your API. ### Doc comments @@ -62,6 +62,9 @@ statement. If your coding standards require a boilerplate copyright notice, then the `-B` flag or `boilerplate=true` will make LDoc ignore the first comment of each module. +Common commenting patterns like '---- (text) -----' are exempted, since they are often used +for programmer-facing documentation. + ### Tags @@ -78,6 +81,29 @@ These follow the convention established by Javadoc and widely used in tools for The order of tags is not important, but as always, consistency is useful. +Here are all the tags known to LDoc: + + * **@module** A Lua module containing functions and tables, which may be inside sections + * **@classmod** Like **@module** but describing a class + * **@submodule** A file containing definitions that you wish to put into the named _master_ module + * **@script** A Lua program + * **@author** (multiple), **copyright**, **@license**, **@release** only used for _project-level_ tags like **@module** + * **@function**, **@lfunction**. Functions inside a module + * **@param** formal arguments of a function (multiple) + * **@return** returned values of a function (multiple) + * **@raise** unhandled error thrown by this function + * **@local** explicitly marks a function as not being exported (unless `--all`) + * **@see** reference other documented items + * **@usage** give an example of a function's use. (Has a somewhat different meaning when used + with **@module**) + * **@table** a Lua table + * **@field** a named member of a table + * **@section** starting a named section for grouping functions or tables together + * **@type** a section which describes a class + * **@within** puts the function or table into an implicit section + * **@fixme**, **@todo** and **@warning** are _annotations_, which are doc comments that + occur inside a function body. + The first important tag to know is the module tag: #### Modules: naming and describing your API module @@ -317,7 +343,7 @@ If you want to document scripts, then use **@script** instead of **@module**. Ne ## See References -The tag 'see' is used to reference other parts of the documentation, and 'usage' can provide +**@see** is used to reference other parts of the documentation, and **@usage** can provide examples of use; there can be multiple such tags: --------- @@ -513,7 +539,7 @@ with the LuaDoc `class` tag.) A section continues until the next section is found, `@section end`, or end of file. -You can put items into an implicit section using the **@within tag**. This allows you to put +You can put items into an implicit section using **@within**. This allows you to put adjacent functions in different sections, so that you are not forced to order your code in a particular way. @@ -876,24 +902,25 @@ module that uses extended LDoc features. The _navigation section_ down the left has several parts: - - The project name ('project' in the config) - - A project description ('description') - - ''Contents'' of the current page - - ''Modules'' listing all the modules in this project + - The project name (`project` in the config) + - A project description (`description`) + - **Contents** of the current page + - **Modules** listing all the modules in this project Note that `description` will be passed through Markdown, if it has been specified for the project. This gives you an opportunity to make lists of links, etc; any '##' headers will be formatted like the other top-level items on the navigation bar. -'Contents' is automatically generated. It will contain any explicit sections, if they have -been used. Otherwise you will get the usual categories: 'Functions', 'Tables' and 'Fields'. +**Contents** is automatically generated. It will contain any explicit sections +as well as the usual categories: 'Functions', 'Tables' and 'Fields'. For a documentation page, +the subtitles become the sections shown here. -'Modules' will appear for any project providing Lua libraries; there may also be a 'Scripts' +**Modules** will appear for any project providing Lua libraries; there may also be a 'Scripts' section if the project contains Lua scripts. For example, [LuaMacro](http://stevedonovan.github.com/LuaMacro/docs/api.html) has a driver script `luam` in this section. The [builtin](http://stevedonovan.github.com/LuaMacro/docs/modules/macro.builtin.html) module -only defines macros, which are defined as a _custom tag type_. +only defines macros, which are defined as a _custom tag type[?]_. The _content section_ on the right shows: @@ -1028,13 +1055,13 @@ Remember that the default is for references in backticks to be resolved; unlike references, it is not an error if the reference cannot be found. The _sections_ of a document (the second-level headings) are also references. This -particular section can be refered to as `@{\doc.md.Resolving_References_in_Documents}` - the +particular section you are reading can be refered to as `@{\doc.md.Readme_files}` - the rule is that any non-alphabetic character is replaced by an underscore. Any indented blocks are assumed to be Lua, unless their first line is `@plain`. New with 1.4 is github-markdown-style fenced code blocks, which start with three backticks optionally followed by a language. The code continues until another three backticks -is found: the language can be `c`,'cpp' or `cxx` for C/C++, anything else is Lua. +is found: the language can be `c`, `cpp` or `cxx` for C/C++, anything else is Lua. ## Tag Modifiers diff --git a/ldoc.lua b/ldoc.lua index 3ada4e5d..e51f8633 100644 --- a/ldoc.lua +++ b/ldoc.lua @@ -230,7 +230,7 @@ local ldoc_contents = { 'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups', 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', 'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles', - 'unqualified', 'custom_display_name_handler', 'kind_names', + 'unqualified', 'custom_display_name_handler', 'kind_names', 'custom_references', } ldoc_contents = tablex.makeset(ldoc_contents) diff --git a/ldoc/markup.lua b/ldoc/markup.lua index c7af788f..ec4acf39 100644 --- a/ldoc/markup.lua +++ b/ldoc/markup.lua @@ -20,7 +20,17 @@ local function resolve_inline_references (ldoc, txt, item, plain) if not qname then qname = name end - local ref,err = markup.process_reference(qname) + local ref, err + local custom_ref, refname = utils.splitv(qname,':') + if custom_ref and ldoc.custom_references then + custom_ref = ldoc.custom_references[custom_ref] + if custom_ref then + ref,err = custom_ref(refname) + end + end + if not ref then + ref,err = markup.process_reference(qname) + end if not ref then err = err .. ' ' .. qname if item and item.warning then item:warning(err)