Re: On keybindings and the slow erosion of help's utility
Stefan Monnier <[email protected]> Mon, 27 Jul 2026 23:57:15 -0400
| Newsgroups | gmane.emacs.devel |
|---|---|
| Message-ID | <[email protected]> |
>> - If we evaluate the `:when` conditions during `access_keymap` (like we
>> currently do for `menu-item`), then we don't want to allow
>> side-effecting `:when`s, since those side-effects would be executed at
>> an inopportune time (e.g. before we set `this-command` and run
>> `pre-command-hook`).
>
> Yes. This is the same as in menu-item.
> We may additionally demand the :when to have 'side-effect-free declared explicitly.
As mentioned earlier, `side-effect-free` is an annotation for the
byte-compiler with a very specific meaning and I'm not sure we want to
tie those two meanings. Also, if the function *has* to be side-effect
free, I don't see much benefit in checking `side-effect-free`.
>> While sleeping I thought of the following:
>>
>> - Define some way to mark some commands as "metafunctions".
>> - When we lookup a (list of) keymap(s) and the command we found is
>> marked as a "metafunction", we continue looking for further bindings
>> and return a composite command made up of the metafunctions (plus
>> potentially a final non-metafunction) found.
>
> Could you provide an example? Your description is not entirely clear for me.
Say we have:
(keymap-global-set "TAB" #'my-foo)
(keymap-set foo-mode-map "TAB" (make-partial-binding #'my-condition #'my-bar))
Then `(key-binding "TAB")` in Foo mode would return something like
#f(composite-command #f(partial-binding my-condition my-bar) my-foo)
So `my-condition` is not executed during keymap lookup but only during
command execution (so we don't need to worry about side-effects during
keymap lookups, tho we may still worry about them if we wan to run
`my-condition` during `C-h k` to whether we'd run `my-foo` or `my-bar`).
Calling `composite-command` could look inside the `partial-binding`
command, tho a more generic approach would be for `composite-command` to
just call its first command with a "continuation argument" which is
the second command so it doesn't need to know about `partial-binding`.
An important question is what to do with
(keymap-set foo-mode-map "TAB" (make-partial-binding #'my-condition1 #'my-bar1))
(keymap-set foo-mode-map "TAB" (make-partial-binding #'my-condition2 #'my-bar2))
(keymap-set foo-mode-map "TAB" (make-partial-binding #'my-condition1 #'my-bar3))
should each call completely override the previous (so after those three
calls we have just a single partial binding that uses `my-bar3` when
`my-condition1` is true)? Or should we just accumulate them (if so,
how do we remove a partial binding without removing them all)?
Or should the third call override the first but not the second (and presumably
allow `(keymap-set ...(make-partial-binding #'my-condition1 nil))`
to remove one of the partial bindings)?
The third option sounds more flexible, but it requires more changes in
the `keymap.c` code.
Also, in the above I assume that we're not interested in something like
(keymap-set foo-mode-map "TAB" (make-partial-binding #'my-condition1 my-keymap))
=== Stefan