trac-bld.lmt /size: 7604 b    last modification: 2024-01-16 10:22
1if not modules then modules = { } end modules ['trac-bld'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to trac-bld.mkxl",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10local nuts          = nodes.nuts
11local tonut         = nodes.tonut
12
13local insertafter   = nuts.insertafter
14local hpack_string  = nuts.typesetters.tohpack
15local hpack         = nuts.hpack
16local getheight     = nuts.getheight
17local getid         = nuts.getid
18local setwhd        = nuts.setwhd
19local setoffsets    = nuts.setoffsets
20local setlink       = nuts.setlink
21
22local newrule       = nuts.pool.virtualrule
23
24local nodecodes     = nodes.nodecodes
25
26local hlist_code    = nodecodes.hlist
27local vlist_code    = nodecodes.vlist
28local rule_code     = nodecodes.rule
29
30local setcolor      = nodes.tracers.colors.set
31
32local buildcodes    = tex.buildcodes
33
34local ejectpenalty  = tex.magicconstants.eject_penalty
35
36local report     = logs.reporter("build")
37
38local xoffset    =  4 * 65536
39local yoffset    =  1 * 65536
40local usedfont   = false
41
42local f_status_y = string.formatters["%i : badness=%i costs=%i height=%p depth=%p stretch=%p shrink=%p hasfil=%l"]
43local f_status_n = string.formatters["%i"]
44
45local variables  = interfaces.variables
46local v_after    = variables.after
47local v_before   = variables.before
48local v_reset    = variables.reset
49local v_yes      = variables.yes
50local v_all      = variables.all
51
52local done       = { }
53local last       = false
54local count      = 0
55local forced     = { } -- we can combine forced and ignored but then we can't disable
56local ignored    = { } -- the callback easily
57local detail     = false
58local enabled    = false
59local ejecting   = false
60
61-- use
62
63local function checkedforce(moveon,fireup,penalty)
64    local how = forced[count]
65    ejecting = fireup and penalty == ejectpenalty
66    if how == v_after then
67        report("forcing pagebreak %s %i",how,count)
68        forced[count]  = nil
69        ignored[count] = true
70-- check on moveon
71        return true, true
72    elseif how == v_before then
73        report("forcing pagebreak %s %i",how,count)
74        forced[count]  = nil
75        ignored[count] = true
76        return false, true
77    else
78        return moveon, fireup
79    end
80end
81
82local function check(current,moveon,fireup,badness,costs,penalty)
83    if last then
84        count = count + 1
85    end
86    return checkedforce(moveon,fireup,penalty)
87end
88
89local forceactions = {
90    [buildcodes.step]  = step,
91    [buildcodes.check] = check,
92}
93
94local function handleforce(what,...)
95    local action = forceactions[what]
96    if action then
97        return action(...)
98    end
99end
100
101local function disableforce()
102    count   = 0
103    forced  = { }
104    ignored = { }
105    if enabled and not trace then
106        callback.register("show_build")
107    end
108    enabled = false
109end
110
111local function enableforce()
112    if not enabled and not trace then
113        callback.register("show_build",handleforce)
114    end
115    enabled = true
116end
117
118interfaces.implement {
119    name      = "registerforcedbreak",
120    public    = true,
121    protected = true,
122    arguments = "2 optionals",
123    actions   = function(where,how)
124        if where == v_reset then
125            disableforce()
126        else
127            for n in string.gmatch(where,"%d+") do
128                forced[tonumber(n)] = how
129            end
130            enableforce()
131        end
132    end
133}
134
135interfaces.implement {
136    name      = "resetforcedbreak",
137    public    = true,
138    protected = true,
139    actions   = disableforce,
140}
141
142-- show
143
144local function initialize()
145    usedfont = nodes.visualizers.getusedfont()
146end
147
148local function step(current)
149    current = tonut(current)
150    local id = getid(current)
151    if id == hlist_code or id == vlist_code or id == rule_code then
152        last = current
153    end
154end
155
156local function check(current,moveon,fireup,badness,costs,penalty)
157    if last then
158        count = count + 1
159        done[#done+1] = {
160            count   = count,
161            line    = last,
162            badness = badness,
163            penalty = penalty,
164            costs   = costs,
165            height  = 0,
166            depth   = 0,
167            stretch = 0,
168            shrink  = 0,
169            hasfil  = false,
170        }
171    end
172    return checkedforce(moveon,fireup,penalty)
173end
174
175-- local function skip(current)
176--     if last then
177--         count = count + 1
178--         done[#done+1] = {
179--             count = count,
180--             line  = last,
181--         }
182--     end
183-- end
184
185local function move(current,height,depth,stretch,shrink,hasfil)
186    local d = done[#done]
187    if d then
188        d.height  = height
189        d.depth   = depth
190        d.stretch = stretch
191        d.shrink  = shrink
192        d.hasfil  = hasfil
193    end
194end
195
196-- local function fireup(current)
197-- end
198
199-- local function wrapup()
200-- end
201
202local showactions = {
203    [buildcodes.initialize] = initialize,
204    [buildcodes.step]       = step,
205    [buildcodes.check]      = check,
206 -- [buildcodes.skip]       = skip,
207    [buildcodes.move]       = move,
208 -- [buildcodes.fireup]     = fireup,
209 -- [buildcodes.wrapup]     = wrapup,
210}
211
212interfaces.implement {
213    name    = "build_page_before_shipout",
214    actions = function()
215        if trace then
216            done[#done] = nil
217            if #done > 0 then
218                for i=1,#done do
219                    local data    = done[i]
220                    local line    = data.line
221                    local count   = data.count
222                    if not ignored[count] then
223                        local badness = data.badness
224                        if detail and badness then
225                            data = f_status_y(count,badness,data.costs,data.height,data.depth,data.stretch,data.shrink,data.hasfil)
226                        else
227                            data = f_status_n(count)
228                        end
229                        local text   = hpack_string(data,usedfont)
230                        local height = getheight(text)
231                        local rule   = newrule(height,height,0)
232                        setcolor(rule,"trace:db")
233                        setoffsets(rule,-(height+xoffset),0)
234                        text = hpack(setlink(rule,text))
235                        setwhd(text,0,0,0)
236                        setoffsets(text,0,-(height+yoffset))
237                        insertafter(line,line,text)
238                    end
239                end
240                if ejecting then
241                    count = 0
242                    done = { }
243                end
244            end
245        end
246    end
247}
248
249interfaces.implement {
250    name    = "build_page_after_shipout",
251    actions = function()
252        if trace then
253            done = { }
254        end
255        last = false
256    end
257}
258
259local function build_page_setup(v)
260    if v == "all" or v == v_all then
261        trace  = true
262        detail = true
263    elseif v == true or v == "yes" or v == v_yes then
264        trace  = true
265        detail = false
266    else
267        trace  = false
268        detail = false
269    end
270    if trace then
271        callback.register("show_build", function(what,...)
272            local action = showactions[what]
273            if action then
274                return action(...)
275            end
276        end)
277    elseif not enabled then
278        callback.register("show_build")
279    end
280end
281
282interfaces.implement {
283    name      = "build_page_setup",
284    arguments = "string",
285    actions   = build_page_setup,
286}
287
288trackers.register("builders.page.show", build_page_setup) -- for old times sake, will go
289