From 85b7bc695629926641c7cb752fd478adfdf374f3 Mon Sep 17 00:00:00 2001 From: Marius Date: Sun, 4 Jul 2010 15:32:09 +0300 Subject: stable 2010-05-24 13:10 --- tex/context/base/buff-ini.lua | 838 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 838 insertions(+) create mode 100644 tex/context/base/buff-ini.lua (limited to 'tex/context/base/buff-ini.lua') diff --git a/tex/context/base/buff-ini.lua b/tex/context/base/buff-ini.lua new file mode 100644 index 000000000..6b1af8f96 --- /dev/null +++ b/tex/context/base/buff-ini.lua @@ -0,0 +1,838 @@ +if not modules then modules = { } end modules ['buff-ini'] = { + version = 1.001, + comment = "companion to core-buf.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 concat, texsprint, texprint, texwrite = table.concat, tex.sprint, tex.print, tex.write +local utfbyte, utffind, utfgsub = utf.byte, utf.find, utf.gsub +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 variables = interfaces.variables +local lpegmatch = lpeg.match + +local data, flags, hooks, visualizers = buffers.data, buffers.flags, buffers.hooks, buffers.visualizers + +visualizers.defaultname = variables.typing + +function buffers.raw(name) + return data[name] or { } +end + +function buffers.erase(name) + data[name] = nil +end + +function buffers.set(name, str) + data[name] = { str } -- CHECK THIS +end + +function buffers.append(name, str) + data[name] = (data[name] or "") .. str +end + + +buffers.flags.store_as_table = true + +-- to be sorted out: crlf + \ ; slow now + +local n = 0 + +function buffers.grab(name,begintag,endtag,bufferdata) + local dn = data[name] or "" + if dn == "" then + buffers.level = 0 + 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 + 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() + end + end + data[name] = dn + cs.testcase(more) +end + +function buffers.exists(name) + return data[name] ~= nil +end + +function buffers.doifelsebuffer(name) + cs.testcase(data[name] ~= nil) +end + +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 + +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 + +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 +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 + +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')) + 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 "" + end + if foundname == "" then + return "" + 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) + end + for i=first,last do + n, line = action(lines[i], n, m, line) + 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 +end + +-- The optional prefix hack is there for the typesetbuffer feature and +-- in mkii we needed that (this hidden feature is used in a manual). + +local function prepared(name,list) -- list is optional + if not list or list == "" then + list = name + end + if not name or name == "" then + name = tex.jobname .. "-" .. list .. ".tmp" + end + local content = buffers.collect(list,nil) or "" + if content == "" then + content = "empty buffer" + end + return name, content +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) + local name, content = prepared(name,list) + 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) + end + io.savedata(name,content) + os.execute(format(command,name)) + elseif trace_run then + commands.writestatus("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 (penddef) 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)) +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 + 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 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 +end + +function buffers.finish_state(state) + if state > 0 then + texsprint(ctxcatcodes,"\\eop") + return 0 + else + return state + 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 +end + +-- new + +function buffers.realign(name,forced_n) -- no, auto, + 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 +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) -- cgit v1.2.3