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

From: Juri Linkov <[email-masked]>
Date: Tue, 05 Nov 2024 20:25:24 +0200

>> >                   activate    continue     bind
>> > :continue         yes         yes          yes
>> > :continue-only    no          yes          yes
>> > :exit             no          no           yes
>> > :enter            yes         no           no
>>
>> Thanks, this table provides a clear overview.
>>
>> So bind-keys doesn't need :enter?  Ok.
>
> I understand the purpose of bind-keys to be "bind keys to commands in
> a keymap".  Since :enter doesn't bind a key, it doesn't fit in
> bind-keys.

In defvar-keymap, :enter defines for a list of commands
that activate a repeat-map with their global keybindings.
Such as e.g. 'C-x u' for 'undo' when there are no keys
with the 'undo' keybinding in the repeat-map.

> There is a missing fourth case ("yes, no, yes") describing a key bound
> to a command in a repeat map which the command activates but the key
> exits.  I haven't been imaginative enough to think of a good use case.
> "Why does this command start a repeat sequence, but then terminate it
> when I try to use it in that sequence?"

I guess the case "yes, no, yes" is not needed because
no one might want to define a global keybinding via
bind-keys or defvar-keymap.

>> Since defvar-keymap uses :continue by default,
>> what is missing in defvar-keymap is :continue-only.
>> For adding :continue-only to defvar-keymap
>> I looked how you implemented it for bind-keys,
>> and it seems making an alias doesn't look
>> like a clean solution.
>>
>> I know that 'define-repeat-map' makes an alias as well.
>> So some time ago I had one idea how such aliases
>> could be avoided.  The solution would be to put
>> a new property like this:
>>
>>   (put 'yank 'repeat-continue-keys '("y"))
>>   (put 'undo 'repeat-continue-keys '("C-/"))
>>
>> Its semantics is that when such a property exists
>> then repeat-mode will check if the last typed keys
>> don't exist in this list, only then the repeat map
>> should be activated.
>
> I didn't quite follow.  If the repeat map is active and the user
> presses "C-/", then repeat-mode will see that the key in question
> exists in the repeat-continue-keys list, and so will not activate the
> repeat map.  This is the opposite of the intended behavior.  Perhaps
> I've misunderstood?

The property 'repeat-continue-keys' is intended to be used only
to decide whether a global key sequence should activate the repeat-map
or not.

So maybe a better property name would be 'repeat-no-enter-keys'.
Then your new bind-keys directive :continue-only could be named
:no-entry (but :continue-only is fine if it's more clear).

In any case such property could check not a command name,
but a global key sequence, then decide whether it should activate
the repeat-map.

> I think that in an ideal world, "repeat-map" would be a property of a
> keybinding inside a keymap, rather than a command.  The alias-based
> approach gives one approximation to that.

Extending the standard keymap format would complicate it.
So a less radical change would be to add extra information
in the symbol property list.  I see now that the previous idea
was insufficient since it lacks a map.  So a better way is to define
a combination of a repeat-map and a key:

  (put 'undo 'repeat-continue-keys '((paragraph-repeat-map . "C-/")))

> I guess one alias-free approach would be to introduce a
> repeat-continue property describing keymaps that a command should
> perpetuate, e.g.,
>
>   (put 'yank 'repeat-continue '(paragraph-repeat-map))
>   (put 'undo 'repeat-continue '(paragraph-repeat-map))

Keys are required as well to be able to decide whether
to activate the repeat-map.

There are two possible ways to do this: opt-in and out-out.

1. opt-in defines all global keys that activate a repeat-map. e.g.:

  (put 'undo 'repeat-entry-keys '((paragraph-repeat-map . "C-x u")))

2. out-out defines only keys from the repeat-map that should not activate it
   by a global key such as the global "u" ('self-insert-command')
   should not activate paragraph-repeat-map:

  (put 'undo 'repeat-no-entry-keys '((paragraph-repeat-map . "u")))

> Anyway, is the idea that you'd like to see equivalent functionality in
> defvar-keymap and/or to have both implementations avoid aliases?

Yes, we need to keep feature parity between bind-keys and defvar-keymap,
and I'd like to avoid aliases in defvar-keymap.