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

From: Stefan Monnier <[email-masked]>
Date: Sun, 02 Mar 2025 13:14:38 -0500

[...]
>> 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".

        Stefan