From: Stefan Monnier Date: Sun, 17 Mar 2024 03:10:48 +0000 (-0400) Subject: debug.el: Prevent re-entering the debugger for the same error X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=0ca017711c7b7535c621ce960b61c7a1be6cbaad;p=emacs.git debug.el: Prevent re-entering the debugger for the same error We can have several active `handler-bind`s that all want to invoke the debugger, in which case we can have the following sequence: - The more deeply nested handler calls the debugger. - After a while the user invokes `debugger-continue`. - `signal_or_quit` propagates the error up the stack to the second handler, which calls the debugger again. - The user thus ends up right back at the same place, as if `debugger-continue` had not be processed. Fix this by remembering the last processed error and skipping the debugger if we bump into it again. * lisp/emacs-lisp/debug.el (debugger--last-error): New var. (debugger--duplicate-p): New function. (debug): Use them. (cherry picked from commit 445e2499baa1b8ef21e8edcc13692b5d78912922) --- diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el index 60d14d11970..ec947c1215d 100644 --- a/lisp/emacs-lisp/debug.el +++ b/lisp/emacs-lisp/debug.el @@ -153,6 +153,12 @@ where CAUSE can be: (insert (debugger--buffer-state-content state))) (goto-char (debugger--buffer-state-pos state))) +(defvar debugger--last-error nil) + +(defun debugger--duplicate-p (args) + (pcase args + (`(error ,err . ,_) (and (consp err) (eq err debugger--last-error))))) + ;;;###autoload (setq debugger 'debug) ;;;###autoload @@ -175,9 +181,14 @@ first will be printed into the backtrace buffer. If `inhibit-redisplay' is non-nil when this function is called, the debugger will not be entered." (interactive) - (if inhibit-redisplay - ;; Don't really try to enter debugger within an eval from redisplay. + (if (or inhibit-redisplay + (debugger--duplicate-p args)) + ;; Don't really try to enter debugger within an eval from redisplay + ;; or if we already popper into the debugger for this error, + ;; which can happen when we have several nested `handler-bind's that + ;; want to invoke the debugger. debugger-value + (setq debugger--last-error nil) (let ((non-interactive-frame (or noninteractive ;FIXME: Presumably redundant. ;; If we're in the initial-frame (where `message' just @@ -318,6 +329,12 @@ the debugger will not be entered." (backtrace-mode)))) (with-timeout-unsuspend debugger-with-timeout-suspend) (set-match-data debugger-outer-match-data))) + (when (eq 'error (car-safe debugger-args)) + ;; Remember the error we just debugged, to avoid re-entering + ;; the debugger if some higher-up `handler-bind' invokes us + ;; again, oblivious that the error was already debugged from + ;; a more deeply nested `handler-bind'. + (setq debugger--last-error (nth 1 debugger-args))) (setq debug-on-next-call debugger-step-after-exit) debugger-value))))