diff options
author | Stefan Monnier <monnier@iro.umontreal.ca> | 2024-03-16 23:10:48 -0400 |
---|---|---|
committer | Stefan Monnier <monnier@iro.umontreal.ca> | 2024-03-16 23:10:48 -0400 |
commit | 445e2499baa1b8ef21e8edcc13692b5d78912922 (patch) | |
tree | 0be2a8e5971f97a48aad835b8fe96390230670f0 /lisp/emacs-lisp/debug.el | |
parent | ad3a3ad6e616a53ec5ae28aed02e8d3461a5ce5c (diff) | |
download | emacs-445e2499baa1b8ef21e8edcc13692b5d78912922.tar.gz emacs-445e2499baa1b8ef21e8edcc13692b5d78912922.tar.bz2 emacs-445e2499baa1b8ef21e8edcc13692b5d78912922.zip |
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.
Diffstat (limited to 'lisp/emacs-lisp/debug.el')
-rw-r--r-- | lisp/emacs-lisp/debug.el | 21 |
1 files changed, 19 insertions, 2 deletions
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)))) |