1if not modules then modules = { } end modules ['buff-ver'] = {
2 version = 1.001,
3 comment = "companion to buff-ver.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
16local type, next, rawset, rawget, setmetatable, getmetatable, tonumber = type, next, rawset, rawget, setmetatable, getmetatable, tonumber
17local lower, upper,match, find, sub = string.lower, string.upper, string.match, string.find, string.sub
18local splitlines = string.splitlines
19local concat = table.concat
20local C, P, R, S, V, Carg, Cc, Cs = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Carg, lpeg.Cc, lpeg.Cs
21local patterns, lpegmatch, is_lpeg = lpeg.patterns, lpeg.match, lpeg.is_lpeg
22
23local trace_visualize = false trackers.register("buffers.visualize", function(v) trace_visualize = v end)
24local report_visualizers = logs.reporter("buffers","visualizers")
25
26local allocate = utilities.storage.allocate
27
28visualizers = visualizers or { }
29local specifications = allocate()
30visualizers.specifications = specifications
31
32local context = context
33local commands = commands
34local implement = interfaces.implement
35
36local formatters = string.formatters
37
38local tabtospace = utilities.strings.tabtospace
39local settings_to_array = utilities.parsers.settings_to_array
40local findfile = resolvers.findfile
41local addsuffix = file.addsuffix
42
43local variables = interfaces.variables
44local v_yes <const> = variables.yes
45local v_no <const> = variables.no
46local v_last <const> = variables.last
47local v_all <const> = variables.all
48local v_absolute <const> = variables.absolute
49
50
51
52
53
54local ctx_inlineverbatimnewline = context.doinlineverbatimnewline
55local ctx_inlineverbatimbeginline = context.doinlineverbatimbeginline
56local ctx_inlineverbatimemptyline = context.doinlineverbatimemptyline
57local ctx_inlineverbatimstart = context.doinlineverbatimstart
58local ctx_inlineverbatimstop = context.doinlineverbatimstop
59
60local ctx_displayverbatiminitialize = context.dodisplayverbatiminitialize
61local ctx_displayverbatimnewline = context.dodisplayverbatimnewline
62local ctx_displayverbatimbeginline = context.dodisplayverbatimbeginline
63local ctx_displayverbatimemptyline = context.dodisplayverbatimemptyline
64local ctx_displayverbatimstart = context.dodisplayverbatimstart
65local ctx_displayverbatimstop = context.dodisplayverbatimstop
66
67local ctx_verbatim = context.verbatim
68local ctx_verbatimspace = context.doverbatimspace
69
70local CargOne = Carg(1)
71
72local function f_emptyline(s,settings)
73 if settings and settings.nature == "inline" then
74 ctx_inlineverbatimemptyline()
75 else
76 ctx_displayverbatimemptyline()
77 end
78end
79
80local function f_beginline(s,settings)
81 if settings and settings.nature == "inline" then
82 ctx_inlineverbatimbeginline()
83 else
84 ctx_displayverbatimbeginline()
85 end
86end
87
88local function f_newline(s,settings)
89 if settings and settings.nature == "inline" then
90 ctx_inlineverbatimnewline()
91 else
92 ctx_displayverbatimnewline()
93 end
94end
95
96local function f_start(s,settings)
97 if settings and settings.nature == "inline" then
98 ctx_inlineverbatimstart()
99 else
100 ctx_displayverbatimstart()
101 end
102end
103
104local function f_stop(s,settings)
105 if settings and settings.nature == "inline" then
106 ctx_inlineverbatimstop()
107 else
108 ctx_displayverbatimstop()
109 end
110end
111
112local function f_default(s)
113 ctx_verbatim(s)
114end
115
116local function f_space()
117 ctx_verbatimspace()
118end
119
120local function f_signal()
121
122end
123
124local signal = "\000"
125
126visualizers.signal = signal
127visualizers.signalpattern = P(signal)
128
129local functions = {
130 __index = {
131 emptyline = f_emptyline,
132 newline = f_newline,
133 default = f_default,
134 beginline = f_beginline,
135 space = f_space,
136 start = f_start,
137 stop = f_stop,
138 signal = f_signal,
139 }
140}
141
142local handlers = { }
143
144function visualizers.newhandler(name,data)
145 local tname = type(name)
146 local tdata = type(data)
147 if tname == "table" then
148 setmetatable(name,getmetatable(name) or functions)
149 return name
150 elseif tname == "string" then
151 if tdata == "string" then
152 local result = { }
153 setmetatable(result,getmetatable(handlers[data]) or functions)
154 handlers[name] = result
155 return result
156 elseif tdata == "table" then
157 setmetatable(data,getmetatable(data) or functions)
158 handlers[name] = data
159 return data
160 else
161 local result = { }
162 setmetatable(result,functions)
163 handlers[name] = result
164 return result
165 end
166 else
167 local result = { }
168 setmetatable(result,functions)
169 return result
170 end
171end
172
173function visualizers.newgrammar(name,t)
174 name = lower(name)
175 t = t or { }
176 local g = visualizers.specifications[name]
177 g = g and g.grammar
178 if g then
179 if trace_visualize then
180 report_visualizers("cloning grammar %a",name)
181 end
182 for k,v in next, g do
183 if not t[k] then
184 t[k] = v
185 end
186 if is_lpeg(v) then
187 t[name..":"..k] = v
188 end
189 end
190 end
191 return t
192end
193
194local function getvisualizer(method,nature)
195 method = lower(method)
196 local m = specifications[method] or specifications.default
197 if nature then
198 if trace_visualize then
199 report_visualizers("getting visualizer %a with nature %a",method,nature)
200 end
201 return m and (m[nature] or m.parser) or nil
202 else
203 if trace_visualize then
204 report_visualizers("getting visualizer %a",method)
205 end
206 return m and m.parser or nil
207 end
208end
209
210local ctx_fallback = ctx_verbatim
211
212local function makepattern(visualizer,replacement,pattern)
213 if not pattern then
214 report_visualizers("error in visualizer %a",replacement)
215 return patterns.alwaystrue
216 else
217 if type(visualizer) == "table" and type(replacement) == "string" then
218 replacement = visualizer[replacement] or ctx_fallback
219 else
220 replacement = ctx_fallback
221 end
222 return (C(pattern) * CargOne) / replacement
223 end
224end
225
226local function makenested(handler,how,start,stop)
227 local b, e, f = P(start), P(stop), how
228 if type(how) == "string" then
229 f = function(s) getvisualizer(how,"direct")(s) end
230 end
231 return makepattern(handler,"name",b)
232 * ((1-e)^1/f)
233 * makepattern(handler,"name",e)
234end
235
236visualizers.pattern = makepattern
237visualizers.makepattern = makepattern
238visualizers.makenested = makenested
239
240function visualizers.load(name)
241 name = lower(name)
242 if rawget(specifications,name) == nil then
243 name = lower(name)
244 local impname = "buff-imp-"..name
245 local texname = findfile(addsuffix(impname,"mkiv"))
246 local luaname = findfile(addsuffix(impname,"lua"))
247 if texname == "" or luaname == "" then
248
249 luaname = findfile(addsuffix(name,"mkiv"))
250 texname = findfile(addsuffix(name,"lua"))
251 end
252 if texname == "" or luaname == "" then
253 if trace_visualize then
254 report_visualizers("unknown visualizer %a",name)
255 end
256 else
257 if trace_visualize then
258 report_visualizers("loading visualizer %a",name)
259 end
260 lua.registercode(luaname)
261 context.input(texname)
262 end
263 if rawget(specifications,name) == nil then
264 rawset(specifications,name,false)
265 end
266 end
267end
268
269function visualizers.register(name,specification)
270 name = lower(name)
271 if trace_visualize then
272 report_visualizers("registering visualizer %a",name)
273 end
274 specifications[name] = specification
275 local parser = specification.parser
276 local handler = specification.handler
277 local displayparser = specification.display or parser
278 local inlineparser = specification.inline or parser
279 local isparser = is_lpeg(parser)
280 local start, stop
281 if isparser then
282 start = makepattern(handler,"start",patterns.alwaysmatched)
283 stop = makepattern(handler,"stop", patterns.alwaysmatched)
284 end
285 if handler then
286 if isparser then
287 specification.display = function(content,settings)
288 if handler.startdisplay then handler.startdisplay(settings) end
289 lpegmatch(start * displayparser * stop,content,1,settings)
290 if handler.stopdisplay then handler.stopdisplay(settings) end
291 end
292 specification.inline = function(content,settings)
293 if handler.startinline then handler.startinline(settings) end
294 lpegmatch(start * inlineparser * stop,content,1,settings)
295 if handler.stopinline then handler.stopinline(settings) end
296 end
297 specification.direct = function(content,settings)
298 lpegmatch(parser,content,1,settings)
299 end
300 elseif parser then
301 specification.display = function(content,settings)
302 if handler.startdisplay then handler.startdisplay(settings) end
303 parser(content,settings)
304 if handler.stopdisplay then handler.stopdisplay(settings) end
305 end
306 specification.inline = function(content,settings)
307 if handler.startinline then handler.startinline(settings) end
308 parser(content,settings)
309 if handler.stopinline then handler.stopinline(settings) end
310 end
311 specification.direct = parser
312 end
313 elseif isparser then
314 specification.display = function(content,settings)
315 lpegmatch(start * displayparser * stop,content,1,settings)
316 end
317 specification.inline = function(content,settings)
318 lpegmatch(start * inlineparser * stop,content,1,settings)
319 end
320 specification.direct = function(content,settings)
321 lpegmatch(parser,content,1,settings)
322 end
323 elseif parser then
324 specification.display = parser
325 specification.inline = parser
326 specification.direct = parser
327 end
328 return specification
329end
330
331function visualizers.getspecification(name)
332 return specifications[lower(name)]
333end
334
335local escapepatterns = allocate()
336visualizers.escapepatterns = escapepatterns
337
338local function texmethod(s)
339 context.bgroup()
340 context(s)
341 context.egroup()
342end
343
344local function texcommand(s)
345 context[s]()
346end
347
348local function defaultmethod(s,settings)
349 lpegmatch(getvisualizer("default"),lower(s),1,settings)
350end
351
352
353
354local space_pattern = patterns.space^0
355local name_pattern = R("az","AZ")^1
356
357
358
359
360local function hack(pattern)
361 return Cs(pattern * Cc(signal))
362end
363
364local split_processor = typesetters.processors.split
365local apply_processor = typesetters.processors.apply
366
367
368
369function visualizers.registerescapepattern(name,befores,afters,normalmethod,escapemethod,processors)
370 local escapepattern = escapepatterns[name]
371 if not escapepattern then
372 if type(befores) ~= "table" then befores = { befores } end
373 if type(afters) ~= "table" then afters = { afters } end
374 if type(processors) ~= "table" then processors = { processors } end
375 for i=1,#befores do
376 local before = befores[i]
377 local after = afters[i]
378 local processor = processors[i]
379 if trace_visualize then
380 report_visualizers("registering escape pattern, name %a, index %a, before %a, after %a, processor %a",
381 name,i,before,after,processor or "default")
382 end
383 before = P(before) * space_pattern
384 after = space_pattern * P(after)
385 local action
386 if processor then
387 action = function(s) apply_processor(processor,s) end
388 else
389 action = escapemethod or texmethod
390 end
391 local ep = (before / "") * ((1 - after)^0 / action) * (after / "")
392 if escapepattern then
393 escapepattern = escapepattern + ep
394 else
395 escapepattern = ep
396 end
397 end
398 escapepattern = (
399 escapepattern
400 + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod)
401 )^0
402 escapepatterns[name] = escapepattern
403 end
404 return escapepattern
405end
406
407function visualizers.registerescapeline(name,befores,normalmethod,escapemethod,processors)
408 local escapepattern = escapepatterns[name]
409 if not escapepattern then
410 if type(befores) ~= "table" then befores = { befores } end
411 if type(processors) ~= "table" then processors = { processors } end
412 for i=1,#befores do
413 local before = befores[i]
414 local processor = processors[i]
415 if trace_visualize then
416 report_visualizers("registering escape line pattern, name %a, before %a, after <<newline>>",name,before)
417 end
418 before = P(before) * space_pattern
419 after = space_pattern * P("\n")
420 local action
421 if processor then
422 action = function(s) apply_processor(processor,s) end
423 else
424 action = escapemethod or texmethod
425 end
426 local ep = (before / "") * ((1 - after)^0 / action) * (space_pattern / "")
427 if escapepattern then
428 escapepattern = escapepattern + ep
429 else
430 escapepattern = ep
431 end
432 end
433 escapepattern = (
434 escapepattern
435 + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod)
436 )^0
437 escapepatterns[name] = escapepattern
438 end
439 return escapepattern
440end
441
442function visualizers.registerescapecommand(name,token,normalmethod,escapecommand,processor)
443 local escapepattern = escapepatterns[name]
444 if not escapepattern then
445 if trace_visualize then
446 report_visualizers("registering escape token, name %a, token %a",name,token)
447 end
448 token = P(token)
449 local notoken = hack((1 - token)^1)
450 local cstoken = Cs(name_pattern * (space_pattern/""))
451 escapepattern = (
452 (token / "")
453 * (cstoken / (escapecommand or texcommand))
454 + (notoken / (normalmethod or defaultmethod))
455 )^0
456 escapepatterns[name] = escapepattern
457 end
458 return escapepattern
459end
460
461local escapedvisualizers = { }
462local f_escapedvisualizer = formatters["%s : %s"]
463
464local function visualize(content,settings)
465 if content and content ~= "" then
466 local method = lower(settings.method or "default")
467 local m = specifications[method] or specifications.default
468 local e = settings.escape
469 if e and e ~= "" and not m.handler.noescape then
470 local newname = f_escapedvisualizer(method,e)
471 local newspec = specifications[newname]
472 if newspec then
473 m = newspec
474 else
475 local starts, stops, processors = { }, { }, { }
476 if e == v_yes then
477 starts[1] = "/BTEX"
478 stops [1] = "/ETEX"
479 else
480 local s = settings_to_array(e,true)
481 for i=1,#s do
482 local si = s[i]
483 local processor, pattern = split_processor(si)
484 si = processor and pattern or si
485 local start, stop = match(si,"^(.-),(.-)$")
486 if start then
487 local n = #starts + 1
488 starts[n] = start
489 stops [n] = stop or ""
490 processors[n] = processor
491 end
492 end
493 end
494 local oldm = m
495 local oldparser = oldm.direct
496 local newhandler = oldm.handler
497 local newparser = oldm.parser
498 if starts[1] and stops[1] ~= "" then
499 newparser = visualizers.registerescapepattern(newname,starts,stops,oldparser,nil,processors)
500 elseif starts[1] then
501 newparser = visualizers.registerescapeline(newname,starts,oldparser,nil,processors)
502 else
503 newparser = visualizers.registerescapecommand(newname,e,oldparser,nil,processors)
504 end
505 m = visualizers.register(newname, {
506 parser = newparser,
507 handler = newhandler,
508 })
509 end
510 else
511 m = specifications[method] or specifications.default
512 end
513 local nature = settings.nature or "display"
514 local n = m and m[nature]
515 if n then
516 if trace_visualize then
517 report_visualizers("visualize using method %a and nature %a",method,nature)
518 end
519 n(content,settings)
520 else
521 if trace_visualize then
522 report_visualizers("visualize using method %a",method)
523 end
524 ctx_fallback(content,1,settings)
525 end
526 end
527end
528
529visualizers.visualize = visualize
530visualizers.getvisualizer = getvisualizer
531
532local fallbacks = { } table.setmetatableindex(fallbacks,function(t,k) local v = { nature = k } t[k] = v return v end)
533
534local function checkedsettings(settings,nature)
535 if not settings then
536
537 return fallbacks[nature]
538 else
539 if not settings.nature then
540 settings.nature = nature
541 end
542 return settings
543 end
544end
545
546function visualizers.visualizestring(content,settings)
547 visualize(content,checkedsettings(settings,"inline"))
548end
549
550function visualizers.visualizefile(name,settings)
551 visualize(resolvers.loadtexfile(name),checkedsettings(settings,"display"))
552end
553
554function visualizers.visualizebuffer(name,settings)
555 visualize(buffers.getcontent(name),checkedsettings(settings,"display"))
556end
557
558
559
560local space = C(patterns.space) * CargOne / f_space
561local newline = C(patterns.newline) * CargOne / f_newline
562local emptyline = C(patterns.emptyline) * CargOne / f_emptyline
563local beginline = C(patterns.beginline) * CargOne / f_beginline
564local anything = C(patterns.somecontent) * CargOne / f_default
565
566
567local verbosed = (space + newline * (emptyline^0) * beginline + newline * emptyline + newline + anything)^0
568
569local function write(s,settings)
570 lpegmatch(verbosed,s,1,settings or false)
571end
572
573visualizers.write = write
574visualizers.writenewline = f_newline
575visualizers.writeemptyline = f_emptyline
576visualizers.writespace = f_space
577visualizers.writedefault = f_default
578
579function visualizers.writeargument(...)
580 context("{")
581 write(...)
582 context("}")
583end
584
585
586
587local function realign(lines,strip)
588 local n
589 if strip == v_yes then
590 n = 0xFFFF
591 for i=1, #lines do
592 local spaces = find(lines[i],"%S")
593 if not spaces then
594
595 elseif spaces == 0 then
596 n = 0
597 break
598 elseif spaces < n then
599 n = spaces
600 end
601 end
602 n = n - 1
603 else
604 n = tonumber(strip)
605 end
606 if n and n > 0 then
607 local copy = { }
608 for i=1,#lines do
609 copy[i] = sub(lines[i],n+1)
610 end
611 return copy
612 end
613 return lines
614end
615
616local onlyspaces = S(" \t\f\n\r")^0 * P(-1)
617
618local function getstrip(lines,first,last)
619 if not first then
620 first = 1
621 end
622 if not last then
623 last = #lines
624 end
625 for i=first,last do
626 local li = lines[i]
627 if #li == 0 or lpegmatch(onlyspaces,li) then
628 first = first + 1
629 else
630 break
631 end
632 end
633 for i=last,first,-1 do
634 local li = lines[i]
635 if #li == 0 or lpegmatch(onlyspaces,li) then
636 last = last - 1
637 else
638 break
639 end
640 end
641 return first, last, last - first + 1
642end
643
644
645
646
647
648
649
650
651
652
653local comment = "^[%%%-#]"
654
655local function getrange(lines,first,last,range)
656 local noflines = #lines
657 local first = first or 1
658 local last = last or noflines
659 if last < 0 then
660 last = noflines + last
661 end
662 local what = settings_to_array(range)
663 local r_first = what[1]
664 local r_last = what[2]
665 local f = tonumber(r_first)
666 local l = tonumber(r_last)
667 if r_first then
668 if f then
669 if f > first then
670 first = f
671 end
672 elseif r_first == "=" then
673 for i=first,last do
674 if find(lines[i],comment) then
675 first = i + 1
676 else
677 break
678 end
679 end
680 elseif r_first ~= "" then
681 local exact, r_first = match(r_first,"^([=]?)(.*)")
682 for i=first,last do
683 if find(lines[i],r_first) then
684 if exact == "=" then
685 first = i
686 else
687 first = i + 1
688 end
689 break
690 else
691 first = i
692 end
693 end
694 end
695 end
696 if r_last then
697 if l then
698 if l < 0 then
699 l = noflines + l
700 end
701 if find(r_last,"^[%+]") then
702 l = first + l
703 end
704 if l < last then
705 last = l
706 end
707 elseif r_first == "=" then
708 for i=first,last do
709 if find(lines[i],comment) then
710 break
711 else
712 last = i
713 end
714 end
715 elseif r_last ~= "" then
716 local exact, r_last = match(r_last,"^([=]?)(.*)")
717 for i=first,last do
718 if find(lines[i],r_last) then
719 if exact == "=" then
720 last = i
721 end
722 break
723 else
724 last = i
725 end
726 end
727 end
728 end
729 return first, last
730end
731
732local tablength = 7
733
734local function dotabs(content,settings)
735 local par = settings.par
736 if par == v_yes then
737 return utilities.strings.striplines(content,"collapse all")
738 else
739 local tab = settings.tab
740 tab = tab and (tab == v_yes and tablength or tonumber(tab))
741 if tab then
742 return tabtospace(content,tab)
743 end
744 end
745 return content
746end
747
748local function filter(lines,settings)
749 local strip = settings.strip
750
751 if strip ~= v_no and strip ~= false then
752 lines = realign(lines,strip)
753 end
754 local line = 0
755 local n = 0
756 local range = settings.range
757 local first, last, m = getstrip(lines)
758 if range then
759 first, last = getrange(lines,first,last,range)
760 first, last = getstrip(lines,first,last)
761 end
762
763 local content = concat(lines,(settings.nature == "inline" and " ") or "\n",first,last)
764 return content, m
765end
766
767local getlines = buffers.getlines
768
769
770
771local function typebuffer(settings)
772 local lines = getlines(settings.name)
773 if lines then
774 ctx_displayverbatiminitialize(#lines)
775 local content, m = filter(lines,settings)
776 if content and content ~= "" then
777
778 content = dotabs(content,settings)
779 visualize(content,checkedsettings(settings,"display"))
780 end
781 end
782end
783
784local function processbuffer(settings)
785 local lines = getlines(settings.name)
786 if lines then
787 local content, m = filter(lines,settings)
788 if content and content ~= "" then
789 content = dotabs(content,settings)
790 visualize(content,checkedsettings(settings,"direct"))
791 end
792 end
793end
794
795
796
797
798
799
800
801
802
803
804
805local fences = S([[[{]])
806local symbols = S([[!#"$%&'*()+,-./:;<=>?@[]^_`{|}~0123456789]])
807local space = S([[ ]])
808local backslash = S([[\]])
809local nospace = space^1/""
810local endstring = P(-1)
811
812local compactors = {
813 [v_all] = Cs((backslash * (1-backslash-space)^1 * nospace * (endstring + fences + #backslash) + 1)^0),
814 [v_absolute] = Cs((backslash * (1-symbols -space)^1 * nospace * (symbols + backslash ) + 1)^0),
815 [v_last] = Cs((space^1 * endstring/"" + 1)^0),
816}
817
818
819
820local function typestring(settings)
821
822 local content = settings.data
823 if content and content ~= "" then
824 local compact = settings.compact
825 local compactor = compact and compactors[compact]
826 if compactor then
827 content = lpegmatch(compactor,content) or content
828 end
829
830
831 visualize(content,checkedsettings(settings,"inline"))
832
833
834 end
835end
836
837local function typefile(settings)
838 local filename = settings.name
839 local foundname = resolvers.findtexfile(filename)
840 if foundname and foundname ~= "" then
841 local str = resolvers.loadtexfile(foundname)
842 if str and str ~= "" then
843 local regime = settings.regime
844 if regime and regime ~= "" then
845 str = regimes.translate(str,regime)
846 end
847 if str and str~= "" then
848
849 local lines = splitlines(str)
850 local content, m = filter(lines,settings)
851 if content and content ~= "" then
852 content = dotabs(content,settings)
853 visualize(content,checkedsettings(settings,"display"))
854 end
855 end
856 end
857 end
858end
859
860implement {
861 name = "type",
862 actions = typestring,
863 arguments = {
864 {
865 { "data" },
866
867
868 { "tab" },
869 { "par" },
870 { "method" },
871 { "compact" },
872 { "nature" },
873 { "escape" },
874 }
875 }
876}
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932implement {
933 name = "processbuffer",
934 actions = processbuffer,
935 arguments = {
936 {
937 { "name" },
938 { "strip" },
939 { "tab" },
940 { "par" },
941 { "method" },
942 { "nature" },
943 }
944 }
945}
946
947implement {
948 name = "typebuffer",
949 actions = typebuffer,
950 arguments = {
951 {
952 { "name" },
953 { "strip" },
954 { "range" },
955 { "regime" },
956 { "tab" },
957 { "par" },
958 { "method" },
959 { "escape" },
960 { "nature" },
961 }
962 }
963}
964
965implement {
966 name = "typefile",
967 actions = typefile,
968 arguments = {
969 {
970 { "name" },
971 { "strip" },
972 { "range" },
973 { "regime" },
974 { "tab" },
975 { "method" },
976 { "escape" },
977 { "nature" },
978 }
979 }
980}
981
982implement {
983 name = "doifelsevisualizer",
984 actions = { visualizers.getspecification, commands.doifelse },
985 arguments = "string"
986}
987
988implement {
989 name = "loadvisualizer",
990 actions = visualizers.load,
991 arguments = "string"
992}
993 |