Package: emacs;
Reported by: Mike Kupfer <kupfer <at> rawbw.com>
Date: Fri, 27 Jun 2025 21:37:02 UTC
Severity: normal
Found in version 31.0.50
Message #41 received at 78916 <at> debbugs.gnu.org (full text, mbox):
From: Pip Cet <pipcet <at> protonmail.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 78916 <at> debbugs.gnu.org, Stefan Monnier <monnier <at> iro.umontreal.ca>, Mike Kupfer <kupfer <at> rawbw.com> Subject: Re: bug#78916: 31.0.50; C-g fails to exit loop Date: Mon, 30 Jun 2025 19:40:43 +0000
Pip Cet <pipcet <at> protonmail.com> writes: > "Stefan Monnier via \"Bug reports for GNU Emacs, the Swiss army knife of text editors\"" <bug-gnu-emacs <at> gnu.org> writes: > >>>> > - Both keys behave like the "old C-g". >>>> > ? >>>> I'm fine with that. >>> Same, but does it mean 'C-g' will do the same as 'q'? If not, what is >>> the difference between them? >> >> Hmm... so `q` is bound to `exit` which consumes the key and exits the >> Q&R loop. In contrast `C-g` and `C-]` were bound to `quit` which is >> not handled exlicitly by the Q&R loop, instead it just pushes the event >> back on the `unread-commend-events` before exiting the loop, so in >> Mike's test case, `q` exits the inner Q&R and passes to the next, >> whereas `C-]` aborts all the subsequent Q&R calls after which it runs >> the global binding of `C-[`. > >> I get the impression that the intention of the `replace.el` code when it >> pushes the event back on `unread-commend-events` is to make it run >> whichever binding is currently active "outside" (i.e. global or local >> keymap). >> >> IOW, we have many different ways to skin this cat and I'm not sure which >> is best. > > I'd just like to speak out in favor of making read-key take an extra > argument to make it handle C-g the way read-event does; IIUC, the lack > of this argument is the real problem here. If we don't, we should be > very careful about replacing read-event by read-key, and revert most > such changes. This patch (which doesn't include documentation changes yet) would make (read-key prompt nil t) DTRT, I think. I've looked at a few callers of read-key and they should probably pass t as the can-quit argument, too, because most of them want to quit for C-g and try to do so, with varying degrees of success. I've yet to verify this fixes the original F-key issue which prompted the change to read-key in the first place. It does appear to fix the buggy behavior observed by the OP. From dd850c5da3591e00dfd2fe1dd3e6d901a53e7176 Mon Sep 17 00:00:00 2001 From: Pip Cet <pipcet <at> protonmail.com> Subject: [PATCH 1/3] Add optional 'can-quit' argument to 'read-key' (bug#78916) * lisp/subr.el (read-key): Add 'can-quit' argument and pass it on. * src/callint.c (Fcall_interactively): * src/keyboard.c (command_loop_1): (read_menu_command): Adjust accordingly. (read_key_sequence): Quit after read_char if can_quit is set. (read_key_sequence_vs): (Fread_key_sequence): (Fread_key_sequence_vector): Add extra argument. --- lisp/subr.el | 5 +++-- src/callint.c | 4 ++-- src/keyboard.c | 32 ++++++++++++++++++++------------ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lisp/subr.el b/lisp/subr.el index 69f6e4dbab8..b06d45d449f 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3425,7 +3425,7 @@ read-key-full-map (defvar read-key-delay 0.01) ;Fast enough for 100Hz repeat rate, hopefully. -(defun read-key (&optional prompt disable-fallbacks) +(defun read-key (&optional prompt disable-fallbacks can-quit) "Read a key from the keyboard, return the event thus read. Contrary to `read-event' this will not return a raw event but instead will obey the input decoding and translations usually done by `read-key-sequence'. @@ -3505,7 +3505,8 @@ read-key map)) (let* ((keys (catch 'read-key (read-key-sequence-vector prompt nil t - nil nil t))) + nil nil t + can-quit))) (key (aref keys 0))) (if (and (> (length keys) 1) (memq key '(mode-line header-line tab-line diff --git a/src/callint.c b/src/callint.c index e0246e5d594..38ffa8804d3 100644 --- a/src/callint.c +++ b/src/callint.c @@ -538,7 +538,7 @@ DEFUN ("call-interactively", Fcall_interactively, Scall_interactively, 1, 3, 0, Qface, Qminibuffer_prompt, callint_message); args[i] = Fread_key_sequence (callint_message, Qnil, Qnil, Qnil, Qnil, - Qnil); + Qnil, Qnil); unbind_to (speccount1, Qnil); visargs[i] = Fkey_description (args[i], Qnil); @@ -569,7 +569,7 @@ DEFUN ("call-interactively", Fcall_interactively, Scall_interactively, 1, 3, 0, Qface, Qminibuffer_prompt, callint_message); args[i] = Fread_key_sequence_vector (callint_message, Qnil, Qt, Qnil, Qnil, - Qnil); + Qnil, Qnil); visargs[i] = Fkey_description (args[i], Qnil); unbind_to (speccount1, Qnil); diff --git a/src/keyboard.c b/src/keyboard.c index 8b2ebd215d2..93149910fa7 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1311,7 +1311,7 @@ some_mouse_moved (void) enum { READ_KEY_ELTS = 30 }; static int read_key_sequence (Lisp_Object *, Lisp_Object, - bool, bool, bool, bool, bool); + bool, bool, bool, bool, bool, bool); static void adjust_point_for_property (ptrdiff_t, bool); static Lisp_Object @@ -1422,7 +1422,7 @@ command_loop_1 (void) raw_keybuf_count = 0; Lisp_Object keybuf[READ_KEY_ELTS]; int i = read_key_sequence (keybuf, Qnil, false, true, true, false, - false); + false, false); /* A filter may have run while we were reading the input. */ if (! FRAME_LIVE_P (XFRAME (selected_frame))) @@ -1698,7 +1698,7 @@ read_menu_command (void) Lisp_Object keybuf[READ_KEY_ELTS]; int i = read_key_sequence (keybuf, Qnil, false, true, true, true, - false); + false, false); unbind_to (count, Qnil); @@ -10544,7 +10544,7 @@ restore_reading_key_sequence (int old_reading_key_sequence) read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, bool dont_downcase_last, bool can_return_switch_frame, bool fix_current_buffer, bool prevent_redisplay, - bool disable_text_conversion_p) + bool disable_text_conversion_p, bool can_quit) { specpdl_ref count = SPECPDL_INDEX (); @@ -10867,6 +10867,11 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, key = read_char (prevent_redisplay ? -2 : NILP (prompt), current_binding, last_nonmenu_event, &used_mouse_menu, NULL); + if (can_quit && EQ (key, make_fixnum (quit_char))) + { + Vquit_flag = Qt; + maybe_quit (); + } used_mouse_menu_history[t] = used_mouse_menu; if ((FIXNUMP (key) && XFIXNUM (key) == -2) /* wrong_kboard_jmpbuf */ /* When switching to a new tty (with a new keyboard), @@ -11546,7 +11551,8 @@ read_key_sequence_vs (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object dont_downcase_last, Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop, bool allow_string, - bool disable_text_conversion) + bool disable_text_conversion, + bool can_quit) { specpdl_ref count = SPECPDL_INDEX (); @@ -11574,7 +11580,7 @@ read_key_sequence_vs (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object keybuf[READ_KEY_ELTS]; int i = read_key_sequence (keybuf, prompt, ! NILP (dont_downcase_last), ! NILP (can_return_switch_frame), false, false, - disable_text_conversion); + disable_text_conversion, can_quit); #if 0 /* The following is fine for code reading a key sequence and then proceeding with a lengthy computation, but it's not good @@ -11596,7 +11602,7 @@ read_key_sequence_vs (Lisp_Object prompt, Lisp_Object continue_echo, (i, keybuf))); } -DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 6, 0, +DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 7, 0, doc: /* Read a sequence of keystrokes and return as a string or vector. The sequence is sufficient to specify a non-prefix command in the current local and global maps. @@ -11650,23 +11656,25 @@ Second (optional) arg CONTINUE-ECHO, if non-nil, means this key echos being sent. */) (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object dont_downcase_last, Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop, - Lisp_Object disable_text_conversion) + Lisp_Object disable_text_conversion, Lisp_Object can_quit) { return read_key_sequence_vs (prompt, continue_echo, dont_downcase_last, can_return_switch_frame, cmd_loop, true, - !NILP (disable_text_conversion)); + !NILP (disable_text_conversion), + !NILP (can_quit)); } DEFUN ("read-key-sequence-vector", Fread_key_sequence_vector, - Sread_key_sequence_vector, 1, 6, 0, + Sread_key_sequence_vector, 1, 7, 0, doc: /* Like `read-key-sequence' but always return a vector. */) (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object dont_downcase_last, Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop, - Lisp_Object disable_text_conversion) + Lisp_Object disable_text_conversion, Lisp_Object can_quit) { return read_key_sequence_vs (prompt, continue_echo, dont_downcase_last, can_return_switch_frame, cmd_loop, false, - !NILP (disable_text_conversion)); + !NILP (disable_text_conversion), + !NILP (can_quit)); } /* Return true if input events are pending. */ -- 2.50.0 From 395ec08d4f5d31f2d6d6022fb40c4c90d44b8aeb Mon Sep 17 00:00:00 2001 From: Pip Cet <pipcet <at> protonmail.com> Subject: [PATCH 2/3] Make C-g quit again in perform-replace (bug#78916) * lisp/replace.el (perform-replace): Pass 't' as 'can-quit' argument to 'read-key'. --- lisp/replace.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/replace.el b/lisp/replace.el index 9939273594f..1d115949599 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -3103,7 +3103,7 @@ perform-replace replacement-presentation)))) ;; Use `read-key' so that escape sequences on TTYs ;; are properly mapped back to the intended key. - (setq key (read-key prompt))) + (setq key (read-key prompt nil t))) ;; Necessary in case something happens during ;; read-event that clobbers the match data. (set-match-data real-match-data) -- 2.50.0
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.