1if not modules then modules = { } end modules ['node-syn'] = {
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
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163local type, rawset = type, rawset
164local concat = table.concat
165local formatters = string.formatters
166local replacesuffix, suffixonly, nameonly, collapsepath = file.replacesuffix, file.suffix, file.nameonly, file.collapsepath
167local openfile, removefile = io.open, os.remove
168
169local report_system = logs.reporter("system")
170
171local tex = tex
172local texget = tex.get
173
174local nuts = nodes.nuts
175
176local getid = nuts.getid
177local getlist = nuts.getlist
178local setlist = nuts.setlist
179local getnext = nuts.getnext
180local getwhd = nuts.getwhd
181local getsubtype = nuts.getsubtype
182
183local nodecodes = nodes.nodecodes
184local kerncodes = nodes.kerncodes
185
186local glyph_code <const> = nodecodes.glyph
187local disc_code <const> = nodecodes.disc
188local glue_code <const> = nodecodes.glue
189local penalty_code <const> = nodecodes.penalty
190local kern_code <const> = nodecodes.kern
191local hlist_code <const> = nodecodes.hlist
192local vlist_code <const> = nodecodes.vlist
193local fontkern_code <const> = kerncodes.fontkern
194
195local insertbefore = nuts.insertbefore
196local insertafter = nuts.insertafter
197
198local nextnode = nuts.traversers.node
199
200local nodepool = nuts.pool
201local new_latelua = nodepool.latelua
202local new_rule = nodepool.rule
203local new_outline_rule = nodepool.outlinerule
204local new_kern = nodepool.kern
205
206local getdimensions = nuts.dimensions
207local getrangedimensions = nuts.rangedimensions
208
209local getinputfields = nuts.getinputfields
210local forceinputstatefile = tex.forceinputstatefile
211local forceinputstateline = tex.forceinputstateline
212local getinputstateline = tex.getinputstateline
213local setinputstatemode = tex.setinputstatemode
214
215local foundintree = resolvers.foundintree
216
217local getpagedimensions = layouts.getpagedimensions
218
219local eol <const> = "\010"
220local z_hlist <const> = "[0,0:0,0:0,0,0\010"
221local s_hlist <const> = "]\010"
222local f_hvoid = formatters["h%i,%i:%i,%i:%i,%i,%i\010"]
223local f_hlist = formatters["(%i,%i:%i,%i:%i,%i,%i\010"]
224local s_hlist <const> = ")\010"
225local f_rule = formatters["r%i,%i:%i,%s:%i,%i,%i\010"]
226local f_plist = formatters["[%i,%i:%i,%i:%i,%i,%i\010"]
227local s_plist <const> = "]\010"
228
229local synctex = luatex.synctex or { }
230luatex.synctex = synctex
231
232local getpos ; getpos = function() getpos = job.positions.getpos return getpos() end
233
234
235
236local enabled = false
237local userule = false
238local paused = 0
239local used = false
240local never = false
241
242
243
244local noftags = 0
245local stnums = { }
246local nofblocked = 0
247local blockedfilenames = { }
248local blockedsuffixes = {
249 mkii = true,
250 mkiv = true,
251 mkvi = true,
252 mkxl = true,
253 mklx = true,
254 mkix = true,
255 mkxi = true,
256
257}
258
259local sttags = table.setmetatableindex(function(t,fullname)
260 local name = collapsepath(fullname)
261 if blockedsuffixes[suffixonly(name)] then
262
263 nofblocked = nofblocked + 1
264 return 0
265 elseif blockedfilenames[nameonly(name)] then
266
267 nofblocked = nofblocked + 1
268 return 0
269 elseif foundintree(name) then
270
271 nofblocked = nofblocked + 1
272 return 0
273 else
274 noftags = noftags + 1
275 t[name] = noftags
276 if name ~= fullname then
277 t[fullname] = noftags
278 end
279 stnums[noftags] = name
280 return noftags
281 end
282end)
283
284function synctex.blockfilename(name)
285 blockedfilenames[nameonly(name)] = name
286end
287
288function synctex.setfilename(name,line)
289 if paused == 0 and name then
290 forceinputstatefile(sttags[name])
291 if line then
292 forceinputstateline(line)
293 end
294 end
295end
296
297function synctex.resetfilename()
298 if paused == 0 then
299 forceinputstatefile(0)
300 forceinputstateline(0)
301 end
302end
303
304do
305
306 local nesting = 0
307 local ignored = false
308
309 function synctex.pushline()
310 nesting = nesting + 1
311 if nesting == 1 then
312 local l = getinputstateline()
313 ignored = l and l > 0
314 if not ignored then
315 forceinputstateline(texget("inputlineno"))
316 end
317 end
318 end
319
320 function synctex.popline()
321 if nesting == 1 then
322 if not ignored then
323 forceinputstateline()
324 ignored = false
325 end
326 end
327 nesting = nesting - 1
328 end
329
330end
331
332
333
334local filehandle = nil
335local nofsheets = 0
336local nofobjects = 0
337local last = 0
338local filesdone = 0
339local sncfile = false
340
341local function writeanchor()
342 local size = filehandle:seek("end")
343 filehandle:write("!",size-last,eol)
344 last = size
345end
346
347local function writefiles()
348 local total = #stnums
349 if filesdone < total then
350 for i=filesdone+1,total do
351 filehandle:write("Input:",i,":",stnums[i],eol)
352 end
353 filesdone = total
354 end
355end
356
357local function makenames()
358 sncfile = replacesuffix(tex.jobname,"synctex")
359end
360
361local function flushpreamble()
362 makenames()
363 filehandle = openfile(sncfile,"wb")
364 if filehandle then
365 filehandle:setvbuf("full",64*1024)
366 filehandle:write("SyncTeX Version:1",eol)
367 writefiles()
368 filehandle:write("Output:pdf",eol)
369 filehandle:write("Magnification:1000",eol)
370 filehandle:write("Unit:1",eol)
371 filehandle:write("X Offset:0",eol)
372 filehandle:write("Y Offset:0",eol)
373 filehandle:write("Content:",eol)
374 flushpreamble = function()
375 writefiles()
376 return filehandle
377 end
378 else
379 enabled = false
380 end
381 return filehandle
382end
383
384function synctex.wrapup()
385 sncfile = nil
386end
387
388local function flushpostamble()
389 if not filehandle then
390 return
391 end
392 writeanchor()
393 filehandle:write("Postamble:",eol)
394 filehandle:write("Count:",nofobjects,eol)
395 writeanchor()
396 filehandle:write("Post scriptum:",eol)
397 filehandle:close()
398 enabled = false
399end
400
401local x_hlist do
402
403 local function doaction(data)
404 local pagewidth, pageheight = getpagedimensions()
405 local x, y = getpos()
406 local t = data[1]
407 local l = data[2]
408 local w = data[3]
409 local h = data[4]
410 local d = data[5]
411 y = pageheight - y
412 if userule then
413
414 filehandle:write(f_hlist(t,l,x,y,0,0,0))
415 filehandle:write(f_rule(t,l,x,y,w,h,d))
416 filehandle:write(s_hlist)
417 else
418
419 filehandle:write(f_hvoid(t,l,x,y,w,h,d))
420 end
421 nofobjects = nofobjects + 1
422 end
423
424 x_hlist = function(head,current,t,l,w,h,d)
425 if filehandle then
426 return insertbefore(head,current,new_latelua { action = doaction, t, l, w, h, d })
427 else
428 return head
429 end
430 end
431
432end
433
434
435
436local collect = nil
437local fulltrace = false
438local trace = false
439local height = 10 * 65536
440local depth = 5 * 65536
441local traceheight = 32768
442local tracedepth = 32768
443local outlinewidth = 65536 / 4
444
445trackers.register("system.synctex.visualize", function(v)
446 trace = v
447 fulltrace = v == "real"
448end)
449
450local collect_min do
451
452 local function inject(head,first,last,tag,line)
453 local w, h, d = getdimensions(first,getnext(last))
454 if h < height then
455 h = height
456 end
457 if d < depth then
458 d = depth
459 end
460 if trace then
461 if fulltrace then
462 head = insertbefore(head,first,new_outline_rule(w,h,d,outlinewidth))
463 else
464 head = insertbefore(head,first,new_rule(w,traceheight,tracedepth))
465 end
466 head = insertbefore(head,first,new_kern(-w))
467 end
468 head = x_hlist(head,first,tag,line,w,h,d)
469 return head
470 end
471
472 collect_min = function(head)
473 local current = head
474 while current do
475 local id = getid(current)
476 if id == glyph_code then
477 local first = current
478 local last = current
479 local tag = 0
480 local line = 0
481 while true do
482 if id == glyph_code then
483 local tc, lc = getinputfields(current)
484 if tc and tc > 0 then
485 tag = tc
486 line = lc
487 end
488 last = current
489 elseif id == disc_code or (id == kern_code and getsubtype(current) == fontkern_code) then
490 last = current
491 else
492 if tag > 0 then
493 head = inject(head,first,last,tag,line)
494 end
495 break
496 end
497 current = getnext(current)
498 if current then
499 id = getid(current)
500 else
501 if tag > 0 then
502 head = inject(head,first,last,tag,line)
503 end
504 return head
505 end
506 end
507 end
508
509 if id == hlist_code or id == vlist_code then
510 local list = getlist(current)
511 if list then
512 local l = collect(list)
513 if l ~= list then
514 setlist(current,l)
515 end
516 end
517 end
518 current = getnext(current)
519 end
520 return head
521 end
522
523end
524
525local collect_max do
526
527 local function inject(parent,head,first,last,tag,line)
528 local w, h, d = getrangedimensions(parent,first,getnext(last))
529 if h < height then
530 h = height
531 end
532 if d < depth then
533 d = depth
534 end
535 if trace then
536 if fulltrace then
537 head = insertbefore(head,first,new_outline_rule(w,h,d,outlinewidth))
538 else
539 head = insertbefore(head,first,new_rule(w,traceheight,tracedepth))
540 end
541 head = insertbefore(head,first,new_kern(-w))
542 end
543 head = x_hlist(head,first,tag,line,w,h,d)
544 return head
545 end
546
547 collect_max = function(head,parent)
548 local current = head
549 while current do
550 local id = getid(current)
551 if id == glyph_code then
552 local first = current
553 local last = current
554 local tag = 0
555 local line = 0
556 while true do
557 if id == glyph_code then
558 local tc, lc = getinputfields(current)
559 if tc and tc > 0 then
560 if tag > 0 and (tag ~= tc or line ~= lc) then
561 head = inject(parent,head,first,last,tag,line)
562 first = current
563 end
564 tag = tc
565 line = lc
566 last = current
567 else
568 if tag > 0 then
569 head = inject(parent,head,first,last,tag,line)
570 tag = 0
571 end
572 first = nil
573 last = nil
574 end
575 elseif id == disc_code then
576 if not first then
577 first = current
578 end
579 last = current
580 elseif id == kern_code and getsubtype(current) == fontkern_code then
581 if first then
582 last = current
583 end
584 elseif id == glue_code then
585 elseif id == penalty_code then
586
587 else
588 if tag > 0 then
589 head = inject(parent,head,first,last,tag,line)
590 tag = 0
591 end
592 break
593 end
594 current = getnext(current)
595 if current then
596 id = getid(current)
597 else
598 if tag > 0 then
599 head = inject(parent,head,first,last,tag,line)
600 end
601 return head
602 end
603 end
604 end
605
606 if id == hlist_code or id == vlist_code then
607 local list = getlist(current)
608 if list then
609 local l = collect(list,current)
610 if l and l ~= list then
611 setlist(current,l)
612 end
613 end
614 end
615 current = getnext(current)
616 end
617 return head
618 end
619
620end
621
622collect = collect_max
623
624function synctex.collect(head,where)
625 if enabled and where ~= "object" then
626 return collect(head,head)
627 else
628 return head
629 end
630end
631
632
633
634function synctex.start()
635 if enabled then
636 nofsheets = nofsheets + 1
637 if flushpreamble() then
638 writeanchor()
639 filehandle:write("{",nofsheets,eol)
640
641
642 filehandle:write(f_plist(1,0,0,0,0,0,0))
643 end
644 end
645end
646
647function synctex.stop()
648 if enabled then
649 filehandle:write(s_plist)
650 writeanchor()
651 filehandle:write("}",nofsheets,eol)
652 nofobjects = nofobjects + 2
653 end
654end
655
656local enablers = { }
657local disablers = { }
658
659function synctex.registerenabler(f)
660 enablers[#enablers+1] = f
661end
662
663function synctex.registerdisabler(f)
664 disablers[#disablers+1] = f
665end
666
667function synctex.enable(use_rule)
668 if tex.getinteraction() ~= tex.interactioncodes.errorstop then
669 report_system("synctex functionality is only enabled in error stop mode")
670 elseif not never and not enabled then
671 enabled = true
672 userule = use_rule
673 setinputstatemode(3)
674 if not used then
675 nodes.tasks.enableaction("shipouts","luatex.synctex.collect")
676 report_system("synctex functionality is enabled, expect 5-10 pct runtime overhead!")
677 used = true
678 end
679 for i=1,#enablers do
680 enablers[i](true)
681 end
682
683 flushpreamble()
684 end
685end
686
687function synctex.disable()
688 if enabled then
689 setinputstatemode(0)
690 report_system("synctex functionality is disabled!")
691 enabled = false
692 for i=1,#disablers do
693 disablers[i](false)
694 end
695 end
696end
697
698function synctex.finish()
699 if enabled then
700 flushpostamble()
701 else
702 makenames()
703 removefile(sncfile)
704 end
705end
706
707local filename = nil
708
709function synctex.pause()
710 paused = paused + 1
711 if enabled and paused == 1 then
712 setinputstatemode(0)
713 end
714end
715
716function synctex.resume()
717 if enabled and paused == 1 then
718 setinputstatemode(3)
719 end
720 paused = paused - 1
721end
722
723
724
725luatex.registerstopactions(synctex.finish)
726
727statistics.register("synctex tracing",function()
728 if used then
729 return string.format("%i referenced files, %i files ignored, %i objects flushed, logfile: %s",
730 noftags,nofblocked,nofobjects,sncfile)
731 end
732end)
733
734local implement = interfaces.implement
735local variables = interfaces.variables
736
737function synctex.setup(t)
738 if t.state == variables.never then
739 synctex.disable()
740 never = true
741 return
742 end
743 if t.method == variables.max then
744 collect = collect_max
745 else
746 collect = collect_min
747 end
748 if t.state == variables.start then
749 synctex.enable(false)
750 elseif t.state == variables["repeat"] then
751 synctex.enable(true)
752 else
753 synctex.disable()
754 end
755end
756
757implement {
758 name = "synctexblockfilename",
759 arguments = "string",
760 protected = true,
761 public = true,
762 actions = synctex.blockfilename,
763}
764
765implement {
766 name = "synctexsetfilename",
767 arguments = "string",
768 protected = true,
769 public = true,
770 actions = synctex.setfilename,
771}
772
773implement {
774 name = "synctexresetfilename",
775 protected = true,
776 public = true,
777 actions = synctex.resetfilename,
778}
779
780implement {
781 name = "setupsynctex",
782 actions = synctex.setup,
783 arguments = {
784 {
785 { "state" },
786 { "method" },
787 },
788 },
789}
790
791implement {
792 name = "synctexpause",
793 actions = synctex.pause,
794}
795
796implement {
797 name = "synctexresume",
798 actions = synctex.resume,
799}
800
801implement {
802 name = "synctexpushline",
803 actions = synctex.pushline,
804}
805
806implement {
807 name = "synctexpopline",
808 actions = synctex.popline,
809}
810
811implement {
812 name = "synctexdisable",
813 actions = synctex.disable,
814}
815 |