diff options
| author | Juri Linkov | 2021-11-30 21:05:05 +0200 |
|---|---|---|
| committer | Juri Linkov | 2021-11-30 21:07:18 +0200 |
| commit | ea5a90b4f47f008a3e2dd58cda5a6b771fb80403 (patch) | |
| tree | 54f96b17219b166eeba477e78e56abf7e8df83fe | |
| parent | ef4954b69c17831f4c8360c436352170305666ea (diff) | |
| download | emacs-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.el | 44 | ||||
| -rw-r--r-- | test/lisp/repeat-tests.el | 34 |
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. | ||
| 365 | When non-nil and the last typed key (with or without modifiers) | ||
| 366 | doesn't exist in the keymap attached by the `repeat-map' property, | ||
| 367 | then don't activate that keymap for the next command. So only the | ||
| 368 | same keys among repeatable keys are allowed in the repeating sequence. | ||
| 369 | For example, with a non-nil value, only `C-x u u' repeats undo, | ||
| 370 | whereas `C-/ u' doesn't. | ||
| 371 | |||
| 372 | You can also set the property `repeat-check-key' on the command symbol. | ||
| 373 | This property can override the value of this variable. | ||
| 374 | When the variable value is non-nil, but the property value is `no', | ||
| 375 | then don't check the last key. Also when the variable value is nil, | ||
| 376 | but 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. |
| 365 | Function is called after every repeatable command with one argument: | 383 | 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." | |||
| 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)) |