Theme: iWiki Log in Register

Diff: Module:Hatnote list

Comparing revision #1 (2021-12-26 20:05:24) with revision #2 (2023-02-04 10:26:38).

OldNew
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--                           Module:Hatnote list                              --
--                           Module:Hatnote list                              --
--                                                                            --
--                                                                            --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements,   --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements,   --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced    --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced    --
-- are andList & orList helpers for formatting lists with those conjunctions. --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mArguments --initialize lazily
local mFormatLink = require('Module:Format link')
local mFormatLink = require('Module:Format link')
local mHatnote = require('Module:Hatnote')
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkType = libraryUtil.checkType
local p = {}
local p = {}
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- List stringification helper functions
-- List stringification helper functions
--
--
-- These functions are used for stringifying lists, usually page lists inside
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
local stringifyListDefaultOptions = {
	conjunction = "and",
	conjunction = "and",
	separator = ",",
	separator = ",",
	altSeparator = ";",
	altSeparator = ";",
	space = " ",
	space = " ",
	formatted = false
	formatted = false
}
}
--Searches display text only
--Searches display text only
local function searchDisp(haystack, needle)
local function searchDisp(haystack, needle)
	return string.find(
	return string.find(
		string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle
		string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle
	)
	)
end
end
-- Stringifies a list generically; probably shouldn't be used directly
-- Stringifies a list generically; probably shouldn't be used directly
local function stringifyList(list, options)
local function stringifyList(list, options)
	-- Type-checks, defaults, and a shortcut
	-- Type-checks, defaults, and a shortcut
	checkType("stringifyList", 1, list, "table")
	checkType("stringifyList", 1, list, "table")
	if #list == 0 then return nil end
	if #list == 0 then return nil end
	checkType("stringifyList", 2, options, "table", true)
	checkType("stringifyList", 2, options, "table", true)
	options = options or {}
	options = options or {}
	for k, v in pairs(stringifyListDefaultOptions) do
	for k, v in pairs(stringifyListDefaultOptions) do
		if options[k] == nil then options[k] = v end
		if options[k] == nil then options[k] = v end
	end
	end
	local s = options.space
	local s = options.space
	-- Format the list if requested
	-- Format the list if requested
	if options.formatted then
	if options.formatted then
		list = mFormatLink.formatPages(
		list = mFormatLink.formatPages(
			{categorizeMissing = mHatnote.missingTargetCat}, list
			{categorizeMissing = mHatnote.missingTargetCat}, list
		)
		)
	end
	end
	-- Set the separator; if any item contains it, use the alternate separator
	-- Set the separator; if any item contains it, use the alternate separator
	local separator = options.separator
	local separator = options.separator
	for k, v in pairs(list) do
	for k, v in pairs(list) do
		if searchDisp(v, separator) then
		if searchDisp(v, separator) then
			separator = options.altSeparator
			separator = options.altSeparator
			break
			break
		end
		end
	end
	end
	-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
	-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
	local conjunction = s .. options.conjunction .. s
	local conjunction = s .. options.conjunction .. s
	if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
	if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
		conjunction = separator .. conjunction
		conjunction = separator .. conjunction
	end
	end
	-- Return the formatted string
	-- Return the formatted string
	return mw.text.listToText(list, separator .. s, conjunction)
	return mw.text.listToText(list, separator .. s, conjunction)
end
end
--DRY function
--DRY function
function p.conjList (conj, list, fmt)
function p.conjList (conj, list, fmt)
	return stringifyList(list, {conjunction = conj, formatted = fmt})
	return stringifyList(list, {conjunction = conj, formatted = fmt})
end
end
-- Stringifies lists with "and" or "or"
-- Stringifies lists with "and" or "or"
function p.andList (...) return p.conjList("and", ...) end
function p.andList (...) return p.conjList("and", ...) end
function p.orList (...) return p.conjList("or", ...) end
function p.orList (...) return p.conjList("or", ...) end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- For see
-- For see
--
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
local forSeeDefaultOptions = {
	andKeyword = 'and',
	andKeyword = 'and',
	title = mw.title.getCurrentTitle().text,
	title = mw.title.getCurrentTitle().text,
	otherText = 'other uses',
	otherText = 'other uses',
	forSeeForm = 'For %s, see %s.',
	forSeeForm = 'For %s, see %s.',
}
}
--Collapses duplicate punctuation
--Collapses duplicate punctuation
local function punctuationCollapse (text)
local function punctuationCollapse (text)
	local replacements = {
	local replacements = {
		["%.%.$"] = ".",
		["%.%.$"] = ".",
		["%?%.$"] = "?",
		["%?%.$"] = "?",
		["%!%.$"] = "!",
		["%!%.$"] = "!",
		["%.%]%]%.$"] = ".]]",
		["%.%]%]%.$"] = ".]]",
		["%?%]%]%.$"] = "?]]",
		["%?%]%]%.$"] = "?]]",
		["%!%]%]%.$"] = "!]]"
		["%!%]%]%.$"] = "!]]"
	}
	}
	for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
	for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
	return text
	return text
