if not modules then modules = { } end modules ['trac-deb'] = { version = 1.001, comment = "companion to trac-deb.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- This is an old mechanism, a result of some experiments in the early days of -- luatex and mkiv, but still nice anyway. I will clean it up in lmtx. local status = status local tonumber, tostring, type = tonumber, tostring, type local format, concat, match, find, gsub = string.format, table.concat, string.match, string.find, string.gsub local report_nl = logs.newline local report_str = logs.writer tracers = tracers or { } local tracers = tracers local implement = interfaces.implement local ioflush = io.flush local ioread = io.read local ossleep = os.sleep local osexit = os.exit local writenl = texio.writenl local write = texio.write local runlocal = tex.runlocal local terminaldata = false local context = context implement { name = "fetchterminaldata", actions = function() context(terminaldata) end, } function texio.terminal() writenl("\n" .. "entering interactive mode, use \\quit to abort reading" .."\n\n") while true do write(">") ioflush() terminaldata = ioread() if terminaldata == "\\quit" then terminaldata = false break else runlocal("t_syst_terminal_data",nil,nil,true) -- obeymode end end end implement { name = "readfromterminal", public = true, protected = true, actions = texio.terminal, } local savedluaerror = nil local usescitelexer = nil local quitonerror = true local function errorreporter(luaerror) local category = luaerror and "lua error" or "tex error" local report = logs.reporter(category) logs.enable(category) return report end function tracers.showlines(filename,linenumber,offset,luaerrorline) local data = io.loaddata(filename) if not data or data == "" then local hash = url.hashed(filename) if not hash.noscheme then local ok, d, n = resolvers.loaders.byscheme(hash.scheme,filename) if ok and n > 0 then data = d end end end local scite = usescitelexer and require("util-sci") if scite then return utilities.scite.tohtml(data,"tex",linenumber or true,false) else local lines = data and string.splitlines(data) if lines and #lines > 0 then if luaerrorline and luaerrorline > 0 then -- lua error: linenumber points to last line local start = "\\startluacode" local stop = "\\stopluacode" local n = linenumber for i=n,1,-1 do local line = lines[i] if not line then break elseif find(line,start) then n = i + luaerrorline - 1 if n <= linenumber then linenumber = n end break end end end offset = tonumber(offset) or 10 linenumber = tonumber(linenumber) or 10 local start = math.max(linenumber - offset,1) local stop = math.min(linenumber + offset,#lines) if stop > #lines then return "" else local result, fmt = { }, "%" .. #tostring(stop) .. "d %s %s" for n=start,stop do result[#result+1] = format(fmt,n,n == linenumber and ">>" or " ",lines[n]) end return concat(result,"\n") end else return "" end end end -- todo: last tex error has ! prepended -- todo: some nested errors have two line numbers -- todo: collect errorcontext in string (after code cleanup) -- todo: have a separate status.lualinenumber -- todo: \starttext bla \blank[foo] bla \stoptext local nop = function() end local resetmessages = status.resetmessages or nop local function processerror(offset,errortype) local readstate = status.getreadstate() local filename = readstate.filename local linenumber = readstate.linenumber local skiplinenumber = readstate.skiplinenumber local errorstate = status.errorstate local lastcontext = errorstate.errorcontext local lasttexerror = errorstate.error or "?" local lastluaerror = errorstate.luaerror or "?" -- lasttexerror local luaerrorline = match(lastluaerror,[[lua%]?:.-(%d+)]]) or (lastluaerror and find(lastluaerror,"?:0:",1,true) and 0) local lastmpserror = match(lasttexerror,[[^.-mp%serror:%s*(.*)$]]) resetmessages() lastluaerror = gsub(lastluaerror,"%[\\directlua%]","[ctxlua]") tracers.printerror { filename = filename, linenumber = linenumber, skiplinenumber = skiplinenumber, offset = tonumber(offset) or 10, lasttexerror = lasttexerror, lastmpserror = lastmpserror, lastluaerror = lastluaerror, -- can be the same as lasttexerror luaerrorline = luaerrorline, lastcontext = lastcontext, lasttexhelp = tex.gethelptext and tex.gethelptext() or nil, errortype = errortype, } if job and type(job.disablesave) == "function" then job.disablesave() end lua.setexitcode(1) end directives.register("system.quitonerror",function(v) quitonerror = toboolean(v) end) directives.register("system.usescitelexer",function(v) usescitelexer = toboolean(v) end) local busy = false function tracers.printerror(specification) if not busy then busy = true local errorvalues = table.swapped(tex.geterrorvalues()) local filename = specification.filename local linenumber = specification.linenumber local lasttexerror = specification.lasttexerror local lastmpserror = specification.lastmpserror local lastluaerror = specification.lastluaerror local lastcontext = specification.lastcontext local luaerrorline = specification.luaerrorline local errortype = specification.errortype local offset = specification.offset local endoffile = specification.endoffile local report = errorreporter(luaerrorline) if errortype == errorvalues.warning then report("warning error: %s",lasttexerror or "-") busy = false; return; elseif errortype == errorvalues.eof then report("runaway error: %s",lasttexerror or "-") if not quitonerror and texio.terminal then texio.terminal() -- not well tested end elseif errortype == errorvalues.condition then linenumber = specification.skiplinenumber if linenumber > 0 then report("condition error on line %s in file %s: %s",linenumber,filename,lasttexerror) report_nl() report_str(tracers.showlines(filename,linenumber,offset,tonumber(luaerrorline))) report_nl() else report("runaway condition error: %s",lasttexerror or "-") end quitonerror = true elseif not filename then report("fuzzy error:") report(" tex: %s",lasttexerror or "-") report(" lua: %s",lastluaerror or "-") report(" mps: %s",lastmpserror or "-") elseif type(filename) == "number" then report("error on line %s of filehandle %s: %s ...",linenumber,lasttexerror) else report_nl() if luaerrorline then if linenumber == 0 or not filename or filename == "" then print("\nfatal lua error:\n\n",lastluaerror,"\n") luatex.abort() return else report("lua error on line %s in file %s:\n\n%s",linenumber,filename,lastluaerror) end elseif lastmpserror then report("mp error on line %s in file %s:\n\n%s",linenumber,filename,lastmpserror) else report("tex error on line %s in file %s: %s",linenumber,filename,lasttexerror) if lastcontext then report_nl() report_str(lastcontext) report_nl() else report_nl() -- tex.showcontext() end if lastluaerror and not match(lastluaerror,"^%s*[%?]*%s*$") then print("\nlua error:\n\n",lastluaerror,"\n") quitonerror = true end end report_nl() report_str(tracers.showlines(filename,linenumber,offset,tonumber(luaerrorline))) report_nl() end if quitonerror then local name = tex.jobname or "" if name ~= "" then table.save(name .. "-error.log",specification) end local help = specification.lasttexhelp if help and #help > 0 then report_nl() report_str(help) report_nl() report_nl() end luatex.abort() end busy = false end end luatex.wrapup(function() os.remove(tex.jobname .. "-error.log") end) local function processwarning(offset) local warningstate = status.warningstate local lastwarning = warningstate.warning or "?" local lastlocation = warningstate.warningtag or "?" resetmessages() tracers.printwarning { lastwarning = lastwarning, lastlocation = lastlocation, } end function tracers.printwarning(specification) logs.report("luatex warning","%s: %s",specification.lastlocation,specification.lastwarning) end directives.register("system.errorcontext", function(v) local register = callback.register if v then register("show_error_message", nop) register("show_warning_message", function() processwarning(v) end) register("intercept_lua_error", function() processerror(v) end) register("intercept_tex_error", function(mode,eof) processerror(v,eof) return mode end) else register("show_error_message", nil) register("show_warning_message", nil) register("intercept_lua_error", nil) register("intercept_tex_error", nil) end end) -- this might move lmx = lmx or { } lmx.htmfile = function(name) return environment.jobname .. "-status.html" end lmx.lmxfile = function(name) return resolvers.findfile(name,'tex') end local function reportback(lmxname,default,variables) if lmxname == false then return variables else local name = lmx.show(type(lmxname) == "string" and lmxname or default,variables) if name then logs.report("context report","file: %s",name) end end end local function showerror(lmxname) local readstate = status.getreadstate() local filename = readstate.filename local linenumber = tonumber(readstate.linenumber) or 0 local errorcontext = "" if not filename then filename = status.iocodes[readstate.iocode] errorcontext = 'error in filename' else errorcontext = tracers.showlines(filename,linenumber,offset) end local variables = { ['title'] = 'ConTeXt Error Information', ['errormessage'] = status.geterrorstate().error or "?", ['linenumber'] = linenumber, ['color-background-one'] = lmx.get('color-background-yellow'), ['color-background-two'] = lmx.get('color-background-purple'), ['filename'] = filename, ['errorcontext'] = errorcontext, } reportback(lmxname,"context-error.lmx",variables) luatex.abort() end lmx.showerror = showerror function lmx.overloaderror(v) if v == "scite" then usescitelexer = true end callback.register('show_error_message', function() showerror() end) -- callback.register('intercept_lua_error', function() showerror() end) -- callback.register('intercept_tex_error', function() showerror() end) end directives.register("system.showerror", lmx.overloaderror) -- local debugger = utilities.debugger -- -- local function trace_calls(n) -- debugger.enable() -- luatex.registerstopactions(function() -- debugger.disable() -- debugger.savestats(tex.jobname .. "-luacalls.log",tonumber(n)) -- end) -- trace_calls = function() end -- end -- -- directives.register("system.tracecalls", function(n) -- trace_calls(n) -- end) -- indirect is needed for nilling -- Obsolete ... not that usefull as normally one runs from an editor and -- when run unattended it makes no sense either. -- local editor = [[scite "-open:%filename%" -goto:%linenumber%]] -- -- directives.register("system.editor",function(v) -- editor = v -- end) -- -- callback.register("call_edit",function(filename,linenumber) -- if editor then -- editor = gsub(editor,"%%s",filename) -- editor = gsub(editor,"%%d",linenumber) -- editor = gsub(editor,"%%filename%%",filename) -- editor = gsub(editor,"%%linenumber%%",linenumber) -- logs.report("system","starting editor: %s",editor) -- os.execute(editor) -- end -- end) implement { name = "showtrackers", public = true, protected = true, actions = trackers.show } implement { name = "enabletrackers", public = true, protected = true, actions = trackers.enable, arguments = "optional" } implement { name = "disabletrackers", public = true, protected = true, actions = trackers.disable, arguments = "optional" } implement { name = "resettrackers", public = true, protected = true, actions = trackers.reset } implement { name = "showdirectives", public = true, protected = true, actions = directives.show } implement { name = "enabledirectives", public = true, protected = true, actions = directives.enable, arguments = "optional" } implement { name = "disabledirectives", public = true, protected = true, actions = directives.disable, arguments = "optional" } implement { name = "showexperiments", public = true, protected = true, actions = experiments.show } implement { name = "enableexperiments", public = true, protected = true, actions = experiments.enable, arguments = "optional" } implement { name = "disableexperiments", public = true, protected = true, actions = experiments.disable, arguments = "optional" } implement { name = "overloaderror", public = true, protected = true, actions = lmx.overloaderror } implement { name = "showlogcategories", public = true, protected = true, actions = logs.show } local debugger = utilities.debugger directives.register("system.profile",function(n) luatex.registerstopactions(function() debugger.disable() debugger.savestats("luatex-profile.log",tonumber(n) or 0) report_nl() logs.report("system","profiler stopped, log saved in %a","luatex-profile.log") report_nl() end) logs.report("system","profiler started") debugger.enable() end) local report = logs.reporter("[[diagnostic]]") implement { name = "diagnostic", public = true, -- protected = false, -- expandable arguments = { "optional", "string" }, actions = function(t,s) if t == "quit" then report(s) osexit() else t = tonumber(t) if t then report("%s (sleep: %.3N)",s,t) ioflush() ossleep(t) else report(s) ioflush() end end end }