Jump to content

Module:Find sources/autodoc: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
use strong tags rather than span tags for wikitext errors
do more thorough type checking in the documentation function
Line 67: Line 67:
end
end
local linkCfg = maybeLoadData(LINK_ROOT .. code)
local linkCfg = maybeLoadData(LINK_ROOT .. code)
return isValidLinkCfg(linkCfg)
end

local function isValidLinkCfg(linkCfg)
if type(linkCfg) ~= 'table' then
if type(linkCfg) ~= 'table' then
return false
return false
Line 88: Line 92:
end
end
local templateCfg = maybeLoadData(TEMPLATE_ROOT .. template)
local templateCfg = maybeLoadData(TEMPLATE_ROOT .. template)
return isValidTemplateCfg(templateCfg)
end

local function isValidTemplateCfg(templateCfg)
if type(templateCfg) ~= 'table' then
if type(templateCfg) ~= 'table' then
return false
return false
Line 300: Line 308:
local function documentation(template)
local function documentation(template)
-- This function makes documentation for the template specified in
-- This function makes documentation for the template specified in
-- frame.args[1]. The template name should be without the "Template:"
-- the template parameter. The template should be without the "Template:"
-- prefix.
-- prefix.


Line 310: Line 318:
-- Load the config files
-- Load the config files
local templateCfg = maybeLoadData(TEMPLATE_ROOT .. template)
local templateCfg = maybeLoadData(TEMPLATE_ROOT .. template)
if not templateCfg then
if not isValidTemplateCfg(templateCfg) then
error(string.format(
error(string.format(
"invalid template name '%s'; no template config found at [[%s]]",
"invalid template name '%s'; no template config found at [[%s]]",
Line 317: Line 325:
))
))
end
end
local autodocCfg = maybeLoadData(TEMPLATE_ROOT .. template .. AUTODOC_SUFFIX) or {}
local autodocCfg = maybeLoadData(TEMPLATE_ROOT .. template .. AUTODOC_SUFFIX)
if not isValidTemplateAutdocCfg(autodocCfg) then
autodocCfg = {}
end


-- Get the documentation content
-- Get the documentation content
Line 337: Line 348:
end
end
for i, code in ipairs(codes) do
for i, code in ipairs(codes) do
local linkAutodocCfg = maybeLoadData(LINK_ROOT .. code .. AUTODOC_SUFFIX) or {}
local linkAutodocCfg = maybeLoadData(LINK_ROOT .. code .. AUTODOC_SUFFIX)
local description = linkAutodocCfg.description
if not isValidLinkAutdocCfg(linkAutodocCfg) then
linkAutodocCfg = {}
codes[i] = type(description) == 'string' and description or code
end
codes[i] = linkAutodocCfg.description or code
end
end
local linkDescriptions = mList.bulleted(codes)
local linkDescriptions = mList.bulleted(codes)

Revision as of 15:26, 29 September 2014

-- Define constants
local ROOT_PAGE = 'Module:Find sources'
local TEMPLATE_ROOT = ROOT_PAGE .. '/templates/' -- for template config modules
local LINK_ROOT = ROOT_PAGE .. '/links/' -- for link config modules
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 escapePattern(s)
	return s:gsub('%p', '%%%0')
end

