Module:FormatTemplate
This module is intended to format templates to make them readable.
It should work by recognizing every beginning that should not be intermingled: [[, {{, {{#, {{{
It will count how many levels deep you've gone.
It will add 4 times that many spaces before each pipe | in a non-[[ element, removing any now present
It will label the beginning and end with a color specific to the type of element even when it can't indent
It will return everything in a nowiki wrapper (excluding the color formatting)
local p={}
local MAXPOSN = 30000 -- usually 50000 was 3 seconds .. not right now though .. local HOLDABLE = {["{"] = true, ["["] = true, ["}"] = true, ["]"] = true} local ACTABLE = {["{"] = true, ["["] = true, ["}"] = true, ["]"] = true, ["|"] = true, [":"] = true} local MARKER = {["{{{"] = "|", ["{{"] = "|", ["{{#"] = ":", ["[["] = "|"} local MATCH = {["{{{"] = "}}}", ["{{#"] = "}}", ["{{"] = "}}", ["[["] = "]]"} local RENDER = {['{{{'] = { -- these are replaced by variables in module
['{{{'] = '</nowiki>{{{', ['}}}'] = '}}}', ['}}'] = '}}', [']]'] = ']]' }, ['{{#'] = { -- these will receive many different specific translations in module ['{{#'] = '{{#', ['}}}'] = '}}}', ['}}'] = '}}', [']]'] = ']]' }, ['{{'] = { -- these might eventually be expanded by the module, but not in the first versions (scotty, try and increase the power!) ['{{'] = '{{', ['}}}'] = '}}}', ['}}'] = '}}', [']]'] = ']]' }, ['[['] = { -- these can be left untouched, I think ['[['] = '[[', ['}}}'] = '}}}', ['}}'] = '}}', [']]'] = ']]' }} local debuglog = "" local text local getletter -- this module is designed around reading ONCE, tracking state; getletter() gets each letter in text once local out = "" local flag = false -- true marks the end of the getletter() stream function getContent(template) local title -- holds the result of the mw.title.xxx call if not(template) then title=mw.title.getCurrentTitle() if not(title) then return "error: failed to getCurrentTitle()" end local tdedoc=mw.ustring.match(title.fullText,"Template:(.-)/doc") if tdedoc then title=mw.title.new("Template:"..tdedoc) end -- SPECIAL CASE: Invoke in the template documentation processes the template instead else title=mw.title.new(page) if not (title) then return "error: failed to mw.title.new(" .. template .. ")" end end -- if not(template) return title.getContent(title) or "" end local function scanabort() flag = true return ":" -- an "actable" to prevent looping end function formatTemplate(text,importstack,posn,template) -- note template is just for the error message local debug="" local letter="" local output="" local outputtable={} posn=tonumber(posn) or 0 if posn>0 then text=string.sub(text,posn,-1) end --- need to chop off the preceding text so it doesn't gmatch to it local stopposn = (string.find(text, "[^{}%[%]|:]", MAXPOSN)) if stopposn then text= string.sub(text, 1, stopposn) end stack = {top = #importstack} for i = 0, stack.top do stack[i] = {} stack[i].feature = importstack[i] stack[i].text = {} stack[i].seg = 1 -- this is NOT ACCURATE, would need to be saved in the transition end stack.push = function(feature) table.insert(stack[stack.top].text, out) stack.top = stack.top + 1 stack[stack.top] = {} stack[stack.top].text = {RENDER[feature][feature]} stack[stack.top].seg = 1 stack[stack.top].feature = feature out = "" end stack.pop = function(feature) local spillover = "" local pop = stack[stack.top].feature if (MATCH[pop] ~= feature and feature == "}}}") then feature = "}}" spillover = "}" end out = out .. RENDER[pop][feature] if (MATCH[pop] ~= feature) then out = out .. "<--- error? " end table.insert(stack[stack.top].text, out) table.insert(stack[stack.top - 1].text, table.concat(stack[stack.top].text)) stack[stack.top] = nil stack.top = stack.top - 1 out = "" return spillover end stack.field = function (letter) local ss = stack[stack.top].feature if (stack[stack.top].seg == 1 and letter == MARKER[ss]) then out = '' .. out .. '' .. letter stack[stack.top].seg = 2 elseif (ss ~= "[[" and letter=="|") then out = out .. "
"..string.rep(" ",4*stack.top).."|" table.insert(stack[stack.top].text, out) stack[stack.top].seg = stack[stack.top].seg + 1 out = "" else out = out .. letter end end stack.write = function() -- out is a simple global variable for repeated concatenations; can't get too big though table.insert(stack[stack.top].text, out) out = "" end template=template or "" getletter = string.gmatch(text,".") out="" repeat local holding = "" repeat letter = letter or "" -- bug that dumps nil letters comes up in the out = out ..letter, NOT while not ACTABLE[letter] ... why? while not ACTABLE[letter] do out = out .. letter letter = getletter() or scanabort() end if HOLDABLE[letter] then holding = letter else stack.field(letter) end letter = "" until holding ~= "" or flag if #out>20 then stack.write() end letter=getletter() or scanabort() -- add the letter to the next feature being parsed if possible if (holding == "[") then -- either [[ or just ignore -- cases based on the next letter after "[" if (letter == "[") then stack.push("[[") letter = "" else out = out .. holding -- single [, treat normally end elseif (holding == "{") then -- cases based on the next letter after "{" if (letter == "{") then letter = getletter() or scanabort() if (letter == "#") then stack.push("{{#") letter = "" elseif (letter == "{") then stack.push("{{{") letter = "" else stack.push("{{") end end elseif (holding == "]") then if (letter == "]") then -- we have a ]] stack.pop("]]") letter = "" else out = out .. holding end elseif (holding == "}") then if (letter == "}") then letter = getletter() if letter == "}" then letter = stack.pop("}}}") else stack.pop("}}") end else out = out .. holding -- lone } is nothing end end until flag if stack.top>0 then out = string.sub(out, 1, -2) .. "<--- end of run --->
run incomplete." stack.write() local stackcrypt = "" for i = stack.top, 1, -1 do table.insert(stack[i-1].text, table.concat(stack[i].text)) stackcrypt = stackcrypt .. stack[i].feature end stackcrypt=string.gsub(stackcrypt,"{","<") stackcrypt=string.gsub(stackcrypt,"%[","(") stackcrypt=string.gsub(stackcrypt,"}",">") stackcrypt=string.gsub(stackcrypt,"%]",")") if string.len(text) >= MAXPOSN then out = out .. "
Note: due to restrictions on Lua time usage, runs are truncated at MAXPOSN characters"
out = out .. "
To continue this run, preview or enter {{#invoke:FormatTemplate|format|page="..template.."|stack="..stackcrypt.."|position="..#text.."}}" else out = out .. "<br />''If you have an additional segment of template to process, preview or enter <nowiki>{{#invoke:FormatTemplate|format|page="..template.."|stack="..stackcrypt.."|position=0}}" end end output=table.concat(stack[0].text) .. out return output end function p.main(frame,fcn) local args=frame.args local parent=frame.getParent(frame) if parent then pargs=parent.args else pargs={} end page=args.page or pargs.page text = getContent(page) local stackcrypt=args.stack or pargs.stack or "" stackcrypt=mw.ustring.gsub(stackcrypt,"<","{") stackcrypt=mw.ustring.gsub(stackcrypt,"%(","[") stackcrypt=mw.ustring.gsub(stackcrypt,">","}") stackcrypt=mw.ustring.gsub(stackcrypt,"%)","]") local stack={} local posn=args.position or pargs.position or 0 local prowl=mw.ustring.gmatch(stackcrypt,"[^,%s]+") repeat local x=prowl() if x then table.insert(stack,x) end until not x fcn=fcn or args["function"] or pargs["function"] or "" fcn=mw.ustring.match(fcn,"%S+") -- text=text or args.text or pargs.text or args[1] or pargs[1] or "" -- doesn't work - gets interpreted or passed as "UNIQ..QINU", either way unusuable! local nowikisafehouse={} local nowikielementnumber=0 local prowl=mw.ustring.gmatch(text,"<nowiki>(.-)") repeat local nowikimatch=prowl() if not(nowikimatch) then break end nowikielementnumber=nowikielementnumber+1 table.insert(nowikisafehouse,nowikimatch) until false text=mw.ustring.gsub(text,"(.-)","<Module:FormatTemplate internal nowiki token>") -- this is the meat of the formatting if fcn=="format" then text=formatTemplate(text,stack,posn,page) end -- unprotect the nowikis from the template itself - but inactivate them on first display! for nw = 1,nowikielementnumber do text=mw.ustring.gsub(text,"<Module:FormatTemplate internal nowiki token>",""..nowikisafehouse[nw].."</nowiki>",1) end -- preprocess as nowiki-bounded text return frame:preprocess(""..text.."" .. "\n" .. debuglog)
end
function p.format(frame)
return p.main(frame,"format")
end
return p