end
end
-- Structures arguments into a table for stringification, & options
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
function p.forSeeArgsToTable (args, from, options)
	-- Type-checks and defaults
	-- Type-checks and defaults
	checkType("forSeeArgsToTable", 1, args, 'table')
	checkType("forSeeArgsToTable", 1, args, 'table')
	checkType("forSeeArgsToTable", 2, from, 'number', true)
	checkType("forSeeArgsToTable", 2, from, 'number', true)
	from = from or 1
	from = from or 1
	checkType("forSeeArgsToTable", 3, options, 'table', true)
	checkType("forSeeArgsToTable", 3, options, 'table', true)
	options = options or {}
	options = options or {}
	for k, v in pairs(forSeeDefaultOptions) do
	for k, v in pairs(forSeeDefaultOptions) do
		if options[k] == nil then options[k] = v end
		if options[k] == nil then options[k] = v end
	end
	end
	-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
	-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
	local maxArg = 0
	local maxArg = 0
	for k, v in pairs(args) do
	for k, v in pairs(args) do
		if type(k) == 'number' and k > maxArg then maxArg = k end
		if type(k) == 'number' and k > maxArg then maxArg = k end
	end
	end
	-- Structure the data out from the parameter list:
	-- Structure the data out from the parameter list:
	-- * forTable is the wrapper table, with forRow rows
	-- * forTable is the wrapper table, with forRow rows
	-- * Rows are tables of a "use" string & a "pages" table of pagename strings
	-- * Rows are tables of a "use" string & a "pages" table of pagename strings
	-- * Blanks are left empty for defaulting elsewhere, but can terminate list
	-- * Blanks are left empty for defaulting elsewhere, but can terminate list
	local forTable = {}
	local forTable = {}
	local i = from
	local i = from
	local terminated = false
	local terminated = false
	-- If there is extra text, and no arguments are given, give nil value
	-- If there is extra text, and no arguments are given, give nil value
	-- to not produce default of "For other uses, see foo (disambiguation)"
	-- to not produce default of "For other uses, see foo (disambiguation)"
	if options.extratext and i > maxArg then return nil end
	if options.extratext and i > maxArg then return nil end
	-- Loop to generate rows
	-- Loop to generate rows
	repeat
	repeat
		-- New empty row
		-- New empty row
		local forRow = {}
		local forRow = {}
		-- On blank use, assume list's ended & break at end of this loop
		-- On blank use, assume list's ended & break at end of this loop
		forRow.use = args[i]
		forRow.use = args[i]
		if not args[i] then terminated = true end
		if not args[i] then terminated = true end
		-- New empty list of pages
		-- New empty list of pages
		forRow.pages = {}
		forRow.pages = {}
		-- Insert first pages item if present
		-- Insert first pages item if present
		table.insert(forRow.pages, args[i + 1])
		table.insert(forRow.pages, args[i + 1])
		-- If the param after next is "and", do inner loop to collect params
		-- If the param after next is "and", do inner loop to collect params
		-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
		-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
		while args[i + 2] == options.andKeyword do
		while args[i + 2] == options.andKeyword do
			if args[i + 3] then 
			if args[i + 3] then 
				table.insert(forRow.pages, args[i + 3])
				table.insert(forRow.pages, args[i + 3])
			end
			end
			-- Increment to next "and"
			-- Increment to next "and"
			i = i + 2
			i = i + 2
		end
		end
		-- Increment to next use
		-- Increment to next use
		i = i + 2
		i = i + 2
		-- Append the row
		-- Append the row
		table.insert(forTable, forRow)
		table.insert(forTable, forRow)
	until terminated or i > maxArg
	until terminated or i > maxArg
	
	
	return forTable
	return forTable
end
end
-- Stringifies a table as formatted by forSeeArgsToTable
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
function p.forSeeTableToString (forSeeTable, options)
	-- Type-checks and defaults
	-- Type-checks and defaults
	checkType("forSeeTableToString", 1, forSeeTable, "table", true)
	checkType("forSeeTableToString", 1, forSeeTable, "table", true)
	checkType("forSeeTableToString", 2, options, "table", true)
	checkType("forSeeTableToString", 2, options, "table", true)
	options = options or {}
	options = options or {}
	for k, v in pairs(forSeeDefaultOptions) do
	for k, v in pairs(forSeeDefaultOptions) do
		if options[k] == nil then options[k] = v end
		if options[k] == nil then options[k] = v end
	end
	end
	-- Stringify each for-see item into a list
	-- Stringify each for-see item into a list
	local strList = {}
	local strList = {}
	if forSeeTable then
	if forSeeTable then
		for k, v in pairs(forSeeTable) do
		for k, v in pairs(forSeeTable) do
			local useStr = v.use or options.otherText
			local useStr = v.use or options.otherText
			local pagesStr =
			local pagesStr =
				p.andList(v.pages, true) or
				p.andList(v.pages, true) or
				mFormatLink._formatLink{
				mFormatLink._formatLink{
					categorizeMissing = mHatnote.missingTargetCat,
					categorizeMissing = mHatnote.missingTargetCat,
					link = mHatnote.disambiguate(options.title)
					link = mHatnote.disambiguate(options.title)
				}
				}
			local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
			local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
			forSeeStr = punctuationCollapse(forSeeStr)
			forSeeStr = punctuationCollapse(forSeeStr)
			table.insert(strList, forSeeStr)
			table.insert(strList, forSeeStr)
		end
		end
	end
	end
	if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
	if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
	-- Return the concatenated list
	-- Return the concatenated list
	return table.concat(strList, ' ')
	return table.concat(strList, ' ')
end
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
function p._forSee (args, from, options)
	local forSeeTable = p.forSeeArgsToTable(args, from, options)
	local forSeeTable = p.forSeeArgsToTable(args, from, options)
	return p.forSeeTableToString(forSeeTable, options)
	return p.forSeeTableToString(forSeeTable, options)
end
end
-- As _forSee, but uses the frame.
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
function p.forSee (frame, from, options)
	mArguments = require('Module:Arguments')
	mArguments = require('Module:Arguments')
	return p._forSee(mArguments.getArgs(frame), from, options)
	return p._forSee(mArguments.getArgs(frame), from, options)
end
end
return p
return p