Module:Dochead: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
undo changes to add wikipedia sidebox (not even rswiki does anything akin to "see documentation on wikipedia")
Tag: Manual revert
Ganaram inukshuk (talk | contribs)
for options that don't explicitly require a corresponding template/module, have the option to turn off corresponding-page detection (default is true)
Line 2: Line 2:
local getArgs = require("Module:Arguments").getArgs
local getArgs = require("Module:Arguments").getArgs
local iutils  = require("Module:Introspection utils")
local iutils  = require("Module:Introspection utils")
local yesno  = require("Module:Yesno")


local p = {}
local p = {}
Line 46: Line 47:


-- Helper function to handle modules
-- Helper function to handle modules
function p.make_module_hatnote(header, pagename, corr_template)
function p.make_module_hatnote(header, pagename, corr_template, detect_corr_page)
-- Check whether corresponding template exists
-- Check whether corresponding template exists
-- Then check whether that template invokes the module
-- Then check whether that template invokes the module
Line 56: Line 57:
end
end
-- Check whether to use detect_corr_page option
-- If the header option is dualuse, metatemplate, or noinvoke, this option
-- is ignored. For any other option, it's not.
if header ~= "dualuse" or header ~= "metatemplate" or header ~= "noinvoke" then
has_template = has_template and detect_corr_page
end
-- Heading meanings and usage on module
-- - dualuse means a template is implement by a module, but its template may
--  be bypassed by using its module directly from other modules.
-- - metatemplate is a special case of dualuse; the template-module pair is
--  for a metatemplate, a template used to build other templates.
-- - noinvoke indicates a lua-based template, but the module's code is so
--  specialized that its code should not be used by other modules or
--  invoked by other templates.
-- - metamodule and library indicate a module whose code is used mainly for
--  other modules. Such modules generally don't have a corresponding
--  template.
-- Does the module have a template?
-- - dualuse, metatemplate, and noinvoke: YES (REQUIRED!!)
-- - metamodule/library: GENERALLY NO
local result = ""
local result = ""
if header == "dualuse" then
if header == "dualuse" then
Line 132: Line 154:


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


