-- filename : core-buf.lua
-- comment : companion to core-buf.tex
-- 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
if not versions then versions = { } end versions['core-buf'] = 1.001
if unicode and not utf then utf = unicode.utf8 end
buffers = { }
buffers.data = { }
buffers.hooks = { }
buffers.flags = { }
buffers.commands = { }
-- if needed we can make 'm local
local concat, texsprint, texprint = table.concat, tex.sprint, tex.print
function buffers.erase(name)
buffers.data[name] = nil
end
function buffers.set(name, str)
buffers.data[name] = { str } -- CHECK THIS
end
function buffers.append(name, str)
buffers.data[name] = (buffers.data[name] or "") .. str
end
buffers.flags.store_as_table = true
-- to be sorted out: crlf + \ ; slow now
function buffers.grab(name,begintag,endtag,data)
if not buffers.data[name] or buffers.data[name] == "" then
buffers.data[name] = ""
buffers.level = 0
end
buffers.level = buffers.level + data:count("\\"..begintag) - data:count("\\"..endtag)
local more = buffers.level>0
if more then
buffers.data[name] = buffers.data[name] .. data .. endtag
buffers.level = buffers.level - 1
else
if buffers.data[name] == "" then
buffers.data[name] = data:sub(1,#data-1)
else
buffers.data[name] = buffers.data[name] .. "\n" .. data:sub(1,#data-1)
end
buffers.data[name] = buffers.data[name]:gsub("[\010\013]$","")
if buffers.flags.store_as_table then
buffers.data[name] = buffers.data[name]:splitlines()
end
end
cs.testcase(more)
end
function buffers.exists(name)
return buffers.data[name] ~= nil
end
function buffers.doifelsebuffer(name)
cs.testcase(buffers.data[name] ~= nil)
end
buffers.flags.optimize_verbatim = true
buffers.flags.count_empty_lines = false
buffers.commands.no_break = "\\doverbatimnobreak"
buffers.commands.do_break = "\\doverbatimgoodbreak"
buffers.commands.begin_of_line_command = "\\doverbatimbeginofline"
buffers.commands.end_of_line_command = "\\doverbatimendofline"
buffers.commands.empty_line_command = "\\doverbatimemptyline"
function buffers.verbatimbreak(n,m)
if buffers.flags.optimize_verbatim then
if n == 2 or n == m then
texsprint(buffers.commands.no_break)
else
texsprint(buffers.commands.do_break)
end
end
end
function buffers.strip(lines)
local first, last = 1, #lines
for i=first,last do
if #lines[i] == 0 then
first = first + 1
else
break
end
end
for i=last,first,-1 do
if #lines[i] == 0 then
last = last - 1
else
break
end
end
return first, last, last - first + 1
end
function buffers.type(name)
local lines = buffers.data[name]
local action = buffers.typeline
if lines then
if type(lines) == "string" then
lines = lines:splitlines()
end
local line, n = 0, 0
local first, last, m = buffers.strip(lines)
for i=first,last do
n, line = action(lines[i], n, m, line)
end
end
end
--~ function buffers.typefile(name)
--~ local t = input.openfile(name)
--~ local action = buffers.typeline
--~ if t then
--~ local line, n, m = 0, 0, t.noflines
--~ while true do
--~ str = t.reader(t)
--~ if str then
--~ n, line = action(str, n, m, line)
--~ else
--~ break
--~ end
--~ end
--~ t.close()
--~ end
--~ end
function buffers.typefile(name)
local t = input.openfile(name)
local action = buffers.typeline
if t then
local lines = { }
while true do
local str = t.reader()
if str then
lines[#lines+1] = str
else
break
end
end
t.close()
local line, n = 0, 0
local first, last, m = buffers.strip(lines)
for i=first,last do
n, line = action(lines[i], n, m, line)
end
end
end
function buffers.typeline(str,n,m,line)
n = n + 1
buffers.verbatimbreak(n,m)
if str:find("%S") then
line = line + 1
buffers.hooks.begin_of_line(line)
buffers.hooks.flush_line(buffers.hooks.line(str))
buffers.hooks.end_of_line()
else
if buffers.flags.count_empty_lines then
line = line + 1
end
buffers.hooks.empty_line(line)
end
return n, line
end
function buffers.save(name)
if not name or name == "" then
name = tex.jobname
end
local b, f = buffers.data[name], tex.jobname .. "-" .. name .. ".tmp"
b = (b and type(b) == "table" and table.join(b,"\n")) or b or ""
io.savedata(f,b)
end
-- todo, use more locals
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
string.piecewise(b, " *[\010\013]", texprint) -- hm, can be faster
end
end
end
function buffers.content(name) -- no print
local b = buffers.data[name]
if b then
if type(b) == "table" then
return concat(b," ")
else
return b
end
else
return ""
end
end
function buffers.collect(names,separator) -- no print
local t = { }
if type(names) == "table" then
for i=1,#names do
local c = buffers.content(names[i])
if c ~= "" then
t[#t+1] = c
end
end
else
for name in names:gmatch("[^,]+") do
local c = buffers.content(name)
if c ~= "" then
t[#t+1] = c
end
end
end
return concat(t,separator or " ") -- maybe this will change to "\n"
end
function buffers.inspect(name)
local b = buffers.data[name]
if b then
if type(b) == "table" then
for _,v in ipairs(b) do
if v == "" then
texsprint(tex.ctxcatcodes,"[crlf]\\par ")
else
texsprint(tex.ctxcatcodes,(b:gsub("(.)",function(c)
return " [" .. string.byte(c) .. "] "
end)) .. "\\par")
end
end
else
texsprint(tex.ctxcatcodes,(b:gsub("(.)",function(c)
return " [" .. string.byte(c) .. "] "
end)))
end
end
end
-- maybe just line(n,str) empty(n,str)
buffers.visualizers = { }
buffers.visualizers.default = { }
buffers.visualizers.tex = { }
buffers.visualizers.mp = { }
buffers.visualizers.escapetoken = nil
buffers.visualizers.tablength = 7
buffers.visualizers.enabletab = false
buffers.visualizers.enableescape = false
function buffers.visualizers.reset()
--~ buffers.visualizers.enabletab = false
--~ buffers.visualizers.enableescape = false
--~ buffers.currentvisualizer = 'default'
end
buffers.currentvisualizer = 'default'
function buffers.setvisualizer(str)
buffers.currentvisualizer = str:lower()
if not buffers.visualizers[buffers.currentvisualizer] then
buffers.currentvisualizer = 'default'
end
end
function buffers.doifelsevisualizer(str)
cs.testcase((str ~= "") and (buffers.visualizers[str:lower()] ~= nil))
end
-- calling routines, don't change
function buffers.hooks.flush_line(str,nesting)
if buffers.visualizers[buffers.currentvisualizer].flush_line then
buffers.visualizers[buffers.currentvisualizer].flush_line(str,nesting)
--~ elseif nesting then
--~ buffers.visualizers.flush_nested(str,false) -- no real nesting
else
buffers.visualizers.default.flush_line(str,nesting)
end
end
function buffers.hooks.begin_of_line(n)
if buffers.visualizers[buffers.currentvisualizer].begin_of_line then
buffers.visualizers[buffers.currentvisualizer].begin_of_line(n)
else
buffers.visualizers.default.begin_of_line(n)
end
end
function buffers.hooks.end_of_line()
if buffers.visualizers[buffers.currentvisualizer].end_of_line then
buffers.visualizers[buffers.currentvisualizer].end_of_line()
else
buffers.visualizers.default.end_of_line(str)
end
end
function buffers.hooks.empty_line()
if buffers.visualizers[buffers.currentvisualizer].empty_line then
buffers.visualizers[buffers.currentvisualizer].empty_line()
else
buffers.visualizers.default.empty_line()
end
end
function buffers.hooks.line(str)
if buffers.visualizers[buffers.currentvisualizer].line then
return buffers.visualizers[buffers.currentvisualizer].line(str)
else
return buffers.visualizers.default.line(str)
end
end
-- defaults
function buffers.visualizers.default.flush_line(str)
texsprint(tex.ctxcatcodes,buffers.escaped(str))
end
function buffers.visualizers.default.begin_of_line(n)
texsprint(tex.ctxcatcodes, buffers.commands.begin_of_line_command .. "{" .. n .. "}")
end
function buffers.visualizers.default.end_of_line()
texsprint(tex.ctxcatcodes,buffers.commands.end_of_line_command)
end
function buffers.visualizers.default.empty_line()
texsprint(tex.ctxcatcodes,buffers.commands.empty_line_command)
end
function buffers.visualizers.default.line(str)
return str
end
-- special one
buffers.commands.nested = "\\switchslantedtype "
-- todo : utf + faster
function buffers.visualizers.flush_nested(str, enable) -- no utf, kind of obsolete mess
local result, c, nested, i = "", "", 0, 1
local sb, ss, sf = string.byte, string.sub, string.find
while i < #str do -- slow
c = ss(str,i,i+1)
if c == "<<" then
nested = nested + 1
if enable then
result = result .. "{" .. buffers.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 = ss(str,i,i)
if c == " " then
result = result .. "\\obs "
elseif sf(c,"%a") then
result = result .. c
else
result = result .. "\\char" .. sb(c) .. " "
end
i = i + 1
end
end
result = result .. "\\char" .. sb(ss(str,i,i)) .. " " .. string.rep("}",nested)
texsprint(tex.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, result)
if n then
if state ~= n then
if state > 0 then
result[#result+1] = "\\sop[" .. buffers.currentcolors[n] .. "]"
else
result[#result+1] = "\\bop[" .. buffers.currentcolors[n] .. "]"
end
return n
end
elseif state > 0 then
result[#result+1] = "\\eop "
return 0
end
return state
end
function buffers.finish_state(state, result)
if state > 0 then
result[#result+1] = "\\eop "
return 0
else
return state
end
end
buffers.open_nested = string.rep("\\char"..string.byte('<').." ",2)
buffers.close_nested = string.rep("\\char"..string.byte('>').." ",2)
function buffers.replace_nested(result)
return (string.gsub(result:gsub(buffers.open_nested,"{"),buffers.close_nested,"}"))
end
function buffers.flush_result(result,nested)
if nested then
texsprint(tex.ctxcatcodes,buffers.replace_nested(concat(result,"")))
else
texsprint(tex.ctxcatcodes,concat(result,""))
end
end
function buffers.escaped(str)
local sb, sf = utf.byte, utf.find
return (utf.gsub(str,"(.)", function(c)
if sf(c,"^(%a%d)$") then
return c
elseif c == " " then
return "\\obs "
else
return "\\char" .. sb(c) .. " "
end
end))
end
function buffers.escaped_chr(ch)
local b = utf.byte(ch)
if b == 32 then
return "\\obs "
else
return "\\char" .. b .. " "
end
end