aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuri Linkov2021-11-30 21:05:05 +0200
committerJuri Linkov2021-11-30 21:07:18 +0200
commitea5a90b4f47f008a3e2dd58cda5a6b771fb80403 (patch)
tree54f96b17219b166eeba477e78e56abf7e8df83fe
parentef4954b69c17831f4c8360c436352170305666ea (diff)
downloademacs-ea5a90b4f47f008a3e2dd58cda5a6b771fb80403.tar.gz
emacs-ea5a90b4f47f008a3e2dd58cda5a6b771fb80403.zip
* 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.
-rw-r--r--lisp/repeat.el44
-rw-r--r--test/lisp/repeat-tests.el34
2 files changed, 69 insertions, 9 deletions
diff --git a/lisp/repeat.el b/lisp/repeat.el
index 5b3ab03796a..664a4d68ec6 100644
--- a/lisp/repeat.el
+++ b/lisp/repeat.el
@@ -360,6 +360,24 @@ of the specified number of seconds."
360 :group 'convenience 360 :group 'convenience
361 :version "28.1") 361 :version "28.1")
362 362
363(defcustom repeat-check-key t
364 "Whether to check that the last key exists in the repeat map.
365When non-nil and the last typed key (with or without modifiers)
366doesn't exist in the keymap attached by the `repeat-map' property,
367then don't activate that keymap for the next command. So only the
368same keys among repeatable keys are allowed in the repeating sequence.
369For example, with a non-nil value, only `C-x u u' repeats undo,
370whereas `C-/ u' doesn't.
371
372You can also set the property `repeat-check-key' on the command symbol.
373This property can override the value of this variable.
374When the variable value is non-nil, but the property value is `no',
375then don't check the last key. Also when the variable value is nil,
376but the property value is `t', then check the last key."
377 :type 'boolean
378 :group 'convenience
379 :version "28.1")
380
363(defcustom repeat-echo-function #'repeat-echo-message 381(defcustom repeat-echo-function #'repeat-echo-message
364 "Function to display a hint about available keys. 382 "Function to display a hint about available keys.
365Function is called after every repeatable command with one argument: 383Function is called after every repeatable command with one argument:
@@ -405,16 +423,26 @@ See `describe-repeat-maps' for a list of all repeatable commands."
405(defvar repeat--prev-mb '(0) 423(defvar repeat--prev-mb '(0)
406 "Previous minibuffer state.") 424 "Previous minibuffer state.")
407 425
426(defun repeat--command-property (property)
427 (or (and (symbolp this-command)
428 (get this-command property))
429 (and (symbolp real-this-command)
430 (get real-this-command property))))
431
432(defun repeat-check-key (key map)
433 "Check if the last key is suitable to activate the repeating MAP."
434 (let ((property (repeat--command-property 'repeat-check-key)))
435 (or (if repeat-check-key (eq property 'no) (not (eq property t)))
436 (lookup-key map (vector key))
437 ;; Try without modifiers:
438 (lookup-key map (vector (event-basic-type key))))))
439
408(defun repeat-post-hook () 440(defun repeat-post-hook ()
409 "Function run after commands to set transient keymap for repeatable keys." 441 "Function run after commands to set transient keymap for repeatable keys."
410 (let ((was-in-progress repeat-in-progress)) 442 (let ((was-in-progress repeat-in-progress))
411 (setq repeat-in-progress nil) 443 (setq repeat-in-progress nil)
412 (when repeat-mode 444 (when repeat-mode
413 (let ((rep-map (or repeat-map 445 (let ((rep-map (or repeat-map (repeat--command-property 'repeat-map))))
414 (and (symbolp this-command)
415 (get this-command 'repeat-map))
416 (and (symbolp real-this-command)
417 (get real-this-command 'repeat-map)))))
418 (when rep-map 446 (when rep-map
419 (when (and (symbolp rep-map) (boundp rep-map)) 447 (when (and (symbolp rep-map) (boundp rep-map))
420 (setq rep-map (symbol-value rep-map))) 448 (setq rep-map (symbol-value rep-map)))
@@ -426,10 +454,8 @@ See `describe-repeat-maps' for a list of all repeatable commands."
426 ;; in the middle of repeating sequence (bug#47566). 454 ;; in the middle of repeating sequence (bug#47566).
427 (or (< (minibuffer-depth) (car repeat--prev-mb)) 455 (or (< (minibuffer-depth) (car repeat--prev-mb))
428 (eq current-minibuffer-command (cdr repeat--prev-mb))) 456 (eq current-minibuffer-command (cdr repeat--prev-mb)))
429 ;; Exit when the last char is not among repeatable keys, 457 (or (not repeat-keep-prefix) prefix-arg)
430 ;; so e.g. `C-x u u' repeats undo, whereas `C-/ u' doesn't. 458 (repeat-check-key last-command-event map))
431 (or (lookup-key map (this-command-keys-vector))
432 prefix-arg))
433 459
434 ;; Messaging 460 ;; Messaging
435 (unless prefix-arg 461 (unless prefix-arg
diff --git a/test/lisp/repeat-tests.el b/test/lisp/repeat-tests.el
index 76abd24ea2f..02d9ddbc96e 100644
--- a/test/lisp/repeat-tests.el
+++ b/test/lisp/repeat-tests.el
@@ -37,6 +37,8 @@
37(defvar repeat-tests-map 37(defvar repeat-tests-map
38 (let ((map (make-sparse-keymap))) 38 (let ((map (make-sparse-keymap)))
39 (define-key map (kbd "C-x w a") 'repeat-tests-call-a) 39 (define-key map (kbd "C-x w a") 'repeat-tests-call-a)
40 (define-key map (kbd "M-C-a") 'repeat-tests-call-a)
41 (define-key map (kbd "M-C-z") 'repeat-tests-call-a)
40 map) 42 map)
41 "Keymap for keys that initiate repeating sequences.") 43 "Keymap for keys that initiate repeating sequences.")
42 44
@@ -70,6 +72,38 @@
70 ;; Check for self-inserting keys 72 ;; Check for self-inserting keys
71 (should (equal (buffer-string) inserted))) 73 (should (equal (buffer-string) inserted)))
72 74
75(ert-deftest repeat-tests-check-key ()
76 (with-repeat-mode
77 (let ((repeat-echo-function 'ignore))
78 (let ((repeat-check-key t))
79 (repeat-tests--check
80 "C-x w a b a c"
81 '((1 a) (1 b) (1 a)) "c")
82 (repeat-tests--check
83 "M-C-a b a c"
84 '((1 a) (1 b) (1 a)) "c")
85 (repeat-tests--check
86 "M-C-z b a c"
87 '((1 a)) "bac")
88 (unwind-protect
89 (progn
90 (put 'repeat-tests-call-a 'repeat-check-key 'no)
91 (repeat-tests--check
92 "M-C-z b a c"
93 '((1 a) (1 b) (1 a)) "c"))
94 (put 'repeat-tests-call-a 'repeat-check-key nil)))
95 (let ((repeat-check-key nil))
96 (repeat-tests--check
97 "M-C-z b a c"
98 '((1 a) (1 b) (1 a)) "c")
99 (unwind-protect
100 (progn
101 (put 'repeat-tests-call-a 'repeat-check-key t)
102 (repeat-tests--check
103 "M-C-z b a c"
104 '((1 a)) "bac"))
105 (put 'repeat-tests-call-a 'repeat-check-key nil))))))
106
73(ert-deftest repeat-tests-exit-key () 107(ert-deftest repeat-tests-exit-key ()
74 (with-repeat-mode 108 (with-repeat-mode
75 (let ((repeat-echo-function 'ignore)) 109 (let ((repeat-echo-function 'ignore))