1if not modules then modules = { } end modules ['mtx-synctex'] = {
2 version = 1.002,
3 comment = "companion to mtxrun.lua",
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
12local tonumber = tonumber
13local find, match, gsub, formatters = string.find, string.match, string.gsub, string.formatters
14local isfile = lfs.isfile
15local max = math.max
16local longtostring = string.longtostring
17
18local helpinfo = [[
19<?xml version="1.0"?>
20<application>
21 <metadata>
22 <entry name="name">mtx-synctex</entry>
23 <entry name="detail">SyncTeX Checker</entry>
24 <entry name="version">1.01</entry>
25 </metadata>
26 <flags>
27 <category name="basic">
28 <subcategory>
29 <flag name="edit"><short>open file at line: --line=.. --editor=.. sourcefile</short></flag>
30 <flag name="list"><short>show all areas: synctexfile</short></flag>
31 <flag name="goto"><short>open file at position: --page=.. --x=.. --y=.. [--tolerance=] --editor=.. synctexfile</short></flag>
32 <flag name="report"><short>show (tex) file and line: [--direct] --page=.. --x=.. --y=.. [--tolerance=] --console synctexfile</short></flag>
33 <flag name="find"><short>find (pdf) page and box: [--direct] --file=.. --line=.. synctexfile</short></flag>
34 </subcategory>
35 </category>
36 </flags>
37</application>
38]]
39
40local application = logs.application {
41 name = "mtx-synctex",
42 banner = "ConTeXt SyncTeX Checker 1.01",
43 helpinfo = helpinfo,
44}
45
46local report = application.report
47
48local template_show = "page=%i llx=%r lly=%r urx=%r ury=%r"
49local template_goto = "filename=%a linenumber=%a tolerance=%a"
50
51local function reportdirect(template,...)
52 print(formatters[template](...))
53end
54
55local editors = {
56 console = function(specification)
57 print(string.formatters["%q %i %i"](specification.filename,specification.linenumber or 1,specification.tolerance))
58 end,
59 scite = sandbox.registerrunner {
60 name = "scite",
61 program = {
62 windows = "scite",
63 unix = "SciTE",
64 },
65 template = longtostring [[
66 "%filename%"
67 "-goto:%linenumber%"
68 ]],
69 },
70}
71
72local function validfile(filename)
73 if not filename or not isfile(filename) then
74 report("invalid synctex log file %a",filename)
75 return false
76 else
77 return true
78 end
79end
80
81local function editfile(filename,line,tolerance,editor)
82 if not validfile(filename) then
83 return
84 end
85 local runner = editors[editor or "scite"] or editors.scite
86 runner {
87 filename = filename,
88 linenumber = tonumber(line) or 1,
89 tolerance = tolerance,
90 }
91end
92
93
94
95
96
97
98
99
100local factor = (7200/7227)/65536
101local quit = true
102
103local function findlocation(filename,page,xpos,ypos)
104 if not validfile(filename) then
105 return
106 elseif not page then
107 page = 1
108 elseif not xpos or not ypos then
109 report("provide x and y coordinates (unit: basepoints)")
110 return
111 end
112 local files = { }
113 local found = false
114 local skip = false
115 local dx = false
116 local dy = false
117 local px = xpos / factor
118 local py = ypos / factor
119 local fi = 0
120 local ln = 0
121 for line in io.lines(filename) do
122 if found then
123 if find(line,"^}") then
124 break
125 else
126
127 local f, l, x, y, w, h, d = match(line,"^h(.-),(.-):(.-),(.-):(.-),(.-),(.-)$")
128 if f and f ~= 0 then
129 x = tonumber(x)
130 if px >= x then
131 w = tonumber(w)
132 if px <= x + w then
133 y = tonumber(y)
134 d = tonumber(d)
135 if py >= y - d then
136 h = tonumber(h)
137 if py <= y + h then
138 if quit then
139
140 fi = f
141 ln = l
142 break
143 else
144 local lx = px - x
145 local rx = x + w - px
146 local by = py - y + d
147 local ty = y + h - py
148 mx = lx < rx and lx or rx
149 my = by < ty and by or ty
150 if not dx then
151 dx = mx
152 dy = my
153 fi = f
154 ln = l
155 else
156 if mx < dx then
157 dx = mx
158 di = f
159 ln = l
160 end
161 if my < dy then
162 dy = my
163 fi = f
164 ln = l
165 end
166 end
167 end
168 end
169 end
170 end
171 end
172 end
173 end
174 elseif skip then
175 if find(line,"^}") then
176 skip = false
177 end
178 elseif find(line,"^{(%d+)") then
179 local p = tonumber(match(line,"^{(%d+)"))
180 if p == page then
181 found = true
182 else
183 skip = true
184 end
185 elseif find(line,"^Input:") then
186 local id, name = match(line,"^Input:(.-):(.-)$")
187 if id then
188 files[id] = name
189 end
190 end
191 end
192 if fi ~= 0 then
193 return files[fi], ln
194 end
195end
196
197local function findlocation(filename,page,xpos,ypos,tolerance)
198 if not validfile(filename) then
199 return
200 elseif not page then
201 page = 1
202 elseif not xpos or not ypos then
203 report("provide x and y coordinates (unit: basepoints)")
204 return
205 end
206 local files = { }
207 local found = false
208 local skip = false
209 local fi = 0
210 local ln = 0
211 local tl = 0
212 local lines = { }
213 for line in io.lines(filename) do
214 if found then
215 if find(line,"^}") then
216 local function locate(x,y)
217 local dx = false
218 local dy = false
219 local px = (xpos + x) / factor
220 local py = (ypos + y) / factor
221 for i=1,#lines do
222 local line = lines[i]
223
224 local f, l, x, y, w, h, d = match(line,"^h(.-),(.-):(.-),(.-):(.-),(.-),(.-)$")
225 if f and f ~= 0 then
226
227 x = tonumber(x)
228 if px >= x then
229 w = tonumber(w)
230 if px <= x + w then
231 y = tonumber(y)
232 d = tonumber(d)
233 if py >= y - d then
234 h = tonumber(h)
235 if py <= y + h then
236 if quit then
237
238 fi = f
239 ln = l
240 return
241 else
242 local lx = px - x
243 local rx = x + w - px
244 local by = py - y + d
245 local ty = y + h - py
246 mx = lx < rx and lx or rx
247 my = by < ty and by or ty
248 if not dx then
249 dx = mx
250 dy = my
251 fi = f
252 ln = l
253 else
254 if mx < dx then
255 dx = mx
256 di = f
257 ln = l
258 end
259 if my < dy then
260 dy = my
261 fi = f
262 ln = l
263 end
264 end
265 end
266 end
267 end
268 end
269 end
270 end
271 end
272 end
273 locate(0,0)
274 if fi ~= 0 then
275 return files[fi], ln, 0
276 end
277 if not tolerance then
278 tolerance = 10
279 end
280 for s=1,tolerance,max(tolerance//10,1) do
281 locate( s, 0) if fi ~= 0 then tl = s ; goto done end
282 locate(-s, 0) if fi ~= 0 then tl = s ; goto done end
283 locate( s, s) if fi ~= 0 then tl = s ; goto done end
284 locate( s,-s) if fi ~= 0 then tl = s ; goto done end
285 locate(-s, s) if fi ~= 0 then tl = s ; goto done end
286 locate(-s,-s) if fi ~= 0 then tl = s ; goto done end
287 locate( 0, s) if fi ~= 0 then tl = s ; goto done end
288 locate( 0,-s) if fi ~= 0 then tl = s ; goto done end
289 end
290 break
291 else
292 lines[#lines+1] = line
293 end
294 elseif skip then
295 if find(line,"^}") then
296 skip = false
297 end
298 elseif find(line,"^{(%d+)") then
299 local p = tonumber(match(line,"^{(%d+)"))
300 if p == page then
301 found = true
302 else
303 skip = true
304 end
305 elseif find(line,"^Input:") then
306 local id, name = match(line,"^Input:(.-):(.-)$")
307 if id then
308 files[id] = name
309 end
310 end
311 end
312 ::done::
313 if fi ~= 0 then
314 return files[fi], ln, tl
315 end
316end
317
318local function showlocation(filename,sourcename,linenumber,direct)
319 if not validfile(filename) then
320 return
321 end
322 local files = { }
323 local found = false
324 local page = 0
325 if sourcename then
326 sourcename = file.collapsepath(sourcename)
327 end
328 for line in io.lines(filename) do
329 if found then
330 if find(line,"^}") then
331 found = false
332 if not sourcename then
333 report("end page: %i",page)
334 end
335 else
336 local f, l, x, y, w, h, d = match(line,"^h(.-),(.-):(.-),(.-):(.-),(.-),(.-)$")
337 if f then
338 x = tonumber(x)
339 y = tonumber(y)
340 local llx = factor * ( x )
341 local lly = factor * ( y - tonumber(d) )
342 local urx = factor * ( x + tonumber(w) )
343 local ury = factor * ( y + tonumber(h) )
344 f = files[f]
345 if not f then
346
347 elseif not sourcename then
348 report(" [% 4r % 4r % 4r % 4r] : % 5i : %s",llx,lly,urx,ury,l,f)
349 elseif f == sourcename and l == linenumber then
350 (direct and reportdirect or report)(template_show,page,llx,lly,urx,ury)
351 return
352 end
353 end
354 end
355 elseif find(line,"^{(%d+)") then
356 page = tonumber(match(line,"^{(%d+)"))
357 found = true
358 if not sourcename then
359 report("begin page: %i",page)
360 end
361 elseif find(line,"^Input:") then
362 local id, name = match(line,"^Input:(.-):(.-)$")
363 if id then
364 files[id] = name
365 end
366 end
367 end
368end
369
370local function gotolocation(filename,page,xpos,ypos,editor,direct,tolerance)
371 if filename then
372 local target, line, t = findlocation(filename,tonumber(page),tonumber(xpos),tonumber(ypos),tonumber(tolerance))
373 if target and line then
374 if editor then
375 editfile(target,line,t,editor)
376 else
377 (direct and reportdirect or report)(template_goto,target,line,t)
378 end
379 end
380 end
381end
382
383
384
385
386
387
388local argument = environment.argument
389local filename = environment.files[1]
390
391if argument("edit") then
392 editfile(filename,argument("line"),argument("editor"))
393elseif argument("goto") then
394 gotolocation(filename,argument("page"),argument("x"),argument("y"),argument("editor"),argument("direct"),argument("tolerance"))
395elseif argument("report") then
396 gotolocation(filename,argument("page"),argument("x"),argument("y"),"console",argument("direct"),argument("tolerance"))
397elseif argument("list") then
398 showlocation(filename)
399elseif argument("find") then
400 showlocation(filename,argument("file"),argument("line"),argument("direct"))
401elseif argument("exporthelp") then
402 application.export(argument("exporthelp"),filename)
403else
404 application.help()
405end
406
407 |