1if not modules then modules = { } end modules ['mtx-cache'] = {
2 version = 1.001,
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
9local concat, sort, insert = table.concat, table.sort, table.insert
10local gsub, format, gmatch, find, upper = string.gsub, string.format, string.gmatch, string.find, string.upper
11local utfchar, utfgsub = utf.char, utf.gsub
12local sortedkeys, sortedhash, serialize = table.sortedkeys, table.sortedhash, table.serialize
13
14
15
16
17
18local helpinfo = [[
19<?xml version="1.0"?>
20<application>
21 <metadata>
22 <entry name="name">mtx-interface</entry>
23 <entry name="detail">ConTeXt Interface Related Goodies</entry>
24 <entry name="version">0.13</entry>
25 </metadata>
26 <flags>
27 <category name="basic">
28 <subcategory>
29 <flag name="scite"><short>generate scite interface</short></flag>
30 <flag name="bbedit"><short>generate bbedit interface files</short></flag>
31 <flag name="jedit"><short>generate jedit interface files</short></flag>
32 <flag name="textpad"><short>generate textpad interface files</short></flag>
33 <flag name="vim"><short>generate vim interface files</short></flag>
34 <flag name="text"><short>create text files for commands and environments</short></flag>
35 <flag name="raw"><short>report commands to the console</short></flag>
36 <flag name="check"><short>generate check file</short></flag>
37 <flag name="meaning"><short>report the mening of commands</short></flag>
38 <flag name="tokens"><short>show the internal representation of commands</short></flag>
39 </subcategory>
40 <subcategory>
41 <flag name="toutf"><short>replace named characters by utf</short></flag>
42 <flag name="preprocess"><short>preprocess mkvi files to tex files [force,suffix]</short></flag>
43 </subcategory>
44 <subcategory>
45 <flag name="suffix"><short>use given suffix for output files</short></flag>
46 <flag name="force"><short>force action even when in doubt</short></flag>
47 </subcategory>
48 <subcategory>
49 <flag name="pattern"><short>a pattern for meaning lookups</short></flag>
50 </subcategory>
51 </category>
52 </flags>
53</application>
54]]
55
56local application = logs.application {
57 name = "mtx-interface",
58 banner = "ConTeXt Interface Related Goodies 0.13",
59 helpinfo = helpinfo,
60}
61
62local report = application.report
63
64scripts = scripts or { }
65scripts.interface = scripts.interface or { }
66
67local flushers = { }
68local userinterfaces = { 'en','cs','de','it','nl','ro','fr','pe' }
69
70local function collect(filename,class,data)
71 if data then
72 local result, r = { }, 0
73 for name, list in sortedhash(data) do
74 r = r + 1 ; result[r] = format("keywordclass.%s.%s=\\\n",class,name)
75 for i=1,#list do
76 if i%5 == 0 then
77 r = r + 1 ; result[r] = "\\\n"
78 end
79 r = r + 1 ; result[r] = format("%s ",list[i])
80 end
81 r = r + 1 ; result[r] = "\n\n"
82 end
83 io.savedata(file.addsuffix(filename,"properties"),concat(result))
84 io.savedata(file.addsuffix(filename,"lua"),serialize(data,true))
85 else
86 os.remove(filename)
87 end
88end
89
90function flushers.scite(collected)
91 local data = { }
92
93
94
95 local function add(target,origin,field)
96 if origin then
97 local list = origin[field]
98 if list then
99 for i=1,#list do
100 target[list[i]] = true
101 end
102 end
103 end
104 end
105
106 for interface, whatever in next, collected do
107 local combined = { }
108 add(combined,whatever,"commands")
109 add(combined,whatever,"environments")
110 if interface == "common" then
111 add(combined,whatever,"textnames")
112 add(combined,whatever,"mathnames")
113 end
114 data[interface] = sortedkeys(combined)
115 end
116
117 collect("scite-context-data-interfaces", "context", data)
118 collect("scite-context-data-metapost", "metapost", dofile(resolvers.findfile("mult-mps.lua")))
119 collect("scite-context-data-metafun", "metafun", dofile(resolvers.findfile("mult-fun.lua")))
120 collect("scite-context-data-context", "context", dofile(resolvers.findfile("mult-low.lua")))
121 collect("scite-context-data-tex", "tex", dofile(resolvers.findfile("mult-prm.lua")))
122end
123
124function flushers.jedit(collected)
125 for interface, whatever in next, collected do
126 local commands = whatever.commands
127 local environments = whatever.environments
128 local result, r = { }, 0
129 r = r + 1 ; result[r] = "<?xml version='1.0'?>"
130 r = r + 1 ; result[r] = "<!DOCTYPE MODE SYSTEM 'xmode.dtd'>\n"
131 r = r + 1 ; result[r] = "<MODE>"
132 r = r + 1 ; result[r] = "\t<RULES>"
133 r = r + 1 ; result[r] = "\t\t<KEYWORDS>"
134 for i=1,#commands do
135 r = r + 1 ; result[r] = format("\t\t\t<KEYWORD2>%s</KEYWORD2>",commands[i])
136 end
137 r = r + 1 ; result[r] = "\t\t</KEYWORDS>"
138 r = r + 1 ; result[r] = "\t</RULES>"
139 r = r + 1 ; result[r] = "</MODE>"
140 io.savedata(format("context-jedit-%s.xml",interface), concat(result),"\n")
141 end
142end
143
144function flushers.bbedit(collected)
145 for interface, whatever in next, collected do
146 local commands = whatever.commands
147 local environments = whatever.environments
148 local result, r = { }, 0
149 r = r + 1 ; result[r] = "<?xml version='1.0'?>"
150 r = r + 1 ; result[r] = "<key>BBLMKeywordList</key>"
151 r = r + 1 ; result[r] = "<array>"
152 for i=1,#commands do
153 r = r + 1 ; result[r] = format("\t<string>\\%s</string>",commands[i])
154 end
155 r = r + 1 ; result[r] = "</array>"
156 io.savedata(format("context-bbedit-%s.xml",interface), concat(result),"\n")
157 end
158end
159
160
161
162local function vimcollect(filename,class,data)
163 if data then
164 local result, r = { }, 0
165 local endline = " contained\n"
166 if find(class,"^meta") then
167 endline = "\n"
168 end
169 r = r + 1 ; result[r] = "vim9script\n\n"
170 r = r + 1 ; result[r] = "# Vim syntax file\n"
171 r = r + 1 ; result[r] = "# Language: ConTeXt\n"
172 r = r + 1 ; result[r] = format("# Automatically generated by mtx-interface (%s)\n\n", os.date())
173 local n = 5
174 for name, list in sortedhash(data) do
175 local i = 1
176 while i <= #list do
177 r = r + 1 ; result[r] = format("syn keyword %s%s", class, (gsub(name,"^%l",upper)))
178 local j = 0
179
180
181
182
183
184
185
186
187 while j < n do
188 local lij = list[i + j]
189 if not lij then
190 break
191 elseif lij == "transparent" then
192 r = r + 1 ; result[r] = format(" %s[]",lij)
193 else
194 r = r + 1 ; result[r] = format(" %s",lij)
195 end
196 j = j + 1
197 end
198 r = r + 1 ; result[r] = endline
199 i = i + n
200 end
201 end
202 io.savedata(file.addsuffix(filename,"vim"),concat(result))
203 else
204 os.remove(filename)
205 end
206end
207
208function flushers.vim(collected)
209 local data = { }
210
211
212
213 local function add(target,origin,field)
214 if origin then
215 local list = origin[field]
216 if list then
217 for i=1,#list do
218 target[list[i]] = true
219 end
220 end
221 end
222 end
223
224 for interface, whatever in next, collected do
225 local combined = { }
226 add(combined,whatever,"commands")
227 add(combined,whatever,"environments")
228 if interface == "common" then
229 add(combined,whatever,"textnames")
230 add(combined,whatever,"mathnames")
231 end
232 data[interface] = sortedkeys(combined)
233 end
234
235 vimcollect("context-data-interfaces", "context", data)
236
237 vimcollect("context-data-metafun", "metafun", dofile(resolvers.findfile("mult-fun.lua")))
238 vimcollect("context-data-context", "context", dofile(resolvers.findfile("mult-low.lua")))
239 vimcollect("context-data-tex", "tex", dofile(resolvers.findfile("mult-prm.lua")))
240end
241
242function flushers.raw(collected)
243 for interface, whatever in next, collected do
244 local commands = whatever.commands
245 local environments = whatever.environments
246 for i=1,#commands do
247 report(commands[i])
248 end
249 end
250end
251
252function flushers.data(collected)
253 return collected
254end
255
256local textpadcreator = "mtx-interface-textpad.lua"
257
258function flushers.text(collected)
259 for interface, whatever in next, collected do
260 local commands = whatever.commands
261 local environments = whatever.environments
262 local c, cname = { }, format("context-commands-%s.txt",interface)
263 local e, ename = { }, format("context-environments-%s.txt",interface)
264 report("saving '%s'",cname)
265 for i=1,#commands do
266 c[#c+1] = format("\\%s",commands[i])
267 end
268 io.savedata(cname,concat(c,"\n"))
269 report("saving '%s'",ename)
270 for i=1,#environments do
271 e[#e+1] = format("\\start%s",environments[i])
272 e[#e+1] = format("\\stop%s", environments[i])
273 end
274 io.savedata(format("context-environments-%s.txt",interface),concat(e,"\n"))
275 end
276end
277
278function flushers.textpad(collected)
279 flushers.text(collected)
280 for interface, whatever in next, collected do
281 local commands = whatever.commands
282 local environments = whatever.environments
283
284
285
286 local function merge(templatedata,destinationdata,categories)
287 report("loading '%s'",templatedata)
288 local data = io.loaddata(templatedata)
289 local done = 0
290 for i=1,#categories do
291 local category = categories[i]
292 local cpattern = ";%s*category:%s*(" .. category .. ")%s*[\n\r]+"
293 local fpattern = ";%s*filename:%s*(" .. "%S+" .. ")%s*[\n\r]+"
294 data = gsub(data,cpattern..fpattern,function(category,filename)
295 local found = resolvers.findfile(filename) or ""
296 local blob = found ~= "" and io.loaddata(found) or ""
297 if blob == "" then
298 report("category: %s, filename: %s, not found",category,filename)
299 else
300 done = done + 1
301 report("category: %s, filename: %s, merged",category,filename)
302 end
303 return format("; category: %s\n; filename: %s\n%s\n\n",category,filename,blob)
304 end)
305 end
306 if done > 0 then
307 report("saving '%s' (%s files merged)",destinationdata,done)
308 io.savedata(destinationdata,data)
309 else
310 report("skipping '%s' (no files merged)",destinationdata)
311 end
312 end
313 local templatename = "textpad-context-template.txt"
314 local templatedata = resolvers.findfile(templatename) or ""
315 if templatedata == "" then
316 report("unable to locate template '%s'",templatename)
317 else
318 merge(templatedata, "context.syn", { "tex commands","context commands" })
319 if environment.argument("textpad") == "latex" then
320 merge(templatedata, "context-latex.syn", { "tex commands","context commands", "latex commands" })
321 end
322 end
323 local r = { }
324 local c = io.loaddata("context-commands-en.txt") or ""
325 local e = io.loaddata("context-environments-en.txt") or ""
326 for s in gmatch(c,"\\(.-)%s") do
327 r[#r+1] = format("\n!TEXT=%s\n\\%s\n!",s,s)
328 end
329 for s in gmatch(e,"\\start(.-)%s+\\stop(.-)") do
330 r[#r+1] = format("\n!TEXT=%s (start/stop)\n\\start%s \\^\\stop%s\n!",s,s,s)
331 end
332 sort(r)
333 insert(r,1,"!TCL=597,\n!TITLE=ConTeXt\n!SORT=N\n!CHARSET=DEFAULT")
334 io.savedata("context.tcl",concat(r,"\n"))
335
336 os.remove("context-commands-en.txt")
337 os.remove("context-environments-en.txt")
338 end
339end
340
341function scripts.interface.editor(editor,split,forcedinterfaces)
342 require("char-def")
343
344 local interfaces = forcedinterfaces or environment.files or userinterfaces
345 if not interfaces.en then
346
347 interfaces = { "en" }
348 end
349
350
351 local filename = "context-en.xml"
352 local xmlfile = resolvers.findfile(filename) or ""
353 if xmlfile == "" then
354 report("unable to locate %a",filename)
355 return
356 end
357
358 local filename = "mult-def.lua"
359 local deffile = resolvers.findfile(filename) or ""
360 if deffile == "" then
361 report("unable to locate %a",filename)
362 return
363 end
364 local interface = dofile(deffile)
365 if not interface or not next(interface) then
366 report("invalid file %a",filename)
367 return
368 end
369 local variables = interface.variables
370 local constants = interface.constants
371 local commands = interface.commands
372 local elements = interface.elements
373
374 local collected = { }
375
376 report("generating files for %a",editor)
377 report("loading %a",xmlfile)
378 local xmlroot = xml.load(xmlfile)
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407 for i=1,#interfaces do
408 local interface = interfaces[i]
409 local i_commands = { }
410 local i_environments = { }
411 local start = elements.start[interface] or elements.start.en
412 local stop = elements.stop [interface] or elements.stop .en
413 for e in xml.collected(xmlroot,"cd:interface/cd:command") do
414 local at = e.at
415 local name = at["name"] or ""
416 local type = at["type"]
417 if name ~= "" then
418 local c = commands[name]
419 local n = c and (c[interface] or c.en) or name
420 local sequence = xml.all(e,"/cd:sequence/*")
421 if at.generated == "yes" then
422 for e in xml.collected(e,"/cd:instances/cd:constant") do
423 local name = e.at.value
424 if name then
425 local c = variables[name]
426 local n = c and (c[interface] or c.en) or name
427 if sequence then
428 local done = { }
429 for i=1,#sequence do
430 local e = sequence[i]
431 if e.tg == "string" then
432 local value = e.at.value
433 if value then
434 done[i] = value
435 else
436 done = false
437 break
438 end
439 else
440 local tg = e.tg
441 if tg == "instance" or tg == "instance:assignment" or tg == "instance:ownnumber" then
442 done[i] = name
443 else
444 done = false
445 break
446 end
447 end
448 end
449 if done then
450 n = concat(done)
451 end
452 end
453 if type ~= "environment" then
454 i_commands[n] = true
455 elseif split then
456 i_environments[n] = true
457 else
458 i_commands[start..n] = true
459 i_commands[stop ..n] = true
460 end
461 end
462 end
463
464 elseif type ~= "environment" then
465 i_commands[n] = at.file or true
466 elseif split then
467 i_environments[n] = at.file or true
468 else
469
470 i_commands[start..n] = at.file or true
471 i_commands[stop ..n] = at.file or true
472 end
473 end
474 end
475 if next(i_commands) then
476 collected[interface] = {
477 commands = i_commands,
478 environments = i_environments,
479 }
480 end
481 end
482
483 local commoncommands = { }
484 local commonenvironments = { }
485 for k, v in next, collected do
486 local c = v.commands
487 local e = v.environments
488 if k == "en" then
489 for k, v in next, c do
490 commoncommands[k] = true
491 end
492 for k, v in next, e do
493 commonenvironments[k] = true
494 end
495 else
496 for k, v in next, c do
497 if not commoncommands[k] then
498 commoncommands[k] = nil
499 end
500 end
501 for k, v in next, e do
502 if not commonenvironments[k] then
503 commonenvironments[k] = nil
504 end
505 end
506 end
507 end
508 local filenames = { }
509 if collected.en then
510 for k, v in next, collected.en.commands do
511 if v ~= true then
512 filenames[k] = v
513 end
514 end
515 for k, v in next, collected.en.environments do
516 if v ~= true then
517 filenames[k] = v
518 end
519 end
520 end
521 for k, v in next, collected do
522 local c = v.commands
523 local e = v.environments
524 for k, v in next, commoncommands do
525 c[k] = nil
526 end
527 for k, v in next, commonenvironments do
528 e[k] = nil
529 end
530 v.commands = sortedkeys(c)
531 v.environments = sortedkeys(e)
532 end
533
534 local mathnames = { }
535 local textnames = { }
536 for k, v in next, characters.data do
537 local name = v.contextname
538 if name then
539 textnames[name] = true
540 end
541 local name = v.mathname
542 if name then
543 mathnames[name] = true
544 end
545 local spec = v.mathspec
546 if spec then
547 for i=1,#spec do
548 local s = spec[i]
549 local name = s.name
550 if name then
551 mathnames[name] = true
552 end
553 end
554 end
555 end
556
557 collected.common = {
558 textnames = sortedkeys(textnames),
559 mathnames = sortedkeys(mathnames),
560 commands = sortedkeys(commoncommands),
561 environments = sortedkeys(commonenvironments),
562 filenames = filenames,
563 }
564
565 local flusher = flushers[editor]
566 if flusher then
567 return flusher(collected)
568 end
569end
570
571function scripts.interface.check()
572 local xmlfile = resolvers.findfile("cont-en.xml") or ""
573 if xmlfile ~= "" then
574 local f = io.open("cont-en-check.tex","w")
575 if f then
576 f:write("\\starttext\n")
577 local x = xml.load(xmlfile)
578 for e, d, k in xml.elements(x,"/cd:interface/cd:command") do
579 local dk = d[k]
580 local at = dk.at
581 if at then
582 local name = xml.filter(dk,"cd:sequence/cd:string/attribute(value)")
583 if name and name ~= "" then
584 if at.type == "environment" then
585 name = "start" .. name
586 end
587 f:write(format("\\doifundefined{%s}{\\writestatus{check}{command '%s' is undefined}}\n",name,name))
588 end
589 end
590 end
591 f:write("\\stoptext\n")
592 f:close()
593 end
594 end
595end
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687function scripts.interface.preprocess()
688 require("luat-mac")
689
690 local newsuffix = environment.argument("suffix") or "log"
691 local force = environment.argument("force")
692 for i=1,#environment.files do
693 local oldname = environment.files[i]
694 local newname = file.replacesuffix(oldname,newsuffix)
695 if oldname == newname then
696 report("skipping '%s' because old and new name are the same",oldname)
697 elseif io.exists(newname) and not force then
698 report("skipping '%s' because new file exists, use --force",oldname)
699 else
700 report("processing '%s' into '%s'",oldname,newname)
701 io.savedata(newname,resolvers.macros.preprocessed(io.loaddata(oldname)))
702 end
703 end
704end
705
706function scripts.interface.bidi()
707 require("char-def")
708
709 local directiondata = { }
710 local mirrordata = { }
711 local textclassdata = { }
712
713 local data = {
714 comment = "generated by: mtxrun -- script interface.lua --bidi",
715 direction = directions,
716 mirror = mirrors,
717 textclass = textclasses,
718 }
719
720 for k, d in next, characters.data do
721 local direction = d.direction
722 local mirror = d.mirror
723 local textclass = d.textclass
724 if direction and direction ~= "l" then
725 directiondata[k] = direction
726 end
727 if mirror then
728 mirrordata[k] = mirror
729 end
730 if textclass then
731 textclassdata[k] = textclass
732 end
733 end
734
735 local filename = "scite-context-data-bidi.lua"
736
737 report("saving %a",filename)
738 table.save(filename,data)
739end
740
741function scripts.interface.toutf()
742 local filename = environment.files[1]
743 if filename then
744 require("char-def")
745 local contextnames = { }
746 for unicode, data in next, characters.data do
747 local contextname = data.contextname
748 if contextname then
749 contextnames[contextname] = utf.char(unicode)
750 end
751 contextnames.uumlaut = contextnames.udiaeresis
752 contextnames.Uumlaut = contextnames.Udiaeresis
753 contextnames.oumlaut = contextnames.odiaeresis
754 contextnames.Oumlaut = contextnames.Odiaeresis
755 contextnames.aumlaut = contextnames.adiaeresis
756 contextnames.Aumlaut = contextnames.Adiaeresis
757 end
758 report("loading %a",filename)
759 local str = io.loaddata(filename) or ""
760 local done = { }
761 str = gsub(str,"(\\)([a-zA-Z][a-zA-Z][a-zA-Z]+)(%s*)", function(b,s,a)
762 local cn = contextnames[s]
763 if cn then
764 done[s] = (done[s] or 0) + 1
765 return cn
766 else
767 done[s] = (done[s] or 0) - 1
768 return b .. s .. a
769 end
770 end)
771 for k, v in table.sortedpairs(done) do
772 if v > 0 then
773 report("+ %5i : %s => %s",v,k,contextnames[k])
774 else
775 report("- %5i : %s",-v,k,contextnames[k])
776 end
777 end
778 filename = filename .. ".toutf"
779 report("saving %a",filename)
780 io.savedata(filename,str)
781 end
782end
783
784function scripts.interface.meaning()
785 local runner = "mtxrun --silent --script context --extra=meaning --once --noconsole --nostatistics"
786 local pattern = environment.arguments.pattern
787 local files = environment.files
788 if type(pattern) == "string" then
789 runner = runner .. ' --pattern="' .. pattern .. '"'
790 elseif files and #files > 0 then
791 for i=1,#files do
792 runner = runner .. ' "' .. files[i] .. '"'
793 end
794 else
795 return
796 end
797 local r = os.resultof(runner)
798 if type(r) == "string" then
799 r = gsub(r,"^.-(meaning%s+>)","\n%1")
800 print(r)
801 end
802end
803
804function scripts.interface.tokens()
805 local runner = "mtxrun --silent --script context --extra=meaning --tokens --once --noconsole --nostatistics"
806 local pattern = environment.arguments.pattern
807 local files = environment.files
808 if type(pattern) == "string" then
809 runner = runner .. ' --pattern="' .. pattern .. '"'
810 elseif files and #files > 0 then
811 for i=1,#files do
812 runner = runner .. ' "' .. files[i] .. '"'
813 end
814 else
815 return
816 end
817 local r = os.resultof(runner)
818 if type(r) == "string" then
819 r = gsub(r,"^.-(tokens%s+>)","\n%1")
820 print(r)
821 end
822end
823
824local ea = environment.argument
825
826
827
828
829
830if ea("preprocess") then
831 scripts.interface.preprocess()
832elseif ea("meaning") then
833 scripts.interface.meaning()
834elseif ea("tokens") then
835 scripts.interface.tokens()
836elseif ea("toutf") then
837 scripts.interface.toutf()
838elseif ea("bidi") then
839 scripts.interface.bidi()
840elseif ea("check") then
841 scripts.interface.check()
842elseif ea("scite") or ea("bbedit") or ea("jedit") or ea("textpad") or ea("vim") or ea("text") or ea("raw") then
843 if ea("scite") then
844 scripts.interface.editor("scite")
845 end
846 if ea("bbedit") then
847 scripts.interface.editor("bbedit")
848 end
849 if ea("jedit") then
850 scripts.interface.editor("jedit")
851 end
852 if ea("textpad") then
853 scripts.interface.editor("textpad",true, { "en" })
854 end
855 if ea("vim") then
856 scripts.interface.editor("vim",true, { "en" })
857 end
858 if ea("text") then
859 scripts.interface.editor("text")
860 end
861 if ea("raw") then
862 scripts.interface.editor("raw")
863 end
864elseif ea("exporthelp") then
865 application.export(ea("exporthelp"),environment.files[1])
866else
867 application.help()
868end
869 |