attempt at better error recovery

- I often find that opening up driver.love and sending any message gets
  my app unwedged. Retrying should be easy. Just press a key.
- There's really no reason for the app to become unresponsive while
  waiting for code changes.

This approach tries to address both. Press any key to retry, or show the
error right on the window while I whip out driver.love. You can also
copy the message to the clipboard by hitting 'c'.

Drawbacks of this approach:
  - nobody's going to read the message. I myself didn't notice the
    default support for copying the error message for _months_.
  - more complexity for anyone reading the code to parse. More stuff
    that can go wrong. Any errors within the handler will crash the app
    hard. (Though now there's less code in the handler, so maybe this is
    an improvement on that score.)

The best plan is still to not rely on this too much. Don't ship bugs.
This commit is contained in:
Kartik K. Agaram 2023-04-20 00:30:25 -07:00
parent 71c82e5c09
commit a2451aa26e
2 changed files with 44 additions and 37 deletions

View File

@ -296,25 +296,18 @@ end
-- return nil to continue the event loop, non-nil to quit
function live.handle_error(err)
-- draw a pause indicator on screen
love.graphics.setColor(1,0,0)
love.graphics.rectangle('fill', 10,10, 3,10)
love.graphics.rectangle('fill', 16,10, 3,10)
love.graphics.present()
-- print stack trace here just in case we ran the app through a terminal
local stack_trace = debug.traceback('Error: '..tostring(err), --[[stack frame]]2):gsub('\n[^\n]+$', '')
print(stack_trace)
print('Look in the driver for options to investigate further.')
print("(You probably can't close the app window at this point. If you don't have the driver set up, you might need to force-quit.)")
-- send stack trace to driver and wait for a response
Mode = 'error'
local stack_trace = debug.traceback('Error: ' .. tostring(err), --[[stack frame]]2):gsub('\n[^\n]+$', '')
live.send_run_time_error_to_driver(stack_trace)
local buf
repeat
buf = live.receive_from_driver()
love.timer.sleep(0.001)
until buf
if buf == 'QUIT' then
return true
Error_message = 'Something is wrong. Sorry!\n\n'..stack_trace..'\n\n'..
"(Note: function names above don't include outer tables. So functions like on.draw might show up as just 'draw', etc.)\n\n"..
'Options:\n'..
'- press "ctrl+c" (without the quotes) to copy this message to your clipboard to send to me: ak@akkartik.com\n'..
'- press any other key to retry, see if things start working again\n'..
'- run driver.love to try to fix it yourself. As you do, feel free to ask me questions: ak@akkartik.com\n'
Error_count = Error_count+1
if Error_count > 1 then
Error_message = Error_message..('\n\nThis is error #%d in this session; things will probably not improve in this session. Please copy the message and send it to me: ak@akkartik.com.'):format(Error_count)
end
live.run(buf)
print(Error_message)
end

View File

@ -13,13 +13,19 @@ require 'edit'
Editor_state = {}
function App.version_check()
-- available modes: run, version_check
Mode = 'version_check'
-- available modes: run, error
Mode = 'run'
Error_message = nil
Error_count = 0
-- we'll reuse error mode on load for an initial version check
Supported_versions = {'11.4', '11.3', '11.2', '11.1', '11.0'} -- keep these sorted in descending order
local major, minor = love.getVersion()
Version = major..'.'..minor
if array.find(Supported_versions, Version) then
Mode = 'run'
if array.find(Supported_versions, Version) == nil then
Mode = 'error'
Error_message = ("This app doesn't support version %s; please use version %s. Press any key to try it with this version anyway."):format(Version, Supported_versions[1])
print(Error_message)
-- continue initializing everything; hopefully we won't have errors during initialization
end
end
@ -139,48 +145,51 @@ function App.filedropped(file)
end
function App.draw()
if Mode == 'version_check' then
love.graphics.setColor(1,1,0)
love.graphics.rectangle('fill', 30,30, 400,400)
love.graphics.setColor(0,0,0)
love.graphics.printf(("This app doesn't support version %s; please use version %s. Press any key to try it with this version anyway."):format(Version, Supported_versions[1]), 40,40, 400)
if Mode == 'error' then
love.graphics.setColor(0,0,1)
love.graphics.rectangle('fill', 0,0, App.screen.width, App.screen.height)
love.graphics.setColor(1,1,1)
love.graphics.printf(Error_message, 40,40, 600)
return
end
if on.draw then on.draw() end
end
function App.update(dt)
if Mode == 'version_check' then return end
if Mode == 'error' then return end
Current_time = Current_time + dt
-- some hysteresis while resizing
if Current_time < Last_resize_time + 0.1 then
return
end
Cursor_time = Cursor_time + dt
-- listen for commands in both 'error' and 'run' modes
live.update(dt)
if on.update then on.update(dt) end
if Mode == 'run' then
if on.update then on.update(dt) end
end
end
function App.mousepressed(x,y, mouse_button)
if Mode == 'version_check' then return end
if Mode == 'error' then return end
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
if on.mouse_press then on.mouse_press(x,y, mouse_button) end
end
function App.mousereleased(x,y, mouse_button)
if Mode == 'version_check' then return end
if Mode == 'error' then return end
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
if on.mouse_release then on.mouse_release(x,y, mouse_button) end
end
function App.wheelmoved(dx,dy)
if Mode == 'version_check' then return end
if Mode == 'error' then return end
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
if on.mouse_wheel_move then on.mouse_wheel_move(dx,dy) end
end
function App.focus(in_focus)
if Mode == 'version_check' then return end
if Mode == 'error' then return end
if in_focus then
Last_focus_time = Current_time
end
@ -195,7 +204,12 @@ end
-- App.keypressed is defined in keychord.lua
function App.keychord_press(chord, key)
if Mode == 'version_check' then return end
if Mode == 'error' then
if chord == 'C-c' then
love.system.setClipboardText(Error_message)
end
return
end
-- ignore events for some time after window in focus (mostly alt-tab)
if Current_time < Last_focus_time + 0.01 then
return
@ -205,7 +219,7 @@ function App.keychord_press(chord, key)
end
function App.textinput(t)
if Mode == 'version_check' then return end
if Mode == 'error' then return end
-- ignore events for some time after window in focus (mostly alt-tab)
if Current_time < Last_focus_time + 0.01 then
return
@ -215,7 +229,7 @@ function App.textinput(t)
end
function App.keyreleased(key, scancode)
if Mode == 'version_check' then
if Mode == 'error' then
Mode = 'run'
return
end