-- Find all invocations for the template
-- Find all invocations for the template and add it as an addl hatnote.
local invocation_hatnote = make_invocation_hatnote(invokes)
local invocation_hatnote = make_invocation_hatnote(invokes)
-- Check whether to use detect_corr_page option
-- If the header option is dualuse or metatemplate, this option is ignored.
-- For any other option, it's not (although it also has no effect on
-- noinvoke or library/metamodule).
if header ~= "dualuse" or header ~= "metatemplate" then
has_module = has_module and detect_corr_page
end
-- Heading meanings and usage on templates
-- - dualuse means a template is implement by a module, but its template may
--  be bypassed by using its module directly from other modules.
-- - metatemplate is a special case of dualuse; the template-module pair is
--  for a metatemplate, a template used to build other templates.
-- - noinvoke and metamodule/library don't have use for templates.
-- Does the template have a module?
-- - dualuse and metatemplate: YES (REQUIRED!!)
-- - noinvoke and metamodule/library: options don't apply to templates.
-- RATIONALE FOR NOINVOKE NOT APPLYING: a template may invoke functions from
-- more than one module, but one of them must be the "main" module. Since
-- there's no way to autodetect that, that info must be manually entered.
-- If that doesn't happen
local result = ""
local result = ""
if header == "dualuse" then
if header == "dualuse" then
Line 152: Line 195:
elseif has_module and not is_module_invoked then
elseif has_module and not is_module_invoked then
result = string.format(
result = string.format(
"This template has a corresponding Lua module [[Module:%s]] that it does not use.",
"This template has a corresponding Lua module [[Module:%s]], but does not invoke its functions.",
corr_module
corr_module
)
)
Line 170: Line 213:
elseif has_module and not is_module_invoked then
elseif has_module and not is_module_invoked then
result = string.format(
result = string.format(
"This metatemplate has a corresponding Lua module [[Module:%s]] that it does not use.",
"This metatemplate has a corresponding Lua module [[Module:%s]], but does not invoke its functions.",
corr_module
corr_module
)
)
Line 189: Line 232:
elseif has_module and header ~= "" and not is_module_invoked then
elseif has_module and header ~= "" and not is_module_invoked then
result = string.format(
result = string.format(
"%s. This template has a corresponding Lua module [[Module:%s]] that it does not use.",
"%s. This template has a corresponding Lua module [[Module:%s]], but does not invoke its functions.",
header,
header,
corr_module
corr_module
Line 200: Line 243:
elseif has_module and header == "" and not is_module_invoked then
elseif has_module and header == "" and not is_module_invoked then
result = string.format(
result = string.format(
"This template has a corresponding Lua module [[Module:%s]] that it does not use.",
"This template has a corresponding Lua module [[Module:%s]], but does not invoke its functions.",
corr_module
corr_module
)
)
Line 227: Line 270:
local corr_template = args["temp"] or pagename
local corr_template = args["temp"] or pagename
local corr_module  = args["mod"]  or pagename
local corr_module  = args["mod"]  or pagename
local detect_corr_page = args["detect_corr_page"]


-- If header is none, skip everything
-- If header is none, skip everything
Line 277: Line 321:
args["temp"] = args["temp"] or args["pagename"]
args["temp"] = args["temp"] or args["pagename"]
args["mod" ] = args["mod" ] or args["pagename"]
args["mod" ] = args["mod" ] or args["pagename"]
-- Option to detect corresponding page
args["detect_corr_page"] = yesno(args["detect_corr_page"], true)


return p._dochead(args)
return p._dochead(args)

Revision as of 07:51, 31 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
49 make_module_hatnote (header, pagename, corr_template, detect_corr_page)
156 make_template_hatnote (header, pagename, corr_module, detect_corr_page)
266 _dochead (main) (args)
303 dochead (invokable) (frame)
330 tester none
Lua modules required (3)
Variable Module Functions used
getArgs Module:Arguments getArgs
iutils Module:Introspection utils page_exists
get_and_preprocess_content
find_invokes
invocation_exists
make_module_link
yesno Module:Yesno yesno

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 yesno   = require("Module:Yesno")

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:match("/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 to handle modules
function p.make_module_hatnote(header, pagename, corr_template, detect_corr_page)
	-- Check whether corresponding template exists
	-- Then check whether that template invokes the module
	local has_template = iutils.page_exists("Template:" .. corr_template)
	if has_template then
		local wikitext = iutils.get_and_preprocess_content("Template", corr_template)
		local invokes = iutils.find_invokes(wikitext)
		has_template = has_template and iutils.invocation_exists(invokes, pagename)
	end
	
	-- Check whether to use detect_corr_page option
	-- If the header option is dualuse, metatemplate, or noinvoke, this option
	-- is ignored. For any other option, it's not.
	if header ~= "dualuse" or header ~= "metatemplate" or header ~= "noinvoke" then
		has_template = has_template and detect_corr_page
	end
	
	-- Heading meanings and usage on module
	-- - dualuse means a template is implement by a module, but its template may
	--   be bypassed by using its module directly from other modules.
	-- - metatemplate is a special case of dualuse; the template-module pair is
	--   for a metatemplate, a template used to build other templates.
	-- - noinvoke indicates a lua-based template, but the module's code is so
	--   specialized that its code should not be used by other modules or
	--   invoked by other templates.
	-- - metamodule and library indicate a module whose code is used mainly for
	--   other modules. Such modules generally don't have a corresponding
	--   template.
	-- Does the module have a template?
	-- - dualuse, metatemplate, and noinvoke: YES (REQUIRED!!)
	-- - metamodule/library: GENERALLY NO
	local result = ""
	if header == "dualuse" then
		if has_template then
			result = string.format(
				"This module may be invoked by templates using its corresponding [[Template:%s|template]], or used directly from other modules.",
				corr_template
			)
		else
			result = string.format(
				"This module has a corresopnding template that is currently missing or does not use this module. ([[Special:EditPage/Template:%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 [[Template:%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 [[Template:%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/Template:%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 [[Template:%s]].", header, corr_template)
		elseif has_template and header == "" then
			result = string.format("This module implements [[Template:%s]].", corr_template)
		else
			result = header
		end
	end
	
	if result == "" then
		return ""
	else
		return string.format(":''%s''", result)
	end
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", hatnote, table.concat(calls, ", "))
	end
end

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

	-- Find all invocations for the template and add it as an addl hatnote.
	local invocation_hatnote = make_invocation_hatnote(invokes)
	
	-- Check whether to use detect_corr_page option
	-- If the header option is dualuse or metatemplate, this option is ignored.
	-- For any other option, it's not (although it also has no effect on
	-- noinvoke or library/metamodule).
	if header ~= "dualuse" or header ~= "metatemplate" then
		has_module = has_module and detect_corr_page
	end
	
	-- Heading meanings and usage on templates
	-- - dualuse means a template is implement by a module, but its template may
	--   be bypassed by using its module directly from other modules.
	-- - metatemplate is a special case of dualuse; the template-module pair is
	--   for a metatemplate, a template used to build other templates.
	-- - noinvoke and metamodule/library don't have use for templates.
	-- Does the template have a module?
	-- - dualuse and metatemplate: YES (REQUIRED!!)
	-- - noinvoke and metamodule/library: options don't apply to templates.
	-- RATIONALE FOR NOINVOKE NOT APPLYING: a template may invoke functions from
	-- more than one module, but one of them must be the "main" module. Since
	-- there's no way to autodetect that, that info must be manually entered.
	-- If that doesn't happen
	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 [[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 has a corresponding Lua module [[Module:%s]], but does not invoke its functions.",
				corr_module
			)
		else
			result = string.format(
				"This template is implemented by a module that is currently missing. [[Special:EditPage/Module:%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 [[Module:%s]].",
				corr_module
			)
		elseif has_module and not is_module_invoked then
			result = string.format(
				"This metatemplate has a corresponding Lua module [[Module:%s]], but does not invoke its functions.",
				corr_module
			)
		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 [[Module:%s]].",
				header,
				corr_module
			)
		elseif has_module and header ~= "" and not is_module_invoked then
			result = string.format(
				"%s. This template has a corresponding Lua module [[Module:%s]], but does not invoke its functions.",
				header,
				corr_module
			)
		elseif has_module and header == "" and is_module_invoked then
			result = string.format(
				"This template is implemented by the Lua module [[Module:%s]].",
				corr_module
			)
		elseif has_module and header == "" and not is_module_invoked then
			result = string.format(
				"This template has a corresponding Lua module [[Module:%s]], but does not invoke its functions.",
				corr_module
			)
		else
			result = header
		end
	end
	
	if result == "" and invocation_hatnote == "" then
		return ""
	elseif result == "" and invocation_hatnote ~= "" then
		return string.format(":''%s''", invocation_hatnote)
	elseif result ~= "" and invocation_hatnote == "" then
		return string.format(":''%s''", result)
	else
		return string.format(":''%s''\n:''%s''", result, invocation_hatnote)
	end
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 pagename
	local corr_module   = args["mod"]  or pagename
	local detect_corr_page = args["detect_corr_page"]

	-- 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 iutils.page_exists("Module:" .. corr_module)
	if result == "" then
		return categorize(namespace, pagename, has_link)
	else
		return string.format("%s%s\n", 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 args["pagename"]
	args["mod" ] = args["mod" ] or args["pagename"]
	
	-- Option to detect corresponding page
	args["detect_corr_page"] = yesno(args["detect_corr_page"], true)

	return p._dochead(args)
end

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

return p