Mô đun: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 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,"Bản mẫu:(.-)/doc")
if tdedoc then title=mw.title.new("Bản mẫu:"..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 .. "' .. out .. .. letter
stack[stack.top].seg = 2
elseif (ss ~= "[[" and letter=="|") then
out = out .. ""..string.rep("&nbsp;",4stack.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) .. "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,"%]",")")
if string.len(text) >= MAXPOSN then
out = out .. "Lưu ý: do hạn chế về thời gian sử dụng Lua, chạy bị bỏ bớt vào ký tự MAXPOSN"

out = out .. "Để tiếp tục hoạt động này, xem trước hoặc nhập "
else out = out .. "Nếu bạn có một phân đoạn bổ sung của bản mẫu để xử lý, xem trước hoặc nhập "
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,"%)","]")
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,"(.-)")
repeat
local nowikimatch=prowl()
if not(nowikimatch) then break end
nowikielementnumber=nowikielementnumber+1
table.insert(nowikisafehouse,nowikimatch)
until false
text=mw.ustring.gsub(text,"(.-)","")
-- 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,"",""..nowikisafehouse[nw].."iki>",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