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

--]=]

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 --

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

--[=[

We are going to traverse the text linearly ourselves.

Using %b[] isn't preferable as nested brackets (E.g. the wikilink to t

in Cap[[tion]]) would be missed and doing a check for

%[%[.-%]%] wouldn't work for the exact same testcase for other reasons

--]=]

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

}