From c257139d1737dd274a8d5754a6e4384259c1cb6c Mon Sep 17 00:00:00 2001 From: Phil Hagelberg Date: Sat, 11 Sep 2021 19:33:52 -0700 Subject: [PATCH] Wrap mode handlers in xpcall+error-mode. When an error occurs in a mode handler, change to error-mode. Within error-mode, display the traceback and allow the user to exit, (which the default love error handler does) but also allows returning back to the previous mode. The assumption is that this would be done after using the repl to ,reload the module containing the bug which caused the error. --- error-mode.fnl | 30 ++++++++++++++++++++++++++++++ wrap.fnl | 23 +++++++++++++++-------- 2 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 error-mode.fnl diff --git a/error-mode.fnl b/error-mode.fnl new file mode 100644 index 0000000..3aed9d5 --- /dev/null +++ b/error-mode.fnl @@ -0,0 +1,30 @@ +;; This mode has two purposes: +;; * display the stack trace that caused the error +;; * allow the user to decide whether to retry after reloading or quit + +;; Since we can't know which module needs to be reloaded, we rely on the user +;; doing a ,reload foo in the repl. + +(var state nil) + +(local explanation "Press escape to quit. +Press space to return to the previous mode after reloading in the repl.") + +(fn draw [] + (love.graphics.clear 0.34 0.61 0.86) + (love.graphics.setColor 0.9 0.9 0.9) + (love.graphics.print state.msg 10 10) + (love.graphics.print explanation 15 25) + (love.graphics.print state.traceback 15 50)) + +(fn keypressed [key set-mode] + (match key + :escape (love.event.quit) + :space (set-mode state.old-mode))) + +(fn activate [old-mode msg traceback] + (print msg) + (print traceback) + (set state {: old-mode : msg : traceback})) + +{: draw : keypressed : activate} diff --git a/wrap.fnl b/wrap.fnl index e76ac1f..51284ec 100644 --- a/wrap.fnl +++ b/wrap.fnl @@ -1,37 +1,44 @@ -(local repl (require "lib.stdio")) +(local fennel (require :lib.fennel)) +(local repl (require :lib.stdio)) (local canvas (let [(w h) (love.window.getMode)] (love.graphics.newCanvas w h))) (var scale 1) ;; set the first mode -(var mode (require "mode-intro")) +(var (mode mode-name) nil) -(fn set-mode [mode-name ...] - (set mode (require mode-name)) +(fn set-mode [new-mode-name ...] + (set (mode mode-name) (values (require new-mode-name) new-mode-name)) (when mode.activate - (mode.activate ...))) + (match (pcall mode.activate ...) + (false msg) (print mode-name "activate error" msg)))) (fn love.load [] + (set-mode :mode-intro) (canvas:setFilter "nearest" "nearest") (repl.start)) +(fn safely [f] + (xpcall f #(set-mode :error-mode mode-name $ (fennel.traceback)))) + (fn love.draw [] ;; the canvas allows you to get sharp pixel-art style scaling; if you ;; don't want that, just skip that and call mode.draw directly. (love.graphics.setCanvas canvas) (love.graphics.clear) (love.graphics.setColor 1 1 1) - (mode.draw) + (safely mode.draw) (love.graphics.setCanvas) (love.graphics.setColor 1 1 1) (love.graphics.draw canvas 0 0 0 scale scale)) (fn love.update [dt] - (mode.update dt set-mode)) + (when mode.update + (safely #(mode.update dt set-mode)))) (fn love.keypressed [key] (if (and (love.keyboard.isDown "lctrl" "rctrl" "capslock") (= key "q")) (love.event.quit) ;; add what each keypress should do in each mode - (mode.keypressed key set-mode))) + (safely #(mode.keypressed key set-mode))))