branch: externals/transient
commit 415f74bf97f6dd8e76131cd5cb3a9a8f77c273e5
Author: Jonas Bernoulli <jo...@bernoul.li>
Commit: Jonas Bernoulli <jo...@bernoul.li>

    Improve kludge to work around an Emacs quitting kludge
    
    Emacs commit 60c10b40d5b addresses this issued.  That hasn't landed
    on master yet (and so the hash is likely to change), so let's copy
    the message here:
    
    > Make C-g in command loops consistently invoke quit keybinding
    >
    > C-g in command loops could unpredictably either signal an error or
    > invoke the quit_char keybinding, depending on the relative timing
    > of input and redisplay.  This change ensures C-g always runs the
    > quit_char keybinding by wrapping read_char to check for quit_flag
    > after reading input.
    
    For older Emacs versions we have to work around the issue using this
    new advice, approximately as suggested by Daniel Colascione at
    https://github.com/magit/transient/commit/45fbefdc5b112f0a15cd93657.
    
    The `unreadp' variable is an additional safety net, which should not
    be necessary.  It would kick in if the unread C-g again causesd quit
    to be signaled, or if somehow our transient keymap got disabled,
    without this advice also getting disabled.
    
    Also see #388.
---
 CHANGELOG         |  6 ++++++
 lisp/transient.el | 29 +++++++++++++++++++++++++----
 2 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index d8c0368f1b..ce3f15ed33 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,10 @@
 # -*- mode: org -*-
+* v0.9.3    UNRELEASED
+
+- Improved kludge to work around a bug in Emacs, which may cause
+  a subprocess to be killed, when the user types ~C-g~, expecting
+  that to quit a transient menu.  #388
+
 * v0.9.2    2025-06-09
 
 - Fixed a regression in v0.9.0, which made it impossible to change the
diff --git a/lisp/transient.el b/lisp/transient.el
index 8501dc3bb7..4117c569a6 100644
--- a/lisp/transient.el
+++ b/lisp/transient.el
@@ -2647,7 +2647,7 @@ value.  Otherwise return CHILDREN as is.")
   (add-hook 'pre-command-hook  #'transient--pre-command 99)
   (add-hook 'post-command-hook #'transient--post-command)
   (advice-add 'recursive-edit :around #'transient--recursive-edit)
-  (set-default-toplevel-value 'inhibit-quit t)
+  (transient--quit-kludge 'enable)
   (when transient--exitp
     ;; This prefix command was invoked as the suffix of another.
     ;; Prevent `transient--post-command' from removing the hooks
@@ -2995,8 +2995,7 @@ value.  Otherwise return CHILDREN as is.")
       (setq transient--current-suffix nil))
     (cond (resume (transient--stack-pop))
           ((not replace)
-           (setq quit-flag nil)
-           (set-default-toplevel-value 'inhibit-quit nil)
+           (transient--quit-kludge 'disable)
            (run-hooks 'transient-post-exit-hook)))))
 
 (defun transient--stack-push ()
@@ -3079,13 +3078,35 @@ When no transient is active (i.e., when 
`transient--prefix' is
 nil) then only reset `inhibit-quit'.  Optional ID is a keyword
 identifying the exit."
   (transient--debug 'emergency-exit id)
-  (set-default-toplevel-value 'inhibit-quit nil)
+  (transient--quit-kludge 'disable)
   (when transient--prefix
     (setq transient--stack nil)
     (setq transient--exitp t)
     (transient--pre-exit)
     (transient--post-exit this-command)))
 
+(defun transient--quit-kludge (action)
+  (static-if (boundp 'redisplay-can-quit) ;Emacs 31
+      action
+    (pcase-exhaustive action
+      ('enable
+       (add-function
+        :around command-error-function
+        (let (unreadp)
+          (lambda (orig data context fn)
+            (cond ((not (eq (car data) 'quit))
+                   (funcall orig data context fn)
+                   (setq unreadp nil))
+                  (unreadp
+                   (remove-function command-error-function "inhibit-quit")
+                   (funcall orig data context fn))
+                  (t
+                   (push ?\C-g unread-command-events)
+                   (setq unreadp t)))))
+        '((name . "inhibit-quit"))))
+      ('disable
+       (remove-function command-error-function "inhibit-quit")))))
+
 ;;; Pre-Commands
 
 (defun transient--call-pre-command ()

Reply via email to