summaryrefslogtreecommitdiff
path: root/tex/context/base/buff-ini.lua
diff options
context:
space:
mode:
authorMarius <mariausol@gmail.com>2010-07-04 15:32:09 +0300
committerMarius <mariausol@gmail.com>2010-07-04 15:32:09 +0300
commit85b7bc695629926641c7cb752fd478adfdf374f3 (patch)
tree80293f5aaa7b95a500a78392c39688d8ee7a32fc /tex/context/base/buff-ini.lua
downloadcontext-85b7bc695629926641c7cb752fd478adfdf374f3.tar.gz
stable 2010-05-24 13:10
Diffstat (limited to 'tex/context/base/buff-ini.lua')
-rw-r--r--tex/context/base/buff-ini.lua838
1 files changed, 838 insertions, 0 deletions
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 (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))
+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 <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
+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, <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
+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)