Re: On keybindings and the slow erosion of help's utility
Ihor Radchenko <[email protected]> Sat, 01 Aug 2026 15:58:07 +0000
| Newsgroups | gmane.emacs.devel |
|---|---|
| Message-ID | <878q6pal55.fsf@localhost> |
Stefan Monnier <[email protected]> writes: >>> - 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`. I understand your concern. However, AFAIK, it is not possible to prevent users from putting functions with side effects as long as we allow arbitrary functions as :when condition. We can document the limitation and warn about consequences. We can also make sure that the function is intentionally written to not produce side effects. My example with byte-compiler annotation was one way to check that. Of course, side-effect-free annotation is what we need to :when condition are not exactly the same. So, for example, we can introduce another form of declaration that is specific to absence of side effects for the purposes of menu. Or we can leave it up to users to take care about putting something appropriate into :when. I think we can go either way, as I do not see risks of Emacs crashing when something inappropriate is used as :when. (Correct me if I am wrong) >>> 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`. What if none of the `partial-binding's match? > 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. IMHO, the best approach is to make the full "partial-binding" named and the each condition also named (as in my suggestion with :name). I described examples earlier. I think that exactly the same structure can be used to manipulate the conditions. (I hope what I said in this paragraph makes sense) > 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)) I am not sure why we need to introduce such a limitation vs. what I proposed. -- Ihor Radchenko // yantar92, Org mode maintainer, Learn more about Org mode at <https://orgmode.org/>. Support Org development at <https://liberapay.com/org-mode>, or support my work at <https://liberapay.com/yantar92>