Skip to content

Instantly share code, notes, and snippets.

@pablomayobre
Last active August 29, 2015 14:12
Show Gist options
  • Select an option

  • Save pablomayobre/a6a0734b5b317d41dc3f to your computer and use it in GitHub Desktop.

Select an option

Save pablomayobre/a6a0734b5b317d41dc3f to your computer and use it in GitHub Desktop.
LÖVE Error handler and Notepad++
--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
--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})
@pablomayobre

Copy link
Copy Markdown
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 pattern

Strong also supports this:

t = j / ":"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment