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