Subject: Re: bug#74140: [PATCH] Add :continue-only directive for repeat maps in bind-keys, use-package

From: "Paul D. Nelson" <[email-masked]>
Date: Sun, 23 Feb 2025 09:30:01 +0100

Juri Linkov <[email-masked]> writes:
>
> I was trying to keep the existing function signature for 'repeat-get-map'
> that was without arguments.  But maybe it's fine to add a new required
> argument.  Then refactoring was straightforward, and now even modified
> tests passed.  Please try it out.

Thanks, Juri.  I've refactored my original patch to use the
'repeat-continue' property that you've added.  Any further comments
would be welcome.

Paul

>From 103eccb7e9fa29d597ed5d48111b52885235f8fc Mon Sep 17 00:00:00 2001
From: Paul Nelson <[email-masked]>
Date: Sat, 22 Feb 2025 23:12:41 +0100
Subject: [PATCH] Add :continue-only directive to bind-keys and use-package

* lisp/bind-key.el (bind-keys-form): Add :continue-only binding.
Fix indentation.

* lisp/use-package/use-package-bind-key.el
(use-package-normalize-binder): Add check for :continue-only.
---
 etc/NEWS                                 |  5 +--
 lisp/bind-key.el                         | 43 ++++++++++++++----------
 lisp/use-package/use-package-bind-key.el |  7 ++--
 3 files changed, 32 insertions(+), 23 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 82a653c4e1e..055ff4c9055 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1541,8 +1541,9 @@ will be calculated based on the window width.
 A command with this symbol property whose value is a list of repeat
 maps will not activate the repeat map in 'repeat-mode'.  It will only
 continue the already activated repeating sequence.  Also 'defvar-keymap'
-supports a new keyword ':continue' with a list of commands that
-only continue the active repeating sequence.
+supports a new keyword ':continue' with a list of commands that only
+continue the active repeating sequence, and the 'use-package' and
+'bind-keys' macros supports a similar keyword ':continue-only'.
 
 ** New function 'completion-table-with-metadata'.
 It offers a more concise way to create a completion table with metadata.
diff --git a/lisp/bind-key.el b/lisp/bind-key.el
index 66cb542d0b4..12417106783 100644
--- a/lisp/bind-key.el
+++ b/lisp/bind-key.el
@@ -290,6 +290,9 @@ bind-keys-form
                          same behavior as if no special keyword had
                          been used (that is, the command is bound, and
                          it's `repeat-map' property set)
+:continue-only BINDINGS - Within the scope of `:repeat-map', will make
+                         the command continue but not enter the repeat
+                         map, via the `repeat-continue' property
 :filter FORM           - optional form to determine when bindings apply
 
 The rest of the arguments are conses of keybinding string and a
@@ -325,11 +328,8 @@ bind-keys-form
                                          override-global-map))))
                    (setq repeat-map (cadr args))
                    (setq map repeat-map))
-                  ((eq :continue (car args))
-                   (setq repeat-type :continue
-                         arg-change-func 'cdr))
-                  ((eq :exit (car args))
-                   (setq repeat-type :exit
+                  ((memq (car args) '(:continue :continue-only :exit))
+                   (setq repeat-type (car args)
                          arg-change-func 'cdr))
                   ((eq :prefix (car args))
                    (setq prefix (cadr args)))
@@ -348,7 +348,7 @@ bind-keys-form
 
     (when repeat-type
       (unless repeat-map
-        (error ":continue and :exit require specifying :repeat-map")))
+        (error ":continue(-only) and :exit require specifying :repeat-map")))
 
     (when (and menu-name (not prefix))
       (error "If :menu-name is supplied, :prefix must be too"))
@@ -369,14 +369,14 @@ bind-keys-form
 
       (cl-flet
           ((wrap (map bindings)
-                 (if (and map pkg (not (memq map '(global-map
-                                                   override-global-map))))
-                     `((if (boundp ',map)
-                           ,(macroexp-progn bindings)
-                         (eval-after-load
-                             ,(if (symbolp pkg) `',pkg pkg)
-                           ',(macroexp-progn bindings))))
-                   bindings)))
+             (if (and map pkg (not (memq map '(global-map
+                                               override-global-map))))
+                 `((if (boundp ',map)
+                       ,(macroexp-progn bindings)
+                     (eval-after-load
+                         ,(if (symbolp pkg) `',pkg pkg)
+                       ',(macroexp-progn bindings))))
+               bindings)))
 
         (append
          (when prefix-map
@@ -401,9 +401,18 @@ bind-keys-form
                           ;; Only needed in this branch, since when
                           ;; repeat-map is non-nil, map is always
                           ;; non-nil
-                          `(,@(when (and repeat-map (not (eq repeat-type :exit)))
-                                `((put ,fun 'repeat-map ',repeat-map)))
-                            (bind-key ,(car form) ,fun ,map ,filter))
+                          (if (eq repeat-type :continue-only)
+                              `((unless (memq ',repeat-map
+                                              (or (get ,fun 'repeat-continue)
+                                                  '()))
+                                  (put ,fun 'repeat-continue
+                                       (append (or (get ,fun 'repeat-continue)
+                                                   '())
+                                               (list ',repeat-map))))
+                                (bind-key ,(car form) ,fun ,map ,filter))
+                            `(,@(when (and repeat-map (not (eq repeat-type :exit)))
+                                  `((put ,fun 'repeat-map ',repeat-map)))
+                              (bind-key ,(car form) ,fun ,map ,filter)))
                         `((bind-key ,(car form) ,fun nil ,filter))))))
                 first))
          (when next
diff --git a/lisp/use-package/use-package-bind-key.el b/lisp/use-package/use-package-bind-key.el
index 396a1b3a2a1..19dc7e71f7a 100644
--- a/lisp/use-package/use-package-bind-key.el
+++ b/lisp/use-package/use-package-bind-key.el
@@ -88,14 +88,13 @@ use-package-normalize-binder
          ;;   :filter SEXP
          ;;   :menu-name STRING
          ;;   :package SYMBOL
-	 ;;   :continue and :exit are used within :repeat-map
+	 ;;   :continue(-only) and :exit are used within :repeat-map
          ((or (and (eq x :map) (symbolp (cadr arg)))
               (and (eq x :prefix) (stringp (cadr arg)))
               (and (eq x :prefix-map) (symbolp (cadr arg)))
               (and (eq x :prefix-docstring) (stringp (cadr arg)))
-	      (and (eq x :repeat-map) (symbolp (cadr arg)))
-	      (eq x :continue)
-	      (eq x :exit)
+              (and (eq x :repeat-map) (symbolp (cadr arg)))
+              (memq x '(:continue :continue-only :exit))
               (and (eq x :repeat-docstring) (stringp (cadr arg)))
               (eq x :filter)
               (and (eq x :menu-name) (stringp (cadr arg)))
-- 
2.39.3 (Apple Git-145)