ماڈیول:FormatTemplate
Appearance
This module is rated as pre-alpha. It is unfinished, and may or may not be in active development. It should not be used from article namespace pages. Modules remain pre-alpha until the original editor (or someone who takes one over if it is abandoned for some time) is satisfied with the basic structure. |
This module formats a template, indenting each line beginning "|" according to the number of parser functions, templates, links, and parameters it is embedded within.
It still needs proper documentation and hopefully some solution to the problem of making the text re-pastable before I'll call it an alpha.
---- 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><span style="color:orange;">{{{</span><nowiki>',
['}}}'] = '</nowiki><span style="color:orange;">}}}</span><nowiki>',
['}}'] = '</nowiki><span style="color:orange;">}}</span><nowiki>',
[']]'] = '</nowiki><span style="color:orange;">]]</span><nowiki>'
}, ['{{#'] = { -- these will receive many different specific translations in module
['{{#'] = '</nowiki><span style="color:blue;">{{#</span><nowiki>',
['}}}'] = '</nowiki><span style="color:blue;">}}}</span><nowiki>',
['}}'] = '</nowiki><span style="color:blue;">}}</span><nowiki>',
[']]'] = '</nowiki><span style="color:blue;">]]</span><nowiki>'
}, ['{{'] = { -- these might eventually be expanded by the module, but not in the first versions (scotty, try and increase the power!)
['{{'] = '</nowiki><span style="color:red;">{{</span><nowiki>',
['}}}'] = '</nowiki><span style="color:red;">}}}</span><nowiki>',
['}}'] = '</nowiki><span style="color:red;">}}</span><nowiki>',
[']]'] = '</nowiki><span style="color:red;">]]</span><nowiki>'
}, ['[['] = { -- these can be left untouched, I think
['[['] = '</nowiki><span style="color:green;">[[</span><nowiki>',
['}}}'] = '</nowiki><span style="color:green;">}}}</span><nowiki>',
['}}'] = '</nowiki><span style="color:green;">}}</span><nowiki>',
[']]'] = '</nowiki><span style="color:green;">]]</span><nowiki>'
}}
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 = '</nowiki><span style = "color:gray;">' .. out .. '</span><nowiki>' .. letter
stack[stack.top].seg = 2
elseif (ss ~= "[[" and letter=="|") then
out = out .. "</nowiki><br /><nowiki>"..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 ---></nowiki><br />'''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 .. "<br />''Note: due to restrictions on Lua time usage, runs are truncated at MAXPOSN characters''"
out = out .. "<br />''To continue this run, preview or enter <nowiki>{{#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>(.-)</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,"<nowiki>(.-)</nowiki>","<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>","<nowiki>"..nowikisafehouse[nw].."</now</nowiki>iki>",1)
end
-- preprocess as nowiki-bounded text
return frame:preprocess("<nowiki>"..text.."</nowiki>" .. "\n" .. debuglog)
end
function p.format(frame)
return p.main(frame,"format")
end
return p