From: Juri Linkov Date: Tue, 30 Nov 2021 19:05:05 +0000 (+0200) Subject: * lisp/repeat.el: Fix long-standing problem when a random key activates map X-Git-Tag: emacs-28.0.90~16 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=ea5a90b4f47f008a3e2dd58cda5a6b771fb80403;p=emacs.git * 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. --- 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." :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 76abd24ea2f..02d9ddbc96e 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))