fix initialization errors using driver.love
Changes inside on.initialize are minefields. Until now, if you made a mistake when modifying on.initialize, you could end up in a situation where the app would fail irrecoverably on the next startup. You'd have to go dig up a text editor to fix it. After this commit, errors in on.initialize wait for commands from driver.love just like any other error. Recovering from errors during initialization is a little different than normal. I don't know how much of initialization completed successfully, so I redo all of it. I think this should be safe; the sorts of things we want to do on startup tend to be idempotent just like the sorts of things we do within an event loop with our existing error handling. Things are still not ideal. Initialization by definition happens only when the app starts up. When you make changes to it, you won't find out about errors until you restart the app[1], which can be much later and a big context switch. But at least you'll be able to fix it in the usual way. Slightly more seamless[2]. One glitch to note: at least on Linux, an app with an initialization error feels "sticky". I can't seem to switch focus away from it using Alt-tab. Hitting F4 on the driver also jarringly brings the client app back in focus when there was an initialization error. But the mouse does work consistently. This feels similar to the issues I find when an app goes unresponsive sometimes. The window manager really wants me to respond to the dialog that it's unresponsive. Still, feels like an improvement. [1] I really need to provide that driver command to restart the app! But there's no room in the menus! I really need a first-class command palette like pensieve.love has! [2] https://lobste.rs/s/idi1wt/open_source_vs_ux
This commit is contained in:
parent
ef5e7e0556
commit
a428068d3e
|
@ -62,6 +62,35 @@ Initializing settings:
|
|||
```
|
||||
Hit F4. The error disappears.
|
||||
|
||||
Driver can connect to app on errors in on.initialize.
|
||||
* clone this repo to a new client app, clear its save dir, run it, run the
|
||||
driver, define on.initialize with a run-time error:
|
||||
```
|
||||
on.initialize = function()
|
||||
foo = bar+1
|
||||
end
|
||||
```
|
||||
Hit F4.
|
||||
Quit the client app and restart. App shows an error.
|
||||
Edit `on.initialize` in the driver and remove the error:
|
||||
```
|
||||
on.initialize = function()
|
||||
end
|
||||
```
|
||||
Hit F4. The error disappears from the app and driver.
|
||||
|
||||
* clone this repo to a new client app, clear its save dir, run it, run the
|
||||
driver, define on.initialize with a run-time error:
|
||||
```
|
||||
on.initialize = function()
|
||||
foo = bar+1
|
||||
end
|
||||
```
|
||||
Hit F4.
|
||||
Quit the client app and restart. App shows an error.
|
||||
Hit F4 again in the driver (without fixing the error).
|
||||
The client app continues to show the error.
|
||||
|
||||
* clone this repo to a new client app, clear its save dir, run it, run the
|
||||
driver, add a definition containing invalid Lua:
|
||||
```
|
||||
|
|
22
app.lua
22
app.lua
|
@ -36,15 +36,17 @@ function love.run()
|
|||
end
|
||||
end
|
||||
|
||||
-- Stash current state of App for tests
|
||||
App_for_tests = {}
|
||||
for k,v in pairs(App) do
|
||||
App_for_tests[k] = v
|
||||
end
|
||||
-- there's one nested table
|
||||
App_for_tests.screen = {}
|
||||
for k,v in pairs(App.screen) do
|
||||
App_for_tests.screen[k] = v
|
||||
-- Stash initial state of App (right after loading files) for tests
|
||||
if App_for_tests == nil then
|
||||
App_for_tests = {}
|
||||
for k,v in pairs(App) do
|
||||
App_for_tests[k] = v
|
||||
end
|
||||
-- there's one nested table
|
||||
App_for_tests.screen = {}
|
||||
for k,v in pairs(App.screen) do
|
||||
App_for_tests.screen[k] = v
|
||||
end
|
||||
end
|
||||
-- Mutate App for the real app
|
||||
-- disable test methods
|
||||
|
@ -140,7 +142,7 @@ function love.run()
|
|||
end
|
||||
|
||||
App.initialize_globals()
|
||||
App.initialize(love.arg.parseGameArguments(arg), arg)
|
||||
xpcall(function() App.initialize(love.arg.parseGameArguments(arg), arg) end, live.handle_initialization_error)
|
||||
|
||||
love.timer.step()
|
||||
local dt = 0
|
||||
|
|
14
live.lua
14
live.lua
|
@ -33,7 +33,9 @@ on = {}
|
|||
-- === on startup, load all files with numeric prefix
|
||||
|
||||
function live.load()
|
||||
live.freeze_all_existing_definitions()
|
||||
if Live.frozen_definitions == nil then -- a second run due to initialization errors will contain definitions we don't want to freeze
|
||||
live.freeze_all_existing_definitions()
|
||||
end
|
||||
|
||||
-- version control
|
||||
Live.filenames_to_load = {} -- filenames in order of numeric prefix
|
||||
|
@ -80,6 +82,11 @@ function live.update(dt)
|
|||
local possibly_mutated = live.run(buf)
|
||||
if possibly_mutated then
|
||||
Mode = 'run'
|
||||
if Redo_initialization then
|
||||
Redo_initialization = nil
|
||||
love.run() -- won't actually replace the event loop;
|
||||
-- we're just running it for its initialization side-effects
|
||||
end
|
||||
end
|
||||
if on.code_change then on.code_change() end
|
||||
end
|
||||
|
@ -392,6 +399,11 @@ function live.handle_error(err)
|
|||
print(Error_message)
|
||||
end
|
||||
|
||||
function live.handle_initialization_error(err)
|
||||
Redo_initialization = true
|
||||
live.handle_error(err)
|
||||
end
|
||||
|
||||
-- I tend to read code from files myself (say using love.filesystem calls)
|
||||
-- rather than offload that to load().
|
||||
-- Functions compiled in this manner have ugly filenames of the form [string "filename"]
|
||||
|
|
8
main.lua
8
main.lua
|
@ -237,7 +237,13 @@ end
|
|||
|
||||
function App.keyreleased(key, scancode)
|
||||
if Mode == 'error' then
|
||||
Mode = 'run'
|
||||
if Redo_initialization then
|
||||
Redo_initialization = nil
|
||||
love.run() -- won't actually replace the event loop;
|
||||
-- we're just running it for its initialization side-effects
|
||||
else
|
||||
Mode = 'run'
|
||||
end
|
||||
return
|
||||
end
|
||||
-- ignore events for some time after window in focus (mostly alt-tab)
|
||||
|
|
Loading…
Reference in New Issue