diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/buff-ini.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/buff-ini.lua | 855 |
1 files changed, 113 insertions, 742 deletions
diff --git a/Master/texmf-dist/tex/context/base/buff-ini.lua b/Master/texmf-dist/tex/context/base/buff-ini.lua index 6b1af8f96f3..70328f0f198 100644 --- a/Master/texmf-dist/tex/context/base/buff-ini.lua +++ b/Master/texmf-dist/tex/context/base/buff-ini.lua @@ -1,266 +1,137 @@ if not modules then modules = { } end modules ['buff-ini'] = { version = 1.001, - comment = "companion to core-buf.mkiv", + comment = "companion to buff-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } --- ctx lua reference model / hooks and such --- to be optimized - --- redefine buffers.get - -buffers = { } -buffers.data = { } -buffers.hooks = { } -buffers.flags = { } -buffers.commands = { } -buffers.visualizers = { } - --- if needed we can make 'm local - local trace_run = false trackers.register("buffers.run", function(v) trace_run = v end) local trace_visualize = false trackers.register("buffers.visualize", function(v) trace_visualize = v end) -local utf = unicode.utf8 +local report_buffers = logs.reporter("buffers","usage") -local concat, texsprint, texprint, texwrite = table.concat, tex.sprint, tex.print, tex.write -local utfbyte, utffind, utfgsub = utf.byte, utf.find, utf.gsub +local concat = table.concat local type, next = type, next -local huge = math.huge -local byte, sub, find, char, gsub, rep, lower, format, gmatch, match = string.byte, string.sub, string.find, string.char, string.gsub, string.rep, string.lower, string.format, string.gmatch, string.match -local utfcharacters, utfvalues = string.utfcharacters, string.utfvalues -local ctxcatcodes = tex.ctxcatcodes +local sub, format, count, splitlines = string.sub, string.format, string.count, string.splitlines + local variables = interfaces.variables -local lpegmatch = lpeg.match +local settings_to_array = utilities.parsers.settings_to_array +local texprint, ctxcatcodes = tex.print, tex.ctxcatcodes + +buffers = { } -local data, flags, hooks, visualizers = buffers.data, buffers.flags, buffers.hooks, buffers.visualizers +local buffers = buffers +local context = context -visualizers.defaultname = variables.typing +local data = { } function buffers.raw(name) - return data[name] or { } + return data[name] or "" end -function buffers.erase(name) +local function erase(name) data[name] = nil end -function buffers.set(name, str) - data[name] = { str } -- CHECK THIS +local function assign(name,str) + data[name] = str end -function buffers.append(name, str) +local function append(name,str) data[name] = (data[name] or "") .. str end +local function exists(name) + return data[name] ~= nil +end -buffers.flags.store_as_table = true - --- to be sorted out: crlf + \ ; slow now +local function getcontent(name) -- == raw + return data[name] or "" +end -local n = 0 +local function getlines(name) + local d = name and data[name] + return d and splitlines(d) +end -function buffers.grab(name,begintag,endtag,bufferdata) - local dn = data[name] or "" - if dn == "" then - buffers.level = 0 +local function collectcontent(names,separator) -- no print + if type(names) == "string" then + names = settings_to_array(names) end - buffers.level = buffers.level + bufferdata:count("\\"..begintag) - bufferdata:count("\\"..endtag) - local more = buffers.level > 0 - if more then - dn = dn .. bufferdata .. endtag - buffers.level = buffers.level - 1 + if #names == 1 then + return getcontent(names[1]) else - if dn == "" then - dn = sub(bufferdata,1,#bufferdata-1) - else - dn = dn .. "\n" .. sub(bufferdata,1,#bufferdata-1) - end - dn = gsub(dn,"[\010\013]$","") - if flags.store_as_table then - dn = dn:splitlines() + local t, n = { }, 0 + for i=1,#names do + local c = getcontent(names[i]) + if c ~= "" then + n = n + 1 + t[n] = c + end end + return concat(t,separator or "\n") -- was \r end - data[name] = dn - cs.testcase(more) end -function buffers.exists(name) - return data[name] ~= nil -end +buffers.erase = erase +buffers.assign = assign +buffers.append = append +buffers.exists = exists +buffers.getcontent = getcontent +buffers.getlines = getlines +buffers.collectcontent = collectcontent -function buffers.doifelsebuffer(name) - cs.testcase(data[name] ~= nil) -end +-- the context interface -flags.optimize_verbatim = true -flags.count_empty_lines = false - -local no_break_command = "\\doverbatimnobreak" -local do_break_command = "\\doverbatimgoodbreak" -local begin_of_line_command = "\\doverbatimbeginofline" -local end_of_line_command = "\\doverbatimendofline" -local empty_line_command = "\\doverbatimemptyline" - -local begin_of_display_command = "\\doverbatimbeginofdisplay" -local end_of_display_command = "\\doverbatimendofdisplay" -local begin_of_inline_command = "\\doverbatimbeginofinline" -local end_of_inline_command = "\\doverbatimendofinline" - -function buffers.verbatimbreak(n,m) - if flags.optimize_verbatim then - if n == 2 or n == m then - texsprint(no_break_command) - elseif n > 1 then - texsprint(do_break_command) - end - end -end +commands.erasebuffer = erase +commands.assignbuffer = assign -function buffers.strip(lines,first,last) - local first, last = first or 1, last or #lines - for i=first,last do - local li = lines[i] - if #li == 0 or find(li,"^%s*$") then - first = first + 1 - else - break - end - end - for i=last,first,-1 do - local li = lines[i] - if #li == 0 or find(li,"^%s*$") then - last = last - 1 - else - break - end - end - return first, last, last - first + 1 -end +local P, patterns, lpegmatch = lpeg.P, lpeg.patterns, lpeg.match -function buffers.range(lines,first,last,range) -- 1,3 1,+3 fromhere,tothere - local first, last = first or 1, last or #lines - local what = aux.settings_to_array(range) - local r_first, r_last = what[1], what[2] - local f, l = tonumber(r_first), tonumber(r_last) - if r_first then - if f then - if f > first then - first = f - end - else - for i=first,last do - if find(lines[i],r_first) then - first, strip = i + 1 - break - end - end - end - end - if r_last then - if l then - if find(r_last,"^[%+]") then -- 1,+3 - l = first + l - end - if l < last then - last = l - end - else - for i=first,last do - if find(lines[i],r_last) then - last = i - 1 - break - end - end - end - end - return first, last +local function countnesting(b,e) + local n + local g = P(b) / function() n = n + 1 end + + P(e) / function() n = n - 1 end + + patterns.anything + local p = patterns.alwaysmatched / function() n = 0 end + * g^0 + * patterns.alwaysmatched / function() return n end + return p end -function buffers.type(name,realign,range) - local lines = data[name] - local action = buffers.typeline - if lines then - if type(lines) == "string" then - lines = lines:splitlines() - data[name] = lines - end - if realign then - lines = buffers.realign(lines,realign) - end - local line, n = 0, 0 - local first, last, m = buffers.strip(lines) - if range then - first, last = buffers.range(lines,first,last,range) - first, last = buffers.strip(lines,first,last) - end - hooks.begin_of_display() - for i=first,last do - n, line = action(lines[i], n, m, line) - end - hooks.end_of_display() - end -end +local counters = { } +local nesting = 0 -function buffers.loaddata(filename) -- this one might go away - -- this will be cleaned up when we have split supp-fil completely - -- instead of half-half - local ok, str, n = resolvers.loaders.tex(filename) - if not str then - ok, str, n = resolvers.loaders.tex(file.addsuffix(filename,'tex')) +function commands.grabbuffer(name,begintag,endtag,bufferdata) -- maybe move \\ to call + local dn = getcontent(name) + if dn == "" then + nesting = 0 end -end - -function buffers.loaddata(filename) -- this one might go away - local foundname = resolvers.findtexfile(filename) or "" - if foundname == "" then - foundname = resolvers.findtexfile(file.addsuffix(filename,'tex')) or "" + -- nesting = nesting + count(bufferdata,"\\"..begintag) - count(bufferdata,"\\"..endtag) + local counter = counters[begintag] + if not counter then + counter = countnesting(begintag,endtag) + counters[begintag] = counter end - if foundname == "" then - return "" + nesting = nesting + lpegmatch(counter,bufferdata) + local more = nesting > 0 + if more then + dn = dn .. bufferdata .. endtag + nesting = nesting - 1 else - return resolvers.loadtexfile(foundname) - end -end - -function buffers.typefile(name,realign,range) -- still somewhat messy, since name can be be suffixless - local str = buffers.loaddata(name) - if str and str~= "" then - local lines = str:splitlines() - if realign then - lines = buffers.realign(lines,realign) - end - local line, n, action = 0, 0, buffers.typeline - local first, last, m = buffers.strip(lines) - hooks.begin_of_display() - if range then - first, last = buffers.range(lines,first,last,range) - first, last = buffers.strip(lines,first,last) + if dn == "" then + dn = sub(bufferdata,1,-2) + else + dn = dn .. "\n" .. sub(bufferdata,1,-2) -- no \r, \n is more generic end - for i=first,last do - n, line = action(lines[i], n, m, line) + local last = sub(dn,-1) + if last == "\n" or last == "\r" then -- \n is unlikely as \r is the endlinechar + dn = sub(dn,1,-2) end - hooks.end_of_display() end -end - -function buffers.typeline(str,n,m,line) - n = n + 1 - buffers.verbatimbreak(n,m) - if find(str,"%S") then - line = line + 1 - hooks.begin_of_line(line) - hooks.flush_line(hooks.line(str)) - hooks.end_of_line() - else - if flags.count_empty_lines then - line = line + 1 - end - hooks.empty_line(line) - end - return n, line + assign(name,dn) + commands.testcase(more) end -- The optional prefix hack is there for the typesetbuffer feature and @@ -273,7 +144,7 @@ local function prepared(name,list) -- list is optional if not name or name == "" then name = tex.jobname .. "-" .. list .. ".tmp" end - local content = buffers.collect(list,nil) or "" + local content = collectcontent(list,nil) or "" if content == "" then content = "empty buffer" end @@ -283,556 +154,56 @@ end local capsule = "\\starttext\n%s\n\\stoptext\n" local command = "context %s" -function buffers.save(name,list,encapsulate) -- list is optional - local name, content = prepared(name,list) - io.savedata(name, (encapsulate and format(capsule,content)) or content) -end - -function commands.savebuffer(list,name) -- name is optional - buffers.save(name,list) -end - -function buffers.run(name,list,encapsulate) +function commands.runbuffer(name,list,encapsulate) local name, content = prepared(name,list) + if encapsulate then + content = format(capsule,content) + end local data = io.loaddata(name) - content = (encapsulate and format(capsule,content)) or content if data ~= content then if trace_run then - commands.writestatus("buffers","changes in '%s', processing forced",name) + report_buffers("changes in '%s', processing forced",name) end io.savedata(name,content) os.execute(format(command,name)) elseif trace_run then - commands.writestatus("buffers","no changes in '%s', not processed",name) + report_buffers("no changes in '%s', not processed",name) end end -local printer = (lpeg.patterns.textline/texprint)^0 - -function buffers.get(name) - local b = buffers.data[name] - if b then - if type(b) == "table" then - for i=1,#b do - texprint(b[i]) - end - else - lpegmatch(printer,b) - end - end -end - -local function content(name,separator) -- no print - local b = data[name] - if b then - if type(b) == "table" then - return concat(b,separator or "\n") - else - return b - end - else - return "" - end -end - -buffers.content = content - -function buffers.collect(names,separator) -- no print - -- maybe we should always store a buffer as table so - -- that we can pass it directly - if type(names) == "string" then - names = aux.settings_to_array(names) - end - local t = { } - for i=1,#names do - local c = content(names[i],separator) - if c ~= "" then - t[#t+1] = c - end - end - return concat(t,separator or "\r") -- "\n" is safer due to comments and such -end - -function buffers.feedback(names,separator) - -- don't change the texprint into texsprint as it fails on mp buffers - -- because (p<nl>enddef) becomes penddef then - texprint(ctxcatcodes,string.splitlines(buffers.collect(names,separator))) -end - -local function tobyte(c) - return " [" .. utfbyte(c) .. "] " -end - -function buffers.inspect(name) - local b = data[name] - if b then - if type(b) == "table" then - for k=1,#b do - local v = b[k] - if v == "" then - texsprint(ctxcatcodes,"[crlf]\\par ") -- space ? - else - texsprint(ctxcatcodes,(gsub(v,"(.)",tobyte)),"\\par") - end - end - else - texsprint(ctxcatcodes,(gsub(b,"(.)",tobyte))) - end - end -end - --- maybe just line(n,str) empty(n,str) - -visualizers.tablength = 7 -visualizers.enabletab = true -- false -visualizers.obeyspace = true - -function buffers.settablength(tablength) - visualizers.tablength = tablength and tonumber(tablength) or 7 -end - -visualizers.handlers = visualizers.handlers or { } - -local handlers = visualizers.handlers - -function buffers.newvisualizer(name) - name = lower(name) - local handler = { } - handlers[name] = handler - return handler -end - -function buffers.getvisualizer(name) - name = lower(name) - return handlers[name] or buffers.loadvisualizer(name) -end - -function buffers.loadvisualizer(name) - name = lower(name) - local hn = handlers[name] - if hn then - return hn - else - environment.loadluafile("pret-" .. name) - local hn = handlers[name] - if not hn then - -- hn = buffers.newvisualizer(name) - hn = handlers[visualizers.defaultname] - handlers[name] = hn - if trace_visualize then - logs.report("buffers","mapping '%s' visualizer onto '%s'",name,visualizers.defaultname) - end - elseif trace_visualize then - logs.report("buffers","loading '%s' visualizer",name) - end - return hn - end -end - --- was "default", should be set at tex end (todo) - -local default = buffers.newvisualizer(visualizers.defaultname) - ---~ print(variables.typing) os.exit() - --- will become cleaner - -local currentvisualizer, currenthandler - -function buffers.setvisualizer(str) - currentvisualizer = lower(str) - currenthandler = handlers[currentvisualizer] - if currenthandler then - -- if trace_visualize then - -- logs.report("buffers","enabling specific '%s' visualizer",currentvisualizer) - -- end - else - currentvisualizer = visualizers.defaultname - currenthandler = handlers.default - -- if trace_visualize then - -- logs.report("buffers","enabling default visualizer '%s'",currentvisualizer) - -- end - end - if currenthandler.reset then - currenthandler.reset() - end -end - -function buffers.resetvisualizer() - currentvisualizer = visualizers.defaultname - currenthandler = handlers.default - if currenthandler.reset then - currenthandler.reset() - end -end - -buffers.setvisualizer(visualizers.defaultname) - -function visualizers.reset() -end - -function buffers.doifelsevisualizer(str) - cs.testcase((str ~= "") and (handlers[lower(str)] ~= nil)) -end - --- calling routines, don't change - -function hooks.begin_of_display() - (currenthandler.begin_of_display or default.begin_of_display)(currentvisualizer) -end - -function hooks.end_of_display() - (currenthandler.end_of_display or default.end_of_display)() -end - -function hooks.begin_of_inline() - (currenthandler.begin_of_inline or default.begin_of_inline)(currentvisualizer) -end - -function hooks.end_of_inline() - (currenthandler.end_of_inline or default.end_of_inline)() -end - -function hooks.flush_line(str,nesting) - local fl = currenthandler.flush_line - if fl then - str = gsub(str," *[\n\r]+ *"," ") ; -- semi colon needed - fl(str,nesting) - else - -- gsub done later - default.flush_line(str,nesting) - end -end - -function hooks.flush_inline(str,nesting) - hooks.begin_of_inline() - hooks.flush_line(str,nesting) - hooks.end_of_inline() -end - -function hooks.begin_of_line(n) - (currenthandler.begin_of_line or default.begin_of_line)(n) -end - -function hooks.end_of_line() - (currenthandler.end_of_line or default.end_of_line)() -end - -function hooks.empty_line() - (currenthandler.empty_line or default.empty_line)() -end - -function hooks.line(str) - if visualizers.enabletab then - str = string.tabtospace(str,visualizers.tablength) - else - str = gsub(str,"\t"," ") - end - return (currenthandler.line or default.line)(str) -end - --- defaults - -function default.begin_of_display(currentvisualizer) - texsprint(ctxcatcodes,begin_of_display_command,"{",currentvisualizer,"}") -end - -function default.end_of_display() - texsprint(ctxcatcodes,end_of_display_command) -end - -function default.begin_of_inline(currentvisualizer) - texsprint(ctxcatcodes,begin_of_inline_command,"{",currentvisualizer,"}") -end - -function default.end_of_inline() - texsprint(ctxcatcodes,end_of_inline_command) -end - -function default.begin_of_line(n) - texsprint(ctxcatcodes, begin_of_line_command,"{",n,"}") -end - -function default.end_of_line() - texsprint(ctxcatcodes,end_of_line_command) -end - -function default.empty_line() - texsprint(ctxcatcodes,empty_line_command) -end - -function default.line(str) - return str -end - -function default.flush_line(str) - str = gsub(str," *[\n\r]+ *"," ") - if visualizers.obeyspace then - for c in utfcharacters(str) do - if c == " " then - texsprint(ctxcatcodes,"\\obs") - else - texwrite(c) - end - end - else - texwrite(str) - end -end - --- not needed any more - -local function escaped_token(c) - if utffind(c,"^(%a%d)$") then - return c - elseif c == " " then - return "\\obs " - else - return "\\char" .. utfbyte(c) .. " " - end -end - -buffers.escaped_token = escaped_token - -function buffers.escaped(str) - -- use the utfcharacters loop - return (utfgsub(str,"(.)", escaped_token)) +function commands.savebuffer(list,name) -- name is optional + local name, content = prepared(name,list) + io.savedata(name,content) end --- special one - -buffers.commands.nested = "\\switchslantedtype " - --- todo : utf + faster, direct print and such. no \\char, vrb catcodes, see end - -function visualizers.flush_nested(str, enable) -- no utf, kind of obsolete mess - str = gsub(str," *[\n\r]+ *"," ") - local result, c, nested, i = "", "", 0, 1 - local commands = buffers.commands -- otherwise wrong commands - while i < #str do -- slow - c = sub(str,i,i+1) - if c == "<<" then - nested = nested + 1 - if enable then - result = result .. "{" .. commands.nested - else - result = result .. "{" - end - i = i + 2 - elseif c == ">>" then - if nested > 0 then - nested = nested - 1 - result = result .. "}" - end - i = i + 2 - else - c = sub(str,i,i) - if c == " " then - result = result .. "\\obs " - elseif find(c,"%a") then - result = result .. c - else - result = result .. "\\char" .. byte(c) .. " " - end - i = i + 1 - end +function commands.getbuffer(name) + local str = data[name] + if str and str ~= "" then + context.viafile(str) end - result = result .. "\\char" .. byte(sub(str,i,i)) .. " " .. rep("}",nested) - texsprint(ctxcatcodes,result) end --- handy helpers --- --- \sop[color] switch_of_pretty --- \bop[color] begin_of_pretty --- \eop end_of_pretty --- \obs obeyedspace --- \char <n> special characters - -buffers.currentcolors = { } - -function buffers.change_state(n, state) - if n then - if state ~= n then - if state > 0 then - texsprint(ctxcatcodes,"\\sop[",buffers.currentcolors[n],"]") - else - texsprint(ctxcatcodes,"\\bop[",buffers.currentcolors[n],"]") - end - return n - end - elseif state > 0 then - texsprint(ctxcatcodes,"\\eop") - return 0 - end - return state +function commands.getbuffermkvi(name) + context.viafile(resolvers.macros.preprocessed(getcontent(name))) end -function buffers.finish_state(state) - if state > 0 then - texsprint(ctxcatcodes,"\\eop") - return 0 +function commands.getbufferctxlua(name) + local ok = loadstring(getcontent(name)) + if ok then + ok() else - return state + report_buffers("invalid lua code in buffer '%s'",name) end end -buffers.open_nested = rep("\\char"..byte('<').." ",2) -buffers.close_nested = rep("\\char"..byte('>').." ",2) - -function buffers.replace_nested(result) - result = gsub(result,buffers.open_nested, "{") - result = gsub(result,buffers.close_nested,"}") - return result -end - -function buffers.flush_result(result,nested) - if nested then - texsprint(ctxcatcodes,buffers.replace_nested(concat(result,""))) - else - texsprint(ctxcatcodes,concat(result,"")) - end +function commands.doifelsebuffer(name) + commands.testcase(exists(name)) end --- new +-- This only used for mp buffers and is a kludge. Don't +-- change the texprint into texsprint as it fails because +-- "p<nl>enddef" becomes "penddef" then. -function buffers.realign(name,forced_n) -- no, auto, <number> - local n, d - if type(name) == "string" then - d = data[name] - if type(d) == "string" then - d = d:splitlines() - end - else - d = name -- already a buffer - end - forced_n = (forced_n == variables.auto and huge) or tonumber(forced_n) - if forced_n then - for i=1, #d do - local spaces = find(d[i],"%S") - if not spaces then - -- empty line - elseif not n then - n = spaces - elseif spaces == 0 then - n = 0 - break - elseif n > spaces then - n = spaces - end - end - if n > 0 then - if n > forced_n then - n = forced_n - end - for i=1,#d do - d[i] = sub(d[i],n) - end - end - end - return d -end - --- escapes: buffers.set_escape("tex","/BTEX","/ETEX") - -local function flush_escaped_line(str,pattern,flushline) - while true do - local a, b, c = match(str,pattern) - if a and a ~= "" then - flushline(a) - end - if b and b ~= "" then - texsprint(ctxcatcodes,"{",b,"}") - end - if c then - if c == "" then - break - else - str = c - end - else - flushline(str) - break - end - end -end - -function buffers.set_escape(name,pair) - if pair and pair ~= "" then - local visualizer = buffers.getvisualizer(name) - visualizer.normal_flush_line = visualizer.normal_flush_line or visualizer.flush_line - if pair == variables.no then - visualizer.flush_line = visualizer.normal_flush_line or visualizer.flush_line - if trace_visualize then - logs.report("buffers","resetting escape range for visualizer '%s'",name) - end - else - local start, stop - if pair == variables.yes then - start, stop = "/BTEX", "/ETEX" - else - pair = string.split(pair,",") - start, stop = string.esc(pair[1] or ""), string.esc(pair[2] or "") - end - if start ~= "" then - local pattern - if stop == "" then - pattern = "^(.-)" .. start .. "(.*)(.*)$" - else - pattern = "^(.-)" .. start .. "(.-)" .. stop .. "(.*)$" - end - function visualizer.flush_line(str) - flush_escaped_line(str,pattern,visualizer.normal_flush_line) - end - if trace_visualize then - logs.report("buffers","setting escape range for visualizer '%s' to %s -> %s",name,start,stop) - end - elseif trace_visualize then - logs.report("buffers","problematic escape specification '%s' for visualizer '%s'",pair,name) - end - end - end +function commands.feedback(names) + texprint(ctxcatcodes,splitlines(collectcontent(names))) end - --- THIS WILL BECOME A FRAMEWORK: the problem with prety printing is that --- we deal with snippets and therefore we need tolerant parsing - ---~ local type = type - ---~ visualizers = visualizers or { } - ---~ local function fallback(s) return s end - ---~ function visualizers.visualize(visualizer,kind,pattern) ---~ if type(visualizer) == "table" and type(kind) == "string" then ---~ kind = visualizer[kind] or visualizer.default or fallback ---~ else ---~ kind = fallback ---~ end ---~ return (lpeg.C(pattern))/kind ---~ end - ---~ local flusher = texio.write ---~ local format = string.format - ---~ local visualizer = { ---~ word = function(s) return flusher(format("\\bold{%s}",s)) end, ---~ number = function(s) return flusher(format("\\slanted{%s}",s)) end, ---~ default = function(s) return flusher(s) end, ---~ } - ---~ local word = lpeg.R("AZ","az")^1 ---~ local number = lpeg.R("09")^1 ---~ local any = lpeg.P(1) - ---~ local pattern = lpeg.P { "start", ---~ start = ( ---~ visualizers.visualize(visualizer,"word",word) + ---~ visualizers.visualize(visualizer,"number",number) + ---~ visualizers.visualize(visualizer,"default",any) ---~ )^1 ---~ } - ---~ str = [[test 123 test $oeps$]] - ---~ lpegmatch(pattern,str) |