emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 445e2499baa: debug.el: Prevent re-entering the debugger for the s


From: Stefan Monnier
Subject: master 445e2499baa: debug.el: Prevent re-entering the debugger for the same error
Date: Sat, 16 Mar 2024 23:11:18 -0400 (EDT)

branch: master
commit 445e2499baa1b8ef21e8edcc13692b5d78912922
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>

    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.
---
 lisp/emacs-lisp/debug.el | 21 +++++++++++++++++++--
 1 file 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))))
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]