diff options
Diffstat (limited to 'tex/context/base/node-fin.lua')
-rw-r--r-- | tex/context/base/node-fin.lua | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/tex/context/base/node-fin.lua b/tex/context/base/node-fin.lua new file mode 100644 index 000000000..c6e3be448 --- /dev/null +++ b/tex/context/base/node-fin.lua @@ -0,0 +1,442 @@ +if not modules then modules = { } end modules ['node-fin'] = { + version = 1.001, + comment = "companion to node-fin.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this module is being reconstructed + +local next, type, format = next, type, string.format +local texsprint = tex.sprint + +local ctxcatcodes = tex.ctxcatcodes + +local glyph = node.id('glyph') +local glue = node.id('glue') +local rule = node.id('rule') +local whatsit = node.id('whatsit') +local hlist = node.id('hlist') +local vlist = node.id('vlist') + +local has_attribute = node.has_attribute +local copy_node = node.copy + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +states = states or { } +shipouts = shipouts or { } + +local numbers = attributes.numbers +local trigger = attributes.private('trigger') +local triggering = false + +-- these two will be like trackers + +function states.enabletriggering() + triggering = true +end +function states.disabletriggering() + triggering = false +end + +-- the following code is no longer needed due to the new backend +-- but we keep it around for a while as an example +-- +-- states.collected = states.collected or { } +-- +-- storage.register("states/collected", states.collected, "states.collected") +-- +-- local collected = states.collected +-- +-- function states.collect(str) +-- collected[#collected+1] = str +-- end +-- +-- function states.flush() +-- if #collected > 0 then +-- for i=1,#collected do +-- texsprint(ctxcatcodes,collected[i]) -- we're in context mode anyway +-- end +-- collected = { } +-- states.collected = collected +-- end +-- end +-- +-- function states.check() +-- texio.write_nl(concat(collected,"\n")) +-- end + +-- we used to do the main processor loop here and call processor for each node +-- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk) +-- so that we moved looping to the processor itself; this may lead to a bit of +-- duplicate code once that we have more state handlers + +local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer + local namespace = plugin.namespace + if namespace.enabled ~= false then -- this test will go away + starttiming(attributes) + local done, used, ok = false, nil, false + local attribute = namespace.attribute or numbers[plugin.name] -- todo: plugin.attribute + local processor = plugin.processor + if processor then + local initializer = plugin.initializer + local resolver = plugin.resolver + local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip ! + if initializer then + initializer(namespace,attribute,head) + end + head, ok = processor(namespace,attribute,head,inheritance) + if ok then + local finalizer = plugin.finalizer + if finalizer then + head, ok, used = finalizer(namespace,attribute,head) + if used then + local flusher = plugin.flusher + if flusher then + head = flusher(namespace,attribute,head,used) + end + end + end + done = true + end + end + stoptiming(attributes) + return head, done + else + return head, false + end +end + +nodes.process_attribute = process_attribute + +function nodes.install_attribute_handler(plugin) -- we need to avoid this nested function + return function(head) + return process_attribute(head,plugin) + end +end + +--~ experiment (maybe local to function makes more sense) +--~ +--~ plugindata = { } +--~ +--~ local template = [[ +--~ local plugin = plugindata["%s"] +--~ local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming +--~ local namespace = plugin.namespace +--~ local attribute = namespace.attribute +--~ local processor = plugin.processor +--~ local initializer = plugin.initializer +--~ local resolver = plugin.resolver +--~ local finalizer = plugin.finalizer +--~ local flusher = plugin.flusher +--~ return function (head) +--~ if namespace.enabled then +--~ starttiming(attributes) +--~ local done, used, ok = false, nil, false +--~ if procesxsor then +--~ local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip ! +--~ if initializer then +--~ initializer(namespace,attribute,head) +--~ end +--~ head, ok = processor(namespace,attribute,head,inheritance) +--~ if ok then +--~ if finalizer then +--~ head, ok, used = finalizer(namespace,attribute,head) +--~ if used and flusher then +--~ head = flusher(namespace,attribute,head,used) +--~ end +--~ end +--~ done = true +--~ end +--~ end +--~ stoptiming(attributes) +--~ return head, done +--~ else +--~ return head, false +--~ end +--~ end +--~ ]] +--~ +--~ function nodes.install_attribute_handler(plugin) -- we need to avoid this nested function +--~ plugindata[plugin.name] = plugin +--~ local str = format(template,plugin.name) +--~ return loadstring(str)() +--~ end + +-- the injectors + +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +local nsdata, nsdone, nsforced, nsselector, nstrigger +local current, current_selector, done = 0, 0, false -- nb, stack has a local current ! + +function states.initialize(namespace,attribute,head) + nsdata, nsnone = namespace.data, namespace.none + nsforced, nsselector, nslistwise = namespace.forced, namespace.selector, namespace.listwise + nstrigger = triggering and namespace.triggering and trigger + current, current_selector, done = 0, 0, false -- todo: done cleanup +end + +function states.finalize(namespace,attribute,head) -- is this one ok? + if current > 0 and nsnone then + local id = head.id + if id == hlist or id == vlist then + local list = head.list + if list then + head.list = insert_node_before(list,list,copy_node(nsnone)) + end + else + head = insert_node_before(head,head,copy_node(nsnone)) + end + return head, true, true + end + return head, false, false +end + +local function process(namespace,attribute,head,inheritance,default) -- one attribute + local stack, done = head, false + while stack do + local id = stack.id + -- we need to deal with literals too (reset as well as oval) + -- if id == glyph or (id == whatsit and stack.subtype == 8) or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + if id == glyph or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + local c = has_attribute(stack,attribute) + if c then + if default and c == inheritance then + if current ~= default then + head = insert_node_before(head,stack,copy_node(nsdata[default])) + current, done = default, true + end + elseif current ~= c then + head = insert_node_before(head,stack,copy_node(nsdata[c])) + current, done = c, true + end + -- here ? compare selective + if id == glue then --leader + -- same as *list + local content = stack.leader + if content then + local savedcurrent = current + local ci = content.id + if ci == hlist or ci == vlist then + -- else we reset inside a box unneeded, okay, the downside is + -- that we trigger color in each repeated box, so there is room + -- for improvement here + current = 0 + end + local ok = false + if nstrigger and has_attribute(stack,nstrigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.leader, ok = process(namespace,attribute,content,inheritance,outer) + else + stack.leader, ok = process(namespace,attribute,content,inheritance,default) + end + else + stack.leader, ok = process(namespace,attribute,content,inheritance,default) + end + current = savedcurrent + done = done or ok + end + end + elseif default and inheritance then + if current ~= default then + head = insert_node_before(head,stack,copy_node(nsdata[default])) + current, done = default, true + end + elseif current > 0 then + head = insert_node_before(head,stack,copy_node(nsnone)) + current, done = 0, true + end + elseif id == hlist or id == vlist then + local content = stack.list + if content then + local ok = false + if nstrigger and has_attribute(stack,nstrigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.list, ok = process(namespace,attribute,content,inheritance,outer) + else + stack.list, ok = process(namespace,attribute,content,inheritance,default) + end + else + stack.list, ok = process(namespace,attribute,content,inheritance,default) + end + done = done or ok + end + end + stack = stack.next + end + return head, done +end + +states.process = process + +-- we can force a selector, e.g. document wide color spaces, saves a little +-- watch out, we need to check both the selector state (like colorspace) and +-- the main state (like color), otherwise we get into troubles when a selector +-- state changes while the main state stays the same (like two glyphs following +-- each other with the same color but different color spaces e.g. \showcolor) + +local function selective(namespace,attribute,head,inheritance,default) -- two attributes + local stack, done = head, false + while stack do + local id = stack.id + -- we need to deal with literals too (reset as well as oval) + -- if id == glyph or (id == whatsit and stack.subtype == 8) or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + if id == glyph or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + local c = has_attribute(stack,attribute) + if c then + if default and c == inheritance then + if current ~= default then + local data = nsdata[default] + head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) + current, done = default, true + end + else + local s = has_attribute(stack,nsselector) + if current ~= c or current_selector ~= s then + local data = nsdata[c] + head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) + current, current_selector, done = c, s, true + end + end + elseif default and inheritance then + if current ~= default then + local data = nsdata[default] + head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) + current, done = default, true + end + elseif current > 0 then + head = insert_node_before(head,stack,copy_node(nsnone)) + current, current_selector, done = 0, 0, true + end + if id == glue then -- leader + -- same as *list + local content = stack.leader + if content then + local savedcurrent = current + local ci = content.id + if ci == hlist or ci == vlist then + -- else we reset inside a box unneeded, okay, the downside is + -- that we trigger color in each repeated box, so there is room + -- for improvement here + current = 0 + end + local ok = false + if nstrigger and has_attribute(stack,nstrigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.leader, ok = selective(namespace,attribute,content,inheritance,outer) + else + stack.leader, ok = selective(namespace,attribute,content,inheritance,default) + end + else + stack.leader, ok = selective(namespace,attribute,content,inheritance,default) + end + current = savedcurrent + done = done or ok + end + end + elseif id == hlist or id == vlist then + local content = stack.list + if content then + local ok = false + if nstrigger and has_attribute(stack,nstrigger) then + local outer = has_attribute(stack,attribute) + if outer ~= inheritance then + stack.list, ok = selective(namespace,attribute,content,inheritance,outer) + else + stack.list, ok = selective(namespace,attribute,content,inheritance,default) + end + else + stack.list, ok = selective(namespace,attribute,content,inheritance,default) + end + done = done or ok + end + end + stack = stack.next + end + return head, done +end + +states.selective = selective + +-- Ideally the next one should be merged with the previous but keeping it separate is +-- safer. We deal with two situations: efficient boxwise (layoutareas) and mixed layers +-- (as used in the stepper). In the stepper we cannot use the box branch as it involves +-- paragraph lines and then getsmixed up. A messy business (esp since we want to be +-- efficient). + +local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise + local stack, done = head, false + local current, depth = default or 0, 0 + while stack do + local id = stack.id + if id == glyph or (id == rule and stack.width ~= 0) or (id == glue and stack.leader) then -- or disc + local c = has_attribute(stack,attribute) + if c then + if current ~= c then + head = insert_node_before(head,stack,copy_node(nsdata[c])) + depth = depth + 1 + current, done = c, true + end + if id == glue then + local content = stack.leader + if content then -- unchecked + local ok = false + stack.leader, ok = stacked(namespace,attribute,content,current) + done = done or ok + end + end + elseif default then + -- + elseif current > 0 then + head = insert_node_before(head,stack,copy_node(nsnone)) + depth = depth - 1 + current, done = 0, true + end + elseif id == hlist or id == vlist then + local content = stack.list + if content then + -- the problem is that broken lines gets the attribute which can be a later one + if nslistwise then + local c = has_attribute(stack,attribute) + if c and current ~= c and nslistwise[c] then -- viewerlayer + local p = current + current, done = c, true + head = insert_node_before(head,stack,copy_node(nsdata[c])) + stack.list = stacked(namespace,attribute,content,current) + head, stack = insert_node_after(head,stack,copy_node(nsnone)) + current = p + else + local ok = false + stack.list, ok = stacked(namespace,attribute,content,current) + done = done or ok + end + else + local ok = false + stack.list, ok = stacked(namespace,attribute,content,current) + done = done or ok + end + end + end + stack = stack.next + end + while depth > 0 do + head = insert_node_after(head,stack,copy_node(nsnone)) + depth = depth -1 + end + return head, done +end + +states.stacked = stacked + +-- -- -- + +statistics.register("attribute processing time", function() + return statistics.elapsedseconds(attributes,"front- and backend") +end) |