From 2de2d3258f1c7b31c6f072132b67d185419ca112 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Tue, 17 Dec 2024 20:45:42 +0200 Subject: [PATCH] Improve support of property repeat-continue-only in repeat-mode (bug#74140) * lisp/repeat.el (repeat-get-map): Add optional arg 'rep-map'. Move the check for 'repeat-continue-only' from 'repeat-pre-hook'. Improve its logic to check if the current map 'repeat-in-progress' exists in the list from the 'repeat-continue-only' property. (repeat-post-hook): Set 'repeat-in-progress' to the symbol from the property 'repeat-map'. * test/lisp/repeat-tests.el (repeat-tests-another-repeat-map): Add new keymap to test multiple parallel repeat-maps. (repeat-tests-continue-another): New test that uses commands from 'repeat-tests-another-repeat-map' shared with 'repeat-tests-repeat-map'. (cherry picked from commit 9232c985ef3d874755b0cbf4399fcc7077c308b5) --- lisp/repeat.el | 66 ++++++++++++++++++++------------------- test/lisp/repeat-tests.el | 63 +++++++++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 41 deletions(-) diff --git a/lisp/repeat.el b/lisp/repeat.el index cea54b75868..a0e4ae6bafb 100644 --- a/lisp/repeat.el +++ b/lisp/repeat.el @@ -451,10 +451,17 @@ See `describe-repeat-maps' for a list of all repeatable commands." (and (symbolp real-this-command) (get real-this-command property)))) -(defun repeat-get-map () +(defun repeat-get-map (&optional rep-map) "Return a transient map for keys repeatable after the current command." (when repeat-mode - (let ((rep-map (or repeat-map (repeat--command-property 'repeat-map)))) + (let ((rep-map (or rep-map repeat-map (repeat--command-property 'repeat-map))) + (continue-only (repeat--command-property 'repeat-continue-only))) + (when continue-only + (if repeat-in-progress + (when (and (consp continue-only) + (memq repeat-in-progress continue-only)) + (setq rep-map repeat-in-progress)) + (setq rep-map nil))) (when rep-map (when (and (symbolp rep-map) (boundp rep-map)) (setq rep-map (symbol-value rep-map))) @@ -500,37 +507,32 @@ See `describe-repeat-maps' for a list of all repeatable commands." (defun repeat-post-hook () "Function run after commands to set transient keymap for repeatable keys." - (let ((was-in-progress repeat-in-progress)) + (let* ((was-in-progress repeat-in-progress) + (map-sym (or repeat-map (repeat--command-property 'repeat-map))) + (map (repeat-get-map map-sym))) (setq repeat-in-progress nil) - (let ((map (repeat-get-map))) - (when (and (repeat-check-map map) - (let ((continue-only (repeat--command-property 'repeat-continue-only))) - (or (null continue-only) - (and (or (not (consp continue-only)) - (memq (repeat--command-property 'repeat-map) - continue-only)) - was-in-progress)))) - ;; Messaging - (funcall repeat-echo-function map) - - ;; Adding an exit key - (when repeat-exit-key - (setq map (copy-keymap map)) - (define-key map (if (key-valid-p repeat-exit-key) - (kbd repeat-exit-key) - repeat-exit-key) - 'ignore)) - - (setq repeat-in-progress t) - (repeat--clear-prev) - (let ((exitfun (set-transient-map map))) - (setq repeat--transient-exitfun exitfun) - - (let* ((prop (repeat--command-property 'repeat-exit-timeout)) - (timeout (unless (eq prop 'no) (or prop repeat-exit-timeout)))) - (when timeout - (setq repeat-exit-timer - (run-with-idle-timer timeout nil #'repeat-exit))))))) + (when (repeat-check-map map) + ;; Messaging + (funcall repeat-echo-function map) + + ;; Adding an exit key + (when repeat-exit-key + (setq map (copy-keymap map)) + (define-key map (if (key-valid-p repeat-exit-key) + (kbd repeat-exit-key) + repeat-exit-key) + 'ignore)) + + (setq repeat-in-progress map-sym) + (repeat--clear-prev) + (let ((exitfun (set-transient-map map))) + (setq repeat--transient-exitfun exitfun) + + (let* ((prop (repeat--command-property 'repeat-exit-timeout)) + (timeout (unless (eq prop 'no) (or prop repeat-exit-timeout)))) + (when timeout + (setq repeat-exit-timer + (run-with-idle-timer timeout nil #'repeat-exit)))))) (setq repeat-map nil) (setq repeat--prev-mb diff --git a/test/lisp/repeat-tests.el b/test/lisp/repeat-tests.el index d69d431146a..4039a84c4d3 100644 --- a/test/lisp/repeat-tests.el +++ b/test/lisp/repeat-tests.el @@ -24,8 +24,8 @@ (require 'ert) (require 'repeat) -;; Key mnemonics: a - Activate (enter, also b), c - Continue (also d), -;; o - continue-Only (not activate), q - Quit (exit) +;; Key mnemonics: a - Activate (enter, also b, s), c - Continue (also d, t), +;; o - continue-Only (not activate, also u), q - Quit (exit) (defvar repeat-tests-calls nil) @@ -53,13 +53,37 @@ (interactive "p") (push `(,arg q) repeat-tests-calls)) +(defun repeat-tests-call-s (&optional arg) + (interactive "p") + (push `(,arg s) repeat-tests-calls)) + +(defun repeat-tests-call-t (&optional arg) + (interactive "p") + (push `(,arg t) repeat-tests-calls)) + +(defun repeat-tests-call-u (&optional arg) + (interactive "p") + (push `(,arg u) repeat-tests-calls)) + ;; Global keybindings -(defvar-keymap repeat-tests-map +(defvar-keymap repeat-tests-global-map :doc "Keymap for keys that initiate repeating sequences." "C-x w a" 'repeat-tests-call-a "C-M-a" 'repeat-tests-call-a "C-M-b" 'repeat-tests-call-b - "C-M-o" 'repeat-tests-call-o) + "C-M-o" 'repeat-tests-call-o + "C-M-s" 'repeat-tests-call-s + "C-M-u" 'repeat-tests-call-u) + +(defvar-keymap repeat-tests-another-repeat-map + :doc "Keymap for repeating other sequences." + :repeat ( :enter (repeat-tests-call-s) + :continue-only (repeat-tests-call-o + repeat-tests-call-u)) + "s" 'ignore ;; for non-nil repeat-check-key only + "t" 'repeat-tests-call-t + "C-M-o" 'repeat-tests-call-o + "C-M-u" 'repeat-tests-call-u) (defvar-keymap repeat-tests-repeat-map :doc "Keymap for repeating sequences." @@ -99,7 +123,7 @@ (should (equal (buffer-string) inserted))) (ert-deftest repeat-tests-check-key () - (with-repeat-mode repeat-tests-map + (with-repeat-mode repeat-tests-global-map (let ((repeat-echo-function 'ignore)) (let ((repeat-check-key t)) (repeat-tests--check @@ -131,7 +155,7 @@ (put 'repeat-tests-call-b 'repeat-check-key nil)))))) (ert-deftest repeat-tests-exit-command () - (with-repeat-mode repeat-tests-map + (with-repeat-mode repeat-tests-global-map (let ((repeat-echo-function 'ignore)) ;; 'c' doesn't continue since 'q' exited (repeat-tests--check @@ -139,7 +163,7 @@ '((1 a) (1 c) (1 d) (1 q)) "c")))) (ert-deftest repeat-tests-exit-key () - (with-repeat-mode repeat-tests-map + (with-repeat-mode repeat-tests-global-map (let ((repeat-echo-function 'ignore)) (let ((repeat-exit-key nil)) (repeat-tests--check @@ -151,7 +175,7 @@ '((1 a) (1 c) (1 d) (1 c)) "z"))))) (ert-deftest repeat-tests-keep-prefix () - (with-repeat-mode repeat-tests-map + (with-repeat-mode repeat-tests-global-map (let ((repeat-echo-function 'ignore)) (repeat-tests--check "C-x w a c d c z" @@ -179,7 +203,7 @@ ;; TODO: :tags '(:expensive-test) for repeat-exit-timeout (ert-deftest repeat-tests-continue-only () - (with-repeat-mode repeat-tests-map + (with-repeat-mode repeat-tests-global-map (let ((repeat-echo-function 'ignore) (repeat-check-key nil)) ;; 'C-M-o' used as continue-only @@ -191,6 +215,27 @@ "C-M-o c z" '((1 o)) "cz")))) +(ert-deftest repeat-tests-continue-another () + (with-repeat-mode repeat-tests-global-map + (let ((repeat-echo-function 'ignore) + (repeat-check-key nil)) + ;; First test without 'C-M-O' + (repeat-tests--check + "C-M-s t t z" + '((1 s) (1 t) (1 t)) "z") + ;; 'C-M-u' used as continue-only + (repeat-tests--check + "C-M-s t C-M-u t z" + '((1 s) (1 t) (1 u) (1 t)) "z") + ;; 'C-M-u' should not activate + (repeat-tests--check + "C-M-u t z" + '((1 u)) "tz") + ;; 'C-M-o' shared with another map should continue current map + (repeat-tests--check + "C-M-s t C-M-o t C-M-o t z" + '((1 s) (1 t) (1 o) (1 t) (1 o) (1 t)) "z")))) + (require 'use-package) -- 2.39.5