Re: On keybindings and the slow erosion of help's utility

Ihor Radchenko <[email protected]> Sun, 02 Aug 2026 09:38:02 +0000
Newsgroups gmane.emacs.devel
Message-ID <871pcg982o.fsf@localhost>
Stefan Monnier <[email protected]> writes:

>> But in the API I described, each condition can only return nil, command,
>> or keymap. If our (org-metaright--try-hook) already executed the
>> command, it cannot return nil - that would make us search further, which
>> we do not want. It cannot return a command or keymap either - we do not
>> want to execute those. Maybe, we can treat non-keymap, non-command
>> return value as "stop here, we are done". That's an option, yes.
>
> I'd let the condition return a dummy command like `ignore`, just
> like we already do for similar purposes in keymaps.

AFAIU, that only works because `ignore' is an actual command that is
executed. If we just handle it as it is currently done, we will end up
executing *2 commands* - one triggered by `org-metaright--try-hook', and
also `ignore'. That will mess up `this-command' and others.

>>>> I proposed a very particular shape for those (to be implemented inside
>>>> user-level `keymap-dynamic-bind'), but we do not have to hard-code the=
m.
>>>
>>> I see.  I'm trying to look at it from the `C-h k` side where we want the
>>> API to be minimalist (the hard coding of sub-elements would be fine on
>>> the side of helper macros/functions to define&set such "dynamic keymaps=
").
>>
>> That's what I meant by
>>
>> 	  (dynamic-bind
>> 	    :filter   FILTER-FN      ; runs hook: hook =E2=86=92 command/keymap=
/nil
>> 	    :doc      DOC-FN         ; (dynamic-binding) =E2=86=92 string for C=
-h k
>> 	    :enumerate ENUM-FN       ; (dynamic-binding) =E2=86=92 list for whe=
re-is
>> 	    :default  COMMAND)       ; fallback when hook returns nil
>>
>> :doc is a custom function that will take care about rendering
>> documentation. The `keymap-dynamic-bind' will define a particular
>> standard shape of :doc function, but it can be anything.
>
> Ah, I see, so that would be the "equivalent" in your model to my
> `composite-command`.
>
> [ FWIW, my mental model of `partial-binding` and `composite-command` are
>   OClosures, for which we can already define dynamic docstrings via
>   `cl-defmethod`.  I'm thinking that we'd have two subtypes of
>   `partial-binding`: those that have a condition and a command (active
>   only when the (side-effect free) condition is satisfied), and those
>   that have only a command (which returns nil to indicate that it didn't
>   do anything and we should keep trying with others, i.e. for when the
>   condition can't be side-effect free).  ]

Yes, OClosures are a good idea here.
In contrast to the hook-inspired system I am proposing, OClosures can
hold a shared state between different conditions. That would, for
example, simplify tracking things like old-tick, old-point, and
old-indent in the `indent-for-tab-command' to be reused by multiple
conditions.

However, I am not quite sure about how to deal with docstrings in OClosures,
especially with my idea with indicating the matching condition as in

		    =E2=97=8B True if point is at a block.        =E2=86=92 org-indent-bl=
ock
		    =E2=97=8F At a heading.                       =E2=86=92 org-do-demote

I imagine that we will need to have a special way to call OClosure
holding the dynamic binding that will avoid side effects.

I am also not sure about facilities to manipulate the proposed
OClosures. Could you give some examples that are less trivial than just
chaining simple conditions?

>>> OTOH, it's fine *if* `lookup-key` doesn't evaluate the conditions and
>>> just returns the whole dynamic keymap.
>> Sure.
>
> But that brings us back to whether the conditions are evaluated during
> keymap lookup or afterwards.

>> I think https://elpa.nongnu.org/nongnu/casual.html as a whole is a
>> counterexample to your intuition. It defines whole keymaps depending on
>> cursor location. For example, see
>> https://elpa.nongnu.org/nongnu/doc/casual.html#Dired-Usage
>>
>> Another example is context menus :)
>
> But these are "all keymaps" aren't they?

Hmm. Yeah. You are right. I need a better example :)
Here is a better example:

In Org mode, C-c C-c can sometimes invoke an Org menu, and sometimes
just calls a command.

On headings, with org-use-fast-tag-selection set to t, Org mode uses an
equivalent of repeat map to toggle tags.
On source blocks, C-c C-c simply runs the code block.

More generally, limiting the dynamic keymap to only return keymaps or
only commands /feels/ like artificial limitation.

>> One important thing in this code that we did not discuss yet is prefix
>> argument. It will need to be passed to the conditions.
>
> Yup.  That's easy if we evaluate the conditions when we run the command
> rather than when we lookup the keymap.

It is easy either way. Just via current-prefix-arg. We just need to make
sure that it is available within conditions.

--=20
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>