Module:Dochead: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
split main function into three
Ganaram inukshuk (talk | contribs)
No edit summary
Line 1: Line 1:
-- This module follows [[User:Ganaram inukshuk/Provisional style guide for Lua]]
-- This module follows [[User:Ganaram inukshuk/Provisional style guide for Lua]]
local getArgs = require("Module:Arguments").getArgs
local getArgs = require("Module:Arguments").getArgs
local iutils  = require("Module:Introspection utils")


local p = {}
local p = {}
Line 12: Line 13:
--  - whether a template has an accompanying module (overridable)
--  - whether a template has an accompanying module (overridable)
--  - what functions from which modules are invoked
--  - what functions from which modules are invoked
-- Options for specific types of templates and modules include:
-- - (TEMPLATES) whether it's a metatemplate
-- - (MODULES) whether it's a metamodule (used the same way as a metatemplate,
--  but direct use of module code is allowed)
-- Helper function
-- Detect whether a page exists, where fullpagename is "Namespace:Title"
local function page_exists(fullpagename)
local title = mw.title.new(fullpagename)
if title and title.exists then
return true
else
return false
end
end


-- Helper function
-- Helper function
Line 72: Line 56:
local calls = {}
local calls = {}
for _, call in ipairs(invokes) do
for _, call in ipairs(invokes) do
table.insert(calls, string.format("'''%s''' from [[Module:%s]]", call.func, call.module))
local module_link = iutils.make_module_link(call.module)
table.insert(calls, string.format("'''%s''' from %s", call.func, module_link))
end
end
return string.format(":''%s %s''\n", hatnote, table.concat(calls, ", "))
return string.format(":''%s %s''\n", hatnote, table.concat(calls, ", "))
Line 79: Line 64:


-- Helper function to handle modules
-- Helper function to handle modules
function p.make_module_hatnote(header, corr_template, has_template)
function p.make_module_hatnote(header, pagename, corr_template)
-- Check whether corresponding template exists
-- Then check whether it invokes the module
local has_template = iutils.page_exists(corr_template)
if has_template then
local wikitext = iutils.get_and_preprocess_content("Template", pagename)
has_template = has_template and iutils.invocation_exists(wikitext, pagename)
end
local result = ""
if header == "dualuse" then
if header == "dualuse" then
if has_template then
if has_template then
return string.format(
result = string.format(
"This module may be invoked by templates using its corresponding [[%s|template]], or used directly from other modules.",
"This module may be invoked by templates using its corresponding [[%s|template]], or used directly from other modules.",
corr_template
corr_template
)
)
else
else
return string.format(
result = string.format(
"This module has a template that is currently missing. [[Special:EditPage/%s]]",
"This module has a template that is currently missing or does not use this module. ([[Special:EditPage/%s | edit template]])",
corr_template
corr_template
)
)
Line 95: Line 89:
elseif header == "metatemplate" then
elseif header == "metatemplate" then
if has_template then
if has_template then
return string.format(
result = string.format(
"This module implements a metatemplate, and may be invoked by templates using its corresponding [[%s|template]], or used directly from other modules.",
"This module implements a metatemplate, and may be invoked by templates using its corresponding [[%s|template]], or used directly from other modules.",
corr_template
corr_template
)
)
else
else
return "This module is used to create other Lua-based templates and has no corresponding template."
result = "This module is used to create other Lua-based templates and has no corresponding template."
end
end


elseif header == "noinvoke" then
elseif header == "noinvoke" then
if has_template then
if has_template then
return string.format(
result = string.format(
"This module should not be invoked directly; use its corresponding [[%s|template]] instead.",
"This module should not be invoked directly; use its corresponding [[%s|template]] instead.",
corr_template
corr_template
)
)
else
else
return string.format(
result = string.format(
"This module implements a template that is currently missing. [[Special:EditPage/%s]]",
"This module implements a template that is currently missing or does not use this module. ([[Special:EditPage/%s | edit template]])",
corr_template
corr_template
)
)
Line 117: Line 111:


