if not modules then modules = { } end modules ['luat-cbk'] = { version = 1.001, comment = "companion to luat-lib.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } local insert, remove, find, format = table.insert, table.remove, string.find, string.format local collectgarbage, type, next = collectgarbage, type, next local round = math.round local trace_checking = false trackers.register("memory.checking", function(v) trace_checking = v end) local report_callbacks = logs.new("callbacks") local report_memory = logs.new("memory") --[[ldx--
Callbacks are the real asset of
When you (temporarily) want to install a callback function, and after a while wants to revert to the original one, you can use the following two functions.
--ldx]]-- local trace_callbacks = false trackers.register("system.callbacks", function(v) trace_callbacks = v end) local trace_calls = false -- only used when analyzing performance and initializations local register_callback, find_callback, list_callbacks = callback.register, callback.find, callback.list local frozen, stack, list = { }, { }, callbacks.list if not callbacks.list then -- otherwise counters get reset list = utilities.storage.allocate(list_callbacks()) for k, _ in next, list do list[k] = 0 end callbacks.list = list end local delayed = table.tohash { "buildpage_filter", } if trace_calls then local functions = { } local original = register_callback register_callback = function(name,func) if type(func) == "function" then if functions[name] then functions[name] = func return find_callback(name) else functions[name] = func local cnuf = function(...) list[name] = list[name] + 1 return functions[name](...) end return original(name,cnuf) end else return original(name,func) end end end local function frozen_message(what,name) report_callbacks("not %s frozen '%s' (%s)",what,name,frozen[name]) end local function frozen_callback(name) return nil, format("callback '%s' is frozen (%s)",name,frozen[name]) end local function state(name) local f = find_callback(name) if f == false then return "disabled" elseif f then return "enabled" else return "undefined" end end function callbacks.known(name) return list[name] end function callbacks.report() for name, _ in table.sortedhash(list) do local str = frozen[name] if str then report_callbacks("%s: %s -> %s",state(name),name,str) else report_callbacks("%s: %s",state(name),name) end end end function callbacks.table() local NC, NR, verbatim = context.NC, context.NR, context.type context.starttabulate { "|l|l|p|" } for name, _ in table.sortedhash(list) do NC() verbatim(name) NC() verbatim(state(name)) NC() context(frozen[name] or "") NC() NR() end context.stoptabulate() end function callbacks.freeze(name,freeze) freeze = type(freeze) == "string" and freeze if find(name,"%*") then local pattern = name for name, _ in next, list do if find(name,pattern) then frozen[name] = freeze or frozen[name] or "frozen" end end else frozen[name] = freeze or frozen[name] or "frozen" end end function callbacks.register(name,func,freeze) if frozen[name] then if trace_callbacks then frozen_message("registering",name) end return frozen_callback(name) elseif freeze then frozen[name] = (type(freeze) == "string" and freeze) or "registered" end if delayed[name] and environment.initex then return nil end return register_callback(name,func) end function callback.register(name,func) -- original if not frozen[name] then return register_callback(name,func) elseif trace_callbacks then frozen_message("registering",name) end return frozen_callback(name) end function callbacks.push(name, func) if not frozen[name] then local sn = stack[name] if not sn then sn = { } stack[name] = sn end insert(sn,find_callback(name)) register_callback(name, func) elseif trace_callbacks then frozen_message("pushing",name) end end function callbacks.pop(name) if not frozen[name] then local sn = stack[name] if not sn or #sn == 0 then -- some error register_callback(name, nil) -- ! really needed else -- this fails: register_callback(name, remove(stack[name])) local func = remove(sn) register_callback(name, func) end end end if trace_calls then statistics.register("callback details", function() local t = { } -- todo: pass function to register and quit at nil for name, n in table.sortedhash(list) do if n > 0 then t[#t+1] = format("%s -> %s",name,n) end end return t end) end --~ -- somehow crashes later on --~ --~ callbacks.freeze("find_.*_file","finding file") --~ callbacks.freeze("read_.*_file","reading file") --~ callbacks.freeze("open_.*_file","opening file") --[[ldx--The simple case is to remove the callback:
callbacks.push('linebreak_filter')
... some actions ...
callbacks.pop('linebreak_filter')
Often, in such case, another callback or a macro call will pop the original.
In practice one will install a new handler, like in:
callbacks.push('linebreak_filter', function(...)
return something_done(...)
end)
Even more interesting is:
callbacks.push('linebreak_filter', function(...)
callbacks.pop('linebreak_filter')
return something_done(...)
end)
This does a one-shot.
--ldx]]-- --[[ldx--Callbacks may result in
At some point in the development we did some tests with counting nodes (in this case 121049).
setstepmul | seconds | megabytes |
200 | 24.0 | 80.5 |
175 | 21.0 | 78.2 |
150 | 22.0 | 74.6 |
160 | 22.0 | 74.6 |
165 | 21.0 | 77.6 |
125 | 21.5 | 89.2 |
100 | 21.5 | 88.4 |
The following code is kind of experimental. In the documents
that describe the development of