Module:Documentation

local p = {} local h = {} local s = require("Module:i18n").getString

local list = require("Module:List").unorderedList local utilsMarkup = require("Module:UtilsMarkup") local utilsString = require("Module:UtilsString") local utilsTable = require("Module:UtilsTable")

local frame = mw.getCurrentFrame function p.Examples(frame) local args = frame:getParent.args return p.template(args) end

function p.Module(frame) local docPage = mw.title.new(frame:getParent:getTitle) local modulePage = "Module:" .. docPage.baseText local module = require(modulePage) if not module.Documentation then error(s("noDocumentationProperty", modulePage)) end return p.module(module, module.Documentation, frame.args, 2) end

function p.module(module, doc, options, headerLevel) local output = '' if headerLevel == 2 then output = "\n" end for _, functionDoc in ipairs(doc) do output = output .. utilsMarkup.header(headerLevel)(functionDoc.name) .. "\n" if functionDoc.wip then output = output .. frame:expandTemplate({title = "UC"}) .. "\n" end output = output .. h.printFunctionSyntax(functionDoc.name, functionDoc.params) output = output .. h.printParamsDescription(functionDoc.params) output = output .. h.printReturnsDescription(functionDoc.returns) output = output .. h.printFunctionCases(module[functionDoc.name], functionDoc.name, functionDoc.cases, functionDoc.resultOnly) end if (doc.sections) then for _, section in ipairs(doc.sections) do output = output .. ("==%s=="):format(section.header) .. "\n" output = output .. p.module(module, section.doc or {}, options, headerLevel + 1) .. "\n" end end return output end

function p.template(templateExamples) if not templateExamples.vertical then local tableData = { hideEmptyColumns = true, rows = { { s("headers.input"), s("headers.output"), s("headers.categoriesAdded"), header = true } }		}		for _, example in ipairs(templateExamples) do			local input, output, categoryList = h.templateExample(example) input = utilsMarkup.format(input, { code = true }) table.insert(tableData.rows, {input, output, categoryList}) end return utilsMarkup.wikitable(tableData) end local result = "" for _, example in ipairs(templateExamples) do		local input, output, categoryList = h.templateExample(example) input = utilsMarkup.format(input, {pre = true}) local headerStyles = { ["width"] = "5rem" -- for alignment. See Template:Letter/Documentation for an example of why this is needed }		result = result .. utilsMarkup.wikitable({			hideEmptyRows = true,			rows = {				{					{ header = true, content= s("headers.input"), styles = headerStyles}, 					input,				},				{					{ header = true, content = s("headers.output"), styles = headerStyles}, 					output				},				{					{ header = true, content = s("headers.categoriesAdded"), styles = headerStyles },					categoryList				},			}		}) .. "\n" end return result end

function h.templateExample(example) local input = mw.text.unstripNoWiki(example) local output = frame:preprocess(input) input = mw.text.trim(input) input = mw.text.nowiki(input) local output, categories = utilsMarkup.stripCategories(output) local output = utilsMarkup.killBacklinks(output) local categoryList = list(categories) return input, output, categoryList end

function h.printFunctionSyntax(functionName, params) local paramSyntax = "" if params then local paramNames = utilsTable.map(h.printParamSyntax)(params) paramSyntax = table.concat(paramNames, ", ") end local output = (" "):format(functionName, paramSyntax) return output .. "\n" end

function h.printParamSyntax(param) local name = param.name if param.optional then name = "[" .. name .. "]"	end return name end

function h.printParamsDescription(params) if not params or #params == 0 then return "" end local output = ";" .. s("headers.parameters") .. "\n" for _, param in ipairs(params) do		local parameter = (""):format(param.name, param.description or "", param.optional and "Optional" or "Required") output = output .. mw.getCurrentFrame:preprocess(parameter) .. "\n" end return output .. "\n" end

function h.printReturnsDescription(returns) if not returns then return "" end local output = ";" .. s("headers.returns") .. "\n" output = output .. "* " .. mw.getCurrentFrame:preprocess(returns) return output .. "\n" end

function h.printFunctionCases(fn, fnName, fnCases, resultOnly) local output = ";" .. s("headers.examples") .. "\n" local inputColumn = s("headers.input") local outputColumn = s("headers.output") local resultColumn = s("headers.result") local statusColumn = utilsMarkup.explain(s("explainStatusColumn"))(s("headers.status")) local headerCells = resultOnly and {inputColumn, resultColumn, statusColumn} or {inputColumn, outputColumn, resultColumn, statusColumn} local tableData = { hideEmptyColumns = true, hideEmptyRows = true, rows = { {				header = true, cells = headerCells, }		},	}	for _, case in ipairs(fnCases) do		local caseRows = h.case(fn, fnName, case, resultOnly) tableData.rows = utilsTable.mergeArrays(tableData.rows, caseRows) end output = output .. utilsMarkup.wikitable(tableData) .. "\n" return output end

function h.case(fn, fnName, case, resultOnly) local rows = {} if case.description then table.insert(rows, {			{				header = true,				colspan = -1,				styles = {					["text-align"] = "left"				},				content = case.description,			}		}) end local argsText = utilsTable.map(utilsTable.print)(case.args) local argsList = table.concat(argsText, ", ") local functionText = ("%s(%s)"):format(fnName, argsList) local inputCell = utilsMarkup.lua(functionText) local output = fn and fn(unpack(case.args)) or "" local formattedOutput = h.formatValue(output) local outputCell = formattedOutput local resultCell, categories, status if type(output == "string") then resultCell = utilsMarkup.killBacklinks(output) resultCell, categories = utilsMarkup.stripCategories(output) end local expected = case.expected and string.gsub(case.expected, "\t", "") local passed = utilsTable.isEqual(expected)(output) local status = expected ~= nil and h.printStatus(passed) or nil if case.expected ~= nil and not passed then local expectedOutput = h.formatValue(expected) outputCell = utilsMarkup.wikitable({			styles = { width = "100%" },			rows = {				{ 					{ 						header = true, 						content = "Expected", 						styles = { width = "1em"}, -- "shrink-wraps" this column					}, 					{ content = expectedOutput },				},				{					{ header = true, content = "Actual" }, 					{ content = formattedOutput },				},			}		}) end

if resultOnly then table.insert(rows, {inputCell, resultCell}) else table.insert(rows, {inputCell, outputCell, resultCell, status}) end return rows end

function h.formatValue(val) if type(val == "string") then return utilsMarkup.pre(val) end return utilsMarkup.lua(val) end

function h.printStatus(success) local img = success and "" or "" local msg = success and s("explainStatusGood") or s("explainStatusBad") img = utilsMarkup.explain(msg)(img) local cat = success and "" or utilsMarkup.category(s("failingTestsCategory")) return img .. cat end

Strings = { en = { noDocumentationProperty = "No `Documentation` property exists in export table of %s", failingTestsCategory = "Category:Pages with failing tests", explainStatusColumn = "Indicates whether a feature is working as expected", explainStatusGood = "This feature is working as expected", explainStatusBad = "This feature is not working as expected", headers = { parameters = "Parameters", returns = "Returns", examples = "Examples", input = "Input", output = "Output", result = "Result", categoriesAdded = "Categories Added", status = "Status", }	} }

return p