elseif header == "library" or header == "metamodule" then
elseif header == "library" or header == "metamodule" then
return "This module primarily serves as a library for other modules and has no corresponding template."
result = "This module primarily serves as a library for other modules and has no corresponding template."


else
else
if has_template and header ~= "" then
if has_template and header ~= "" then
return string.format("%s. This module implements [[%s]].", header, corr_template)
result = string.format("%s. This module implements [[%s]].", header, corr_template)
elseif has_template and header == "" then
elseif has_template and header == "" then
return string.format("This module implements [[%s]].", corr_template)
result = string.format("This module implements [[%s]].", corr_template)
else
else
return header
result = header
end
end
end
end
return string.format(":''%s''\n", result)
end
end




-- Helper function to handle templates
-- Helper function to handle templates
function p.make_template_hatnote(header, corr_module, has_module)
function p.make_template_hatnote(header, pagename, corr_module)
-- Check whether corresponding module exists
-- Then check whether this template invokes the module
local has_module = iutils.page_exists(corr_module)
local wikitext = iutils.get_and_preprocess_content("Template", pagename)
local is_module_invoked = iutils.invocation_exists(wikitext, pagename)
 
-- Find all invocations for the template
local invokes = iutils.find_invokes(wikitext)
local invocation_hatnote = make_invocation_hatnote(invokes)
local result = ""
if header == "dualuse" then
if header == "dualuse" then
if has_module then
if has_module and is_module_invoked then
return string.format(
result = string.format(
"This template is implemented by the Lua module [[%s]]. See its module page for Lua-based template implementation.",
"This template is implemented by the Lua module [[%s]]. See its module page for Lua-based template implementation.",
corr_module
corr_module
)
elseif has_module and not is_module_invoked then
result = string.format(
"This template is implemented by the Lua module [[%s]] but does not use it. ([[Special:EditPage/%s | edit template]])",
corr_module,
"Template:" .. pagename
)
)
else
else
return string.format(
result = string.format(
"This template is implemented by a module that is currently missing. [[Special:EditPage/%s]]",
"This template is implemented by a module that is currently missing. [[Special:EditPage/%s]]",
corr_module
corr_module
Line 147: Line 160:


elseif header == "metatemplate" then
elseif header == "metatemplate" then
if has_module then
if has_module and is_module_invoked then
return string.format(
result = string.format(
"This template is a metatemplate. It is used to build other templates and should not be used standalone, except for testing or simple usage. This template is implemented by the Lua module [[%s]].",
"This template is a metatemplate. It is used to build other templates and should not be used standalone, except for testing or simple usage. This template is implemented by the Lua module [[%s]].",
corr_module
corr_module
)
elseif has_module and not is_module_invoked then
result = string.format(
"This metatemplate is implemented by the Lua module [[%s]] but does not use it. ([[Special:EditPage/%s | edit template]])",
corr_module,
"Template:" .. pagename
)
)
else
else
return "This template is a metatemplate. It is used to build other templates and should not be used standalone, except for testing or simple usage."
result = "This template is a metatemplate. It is used to build other templates and should not be used standalone, except for testing or simple usage."
end
end


elseif header == "noinvoke" or header == "library" or header == "metamodule" then
elseif header == "noinvoke" or header == "library" or header == "metamodule" then
return "This template has a header option in the wrong namespace. It should be used within the Module namespace."
result = "This template has a header option in the wrong namespace. It should be used within the Module namespace."


else
else
if has_module and header ~= "" then
if has_module and header ~= "" and is_module_invoked then
return string.format("%s. This template is implemented by the Lua module [[%s]].", header, corr_module)
result = string.format(
elseif has_module and header == "" then
"%s. This template is implemented by the Lua module [[%s]].",
return string.format("This template is implemented by the Lua module [[%s]].", corr_module)
header,
corr_module
)
elseif has_module and header ~= "" and not is_module_invoked then
result = string.format(
"%s. This template is implemented by the Lua module [[%s]] but does not use it. ([[Special:EditPage/%s | edit template]])",
header,
corr_module,
"Template:" .. pagename
)
elseif has_module and header == "" and is_module_invoked then
result = string.format(
"This template is implemented by the Lua module [[%s]].",
corr_module
)
elseif has_module and header == "" and not is_module_invoked then
result = string.format(
"This template is implemented by the Lua module [[%s]] but does not use it. ([[Special:EditPage/%s | edit template]])",
corr_module,
"Template:" .. pagename
)
else
else
return header
result = header
end
end
end
end
return string.format(":''%s''\n", result) .. invocation_hatnote
end
end


Line 197: Line 238:


-- Call appropriate hatnote-making function
-- Call appropriate hatnote-making function
local has_template = page_exists(corr_template)
local has_module  = page_exists(corr_module)
local result = ""
local result = ""
if namespace == "Module" then
if namespace == "Module" then
result = p.make_module_hatnote(header, corr_template, has_template)
result = p.make_module_hatnote(header, pagename, corr_template)
elseif namespace == "Template" then
elseif namespace == "Template" then
result = p.make_template_hatnote(header, corr_module, has_module)
result = p.make_template_hatnote(header, pagename, corr_module)
else
else
result = "This template is in the wrong namespace. It should be within the Template or Module namespaces."
result = ":''This template is in the wrong namespace. It should be within the Template or Module namespaces.''\n"
end
end


Line 213: Line 252:
return categorize(namespace, pagename, has_link)
return categorize(namespace, pagename, has_link)
else
else
return string.format(":''%s'' %s\n", result, categorize(namespace, pagename, has_link))
return string.format("%s%s", result, categorize(namespace, pagename, has_link))
end
end
end
end
Line 239: Line 278:
args["mod" ] = args["mod" ] or ("Module:"  .. args["pagename"])
args["mod" ] = args["mod" ] or ("Module:"  .. args["pagename"])


return p._dochead(args)
end
function p.tester()
local args = {}
args["namespace"] = "Template"
args["pagename" ] = "Module introspection"
args["temp"] = "Template:" .. args["pagename"]
args["mod" ] = "Module:"  .. args["pagename"]
args["header"] = ""
return p._dochead(args)
return p._dochead(args)
end
end


return p
return p

Revision as of 05:09, 30 October 2025

Module documentation[view] [edit] [history] [purge]
This module implements a template that is currently missing or does not use this module. ( edit template)
Module:Dochead is ready for use. This message indicates that a module is ready for use, or has recently been repaired. This message may be removed once this module has been used on several pages or once it is verified to work as intended.

Details: Functionally complete. Edge-case observations still ongoing. Links can now be viewed on /doc pages.

Introspection summary for Module:Dochead 
Functions provided (5)
Line Function Params
66 make_module_hatnote (header, pagename, corr_template)
130 make_template_hatnote (header, pagename, corr_module)
215 _dochead (main) (args)
259 dochead (invokable) (frame)
283 tester none
Lua modules required (2)
Variable Module Functions used
getArgs Module:Arguments getArgs
iutils Module:Introspection utils make_module_link
page_exists
get_and_preprocess_content
invocation_exists
find_invokes

No function descriptions were provided. The Lua code may have further information.


-- This module follows [[User:Ganaram inukshuk/Provisional style guide for Lua]]
local getArgs = require("Module:Arguments").getArgs
local iutils  = require("Module:Introspection utils")

local p = {}

-- Produces a hatnote that is placed on the top of a documentation page (either
-- template or module documentation) and can autodetect and categorize:
-- - FOR MODULES:
--   - whether a module has an accompanying template (overridable)
--   - whether a module is meant as a library for other modules
-- - FOR TEMPLATES:
--   - whether a template has an accompanying module (overridable)
--   - what functions from which modules are invoked

-- Helper function
-- Categorizes pages if they're in the right namespace, and based on the nature
-- of the page
local function categorize(namespace, pagename, is_lua_based_template)
	local is_lua_based_template = is_lua_based_template or false
	local cats = ""

	if pagename:sub(-4) == "/doc" then
		-- Documentation subpages
		if namespace == "Template" then
			cats = "[[Category:Template documentation]]"
		elseif namespace == "Module" then
			cats = "[[Category:Module documentation]]"
		end
	else
		-- Main pages
		if namespace == "Template" then
			cats = "[[Category:Templates]]"
			if is_lua_based_template then
				cats = cats .. " [[Category:Lua-based templates]]"
			end
		elseif namespace == "Module" then
			cats = "[[Category:Lua modules]]"
		else
			--cats = "[[Category:Templates in the incorrect namespace]]"
		end
	end

	return cats
end

-- Helper function
-- Creates an additional hatnote denoting which functions are invoked from which
-- modules, if a has more than one invoke.
local function make_invocation_hatnote(invokes)
	local hatnote = ""
	if #invokes == 0 then
		return ""
	else
		hatnote = "This template invokes the following functions:"
		local calls = {}
		for _, call in ipairs(invokes) do
			local module_link = iutils.make_module_link(call.module)
			table.insert(calls, string.format("'''%s''' from %s", call.func, module_link))
		end
		return string.format(":''%s %s''\n", hatnote, table.concat(calls, ", "))
	end
end

-- Helper function to handle modules
function p.make_module_hatnote(header, pagename, corr_template)
	-- Check whether corresponding template exists
	-- Then check whether it invokes the module
	local has_template = iutils.page_exists(corr_template)
	if has_template then
		local wikitext = iutils.get_and_preprocess_content("Template", pagename)
		has_template = has_template and iutils.invocation_exists(wikitext, pagename)
	end
	
	local result = ""
	if header == "dualuse" then
		if has_template then
			result = string.format(
				"This module may be invoked by templates using its corresponding [[%s|template]], or used directly from other modules.",
				corr_template
			)
		else
			result = string.format(
				"This module has a template that is currently missing or does not use this module. ([[Special:EditPage/%s | edit template]])",
				corr_template
			)
		end

	elseif header == "metatemplate" then
		if has_template then
			result = string.format(
				"This module implements a metatemplate, and may be invoked by templates using its corresponding [[%s|template]], or used directly from other modules.",
				corr_template
			)
		else
			result = "This module is used to create other Lua-based templates and has no corresponding template."
		end

	elseif header == "noinvoke" then
		if has_template then
			result = string.format(
				"This module should not be invoked directly; use its corresponding [[%s|template]] instead.",
				corr_template
			)
		else
			result = string.format(
				"This module implements a template that is currently missing or does not use this module. ([[Special:EditPage/%s | edit template]])",
				corr_template
			)
		end

	elseif header == "library" or header == "metamodule" then
		result = "This module primarily serves as a library for other modules and has no corresponding template."

	else
		if has_template and header ~= "" then
			result = string.format("%s. This module implements [[%s]].", header, corr_template)
		elseif has_template and header == "" then
			result = string.format("This module implements [[%s]].", corr_template)
		else
			result = header
		end
	end
	
	return string.format(":''%s''\n", result)
end


-- Helper function to handle templates
function p.make_template_hatnote(header, pagename, corr_module)
	-- Check whether corresponding module exists
	-- Then check whether this template invokes the module
	local has_module = iutils.page_exists(corr_module)
	local wikitext = iutils.get_and_preprocess_content("Template", pagename)
	local is_module_invoked = iutils.invocation_exists(wikitext, pagename)

	-- Find all invocations for the template
	local invokes = iutils.find_invokes(wikitext)
	local invocation_hatnote = make_invocation_hatnote(invokes)
	
	local result = ""
	if header == "dualuse" then
		if has_module and is_module_invoked then
			result = string.format(
				"This template is implemented by the Lua module [[%s]]. See its module page for Lua-based template implementation.",
				corr_module
			)
		elseif has_module and not is_module_invoked then
			result = string.format(
				"This template is implemented by the Lua module [[%s]] but does not use it. ([[Special:EditPage/%s | edit template]])",
				corr_module,
				"Template:" .. pagename
			)
		else
			result = string.format(
				"This template is implemented by a module that is currently missing. [[Special:EditPage/%s]]",
				corr_module
			)
		end

	elseif header == "metatemplate" then
		if has_module and is_module_invoked then
			result = string.format(
				"This template is a metatemplate. It is used to build other templates and should not be used standalone, except for testing or simple usage. This template is implemented by the Lua module [[%s]].",
				corr_module
			)
		elseif has_module and not is_module_invoked then
			result = string.format(
				"This metatemplate is implemented by the Lua module [[%s]] but does not use it. ([[Special:EditPage/%s | edit template]])",
				corr_module,
				"Template:" .. pagename
			)
		else
			result = "This template is a metatemplate. It is used to build other templates and should not be used standalone, except for testing or simple usage."
		end

	elseif header == "noinvoke" or header == "library" or header == "metamodule" then
		result = "This template has a header option in the wrong namespace. It should be used within the Module namespace."

	else
		if has_module and header ~= "" and is_module_invoked then
			result = string.format(
				"%s. This template is implemented by the Lua module [[%s]].",
				header,
				corr_module
			)
		elseif has_module and header ~= "" and not is_module_invoked then
			result = string.format(
				"%s. This template is implemented by the Lua module [[%s]] but does not use it. ([[Special:EditPage/%s | edit template]])",
				header,
				corr_module,
				"Template:" .. pagename
			)
		elseif has_module and header == "" and is_module_invoked then
			result = string.format(
				"This template is implemented by the Lua module [[%s]].",
				corr_module
			)
		elseif has_module and header == "" and not is_module_invoked then
			result = string.format(
				"This template is implemented by the Lua module [[%s]] but does not use it. ([[Special:EditPage/%s | edit template]])",
				corr_module,
				"Template:" .. pagename
			)
		else
			result = header
		end
	end
	
	return string.format(":''%s''\n", result) .. invocation_hatnote
end


-- Main function
function p._dochead(args)
	local namespace = args["namespace"]
	local pagename  = args["pagename"]
	local header    = args["header"] or "none"
	local corr_template = args["temp"] or ("Template:" .. pagename)
	local corr_module   = args["mod"]  or ("Module:"   .. pagename)

	-- Normalize namespace prefixes
	if not corr_template:match("^Template:") then
		corr_template = "Template:" .. corr_template
	end
	if not corr_module:match("^Module:") then
		corr_module = "Module:" .. corr_module
	end

	-- If header is none, skip everything
	if header == "none" then
		return categorize(namespace, pagename)
	end

	-- Remove /doc suffix, if this is on a /doc page
	corr_template = corr_template:gsub("/doc$", "")
	corr_module   = corr_module:gsub("/doc$", "")

	-- Call appropriate hatnote-making function
	local result = ""
	if namespace == "Module" then
		result = p.make_module_hatnote(header, pagename, corr_template)
	elseif namespace == "Template" then
		result = p.make_template_hatnote(header, pagename, corr_module)
	else
		result = ":''This template is in the wrong namespace. It should be within the Template or Module namespaces.''\n"
	end

	-- Categorization and output
	local has_link = namespace == "Template" and has_module
	if result == "" then
		return categorize(namespace, pagename, has_link)
	else
		return string.format("%s%s", result, categorize(namespace, pagename, has_link))
	end
end

-- Wrapper to be invoked
function p.dochead(frame)
	local args = getArgs(frame, { removeBlanks = true }) or {}
	local title = mw.title.getCurrentTitle()

	-- Extract or parse args
	args["namespace"] = args["namespace"] or title.nsText
	args["pagename" ] = args["pagename" ] or title.text

	-- Extract header or set it to defaults
	if args["header"] == nil or args["header"] == "" then
		if args["namespace"] == "Module" then
			args["header"] = "noinvoke"
		else
			args["header"] = ""
		end
	end
	
	-- Extract template/module names, or autogenerate them
	args["temp"] = args["temp"] or ("Template:" .. args["pagename"])
	args["mod" ] = args["mod" ] or ("Module:"   .. args["pagename"])

	return p._dochead(args)
end

function p.tester()
	local args = {}
	
	args["namespace"] = "Template"
	args["pagename" ] = "Module introspection"
	
	args["temp"] = "Template:" .. args["pagename"]
	args["mod" ] = "Module:"   .. args["pagename"]
	
	args["header"] = ""
	
	return p._dochead(args)
end

return p