Last active
August 29, 2015 14:12
-
-
Save pablomayobre/a6a0734b5b317d41dc3f to your computer and use it in GitHub Desktop.
LÖVE Error handler and Notepad++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --No credit nor license is required for this function | |
| --It is based on the original code by LÖVE developers | |
| --Just minor functionality was added, if at all. | |
| return function (src) | |
| local dir = string.gsub(src,"([\\])","/") | |
| local src = dir:match("/$") and dir or (dir.."/") | |
| function love.errhand(msg) | |
| msg = tostring(msg) | |
| print("Error:") | |
| print(debug.traceback(tostring(msg), 3):gsub("\n[^\n]+$", "")) | |
| local j = string.gsub(msg,"^(Syntax error: )","") | |
| local t = {} | |
| for i=1,2 do | |
| local pos1, pos2 = string.find(j,":", 1, true) | |
| if not pos1 then | |
| t[#t + 1] = j | |
| break | |
| end | |
| t[#t + 1] = string.sub(j, 1, pos1 - 1) | |
| j = string.sub(j, pos2 + 1) | |
| end | |
| if string.match(t[1] or "","^%[.+%]$") then | |
| print("The error is in the internal function: "..(t[1] or "")) | |
| else | |
| local f,l = ((src or "")..t[1] or ""),t[2] or "" | |
| print (string.format("FILE = %s, LINE = %s",f,l)) | |
| end | |
| if not love.window or not love.graphics or not love.event then | |
| return | |
| end | |
| if not love.graphics.isCreated() or not love.window.isCreated() then | |
| if not pcall(love.window.setMode, 800, 600) then | |
| return | |
| end | |
| end | |
| if love.mouse then | |
| love.mouse.setVisible(true) | |
| love.mouse.setGrabbed(false) | |
| end | |
| if love.joystick then | |
| for i,v in ipairs(love.joystick.getJoysticks()) do | |
| v:setVibration() | |
| end | |
| end | |
| if love.audio then love.audio.stop() end | |
| local pxs = love.window.getPixelScale and love.window.getPixelScale() or 1 | |
| love.graphics.reset() | |
| local font = love.graphics.setNewFont(math.floor(14 * pxs)) | |
| local sRGB = select(3, love.window.getMode()).srgb | |
| if sRGB then | |
| love.graphics.setBackgroundColor(love.math.gammaToLinear(89, 157, 220)) | |
| else | |
| love.graphics.setBackgroundColor(89, 157, 220) | |
| end | |
| love.graphics.setColor(255, 255, 255, 255) | |
| local trace = debug.traceback() | |
| love.graphics.clear() | |
| love.graphics.origin() | |
| local err = {} | |
| table.insert(err, "Error\n") | |
| table.insert(err, msg.."\n\n") | |
| for l in string.gmatch(trace, "(.-)\n") do | |
| if not string.match(l, "boot.lua") then | |
| l = string.gsub(l, "stack traceback:", "Traceback\n") | |
| table.insert(err, l) | |
| end | |
| end | |
| local p = table.concat(err, "\n") | |
| p = string.gsub(p, "\t", "") | |
| p = string.gsub(p, "%[string \"(.-)\"%]", "%1") | |
| local function draw() | |
| local pos = 70 * pxs | |
| love.graphics.clear() | |
| love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos) | |
| love.graphics.present() | |
| end | |
| while true do | |
| love.event.pump() | |
| for e, a, b, c in love.event.poll() do | |
| if e == "quit" then | |
| return | |
| end | |
| if e == "keypressed" and a == "escape" then | |
| return | |
| end | |
| end | |
| draw() | |
| if love.timer then | |
| love.timer.sleep(0.1) | |
| end | |
| end | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --1 | |
| -- Modified version of lurker | |
| -- Changes: | |
| -- Doesnt depend on Lume | |
| -- Better error message (now with the FILE and LINE) | |
| -- Draw function can be easily changed | |
| -- Stops all the stuff that can be annoying to the user | |
| -- Works better when sRGB or HighDPI is enabled | |
| -- Less stuff printed to the console (because that can turn to be annoying) | |
| -- More callbacks supported | |
| -- | |
| -- Copyright (c) 2015 rxi | |
| -- | |
| -- This library is free software; you can redistribute it and/or modify it | |
| -- under the terms of the MIT license. See LICENSE for details. | |
| local lurker = { _version = "1.0.1" } | |
| local lovecallbacknames = { | |
| "update", | |
| "load", | |
| "draw", | |
| "mousepressed", | |
| "mousereleased", | |
| "keypressed", | |
| "keyreleased", | |
| "focus", | |
| "quit", | |
| } | |
| function lurker.init(src) | |
| local src = string.gsub(src,"([\\])","/") | |
| lurker.src = src:match("/$") and src or (src.."/") | |
| lurker.path = "." | |
| lurker.preswap = function() end | |
| lurker.postswap = function() end | |
| lurker.interval = .5 | |
| lurker.protected = true | |
| lurker.quiet = false | |
| lurker.lastscan = 0 | |
| lurker.lasterrorfile = nil | |
| lurker.files = {} | |
| lurker.funcwrappers = {} | |
| lurker.lovefuncs = {} | |
| lurker.state = "init" | |
| for _, v in ipairs(lurker.getchanged()) do lurker.resetfile(v) end | |
| return lurker | |
| end | |
| function lurker.listdir(path, recursive, skipdotfiles, t) | |
| path = (path == ".") and "" or path | |
| local function fullpath(x) return path .. "/" .. x end | |
| local t = {} | |
| for _, f in ipairs(love.filesystem.getDirectoryItems(path)) do | |
| local f = fullpath(f) | |
| if not skipdotfiles or not f:match("/%.[^/]*$") then | |
| if recursive and love.filesystem.isDirectory(f) then | |
| lurker.listdir(f, true, skipdotfiles, t) | |
| else | |
| table.insert(t, f:match("^[/]*(.-)[/]*$")) | |
| end | |
| end | |
| end | |
| return t | |
| end | |
| function lurker.initwrappers() | |
| for _, v in pairs(lovecallbacknames) do | |
| lurker.funcwrappers[v] = function(...) | |
| local args = {...} | |
| xpcall(function() | |
| return lurker.lovefuncs[v] and lurker.lovefuncs[v](unpack(args)) | |
| end, lurker.onerror) | |
| end | |
| lurker.lovefuncs[v] = love[v] | |
| end | |
| lurker.updatewrappers() | |
| end | |
| function lurker.updatewrappers() | |
| for _, v in pairs(lovecallbacknames) do | |
| if love[v] ~= lurker.funcwrappers[v] then | |
| lurker.lovefuncs[v] = love[v] | |
| love[v] = lurker.funcwrappers[v] | |
| end | |
| end | |
| end | |
| function lurker.draw () | |
| local sRGB = select(3, love.window.getMode()).srgb | |
| if sRGB then | |
| love.graphics.setBackgroundColor(love.math.gammaToLinear(89, 157, 220)) | |
| else | |
| love.graphics.setBackgroundColor(89, 157, 220) | |
| end | |
| love.graphics.setColor(255, 255, 255, 255) | |
| local pxs = love.window.getPixelScale and love.window.getPixelScale() or 1 | |
| local pos = 70 * pxs | |
| love.graphics.printf(lurker.errmsg, pos, pos, love.graphics.getWidth() - pos*2) | |
| end | |
| function lurker.onerror(e, nostacktrace) | |
| lurker.state = "error" | |
| -- Set up callbacks | |
| for _, v in pairs(lovecallbacknames) do | |
| love[v] = function() end | |
| end | |
| msg = tostring(e) | |
| print("Error:") | |
| print(debug.traceback(msg, 2):gsub("\n[^\n]+$", "")) | |
| local j = string.gsub(msg,"^(Syntax error: )","") | |
| local t = {} | |
| for i=1, 2 do | |
| local pos1, pos2 = string.find(j,":", 1, true) | |
| if not pos1 then | |
| t[#t + 1] = j | |
| break | |
| end | |
| t[#t + 1] = string.sub(j, 1, pos1 - 1) | |
| j = string.sub(j, pos2 + 1) | |
| end | |
| if string.match(t[1] or "","^%[.+%]$") then | |
| print("The error is in the internal function: "..(t[1] or "")) | |
| else | |
| local f,l = ((lurker.src or "")..t[1] or ""),t[2] or "" | |
| print (string.format("FILE = %s, LINE = %s",f,l)) | |
| end | |
| if not love.window or not love.graphics or not love.event then | |
| love.event.quit() | |
| end | |
| if not love.graphics.isCreated() or not love.window.isCreated() then | |
| if not pcall(love.window.setMode, 800, 600) then | |
| love.event.quit() | |
| end | |
| end | |
| if love.mouse then | |
| love.mouse.setVisible(true) | |
| love.mouse.setGrabbed(false) | |
| end | |
| if love.joystick then | |
| for i,v in ipairs(love.joystick.getJoysticks()) do | |
| v:setVibration() | |
| end | |
| end | |
| if love.audio then love.audio.stop() end | |
| local err = {} | |
| table.insert(err, "Error\n") | |
| table.insert(err, msg.."\n\n") | |
| local trace = debug.traceback() | |
| for l in string.gmatch(trace, "(.-)\n") do | |
| if not string.match(l, "boot.lua") then | |
| l = string.gsub(l, "stack traceback:", "Traceback\n") | |
| table.insert(err, l) | |
| end | |
| end | |
| lurker.errmsg = table.concat(err, "\n") | |
| lurker.errmsg = string.gsub(lurker.errmsg, "\t", "") | |
| lurker.errmsg = string.gsub(lurker.errmsg, "%[string \"(.-)\"%]", "%1") | |
| local pxs = love.window.getPixelScale and love.window.getPixelScale() or 1 | |
| local font = love.graphics.setNewFont(math.ceil(14 * pxs)) | |
| love.draw = lurker.draw or function () end | |
| love.graphics.push("all") --Go back to default settings but save old ones | |
| love.graphics.reset() | |
| love.update = lurker.update | |
| love.keypressed = function(k) | |
| if k == "escape" then | |
| love.event.quit() | |
| end | |
| end | |
| end | |
| function lurker.exitinitstate() | |
| lurker.state = "normal" | |
| if lurker.protected then | |
| lurker.initwrappers() | |
| end | |
| end | |
| function lurker.exiterrorstate() | |
| lurker.state = "normal" | |
| for _, v in pairs(lovecallbacknames) do | |
| love[v] = lurker.funcwrappers[v] | |
| end | |
| love.graphics.pop() | |
| end | |
| function lurker.update() | |
| if lurker.state == "init" then | |
| lurker.exitinitstate() | |
| end | |
| local diff = love.timer.getTime() - lurker.lastscan | |
| if diff > lurker.interval then | |
| lurker.lastscan = lurker.lastscan + diff | |
| local changed = lurker.scan() | |
| if #changed > 0 and lurker.lasterrorfile then | |
| local f = lurker.lasterrorfile | |
| lurker.lasterrorfile = nil | |
| lurker.hotswapfile(f) | |
| end | |
| end | |
| end | |
| function lurker.getchanged() | |
| local function fn(f) | |
| return f:match("%.lua$") and lurker.files[f] ~= love.filesystem.getLastModified(f) | |
| end | |
| local t = lurker.listdir(lurker.path, true, true) | |
| local rtn = {} | |
| for k, v in ipairs(t) do | |
| if fn(v) then rtn[#rtn + 1] = v end | |
| end | |
| return rtn | |
| end | |
| function lurker.modname(f) | |
| return (f:gsub("%.lua$", ""):gsub("[/\\]", ".")) | |
| end | |
| function lurker.resetfile(f) | |
| lurker.files[f] = love.filesystem.getLastModified(f) | |
| end | |
| function lurker.hotswapfile(f) | |
| if lurker.state == "error" then | |
| lurker.exiterrorstate() | |
| end | |
| if lurker.preswap(f) then | |
| lurker.resetfile(f) | |
| return | |
| end | |
| local modname = lurker.modname(f) | |
| local oldglobal = {} | |
| for k, v in pairs(_G) do oldglobal[k] = v end | |
| local updated = {} | |
| local function update(old, new) | |
| if updated[old] then return end | |
| updated[old] = true | |
| local oldmt, newmt = getmetatable(old), getmetatable(new) | |
| if oldmt and newmt then update(oldmt, newmt) end | |
| for k, v in pairs(new) do | |
| if type(v) == "table" then update(old[k], v) else old[k] = v end | |
| end | |
| end | |
| local err = nil | |
| local function onerror(e) | |
| for k, v in pairs(_G) do _G[k] = oldglobal[k] end | |
| err = e | |
| end | |
| local ok, oldmod = pcall(require, modname) | |
| oldmod = ok and oldmod or nil | |
| xpcall(function() | |
| package.loaded[modname] = nil | |
| local newmod = require(modname) | |
| if type(oldmod) == "table" then update(oldmod, newmod) end | |
| for k, v in pairs(oldglobal) do | |
| if v ~= _G[k] and type(v) == "table" then | |
| update(v, _G[k]) | |
| _G[k] = v | |
| end | |
| end | |
| end, onerror) | |
| package.loaded[modname] = oldmod | |
| if err then | |
| if not lurker.quiet and lurker.protected then | |
| lurker.lasterrorfile = f | |
| lurker.onerror(err, true) | |
| lurker.resetfile(f) | |
| return | |
| end | |
| end | |
| lurker.resetfile(f) | |
| lurker.postswap(f) | |
| if lurker.protected then | |
| lurker.updatewrappers() | |
| end | |
| end | |
| function lurker.scan() | |
| if lurker.state == "init" then | |
| lurker.exitinitstate() | |
| end | |
| local changed = lurker.getchanged() | |
| for _, v in ipairs(changed) do lurker.hotswapfile(v) end | |
| return changed | |
| end | |
| return setmetatable(lurker,{__call = function(self,...) return self.init(...) end}) |
Author
Author
PS: It also fix the errors in HighDPI screens like Mac OS X Retina Displays and a bug in the color when in sRGB mode. This bugs where fixed in 0.9.2, but this file works in all 0.9.X versions.
Author
If you use strong or a similar library in your project, you can replace this:
for i=1,2 do
local pos1, pos2 = string.find(j,":", 1, true)
if not pos1 then
t[#t + 1] = j
break
end
t[#t + 1] = string.sub(j, 1, pos1 - 1)
j = string.sub(j, pos2 + 1)
end with this:
t = string.split(j,":",false) --False because it's not a patternStrong also supports this:
t = j / ":"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Description
This is an error handler for LÖVE, it does the same thing as the standard error handler, but adds a line to the error description printed to the console.
This extra line is useful to determine where the error is present, and it has this format :
FILE = C:/path/to the/error/file.lua, LINE = nThis, in turn, can be used with Notepad++ Plugin NppExec to add a link that will take you to that place
Including this error handler in your code
To use this error handler you need to require it in your
main.lua, like this:Configuring N++
Install NppExec plugin in Notepad++ and add this Filter to
Console Output Filters... > HighlightYou'll need to open the file with NppExec to get the output to the console. There are some problems opening LÖVE directly from NppExec, so we instead
callit from a.batfile, calledrun.bat, with the following code:To run this bat configure this command in NppExec and save it as
Love(NPP_SAVEALLis not mandatory)Then in
Plugin > NppExec > Advance Options > Menu ItemAdd an Item calledLovethat triggers theLovecommand.After that go to
Execute > Modify Shortcuts / Delete Commands > Plugin Commandand add a key shortcut to this command, I preferCTRL + LProblems
conf.luayou definet.console = truethe console will never be spawn, even then, you can still get the console output in the NppExec console.