node-pro.lua /size: 6778 b    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['node-pro'] = {
2    version   = 1.001,
3    comment   = "companion to node-ini.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local trace_callbacks  = false  trackers  .register("nodes.callbacks",        function(v) trace_callbacks  = v end)
10local force_processors = false  directives.register("nodes.processors.force", function(v) force_processors = v end)
11
12local report_nodes = logs.reporter("nodes","processors")
13
14local nodes        = nodes
15local tasks        = nodes.tasks
16local nuts         = nodes.nuts
17local tonut        = nodes.tonut
18
19nodes.processors   = nodes.processors or { }
20local processors   = nodes.processors
21
22-- vbox: grouptype: vbox vtop output split_off split_keep  | box_type: exactly|aditional
23-- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode)     | box_type: exactly|aditional
24
25local actions = tasks.actions("processors")
26
27do
28
29    local isglyph = nuts.isglyph
30    local getnext = nuts.getnext
31
32    local utfchar = utf.char
33    local concat  = table.concat
34
35    local n = 0
36
37    local function reconstruct(head) -- we probably have a better one
38        local t, n, h = { }, 0, head
39        while h do
40            n = n + 1
41            local char, id = isglyph(h)
42            if char then -- todo: disc etc
43                t[n] = utfchar(char)
44            else
45                t[n] = "[]"
46            end
47            h = getnext(h)
48        end
49        return concat(t)
50    end
51
52    function processors.tracer(what,head,groupcode,before,after,show)
53        if not groupcode then
54            groupcode = "unknown"
55        elseif groupcode == "" then
56            groupcode = "mvl"
57        end
58        n = n + 1
59        if show then
60            report_nodes("%s: location %a, group %a, # before %a, # after %s, stream: %s",what,n,groupcode,before,after,reconstruct(head))
61        else
62            report_nodes("%s: location %a, group %a, # before %a, # after %s",what,n,groupcode,before,after)
63        end
64    end
65
66end
67
68do
69
70    local hasglyph    = nodes.hasglyph
71    local count_nodes = nodes.countall
72
73    local texget      = tex.get
74
75    local tracer      = processors.tracer
76
77    local function pre_linebreak_filter(head,groupcode)
78        local found = force_processors or hasglyph(head)
79        if found then
80            if trace_callbacks then
81                local before = count_nodes(head,true)
82                head = actions(head,groupcode)
83                local after = count_nodes(head,true)
84                tracer("pre_linebreak",head,groupcode,before,after,true)
85            else
86                head = actions(head,groupcode)
87            end
88        elseif trace_callbacks then
89            local n = count_nodes(head,false)
90            tracer("pre_linebreak",head,groupcode,n,n)
91        end
92        return head
93    end
94
95    local function hpack_filter(head,groupcode,size,packtype,direction,attributes)
96        local found = force_processors or hasglyph(head)
97        if found then
98            --
99            -- yes or no or maybe an option
100            --
101            if not direction then
102                direction = texget("textdir")
103            end
104            --
105            if trace_callbacks then
106                local before = count_nodes(head,true)
107                head = actions(head,groupcode,size,packtype,direction,attributes)
108                local after = count_nodes(head,true)
109                tracer("hpack",head,groupcode,before,after,true)
110            else
111                head = actions(head,groupcode,size,packtype,direction,attributes)
112            end
113        elseif trace_callbacks then
114            local n = count_nodes(head,false)
115            tracer("hpack",head,groupcode,n,n)
116        end
117        return head
118    end
119
120    processors.pre_linebreak_filter = pre_linebreak_filter
121    processors.hpack_filter         = hpack_filter
122
123    do
124
125        local hpack = nodes.hpack
126
127        function nodes.fullhpack(head,...)
128            return hpack((hpack_filter(head)),...)
129        end
130
131    end
132
133    do
134
135        local hpack = nuts.hpack
136
137        function nuts.fullhpack(head,...)
138            return hpack(tonut(hpack_filter(tonode(head))),...)
139        end
140
141    end
142
143    callbacks.register('pre_linebreak_filter', pre_linebreak_filter, "horizontal manipulations (before par break)")
144    callbacks.register('hpack_filter'        , hpack_filter,         "horizontal manipulations (before hbox creation)")
145
146end
147
148do
149    -- Beware, these are packaged boxes so no firstglyph test needed. Maybe some day I'll add a hash
150    -- with valid groupcodes. Watch out, much can pass twice, for instance vadjust passes two times,
151
152    local actions     = tasks.actions("finalizers") -- head, where
153    local count_nodes = nodes.countall
154
155    local tracer      = processors.tracer
156
157    local function post_linebreak_filter(head,groupcode)
158        if trace_callbacks then
159            local before = count_nodes(head,true)
160            head = actions(head,groupcode)
161            local after = count_nodes(head,true)
162            tracer("post_linebreak",head,groupcode,before,after,true)
163        else
164            head = actions(head,groupcode)
165        end
166        return head
167    end
168
169    processors.post_linebreak_filter = post_linebreak_filter
170
171    callbacks.register("post_linebreak_filter", post_linebreak_filter,"horizontal manipulations (after par break)")
172
173end
174
175do
176
177    ----- texnest       = tex.nest
178    local getnest       = tex.getnest
179
180    local getlist       = nuts.getlist
181    local setlist       = nuts.setlist
182    local getsubtype    = nuts.getsubtype
183
184    local linelist_code = nodes.listcodes.line
185
186    local actions       = tasks.actions("contributers")
187
188    function processors.contribute_filter(groupcode)
189        if groupcode == "box" then -- "pre_box"
190            local whatever = getnest()
191            if whatever then
192                local line = whatever.tail
193                if line then
194                    line = tonut(line)
195                    if getsubtype(line) == linelist_code then
196                        local head = getlist(line)
197                        if head then
198                            local result = actions(head,groupcode,line)
199                            if result and result ~= head then
200                                setlist(line,result)
201                            end
202                        end
203                    end
204                end
205            end
206        end
207    end
208
209    callbacks.register("contribute_filter", processors.contribute_filter,"things done with lines")
210
211end
212
213statistics.register("h-node processing time", function()
214    return statistics.elapsedseconds(nodes,"including kernel") -- hm, ok here?
215end)
216