if not modules then modules = { } end modules ['node-tra'] = { version = 1.001, comment = "companion to node-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } --[[ldx--
This is rather experimental. We need more control and some of this might become a runtime module instead. This module will be cleaned up!
--ldx]]-- local utfchar = utf.char local format, match, gmatch, concat, rep = string.format, string.match, string.gmatch, table.concat, string.rep local lpegmatch = lpeg.match local clock = os.gettimeofday or os.clock -- should go in environment local report_nodes = logs.reporter("nodes","tracing") nodes = nodes or { } local nodes, node, context = nodes, node, context local texgetattribute = tex.getattribute local tracers = nodes.tracers or { } nodes.tracers = tracers local tasks = nodes.tasks or { } nodes.tasks = tasks local handlers = nodes.handlers or {} nodes.handlers = handlers local injections = nodes.injections or { } nodes.injections = injections local traverse_nodes = node.traverse local traverse_by_id = node.traverse_id local count_nodes = nodes.count local nodecodes = nodes.nodecodes local whatcodes = nodes.whatcodes local skipcodes = nodes.skipcodes local fillcodes = nodes.fillcodes local glyph_code = nodecodes.glyph local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist local disc_code = nodecodes.disc local glue_code = nodecodes.glue local kern_code = nodecodes.kern local rule_code = nodecodes.rule local whatsit_code = nodecodes.whatsit local spec_code = nodecodes.glue_spec local localpar_code = whatcodes.localpar local dir_code = whatcodes.dir local nodepool = nodes.pool local new_rule = nodepool.rule local dimenfactors = number.dimenfactors local formatters = string.formatters -- this will be reorganized: function nodes.showlist(head, message) if message then report_nodes(message) end for n in traverse_nodes(head) do report_nodes(tostring(n)) end end function nodes.handlers.checkglyphs(head,message) local t = { } for g in traverse_by_id(glyph_code,head) do t[#t+1] = formatters["%U:%s"](g.char,g.subtype) end if #t > 0 then if message and message ~= "" then report_nodes("%s, %s glyphs: % t",message,#t,t) else report_nodes("%s glyphs: % t",#t,t) end end return false end function nodes.handlers.checkforleaks(sparse) local l = { } local q = node.usedlist() for p in traverse(q) do local s = table.serialize(nodes.astable(p,sparse),nodecodes[p.id]) l[s] = (l[s] or 0) + 1 end node.flush_list(q) for k, v in next, l do write_nl(formatters["%s * %s"](v,k)) end end local f_sequence = formatters["U+%04X:%s"] local function tosequence(start,stop,compact) if start then local t = { } while start do local id = start.id if id == glyph_code then local c = start.char if compact then if start.components then t[#t+1] = tosequence(start.components,nil,compact) else t[#t+1] = utfchar(c) end else t[#t+1] = f_sequence(c,utfchar(c)) end elseif id == whatsit_code and start.subtype == localpar_code or start.subtype == dir_code then t[#t+1] = "[" .. start.dir .. "]" elseif id == rule_code then if compact then t[#t+1] = "|" else t[#t+1] = nodecodes[id] end else if compact then t[#t+1] = "[]" else t[#t+1] = nodecodes[id] end end if start == stop then break else start = start.next end end if compact then return concat(t) else return concat(t," ") end else return "[empty]" end end nodes.tosequence = tosequence function nodes.report(t,done) report_nodes("output %a, %changed %a, %s nodes",status.output_active,done,count_nodes(t)) end function nodes.packlist(head) local t = { } for n in traverse(head) do t[#t+1] = tostring(n) end return t end function nodes.idstostring(head,tail) local t, last_id, last_n = { }, nil, 0 for n in traverse_nodes(head,tail) do -- hm, does not stop at tail local id = n.id if not last_id then last_id, last_n = id, 1 elseif last_id == id then last_n = last_n + 1 else if last_n > 1 then t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?") else t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?") end last_id, last_n = id, 1 end if n == tail then break end end if not last_id then t[#t+1] = "no nodes" elseif last_n > 1 then t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?") else t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?") end return concat(t," ") end -- function nodes.xidstostring(head,tail) -- only for special tracing of backlinks -- local n = head -- while n.next do -- n = n.next -- end -- local t, last_id, last_n = { }, nil, 0 -- while n do -- local id = n.id -- if not last_id then -- last_id, last_n = id, 1 -- elseif last_id == id then -- last_n = last_n + 1 -- else -- if last_n > 1 then -- t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?") -- else -- t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?") -- end -- last_id, last_n = id, 1 -- end -- if n == head then -- break -- end -- n = n.prev -- end -- if not last_id then -- t[#t+1] = "no nodes" -- elseif last_n > 1 then -- t[#t+1] = formatters["[%s*%s]"](last_n,nodecodes[last_id] or "?") -- else -- t[#t+1] = formatters["[%s]"](nodecodes[last_id] or "?") -- end -- return table.concat(table.reversed(t)," ") -- end local function showsimplelist(h,depth,n) while h do write_nl(rep(" ",n) .. tostring(h)) if not depth or n < depth then local id = h.id if id == hlist_code or id == vlist_code then showsimplelist(h.list,depth,n+1) end end h = h.next end end --~ \startluacode --~ callback.register('buildpage_filter',function() nodes.show_simple_list(tex.lists.contrib_head) end) --~ \stopluacode --~ \vbox{b\footnote{n}a} --~ \startluacode --~ callback.register('buildpage_filter',nil) --~ \stopluacode nodes.showsimplelist = function(h,depth) showsimplelist(h,depth,0) end local function listtoutf(h,joiner,textonly,last) local joiner = (joiner == true and utfchar(0x200C)) or joiner -- zwnj local w = { } while h do local id = h.id if id == glyph_code then -- always true w[#w+1] = utfchar(h.char) if joiner then w[#w+1] = joiner end elseif id == disc_code then local pre, rep, pos = h.pre, h.replace, h.post w[#w+1] = formatters["[%s|%s|%s]"] ( pre and listtoutf(pre,joiner,textonly) or "", rep and listtoutf(rep,joiner,textonly) or "", mid and listtoutf(mid,joiner,textonly) or "" ) elseif textonly then if id == glue_code and h.spec and h.spec.width > 0 then w[#w+1] = " " end else w[#w+1] = "[-]" end if h == last then break else h = h.next end end return concat(w) end nodes.listtoutf = listtoutf local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" } local function showboxes(n,symbol,depth) depth, symbol = depth or 0, symbol or "." for n in traverse_nodes(n) do local id = n.id if id == hlist_code or id == vlist_code then local s = n.subtype report_nodes(rep(symbol,depth) .. what[s] or s) showboxes(n.list,symbol,depth+1) end end end nodes.showboxes = showboxes local ptfactor = dimenfactors.pt local bpfactor = dimenfactors.bp local stripper = lpeg.patterns.stripzeros -- start redefinition -- -- -- if fmt then -- -- return formatters[fmt](n*dimenfactors[unit],unit) -- -- else -- -- return match(formatters["%.20f"](n*dimenfactors[unit]),"(.-0?)0*$") .. unit -- -- end -- -- redefined: local dimenfactors = number.dimenfactors local function numbertodimen(d,unit,fmt,strip) if not d then local str = formatters[fmt](0,unit) return strip and lpegmatch(stripper,str) or str end local t = type(d) if t == 'string' then return d end if unit == true then unit = "pt" fmt = "%0.5f%s" else unit = unit or 'pt' if not fmt then fmt = "%s%s" elseif fmt == true then fmt = "%0.5f%s" end end if t == "number" then local str = formatters[fmt](d*dimenfactors[unit],unit) return strip and lpegmatch(stripper,str) or str end local id = d.id if id == kern_code then local str = formatters[fmt](d.width*dimenfactors[unit],unit) return strip and lpegmatch(stripper,str) or str end if id == glue_code then d = d.spec end if not d or not d.id == spec_code then local str = formatters[fmt](0,unit) return strip and lpegmatch(stripper,str) or str end local width = d.width local plus = d.stretch_order local minus = d.shrink_order local stretch = d.stretch local shrink = d.shrink if plus ~= 0 then plus = " plus " .. stretch/65536 .. fillcodes[plus] elseif stretch ~= 0 then plus = formatters[fmt](stretch*dimenfactors[unit],unit) plus = " plus " .. (strip and lpegmatch(stripper,plus) or plus) else plus = "" end if minus ~= 0 then minus = " minus " .. shrink/65536 .. fillcodes[minus] elseif shrink ~= 0 then minus = formatters[fmt](shrink*dimenfactors[unit],unit) minus = " minus " .. (strip and lpegmatch(stripper,minus) or minus) else minus = "" end local str = formatters[fmt](d.width*dimenfactors[unit],unit) return (strip and lpegmatch(stripper,str) or str) .. plus .. minus end number.todimen = numbertodimen function number.topoints (n,fmt) return numbertodimen(n,"pt",fmt) end function number.toinches (n,fmt) return numbertodimen(n,"in",fmt) end function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end function number.toscaledpoints(n) return n .. "sp" end function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end function number.topicas (n,fmt) return numbertodimen(n "pc",fmt) end function number.todidots (n,fmt) return numbertodimen(n,"dd",fmt) end function number.tociceros (n,fmt) return numbertodimen(n,"cc",fmt) end function number.tonewdidots (n,fmt) return numbertodimen(n,"nd",fmt) end function number.tonewciceros (n,fmt) return numbertodimen(n,"nc",fmt) end -- stop redefinition local points = function(n) if not n or n == 0 then return "0pt" elseif type(n) == "number" then return lpegmatch(stripper,format("%.5fpt",n*ptfactor)) -- faster than formatter else return numbertodimen(n,"pt",true,true) -- also deals with nodes end end local basepoints = function(n) if not n or n == 0 then return "0bp" elseif type(n) == "number" then return lpegmatch(stripper,format("%.5fbp",n*bpfactor)) -- faster than formatter else return numbertodimen(n,"bp",true,true) -- also deals with nodes end end local pts = function(n) if not n or n == 0 then return "0pt" elseif type(n) == "number" then return format("%.5fpt",n*ptfactor) -- faster than formatter else return numbertodimen(n,"pt",true) -- also deals with nodes end end local nopts = function(n) if not n or n == 0 then return "0" else return format("%.5f",n*ptfactor) -- faster than formatter end end number.points = points number.basepoints = basepoints number.pts = pts number.nopts = nopts local colors = { } tracers.colors = colors local unsetvalue = attributes.unsetvalue local a_color = attributes.private('color') local a_colormodel = attributes.private('colormodel') local m_color = attributes.list[a_color] or { } function colors.set(n,c,s) local mc = m_color[c] if not mc then n[a_color] = unsetvalue else if not n[a_colormodel] then n[a_colormodel] = s or 1 end n[a_color] = mc end return n end function colors.setlist(n,c,s) local f = n while n do local mc = m_color[c] if not mc then n[a_color] = unsetvalue else if not n[a_colormodel] then n[a_colormodel] = s or 1 end n[a_color] = mc end n = n.next end return f end function colors.reset(n) n[a_color] = unsetvalue return n end -- maybe local transparencies = { } tracers.transparencies = transparencies local a_transparency = attributes.private('transparency') local m_transparency = attributes.list[a_transparency] or { } function transparencies.set(n,t) local mt = m_transparency[t] if not mt then n[a_transparency] = unsetvalue else n[a_transparency] = mt end return n end function transparencies.setlist(n,c,s) local f = n while n do local mt = m_transparency[c] if not mt then n[a_transparency] = unsetvalue else n[a_transparency] = mt end n = n.next end return f end function transparencies.reset(n) n[a_transparency] = unsetvalue return n end -- for the moment here local visualizers = nodes.visualizers or { } nodes.visualizers = visualizers function visualizers.handler(head) return head, false end -- we could cache attribute lists and set attr (copy will increment count) .. todo .. -- although tracers are used seldom local function setproperties(n,c,s) local mm = texgetattribute(a_colormodel) n[a_colormodel] = mm > 0 and mm or 1 n[a_color] = m_color[c] n[a_transparency] = m_transparency[c] return n end tracers.setproperties = setproperties function tracers.setlistv(n,c,s) local f = n local mc = m_color[c] local mt = m_transparency[c] local mm = texgetattribute(a_colormodel) if mm <= 0 then mm = 1 end while n do n[a_colormodel] = mm n[a_color] = mc n[a_transparency] = mt n = n.next end return f end function tracers.resetproperties(n) n[a_color] = unsetvalue n[a_transparency] = unsetvalue return n end function tracers.rule(w,h,d,c,s) -- so some day we can consider using literals (speedup) return setproperties(new_rule(w,h,d),c,s) end