# Module:Format link

---
-- 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 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  
-- Fetches the arguments from the parent . Whitespace is trimmed and  
-- blanks are removed.  
mArguments = require('Module:Arguments')  
return mArguments.getArgs(, {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! (help)'  
}  
end  

---
-- Main functions  
---

function p.formatLink  
-- The formatLink export function, for use in templates.  
yesno = require('Module:Yesno')  
local args = getArgs  
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('', 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', 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