buff-ver.lmt /size: 30 Kb    last modification: 2025-02-21 11:03
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 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----- v_inline             <const> = variables.inline  -- not !
50----- v_display            <const> = variables.display -- not !
51
52-- beware, all macros have an argument:
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 -- the number of arguments might change over time
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) -- (s,settings)
113    ctx_verbatim(s)
114end
115
116local function f_space() -- (s,settings)
117    ctx_verbatimspace()
118end
119
120local function f_signal() -- (s,settings)
121    -- we use these for special purposes
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 -- (data)
148        setmetatable(name,getmetatable(name) or functions)
149        return name
150    elseif tname == "string" then
151        if tdata == "string" then -- ("name","parent")
152            local result = { }
153            setmetatable(result,getmetatable(handlers[data]) or functions)
154            handlers[name] = result
155            return result
156        elseif tdata == "table" then -- ("name",data)
157            setmetatable(data,getmetatable(data) or functions)
158            handlers[name] = data
159            return data
160        else -- ("name")
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            -- assume a user specific file
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) -- only used here, end up in format
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-- we can consider using a nested instead
353
354local space_pattern = patterns.space^0
355local name_pattern  = R("az","AZ")^1
356
357-- the hack is needed in order to retain newlines when an escape happens at the
358-- at the begin of a line; it also ensures proper line numbering; a bit messy
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-- todo: { before = b, after = a, processor = p }, ...
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) -- maybe also method in 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 -- nil
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 -- for old times sake: /em
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        -- let's avoid dummy tables as much as possible
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----- verbosed  = (space + newline * (emptyline^0) * beginline + anything)^0
567local verbosed  = (space + newline * (emptyline^0) * beginline + newline * emptyline + newline + anything)^0
568
569local function write(s,settings) -- bad name
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("{")  -- If we didn't have tracing then we could
581    write(...)    -- use a faster print to tex variant for the
582    context("}")  -- { } tokens as they always have ctxcatcodes.
583end
584
585-- helpers
586
587local function realign(lines,strip) -- "yes", <number>
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") -- can be lpeg
593            if not spaces then
594                -- empty line
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-- we look for text (todo): (needs documentation)
645--
646-- "foo"  : start after line with "foo"
647-- "="    : ignore first blob
648-- "=foo" : start at "foo"
649-- "!foo" : maybe a not "foo"
650
651-- % - # lines start a comment
652
653local comment = "^[%%%-#]"
654
655local function getrange(lines,first,last,range) -- 1,3 1,+3 fromhere,tothere
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) -- maybe also n:m
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 -- 1,+3
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) -- todo: inline or display in settings
749    local strip = settings.strip
750 -- if strip and strip == "" then
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    -- \r is \endlinechar but \n would is more generic so this choice is debatable
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-- local decodecomment = resolvers.macros.decodecomment -- experiment
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         -- content = decodecomment(content)
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-- not really buffers but it's closely related
796
797-- A string.gsub(str,"(\\.-) +$","%1") is faster than an lpeg when there is a
798-- match but slower when there is no match. But anyway, we need a more clever
799-- parser so we use lpeg.
800--
801-- [[\text ]]  [[\text{}]]  [[\foo\bar .tex]] [[\text \text ]]  [[\text \\ \text ]]
802--
803-- needed in e.g. tabulate (manuals)
804
805local fences    = S([[[{]])
806local symbols   = S([[!#"$%&'*()+,-./:;<=>?@[]^_`{|}~0123456789]]) -- digits added but maybe split it
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-- local content = ""
819
820local function typestring(settings)
821--     content = settings.data
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     -- content = decodecomment(content)
830     -- content = dotabs(content,settings)
831        visualize(content,checkedsettings(settings,"inline"))
832--     else
833--         content = ""
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             -- content = decodecomment(content)
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--             { "data", "detokened" },
867--             { "data", "tokenstring" },
868            { "tab"     },
869            { "par"     },
870            { "method"  },
871            { "compact" },
872            { "nature"  },
873            { "escape"  },
874        }
875    }
876}
877
878-- implement {
879--     name    = "gettypestring",
880--     public  = true,
881--     actions = function()
882--         if content ~= "" then
883--             context(content)
884--         end
885--     end
886-- }
887
888-- implement {
889--     name      = "type_x",
890--     actions   = typestring,
891--     arguments = {
892--         {
893--             { "data", "verbatim" },
894--             { "tab"     },
895--             { "method"  },
896--             { "compact" },
897--             { "nature"  },
898--             { "escape"  },
899--         }
900--     }
901-- }
902
903-- local function typestring_y(settings)
904--     local content = tex.toks[settings.n]
905--     if content and content ~= "" then
906--         local compact   = settings.compact
907--         local compactor = compact and compactors[compact]
908--         if compactor then
909--             content = lpegmatch(compactor,content)
910--         end
911--      -- content = decodecomment(content)
912--      -- content = dotabs(content,settings)
913--         visualize(content,checkedsettings(settings,"inline"))
914--     end
915-- end
916
917-- implement {
918--     name      = "type_y",
919--     actions   = typestring_y,
920--     arguments = {
921--         {
922--             { "n", "integer" },
923--             { "tab"     },
924--             { "method"  },
925--             { "compact" },
926--             { "nature"  },
927--             { "escape"  },
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