emacs-diffs
[Top][All Lists]
Advanced

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

emacs-28 ea5a90b 3/3: * lisp/repeat.el: Fix long-standing problem when a


From: Juri Linkov
Subject: emacs-28 ea5a90b 3/3: * lisp/repeat.el: Fix long-standing problem when a random key activates map
Date: Tue, 30 Nov 2021 14:08:59 -0500 (EST)

branch: emacs-28
commit ea5a90b4f47f008a3e2dd58cda5a6b771fb80403
Author: Juri Linkov <juri@linkov.net>
Commit: Juri Linkov <juri@linkov.net>

    * lisp/repeat.el: Fix long-standing problem when a random key activates map
    
    * lisp/repeat.el (repeat-check-key): New defcustom (bug#51390).
    (repeat--command-property): New internal function.
    (repeat-check-key): New function.
    (repeat-post-hook): Use repeat--command-property and repeat-check-key.
    
    * test/lisp/repeat-tests.el (repeat-tests-check-key): New test.
---
 lisp/repeat.el            | 44 +++++++++++++++++++++++++++++++++++---------
 test/lisp/repeat-tests.el | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+), 9 deletions(-)

diff --git a/lisp/repeat.el b/lisp/repeat.el
index 5b3ab03..664a4d6 100644
--- a/lisp/repeat.el
+++ b/lisp/repeat.el
@@ -360,6 +360,24 @@ of the specified number of seconds."
   :group 'convenience
   :version "28.1")
 
+(defcustom repeat-check-key t
+  "Whether to check that the last key exists in the repeat map.
+When non-nil and the last typed key (with or without modifiers)
+doesn't exist in the keymap attached by the `repeat-map' property,
+then don't activate that keymap for the next command.  So only the
+same keys among repeatable keys are allowed in the repeating sequence.
+For example, with a non-nil value, only `C-x u u' repeats undo,
+whereas `C-/ u' doesn't.
+
+You can also set the property `repeat-check-key' on the command symbol.
+This property can override the value of this variable.
+When the variable value is non-nil, but the property value is `no',
+then don't check the last key.  Also when the variable value is nil,
+but the property value is `t', then check the last key."
+  :type 'boolean
+  :group 'convenience
+  :version "28.1")
+
 (defcustom repeat-echo-function #'repeat-echo-message
   "Function to display a hint about available keys.
 Function is called after every repeatable command with one argument:
@@ -405,16 +423,26 @@ See `describe-repeat-maps' for a list of all repeatable 
commands."
 (defvar repeat--prev-mb '(0)
   "Previous minibuffer state.")
 
+(defun repeat--command-property (property)
+  (or (and (symbolp this-command)
+           (get this-command property))
+      (and (symbolp real-this-command)
+           (get real-this-command property))))
+
+(defun repeat-check-key (key map)
+  "Check if the last key is suitable to activate the repeating MAP."
+  (let ((property (repeat--command-property 'repeat-check-key)))
+    (or (if repeat-check-key (eq property 'no) (not (eq property t)))
+        (lookup-key map (vector key))
+        ;; Try without modifiers:
+        (lookup-key map (vector (event-basic-type key))))))
+
 (defun repeat-post-hook ()
   "Function run after commands to set transient keymap for repeatable keys."
   (let ((was-in-progress repeat-in-progress))
     (setq repeat-in-progress nil)
     (when repeat-mode
-      (let ((rep-map (or repeat-map
-                         (and (symbolp this-command)
-                              (get this-command 'repeat-map))
-                         (and (symbolp real-this-command)
-                              (get real-this-command 'repeat-map)))))
+      (let ((rep-map (or repeat-map (repeat--command-property 'repeat-map))))
         (when rep-map
           (when (and (symbolp rep-map) (boundp rep-map))
             (setq rep-map (symbol-value rep-map)))
@@ -426,10 +454,8 @@ See `describe-repeat-maps' for a list of all repeatable 
commands."
                    ;; in the middle of repeating sequence (bug#47566).
                    (or (< (minibuffer-depth) (car repeat--prev-mb))
                        (eq current-minibuffer-command (cdr repeat--prev-mb)))
-                   ;; Exit when the last char is not among repeatable keys,
-                   ;; so e.g. `C-x u u' repeats undo, whereas `C-/ u' doesn't.
-                   (or (lookup-key map (this-command-keys-vector))
-                       prefix-arg))
+                   (or (not repeat-keep-prefix) prefix-arg)
+                   (repeat-check-key last-command-event map))
 
               ;; Messaging
               (unless prefix-arg
diff --git a/test/lisp/repeat-tests.el b/test/lisp/repeat-tests.el
index 76abd24..02d9ddb 100644
--- a/test/lisp/repeat-tests.el
+++ b/test/lisp/repeat-tests.el
@@ -37,6 +37,8 @@
 (defvar repeat-tests-map
   (let ((map (make-sparse-keymap)))
     (define-key map (kbd "C-x w a") 'repeat-tests-call-a)
+    (define-key map (kbd "M-C-a") 'repeat-tests-call-a)
+    (define-key map (kbd "M-C-z") 'repeat-tests-call-a)
     map)
   "Keymap for keys that initiate repeating sequences.")
 
@@ -70,6 +72,38 @@
   ;; Check for self-inserting keys
   (should (equal (buffer-string) inserted)))
 
+(ert-deftest repeat-tests-check-key ()
+  (with-repeat-mode
+   (let ((repeat-echo-function 'ignore))
+     (let ((repeat-check-key t))
+       (repeat-tests--check
+        "C-x w a b a c"
+        '((1 a) (1 b) (1 a)) "c")
+       (repeat-tests--check
+        "M-C-a b a c"
+        '((1 a) (1 b) (1 a)) "c")
+       (repeat-tests--check
+        "M-C-z b a c"
+        '((1 a)) "bac")
+       (unwind-protect
+           (progn
+             (put 'repeat-tests-call-a 'repeat-check-key 'no)
+             (repeat-tests--check
+              "M-C-z b a c"
+              '((1 a) (1 b) (1 a)) "c"))
+         (put 'repeat-tests-call-a 'repeat-check-key nil)))
+     (let ((repeat-check-key nil))
+       (repeat-tests--check
+        "M-C-z b a c"
+        '((1 a) (1 b) (1 a)) "c")
+       (unwind-protect
+           (progn
+             (put 'repeat-tests-call-a 'repeat-check-key t)
+             (repeat-tests--check
+              "M-C-z b a c"
+              '((1 a)) "bac"))
+         (put 'repeat-tests-call-a 'repeat-check-key nil))))))
+
 (ert-deftest repeat-tests-exit-key ()
   (with-repeat-mode
    (let ((repeat-echo-function 'ignore))



reply via email to

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