Subject: bug#76669: read_key_sequence discards events without attempting remapping

From: Daniel Colascione <[email-masked]>
Date: Sun, 02 Mar 2025 20:24:12 -0500

On March 2, 2025 1:14:38 PM EST, Stefan Monnier <[email-masked]> wrote:
>[...]
>>> FWIW, I have several times wished the `function-key-map`-style "remap if
>>> there's no binding" was applied repeatedly.
>[...]
>> No --- I meant we'd have the remapping code *do* the dropping.
>> Apply the remapping in a loop until we reach a fixed point.
>
>That's what I meant by "applied repeatedly", yes.
>`function-key-map` is *defined* as not being applied again after itself,
>so we'd need to introduce a new keymap (or equivalent).
>
>> Remapping phases:
>>
>> 1. [s-down-mouse-1 s-mouse-1]
>> 2. [down-mouse-1 mouse-1]
>> 3. [mouse-1]
>
>[ Of course, there are other remapping steps and orders possible.  🙁 ]
>
>>> In my wildest dreams, `read_key_sequence` has an `input-fallback-map`
>>> which is applied repeatedly, and keymaps can be functions so that they
>>> can do things like remap anything that looks like `FOO-mouse-1` to
>>> `FOO-mouse-2` (without having to list the hundreds of possible FOO) or
>>> remap anything that looks like `down-FOO` to `FOO`.
>>> Then `read_key_sequence` doesn't treat shift and `down` specially,
>>> instead that can be moved out to `input-fallback-map`.
>> Keymap entries can already be functions, and I think the function entry
>> would work with the fallback finding t too.
>
>Indeed, we have things which cover various parts (there are also those
>`:filter` functions in `menu-item`s), but not well enough to be usable
>in practice.
>
>> There's no good way to compose such functions, though --- not without
>> just clobbering them with add-function and such.  Child keymap
>> fallback entries will shadow those of the parents.
>
>Maybe we only need this flexibility for key remapping and not for normal
>keymaps, so we could have a "sequence of remapping thingies" where each
>thingy can be a keymap or a function so we don't add any special
>functionality to keymaps?
>
>>> But, yeah, that still leaves open the question of whether we should drop
>>> `shift` before we drop `down` or the reverse, and whether the
>>> `FOO-mouse-1` to `FOO-mouse-2` remapping should happen before or after
>>> dropping `down`.  The only "principled" way to solve this problem,
>>> AFAICT, would be to consider those remapping and returning all the
>>> possibilities (as a set of possible keysequences) and then when we do
>>> the corresponding key lookup, we'd have to decide what to do if there's
>>> more than one binding for those keysequences.
>>
>> If we're going to fix this, we might as well move read-key-sequence to
>> Lisp, where it's easier to express the right algorithm.
>
>That could be nice, yes.  Tho some of the complexity is not just in the
>fact that it's written in C but in the details of the semantics
>(especially the various remapping keymaps and their interactions, as
>well as their interaction with the decision of when exactly to stop
>waiting for more input).
>
>>   (cl-loop
>>     with want-lookahead = nil
>>     for keymap in all-keymaps
>>     for binding = (keymap-try-get-binding keymap candidate-keys)
>>     if (eq binding :want-lookahead)
>>     do (setf want-lookahead :want-lookahead)
>>     else if binding return binding
>>     finally return want-lookahead))
>
>I think this can be replaced with just
>
>    (lookup-key all-keymaps candidate-keys)
>
>where `integerp` tells us if we "want-lookahead".

lookup-key wouldn't know to recurse after remapping though.

We'd also have to do something about terminal/keyboard switching and input methods.