Module:File link and Module:Format link: Difference between pages

From Mor Afgin Website
(Difference between pages)
m (1 revision imported)
 
Template:Infobox>Pppery
(Avoid Lua erroring when we run out of expensive parser function calls)
 
Line 1: Line 1:
-- This module provides a library for formatting file wikilinks.
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local mError -- lazily initialise [[Module:Error]]
local yesno -- lazily initialise [[Module:Yesno]]


local yesno = require('Module:Yesno')
local p = {}
local checkType = require('libraryUtil').checkType
 
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------


local p = {}
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end


function p._main(args)
local function removeInitialColon(s)
checkType('_main', 1, args, 'table')
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end


-- This is basically libraryUtil.checkTypeForNamedArg, but we are rolling our
local function maybeItalicize(s, shouldItalicize)
-- own function to get the right error level.
-- Italicize s if s is a string and the shouldItalicize parameter is true.
local function checkArg(key, val, level)
if s and shouldItalicize then
if type(val) ~= 'string' then
return '<i>' .. s .. '</i>'
error(string.format(
else
"type error in '%s' parameter of '_main' (expected string, got %s)",
return s
key, type(val)
), level)
end
end
end
end


local ret = {}
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
 
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link


-- Adds a positional parameter to the buffer.
-- Find the page, if it exists.
local function addPositional(key)
-- For links like [[#Bar]], the page will be nil.
local val = args[key]
local preHash, postHash = link:match('^(.-)#(.*)$')
if not val then
local page
return nil
if not preHash then
end
-- We have a link like [[Foo]].
checkArg(key, val, 4)
page = link
ret[#ret + 1] = val
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
end


-- Adds a named parameter to the buffer. We assume that the parameter name
-- Find the section, if it exists.
-- is the same as the argument key.
local section
local function addNamed(key)
if postHash and postHash ~= '' then
local val = args[key]
section = postHash
if not val then
end
return nil
end
return {
checkArg(key, val, 4)
link = link,
ret[#ret + 1] = key .. '=' .. val
page = page,
section = section,
display = display,
}
end
 
local function formatDisplay(parsed, options)
-- Formats a display string based on a parsed link table (matching the
-- output of parseLink) and an options table (matching the input options for
-- _formatLink).
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if (not section) then
return page
elseif (not page) then
return mw.ustring.format('§&nbsp;%s', section)
else
return mw.ustring.format('%s §&nbsp;%s', page, section)
end
end
 
local function missingArgError(target)
mError = require('Module:Error')
return mError.error{message =
'Error: no link or target specified! ([[' .. target .. '#Errors|help]])'
}
end
 
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
 
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1] or args.link
local target = args[3] or args.target
if not (link or target) then
return missingArgError('Template:Format link')
end
end


-- Filename
return p._formatLink{
checkArg('file', args.file, 3)
link = link,
ret[#ret + 1] = 'File:' .. args.file
display = args[2] or args.display,
target = target,
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end


-- Format
function p._formatLink(options)
if args.format then
-- The formatLink export function, for use in modules.
checkArg('format', args.format)
checkType('_formatLink', 1, options, 'table')
if args.formatfile then
local function check(key, expectedType) --for brevity
checkArg('formatfile', args.formatfile)
checkTypeForNamedArg(
ret[#ret + 1] = args.format .. '=' .. args.formatfile
'_formatLink', key, options[key], expectedType or 'string', true
else
)
ret[#ret + 1] = args.format
end
end
end
check('link')
check('display')
check('target')
check('italicizePage', 'boolean')
check('italicizeSection', 'boolean')
check('categorizeMissing')


-- Border
-- Normalize link and target and check that at least one is present
if yesno(args.border) then
if options.link == '' then options.link = nil end
ret[#ret + 1] = 'border'
if options.target == '' then options.target = nil end
if not (options.link or options.target) then
return missingArgError('Module:Format link')
end
end


addPositional('location')
local parsed = parseLink(options.link)
addPositional('alignment')
local display = options.display or parsed.display
addPositional('size')
local catMissing = options.categorizeMissing
addNamed('upright')
local category = ''
addNamed('link')
addNamed('alt')
addNamed('page')
addNamed('class')
addNamed('lang')
addNamed('start')
addNamed('end')
addNamed('thumbtime')
addPositional('caption')


return string.format('[[%s]]', table.concat(ret, '|'))
-- Find the display text
end
if not display then display = formatDisplay(parsed, options) end


function p.main(frame)
-- Handle the target option if present
local origArgs = require('Module:Arguments').getArgs(frame, {
if options.target then
wrappers = 'Template:File link'
local parsedTarget = parseLink(options.target)
})
parsed.link = parsedTarget.link
if not origArgs.file then
parsed.page = parsedTarget.page
error("'file' parameter missing from [[Template:File link]]", 0)
end
end


-- Copy the arguments that were passed to a new table to avoid looking up
-- Test if page exists if a diagnostic category is specified
-- every possible parameter in the frame object.
if catMissing and (mw.ustring.len(catMissing) > 0) then
local args = {}
local title = nil
for k, v in pairs(origArgs) do
if parsed.page then title = mw.title.new(parsed.page) end
-- Make _BLANK a special argument to add a blank parameter. For use in
if title and (not title.isExternal) then
-- conditional templates etc. it is useful for blank arguments to be
local success, exists = pcall(function() return title.exists end)
-- ignored, but we still need a way to specify them so that we can do
if success and not exists then
-- things like [[File:Example.png|link=]].
category = mw.ustring.format('[[Category:%s]]', catMissing)
if v == '_BLANK' then
end
v = ''
end
end
args[k] = v
end
end
return p._main(args)
-- Format the result as a link
if parsed.link == display then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
else
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
end
 
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
 
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
end


return p
return p

Revision as of 13:37, 4 October 2022

Documentation for this module may be created at Module:Format link/doc

--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local mError -- lazily initialise [[Module:Error]]
local yesno -- lazily initialise [[Module:Yesno]]

local p = {}

--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------

local function getArgs(frame)
	-- Fetches the arguments from the parent frame. Whitespace is trimmed and
	-- blanks are removed.
	mArguments = require('Module:Arguments')
	return mArguments.getArgs(frame, {parentOnly = true})
end

local function removeInitialColon(s)
	-- Removes the initial colon from a string, if present.
	return s:match('^:?(.*)')
end

local function maybeItalicize(s, shouldItalicize)
	-- Italicize s if s is a string and the shouldItalicize parameter is true.
	if s and shouldItalicize then
		return '<i>' .. s .. '</i>'
	else
		return s
	end
end

local function parseLink(link)
	-- Parse a link and return a table with the link's components.
	-- These components are:
	-- - link: the link, stripped of any initial colon (always present)
	-- - page: the page name (always present)
	-- - section: the page name (may be nil)
	-- - display: the display text, if manually entered after a pipe (may be nil)
	link = removeInitialColon(link)

	-- Find whether a faux display value has been added with the {{!}} magic
	-- word.
	local prePipe, display = link:match('^(.-)|(.*)$')
	link = prePipe or link

	-- Find the page, if it exists.
	-- For links like [[#Bar]], the page will be nil.
	local preHash, postHash = link:match('^(.-)#(.*)$')
	local page
	if not preHash then
		-- We have a link like [[Foo]].
		page = link
	elseif preHash ~= '' then
		-- We have a link like [[Foo#Bar]].
		page = preHash
	end

	-- Find the section, if it exists.
	local section
	if postHash and postHash ~= '' then
		section = postHash
	end
	
	return {
		link = link,
		page = page,
		section = section,
		display = display,
	}
end

local function formatDisplay(parsed, options)
	-- Formats a display string based on a parsed link table (matching the
	-- output of parseLink) and an options table (matching the input options for
	-- _formatLink).
	local page = maybeItalicize(parsed.page, options.italicizePage)
	local section = maybeItalicize(parsed.section, options.italicizeSection)
	if (not section) then
		return page
	elseif (not page) then
		return mw.ustring.format('§&nbsp;%s', section)
	else
		return mw.ustring.format('%s §&nbsp;%s', page, section)
	end
end

local function missingArgError(target)
	mError = require('Module:Error')
	return mError.error{message =
		'Error: no link or target specified! ([[' .. target .. '#Errors|help]])'
	}
end

--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------

function p.formatLink(frame)
	-- The formatLink export function, for use in templates.
	yesno = require('Module:Yesno')
	local args = getArgs(frame)
	local link = args[1] or args.link
	local target = args[3] or args.target
	if not (link or target) then
		return missingArgError('Template:Format link')
	end

	return p._formatLink{
		link = link,
		display = args[2] or args.display,
		target = target,
		italicizePage = yesno(args.italicizepage),
		italicizeSection = yesno(args.italicizesection),
		categorizeMissing = args.categorizemissing
	}
end

function p._formatLink(options)
	-- The formatLink export function, for use in modules.
	checkType('_formatLink', 1, options, 'table')
	local function check(key, expectedType) --for brevity
		checkTypeForNamedArg(
			'_formatLink', key, options[key], expectedType or 'string', true
		)
	end
	check('link')
	check('display')
	check('target')
	check('italicizePage', 'boolean')
	check('italicizeSection', 'boolean')
	check('categorizeMissing')

	-- Normalize link and target and check that at least one is present
	if options.link == '' then options.link = nil end
	if options.target == '' then options.target = nil end
	if not (options.link or options.target) then
		return missingArgError('Module:Format link')
	end

	local parsed = parseLink(options.link)
	local display = options.display or parsed.display
	local catMissing = options.categorizeMissing
	local category = ''

	-- Find the display text
	if not display then display = formatDisplay(parsed, options) end

	-- Handle the target option if present
	if options.target then
		local parsedTarget = parseLink(options.target)
		parsed.link = parsedTarget.link
		parsed.page = parsedTarget.page
	end

	-- Test if page exists if a diagnostic category is specified
	if catMissing and (mw.ustring.len(catMissing) > 0) then
		local title = nil
		if parsed.page then title = mw.title.new(parsed.page) end
		if title and (not title.isExternal) then
			local success, exists = pcall(function() return title.exists end)
			if success and not exists then
				category = mw.ustring.format('[[Category:%s]]', catMissing)
			end
		end
	end
	
	-- Format the result as a link
	if parsed.link == display then
		return mw.ustring.format('[[:%s]]%s', parsed.link, category)
	else
		return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
	end
end

--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------

function p.formatPages(options, pages)
	-- Formats an array of pages using formatLink and the given options table,
	-- and returns it as an array. Nil values are not allowed.
	local ret = {}
	for i, page in ipairs(pages) do
		ret[i] = p._formatLink{
			link = page,
			categorizeMissing = options.categorizeMissing,
			italicizePage = options.italicizePage,
			italicizeSection = options.italicizeSection
		}
	end
	return ret
end

return p