Module:Module documentation

From The Languages of David J. Peterson
Jump to navigation Jump to search

This module automatically generates documentation for other modules. It fetches in-line comments in Lua code and converts them into a form that can be used on a documentation page via {{module documentation}}. In fact, this module's documentation is an example of it in action!

It's helpful to do documentation this way, as it means function/method documentation is available in two places: at the top of the module page (as conventional Wikitext), and above the function itself (as a Lua comment). Each suits a different kind of editing style, and doing it this way keeps them synchronised.

A section of documentation is given using Lua's multi-line comment syntax, which looks something like this: --[==[ ... ]==].

export.show

function export.show(frame)

The main entrypoint for {{module documentation}}. The frame object can contain 3 optional arguments:

  • |comment_level=: The number of equals signs (=) a given section uses. Default: 2 (i.e. --[==[ ... (comment block) ]==])
    e.g. The value 4 means --[====[ ... (comment block) ]====].
  • |section_level=: The header level used for each function/method. Default: 2 (i.e. L2: == ... ==).
  • |identifier=: A Lua string pattern. Only the comments of functions whose names match this pattern are used. When not given, all function are accepted.
    This is useful when giving object methods, using a pattern such as ^object_name:.

Usage notes

  • All local functions are ignored, because they are invisible outside the module.
  • The one exception to this is when object methods are defined inside a local function, as the object may be returned by the module.
  • Curly brackets in comments are used as a shortcut for syntaxhighlight, as using syntaxhighlight doesn't work otherwise.

--[===[
This module automatically generates documentation for other modules. It fetches in-line comments in Lua code and converts them into a form that can be used on a documentation page via {{tl|module documentation}}. In fact, this module's documentation is an example of it in action!

It's helpful to do documentation this way, as it means function/method documentation is available in two places: at the top of the module page (as conventional Wikitext), and above the function itself (as a Lua comment). Each suits a different kind of editing style, and doing it this way keeps them synchronised.

A section of documentation is given using Lua's multi-line comment syntax, which looks something like this: {--[==[ ... ]==]}.
]===]

local export = {}

local function format_doc(str)
	return (str
    	:gsub("([^\n])\n[ \t]*([^ \t\n#*:;])", "%1 %2") -- join continuation lines
    	:gsub("([^\n])\n[ \t]*([^ \t\n#*:;])", "%1 %2") -- do it twice in case of a single-character line (admittedly rare)
		:gsub('\n[ \t]+%f[*#:;]', '\n') -- remove indentation for list items
		:gsub('%f[\n,{]\n%f[^\n*#:;]', '\n\n') -- wiki newlines
		:gsub('(\n[ *#:]*)(|?[_%w]+=?):', '%1<code><b>%2</b></code>:') -- parameter names
		:gsub('``([A-Za-z0-9_%-]+)``', '<var>%1</var>') -- placeholder variable names between double backquotes
		:gsub('`([A-Za-z0-9_%-]+)`', '<code class="n">%1</code>') -- variable/parameter names between backquotes
		:gsub('%b{}', function(m0) -- {} blocks
			if m0:sub(2, 2) == '{' and m0:sub(-2, -2) == '}' then return nil end
			return '<syntaxhighlight lang=lua' .. (m0:match'\n' and '' or ' inline') .. '>' .. m0:sub(2, -2) .. '</syntaxhighlight>'
		end))
end

--[===[
The main entrypoint for {{tl|module documentation}}. The frame object can contain 3 optional arguments:
* |comment_level=: The number of equals signs (=) a given section uses. Default: 2 (i.e. {--[==[ ... (comment block) ]==]})
*: e.g. The value 4 means {--[====[ ... (comment block) ]====]}.
* |section_level=: The header level used for each function/method. Default: 2 (i.e. L2: {== ... ==}).
* |identifier=: A Lua string pattern. Only the comments of functions whose names match this pattern are used. When not given, all function are accepted.
*: This is useful when giving object methods, using a pattern such as {^object_name:}.
]===]
function export.show(frame)
	local args = frame:getParent().args or {}
	
	local comment_level = tonumber(args['comment_level']) or 2
	local pattern_comment = '%-%-%[' .. ('='):rep(comment_level) .. '%[\n?(.-)\n?]' .. ('='):rep(comment_level) .. ']()'
	local section_mark = ('='):rep(tonumber(args['section_level']) or 2)
	local pattern_identifier = args['identifier'] or ''
	
	local mod_title = mw.title.getCurrentTitle()
	if mod_title.text:match'/documentation$' then return '(<i>The generated documentation is located at the module page.</i>)' end
	local mod_text = mod_title:getContent()
	if not mod_text then return '(<i>The module page does not exist now.</i>)' end
	
	local chunk
	
	local leading_section_raw = mod_text:match('^' .. pattern_comment)
	if leading_section_raw then
		chunk = { format_doc(leading_section_raw) }
	else chunk = {} end
	
	for p0, f, fn in mod_text:gmatch('()\n[ \t]*function +(([^\n(]+)[^\n)]+%))') do
		if fn:match(pattern_identifier) then			
			local c = mod_text:sub(1, p0 - 1):match('^.*' .. pattern_comment .. '%s*$')
			table.insert(chunk, section_mark .. fn .. section_mark .. '\n\n' .. '<syntaxhighlight lang=lua inline>function ' .. f .. '</syntaxhighlight>\n\n' .. format_doc(c or '<strong class="error">This function lacks documentation. Please add a description of its usages, inputs and outputs, or its difference from similar functions, or make it local to remove it from the function list.</strong>[[Category:Templates and modules needing documentation]]'))
		end
	end
	
	local postscriptum = mod_text:match('^.*' .. pattern_comment .. '%s*$')
	if postscriptum then
		table.insert(chunk, section_mark .. 'Usage notes' .. section_mark .. '\n\n' .. format_doc(postscriptum))
	end
	
	return frame:preprocess(table.concat(chunk, '\n\n'))
end

return export
--[===[
* All local functions are ignored, because they are invisible outside the module.
* The one exception to this is when object methods are defined inside a local function, as the object may be returned by the module.
* Curly brackets in comments are used as a shortcut for syntaxhighlight, as using syntaxhighlight doesn't work otherwise.
]===]