Module:Sandbox/Aidan9382/Link once

require("strict") local yesno = require("Module:Yesno")

-- Behaviour for these functions determined via Help:Pipe trick local function wlPipeTrick(target) target = target:gsub("^[a-zA-Z0-9 _-]-:(.*)$", "%1") --Remove the namespace if target:find("%(.+%)$") then --If ending parenthesis target = target:gsub("^(.-) *%(.+%)$", "%1") --Remove ending parenthesis else target = target:gsub("^(.-), .*$", "%1") --Else, remove ending comma end return target end

local function wlReversePipeTrick(target) local current = mw.title.getCurrentTitle.prefixedText if current:find("%(.+%)$") then --If ending parenthesis target = target .. current:gsub("^.-( *%(.+%))$", "%1") --Append ending parenthesis else target = target .. current:gsub("^.-(, .*)$", "%1") --Else, append ending comma end return target end

local function getWikilinkInfo(wikilink) --[=[		Returns the wikilink's target and its display text. Automatically recreates the effect of any Help:pipe tricks --]=]	local trim = mw.text.trim local trimmed = string.sub(wikilink, 3, -3) local firstPipe = string.find(trimmed, "|") if firstPipe then local target = string.sub(trimmed, 1, firstPipe-1) local displayText = string.sub(trimmed, firstPipe+1) if target == "" then -- |XYZ return trim(wlReversePipeTrick(displayText)), trim(displayText) elseif displayText == "" then -- XYZ return trim(target), trim(wlPipeTrick(target)) else --XYZ return trim(target), trim(displayText) end else local out = trim(trimmed) if out:find("^/.-/+$") and mw.title.getCurrentTitle.namespace ~= 0 then -- /Test/ return out, out:gsub("^/(.-)/+$", "%1") else -- Test return out, nil end end end

local function linkOnce(text, options) -- Module entry point --	local options = options or {follow_redirects=true} local newText = {} local scannerPosition = 1 local existingWikilinks = {} local openWikilinks = {} while true do		local Position, _, Character = string.find(text, "([%[%]])%1", scannerPosition) local container = (openWikilinks[#openWikilinks] or {Text=newText}).Text if not Position then --Done container[#container+1] = string.sub(text, scannerPosition) break end container[#container+1] = string.sub(text, scannerPosition, Position-1)

scannerPosition = Position+2 --+2 to pass the / if Character == "[" then --Add a [[ to the pending wikilink queue			openWikilinks[#openWikilinks+1] = {Position = Position, Text = {"[["}}

else --Pair up the ]] to any available [[ if #openWikilinks >= 1 then local openingPair = table.remove(openWikilinks) --Pop the latest 				local wlStart, wlText = openingPair.Position, table.concat(openingPair.Text, "") .. "" local wikilink = string.sub(text, wlStart, Position+1) local wlTarget, wlPiped = getWikilinkInfo(wikilink)

local newContainer = (openWikilinks[#openWikilinks] or {Text=newText}).Text if wlTarget:find("^[Ii]mage:") or wlTarget:find("^[Ff]ile:") or wlTarget:find("^[Cc]ategory:") then --Files/Images/Categories aren't processed (they aren't really wikilinks) newContainer[#newContainer+1] = wlText else local realTarget = wlTarget:sub(1, 1):upper .. wlTarget:sub(2) if existingWikilinks[realTarget] then newContainer[#newContainer+1] = wlPiped or wlTarget else local resolvedTarget = realTarget if options.follow_redirects then local titleObj = mw.title.new(wlTarget) if titleObj then local newTarget = titleObj.isRedirect and titleObj.redirectTarget.fullText or titleObj.fullText resolvedTarget = newTarget:sub(1, 1):upper .. newTarget:sub(2) end end if existingWikilinks[resolvedTarget] then newContainer[#newContainer+1] = wlPiped or wlTarget else existingWikilinks[realTarget] = true existingWikilinks[resolvedTarget] = true newContainer[#newContainer+1] = wlText end end end

else --Just a random ]] with no matching pair, dont process it				newText[#newText+1] = "]]" end end end

if #openWikilinks > 0 then --Random [[ with no matching pair, dont process it		for i = #openWikilinks, 2, -1 do			local nextLink = openWikilinks[i-1]			nextLink.Text[#nextLink.Text+1] = table.concat(openWikilinks[i].Text, "")		end		newText[#newText+1] = table.concat(openWikilinks[1].Text, "")	end	return table.concat(newText, "") end

local function main(frame) -- Template entry point local args = require('Module:Arguments').getArgs(frame) return linkOnce(args[1] or "", {		follow_redirects = yesno(args.follow_redirects) or true,	}) end

return { -- Main entry points main = main, linkOnce = linkOnce, -- Helper functions wlPipeTrick = wlPipeTrick, wlReversePipeTrick = wlReversePipeTrick, getWikilinkInfo = getWikilinkInfo }