1if not modules then modules = { } end modules ['page-lin'] = {
2 version = 1.001,
3 comment = "companion to page-lin.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 next, tonumber = next, tonumber
10
11local trace_numbers = false trackers.register("lines.numbers", function(v) trace_numbers = v end)
12
13local report_lines = logs.reporter("lines")
14
15local attributes = attributes
16local nodes = nodes
17local context = context
18
19local implement = interfaces.implement
20
21nodes.lines = nodes.lines or { }
22local lines = nodes.lines
23
24lines.data = lines.data or { }
25local data = lines.data
26local last = #data
27
28storage.register("lines/data", data, "nodes.lines.data")
29
30local variables = interfaces.variables
31
32local v_next = variables.next
33local v_page = variables.page
34local v_no = variables.no
35
36local properties = nodes.properties.data
37
38local nodecodes = nodes.nodecodes
39local hlist_code = nodecodes.hlist
40local vlist_code = nodecodes.vlist
41local whatsit_code = nodecodes.whatsit
42local glyph_code = nodecodes.glyph
43
44local linelist_code = nodes.listcodes.line
45
46local latelua_code = nodes.whatsitcodes.latelua
47
48local localtexrun = tex.runlocal
49
50
51local a_linenumber = attributes.private('linenumber')
52local a_linereference = attributes.private('linereference')
53
54local cross_references = { }
55
56local nuts = nodes.nuts
57
58local getid = nuts.getid
59local getattr = nuts.getattr
60local getlist = nuts.getlist
61local getbox = nuts.getbox
62local gettotal = nuts.gettotal
63
64local takebox = nuts.takebox
65
66local nexthlist = nuts.traversers.hlist
67local nextvlist = nuts.traversers.vlist
68local nextcontent = nuts.traversers.content
69local nextlist = nuts.traversers.list
70local nextnode = nuts.traversers.node
71local nextwhatsit = nuts.traversers.whatsit
72
73local is_display_math = nuts.is_display_math
74
75local ctx_convertnumber = context.convertnumber
76local ctx_makelinenumber = context.makelinenumber
77
78local paragraphs = typesetters.paragraphs
79local addtoline = paragraphs.addtoline
80local checkline = paragraphs.checkline
81
82
83
84function lines.number(n)
85 n = tonumber(n)
86 local cr = cross_references[n] or 0
87 cross_references[n] = nil
88 return cr
89end
90
91function lines.finalize(t)
92 local getnumber = lines.number
93 for _,p in next, t do
94 for _,r in next, p do
95 local m = r.metadata
96 if m and m.kind == "line" then
97 local e = r.entries
98 local u = r.userdata
99 e.linenumber = getnumber(e.text or 0)
100 e.conversion = u and u.conversion
101 r.userdata = nil
102 end
103 end
104 end
105end
106
107local filters = structures.references.filters
108local helpers = structures.helpers
109
110structures.references.registerfinalizer(lines.finalize)
111
112filters.line = filters.line or { }
113
114function filters.line.default(data)
115 ctx_convertnumber(data.entries.conversion or "numbers",data.entries.linenumber or "0")
116end
117
118function filters.line.page(data,prefixspec,pagespec)
119 helpers.prefixpage(data,prefixspec,pagespec)
120end
121
122function filters.line.linenumber(data)
123 context(data.entries.linenumber or "0")
124end
125
126
127
128local boxed = lines.boxed or { }
129lines.boxed = boxed
130
131
132
133
134function boxed.register(configuration)
135 last = last + 1
136 data[last] = configuration
137 if trace_numbers then
138 report_lines("registering setup %a",last)
139 end
140 return last
141end
142
143implement {
144 name = "registerlinenumbering",
145 actions = { boxed.register, context },
146 arguments = {
147 {
148 { "continue" },
149 { "start", "integer" },
150 { "step", "integer" },
151 { "method" },
152 { "tag" },
153 }
154 }
155}
156
157function boxed.setup(n,configuration)
158 local d = data[n]
159 if d then
160 if trace_numbers then
161 report_lines("updating setup %a",n)
162 end
163 for k,v in next, configuration do
164 d[k] = v
165 end
166 else
167 if trace_numbers then
168 report_lines("registering setup %a (br)",n)
169 end
170 data[n] = configuration
171 end
172 return n
173end
174
175implement {
176 name = "setuplinenumbering",
177 actions = boxed.setup,
178 arguments = {
179 "integer",
180 {
181 { "continue" },
182 { "start", "integer" },
183 { "step", "integer" },
184 { "method" },
185 { "tag" },
186 }
187 }
188}
189
190local function resolve(n,m)
191 for current, id, subtype, content in nextlist, n do
192 if content then
193 if id == hlist_code then
194 for current, subtype in nextwhatsit, content do
195 if subtype == latelua_code then
196 local a = getattr(current,a_linereference)
197 if a then
198 cross_references[a] = m
199 end
200 end
201 end
202 end
203 resolve(content,m)
204 end
205 end
206end
207
208local function check_number(n,b,a,skip)
209 local d = data[a]
210 if d then
211 local tag = d.tag or ""
212 local s = d.start or 1
213 local okay = not skip and s % d.step == 0
214 if trace_numbers then
215 report_lines("%s number for setup %a, tag %a, line %s, continued %a",okay and "making" or "skipping",a,tag,s,d.continue or v_no)
216 end
217 if okay then
218 local p = checkline(n)
219 if p then
220 localtexrun(function() ctx_makelinenumber(tag,s,p.hsize,p.reverse and 1 or 0) end)
221 local l = takebox(b)
222 if l then
223 addtoline(n,l)
224 else
225
226 end
227 end
228 end
229 resolve(n,s)
230 d.start = s + 1
231 end
232end
233
234
235
236
237
238
239
240local function lineisnumbered(n)
241 for n, id, subtype, content in nextcontent, getlist(n) do
242 local a = getattr(n,a_linenumber)
243 if a and a > 0 then
244 return a
245 end
246 end
247end
248
249local function listisnumbered(list)
250 if list then
251 for n, subtype in nexthlist, list do
252 if subtype == linelist_code then
253 local a = getattr(n,a_linenumber)
254 if a then
255
256 return a > 0 and list or false
257 else
258
259 if lineisnumbered(n) then
260 return list
261 end
262 end
263 end
264 end
265 end
266end
267
268local function findnumberedlist(list)
269
270 for n, id, subtype, content in nextcontent, list do
271 if id == hlist_code then
272 if subtype == linelist_code then
273 local a = getattr(n,a_linenumber)
274 if a then
275 return a > 0 and list
276 end
277 return
278 else
279 if lineisnumbered(content) then
280 return n
281 end
282 local okay = findnumberedlist(content)
283 if okay then
284 return okay
285 end
286 end
287 elseif id == vlist_code then
288 if listisnumbered(content) then
289 return content
290 end
291 local okay = findnumberedlist(content)
292 if okay then
293 return okay
294 end
295 elseif id == glyph_code then
296 return
297 end
298 end
299end
300
301
302
303
304
305local function findcolumngap(list)
306
307 for n, id, subtype, content in nextlist, list do
308 if id == hlist_code or id == vlist_code then
309 local p = properties[n]
310 if p and p.columngap then
311 if trace_numbers then
312 report_lines("first column gap %a",p.columngap)
313 end
314 return n
315 elseif content then
316 local okay = findcolumngap(content)
317 if okay then
318 return okay
319 end
320 end
321 end
322 end
323end
324
325function boxed.addlinenumbers(n,b,nested)
326 local box = getbox(n)
327 if not box then
328 return
329 end
330 local list = getlist(box)
331 if not list then
332 return
333 end
334 local last_a = nil
335 local last_v = -1
336 local skip = false
337
338 local function check()
339 for n, subtype in nexthlist, list do
340 if subtype ~= linelist_code then
341
342 elseif gettotal(n) == 0 then
343
344 else
345 local a = lineisnumbered(n)
346 if a then
347 if last_a ~= a then
348 local da = data[a]
349 local ma = da.method
350 if ma == v_next then
351 skip = true
352 elseif ma == v_page then
353 da.start = 1
354 end
355 last_a = a
356 if trace_numbers then
357 report_lines("starting line number range %s: start %s, continue %s",a,da.start,da.continue or v_no)
358 end
359 end
360
361
362
363 check_number(n,b,a,skip)
364
365 skip = false
366 end
367 end
368 end
369 end
370
371 if nested == 0 then
372 if list then
373 check()
374 end
375 elseif nested == 1 then
376 local id = getid(box)
377 if id == vlist_code then
378 if listisnumbered(list) then
379
380 else
381 list = findnumberedlist(list)
382 end
383 else
384 list = findnumberedlist(list)
385 end
386 if list then
387 check()
388 end
389 elseif nested == 2 then
390 list = findcolumngap(list)
391
392 if not list then
393 return
394 end
395 for n in nextvlist, list do
396 local p = properties[n]
397 if p and p.columngap then
398 if trace_numbers then
399 report_lines("found column gap %a",p.columngap)
400 end
401 list = getlist(n)
402 if list then
403 check()
404 end
405 end
406 end
407 else
408
409 end
410end
411
412
413
414implement {
415 name = "addlinenumbers",
416 actions = boxed.addlinenumbers,
417 arguments = "3 integers",
418}
419 |