local function getPrefixPagenames(prefix)
	local specialText = string.format('{{Special:PrefixIndex/%s}}', prefix)
	specialText = mw.getCurrentFrame():preprocess(specialText)
	specialText = mw.text.unstrip(specialText)
	local pagenames = {}
	for s in string.gmatch(specialText, '<a href="[^"]*" title="([^"]*)"[^>]*>[^<]*</a>') do
		pagenames[#pagenames + 1] = mw.text.decode(s)
	end
	return pagenames
end

local function getSubpages(pagenames, prefix)
	local stripped = {}
	for i, page in ipairs(pagenames) do
		local pattern = '^' .. escapePattern(prefix) -- Turn the prefix into a Lua pattern
		stripped[i] = mw.ustring.gsub(page, pattern, '')
	end
	return stripped
end

local function getPrefixSubpages(prefix)
	return getSubpages(getPrefixPagenames(prefix), prefix)
end

local function getFilteredPrefixSubpages(prefix)
	-- Filter out unwanted pages from the subpage list. For now, this means
	-- the autodoc pages.
	local subpages = getPrefixSubpages(prefix)
	local pattern = escapePattern(AUTODOC_SUFFIX) .. '$'
	local filtered = {}
	for _, subpage in ipairs(subpages) do
		if not mw.ustring.find(subpage, pattern) then
			filtered[#filtered + 1] = subpage
		end
	end
	return filtered
end	

local function isValidLink(code)
	if type(code) ~= 'string' or code == '' then
		return false
	end
	local linkCfg = maybeLoadData(LINK_ROOT .. code)
	return isValidLinkCfg(linkCfg)
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 isValidTemplate(template)
	if type(template) ~= 'string' or template == '' then
		return false
	end
	local templateCfg = maybeLoadData(TEMPLATE_ROOT .. template)
	return isValidTemplateCfg(templateCfg)
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[s] and templateCfg[s] ~= 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 isValidLinkAutdocCfg(t)
	if type(t) ~= 'table' then
		return false
	end
	for _, s in ipairs{'description', 'notes'} do
		if t[s] and type(t[s]) ~= 'string' then
			return false
		end
	end
	return true
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] = '{| class="wikitable"'

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

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

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

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

local function grey(s)
	return string.format('<span style="color: gray;">%s</span>', s)
end

local function bold(s)
	return string.format("'''%s'''", s)
end

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

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

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

function p.linkTable()
	local codes = getFilteredPrefixSubpages(LINK_ROOT)
	local headers = {
		cfg['link-table-code-header'],
		cfg['link-table-description-header'],
		cfg['link-table-example-header'],
		cfg['link-table-config-header'],
		cfg['link-table-notes-header']
	}
	local rows = {}
	for i, code in ipairs(codes) do
		if isValidLink(code) then
			local configPage = LINK_ROOT .. code
			local autodocConfigPage = configPage .. AUTODOC_SUFFIX
			local linkData = maybeLoadData(autodocConfigPage)
			if not isValidLinkAutdocCfg(linkData) then
				linkData = {}
			end
			local row = {
				bold(code),
				linkData.description or grey("''No description available''"),
				mFindSources._renderLink(code, {cfg['example-search-term']}),
				table.concat({
					makeWikilink(configPage, cfg['link-table-main-config-link-display']),
					makeWikilink(autodocConfigPage, cfg['link-table-autodoc-config-link-display'])
				}, cfg['table-config-separator']),
				linkData.notes or ''
			}
			rows[i] = row
		else
			local msg = substituteParams(
				cfg['invalid-link-config-error'],
				LINK_ROOT .. code
			)
			msg = makeWikitextError(msg)
			msg = colspan(msg, 5)
			rows[i] = {msg}
		end
	end
	return makeWikitable(headers, rows)
end

function p.templateTable()
	local templates = getFilteredPrefixSubpages(TEMPLATE_ROOT)
	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
			local row = {
				bold(makeWikilink(mw.site.namespaces[10].name .. ':' .. template, template)),
				templateData.description or grey("''No description available''"),
				mFindSources._main(template, {cfg['example-search-term']}),
				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 frame = mw.getCurrentFrame()

	-- Load the config files
	local templateCfg = maybeLoadData(TEMPLATE_ROOT .. template)
	if not isValidTemplateCfg(templateCfg) then
		error(string.format(
			"invalid template name '%s'; no template config found at [[%s]]",
			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 = frame: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
		for i, code in ipairs(codes) do
			local linkAutodocCfg = maybeLoadData(LINK_ROOT .. code .. AUTODOC_SUFFIX)
			if not isValidLinkAutdocCfg(linkAutodocCfg) then
				linkAutodocCfg = {}
			end
			codes[i] = linkAutodocCfg.description or code
		end
		local linkDescriptions = mList.bulleted(codes)

		-- Build the content.
		content = frame: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