Module:Format link

    From Wikinoor
    Revision as of 17:07, 4 October 2022 by en>Wikinoor.ir (Avoid Lua erroring when we run out of expensive parser function calls)
    (diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

    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