# Module:Find sources/autodoc

-- Define constants  
local ROOT_PAGE = 'Module:Find sources'  
local TEMPLATE_LIST = ROOT_PAGE .. '/templates' -- template config module list  
local TEMPLATE_ROOT = TEMPLATE_LIST .. '/' -- template config module prefix  
local LINK_CONFIG = ROOT_PAGE .. '/links' -- link config module list  
local AUTODOC_SUFFIX = '/autodoc'  

-- Load necessary modules.  
local mFindSources = require('Module:Find sources')  
local cfg = mw.loadData('Module:Find sources/autodoc/config')  

local p = {}  

local function maybeLoadData(page)  
local success, data = pcall(mw.loadData, page)  
return success and data  
end  

local function substituteParams(msg, ...)  
return mw.message.newRawMessage(msg, ...):plain  
end  

local function sortKeys(t)  
local ret = {}  
for k in pairs(t) do  
ret[#ret + 1] = k  
end  
table.sort(ret)  
return ret  
end  

local function isValidLinkCfg(linkCfg)  
if type(linkCfg) ~= 'table' then  
return false  
end  
for _, s in ipairs{'url', 'display'} do  
if type(linkCfg[s]) ~= 'string' then  
return false  
end  
end  
for _, s in ipairs{'separator'} do  
if linkCfg[s] ~= nil and type(linkCfg[s]) ~= 'string' then  
return false  
end  
end  
return true  
end  

local function isValidLink(code)  
if type(code) ~= 'string' or code == '' then  
return false  
end  
local links = maybeLoadData(LINK_CONFIG)  
local linkCfg = links[code]  
return isValidLinkCfg(linkCfg)  
end  

local function isValidTemplateCfg(templateCfg)  
if type(templateCfg) ~= 'table' then  
return false  
end  
for _, s in ipairs{'blurb'} do  
if type(templateCfg[s]) ~= 'string' then  
return false  
end  
end  
for _, s in ipairs{'separator', 'class', 'style'} do  
if templateCfg[s] ~= nil and type(templateCfg[s]) ~= 'string' then  
return false  
end  
end  
if templateCfg.isUsedInMainspace and templateCfg.isUsedInMainspace ~= true then  
return false  
end  
if type(templateCfg.links) ~= 'table' then  
return false  
end  

local function isValidLinkTable(t)  
if type(t) ~= 'table' then  
return false  
end  
if type(t.code) ~= 'string' then  
return false  
end  
if t.display and type(t.display) ~= 'string' then  
return false  
end  
return true  
end  

if templateCfg.introLink and not isValidLinkTable(templateCfg.introLink) then  
return false  
end  
for _, t in ipairs(templateCfg.links) do  
if not isValidLinkTable(t) then  
return false  
end  
end  

return true  
end  

local function isValidTemplate(template)  
if type(template) ~= 'string' or template == '' then  
return false  
end  
local templateCfg = maybeLoadData(TEMPLATE_ROOT .. template)  
return isValidTemplateCfg(templateCfg)  
end  

local function isValidTemplateAutdocCfg(t)  
if type(t) ~= 'table' then  
return false  
end  
for _, s in ipairs{'description', 'docIntro'} do  
if t[s] and type(t[s]) ~= 'string' then  
return false  
end  
end  
if t.shortcuts and type(t.shortcuts) ~= 'table' then  
return false  
elseif t.shortcuts then  
for _, s in ipairs(t.shortcuts) do  
if type(s) ~= 'string' then  
return false  
end  
end  
end  
return true  
end  

local function makeWikitable(headers, rows)  
local ret = {}  

-- Table start  
ret[#ret + 1] = '{|'  

-- Headers  
ret[#ret + 1] = '|-'  
for i, header in ipairs(headers) do  
ret[#ret + 1] = '! scope="col | ' .. header  
end  

-- Rows  
for i, row in ipairs(rows) do  
ret[#ret + 1] = '|-'  
for j, cell in ipairs(row) do  
if j == 1 then  
ret[#ret + 1] = '! scope="row" | ' .. cell  
else  
ret[#ret + 1] = '| ' .. cell  
end  
end  
end  

-- Table end  
ret[#ret + 1] = '|}'  

return table.concat(ret, '\n')  
end  

local function grey(s)  
return string.format('<span>%s</span>', s)  
end  

local function colspan(s, n)  
return string.format(' | %s', n, s)  
end  

local function makeWikitextError(msg)  
return string.format('<strong>%s</strong>', msg)  
end  

local function makeWikilink(page, display)  
if display then  
return string.format('%s', page, display)  
else  
return string.format('%s', page)  
end  
end  

function p.linkTable  
local codes = sortKeys(require(LINK_CONFIG))  
local headers = {  
cfg['link-table-code-header'],  
cfg['link-table-description-header'],  
cfg['link-table-example-header'],  
cfg['link-table-notes-header']  
}  
local rows = {}  
local links = maybeLoadData(LINK_CONFIG)  
for i, code in ipairs(codes) do  
if isValidLink(code) then  

local linkData = links[code]  

-- Make the example link.  
local success, link = pcall(  
mFindSources._renderLink,  
code,  
{cfg['example-search-term']}  
if not success then  
link = makeWikitextError(link)  
end  

-- Build the row.  
local row = {  
code,  
linkData.description or grey("*No description available*"),  
link,  
linkData.notes or '',  
}  
rows[i] = row  
else  
local msg = substituteParams(cfg['invalid-link-config-error'], code)  
msg = makeWikitextError(msg)  
msg = colspan(msg, 5)  
rows[i] = {msg}  
end  
end  
return makeWikitable(headers, rows)  
end  

function p.templateTable  
local templates = sortKeys(require(TEMPLATE_LIST))  
local headers = {  
cfg['template-table-template-header'],  
cfg['template-table-description-header'],  
cfg['template-table-example-header'],  
cfg['template-table-config-header'],  
}  
local rows = {}  
for i, template in ipairs(templates) do  
if isValidTemplate(template) then  
local configPage = TEMPLATE_ROOT .. template  
local autodocConfigPage = configPage .. AUTODOC_SUFFIX  
local templateData = maybeLoadData(autodocConfigPage)  
if not isValidTemplateAutdocCfg(templateData) then  
templateData = {}  
end  

-- Make the example text  
local success, example = pcall(  
mFindSources._main,  
template,  
{cfg['example-search-term']}  
if not success then  
example = makeWikitextError(example)  
end  

-- Build the row.  
local row = {  
makeWikilink(mw.site.namespaces[10].name .. ':' .. template, template),  
templateData.description or grey("*No description available*"),  
example,  
table.concat({  
makeWikilink(configPage, cfg['template-table-main-config-link-display']),  
makeWikilink(autodocConfigPage, cfg['template-table-autodoc-config-link-display'])  
}, cfg['table-config-separator'])  
}  
rows[i] = row  
else  
local msg = substituteParams(  
cfg['invalid-template-config-error'],  
TEMPLATE_ROOT .. template  
msg = makeWikitextError(msg)  
msg = colspan(msg, 4)  
rows[i] = {msg}  
end  
end  
return makeWikitable(headers, rows)  
end  

local function documentation(template)  
-- This function makes documentation for the template specified in  
-- the template parameter. The template should be without the "Template:"  
-- prefix.  

-- Load necessary modules  
local mDocumentation = require('Module:Documentation')  
local mList = require('Module:List')  
local = mw.getCurrentFrame  

-- Load the config files  
local templateCfg = maybeLoadData(TEMPLATE_ROOT .. template)  
if not isValidTemplateCfg(templateCfg) then  
error(substituteParams(  
cfg['invalid-template-name-error'],  
template,  
TEMPLATE_ROOT .. template  
))  
end  
local autodocCfg = maybeLoadData(TEMPLATE_ROOT .. template .. AUTODOC_SUFFIX)  
if not isValidTemplateAutdocCfg(autodocCfg) then  
autodocCfg = {}  
end  

-- Get the documentation content  
local content  
do  
-- Shortcuts  
local shortcuts  
if autodocCfg.shortcuts then  
shortcuts = :expandTemplate{title = 'Template shortcut', args = autodocCfg.shortcuts}  
end  

-- Link descriptions  
local codes = {}  
if templateCfg.introLink then  
codes[#codes + 1] = templateCfg.introLink.code  
end  
for _, t in ipairs(templateCfg.links) do  
codes[#codes + 1] = t.code  
end  

local links = maybeLoadData(LINK_CONFIG)  
for i, code in ipairs(codes) do  
if links[code] then  
codes[i] = links[code].description or code  
else  
codes[i] = code  
end  
end  
local linkDescriptions = mList.bulleted(codes)  

-- Build the content.  
content = :expandTemplate{title = 'Find sources documentation', args = {  
template = template,  
shortcuts = shortcuts,  
docIntro = autodocCfg.docIntro,  
isUsedInMainspace = templateCfg.isUsedInMainspace and 'yes' or nil,  
linkDescriptions = linkDescriptions  
}}  
end  

return mDocumentation.main{content = content, ['link box'] = cfg['end-box-blurb']}  
end  

setmetatable(p, { __index = function(t, template)  
return function  
return documentation(template)  
end  
end})  

return p