On Wednesday, May 13, 2020 at 8:16:27 AM UTC-5, Edward K. Ream wrote:

> #1269 
<https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fleo-editor%2Fleo-editor%2Fissues%2F1269&sa=D&sntz=1&usg=AFQjCNE35O8zVtzjAu9p5JDfPmQG1F6arg>
 suggests 
a radical simplification of Leo's key handling code.

...
> The new code will compute...*binding dictionaries* *during startup*. 


Here I'll start making the plan more specific. Let's start with the 
following pseudo code, for a new KeyHandlerClass method:


def handle_binding(self, event):
    """Handle the given key event."""
    # Use per-state bindings if they exists.
    binding, func = event.binding, None
    if state:
        d = self.state_dict.get(self.state)
        func = d.get(binding) or d.get('default')
    # Otherwise, use per-window bindings.
    if not func:
        d = self.window_dict.get(event.window)
        func = d.get(binding) or d.get('default')
    return func(binding, char)

In this new world, a python dict will define every state. Keys will be *binding 
strings*, a canonical representation of the incoming keystroke. A binding 
string corresponds to what is now called a "stroke".


The new states will be fine-grained. Some examples:


1. vim mode will consist of many sub-states. This will eliminate the 
"dispatch" code in leoVim.py. For example, the letter 'a' will be handled 
one way in vim-insert state, another way in vim-colon state, and a third 
way in vim-normal state. In vim-normal state, the 'd' character will switch 
to vim-d1 mode, which will handle the expected character of the d command.


2. Alt-x will enter "full-command" state, as at present. In the new code, 
k.fullCommand will simply init the minibuffer, as in the "state 0" case. 
There will be no need for a switch on later characters. Instead, 
k.state_dict('full-command') will return an *inner state dict* for the 
"full-command" state, which will dispatch later events to helpers based on 
binding strings. 


State dicts don't simplify k.fullCommand all that much, but the general 
idea will be useful for user modes.


The big payoff lies in the code:


if not func:
    d = self.window_dict.get(event.window)
    func = d.get(binding) or d.get('default')

This code handles per-pane bindings, which at present takes a huge amount 
of work at run time. All that work will be done (once per outline) at 
startup time. k.handle_binding will eliminate k.masterKeyHandler and 
k.masterCommand. Many helpers will still be needed, but they will be much 
simpler because they will be much more specific.


In particular, k.handleDefaultChar, k.doUnboundPlainKey and especially 
c.editCommands.selfInsertCommand will disappear. They will be replaced by 
simple, special-purpose methods.


Special-case tests now contained in k.masterKeyHandler and its helpers will 
be refactored into various sub-dictionaries contained in k.window_dict. 
Yes, there will still be many special cases, but they will appear 
explicitly in dictionaries.


Finally, all dictionaries should have an entry for the Ctrl-g binding 
string. The code that computes the dictionaries will likely add that 
binding string automatically. Similarly, there is a weird special case for 
the demo.py plugin. Instead, this plugin will call a new (startup) method 
that will place the required bindings in all (or almost all) dicts.


*Testing*


I could not have contemplated such a major refactoring without a testing 
plan. I'll create two new decorators, one for new code and one for the old. 
The decorators will indicate (somehow!) to the test code that a specific 
key handler has been called. The new unit tests will then verify that 
corresponding decorators are called in the old and new code. I am fairly 
confident that such tests are feasible.


*Summary*


Leo will create binding dictionaries at startup, once per commander. 
Creating these dicts is itself a major project.


The new k.handle_binding method will replace horrendous code in 
k.masterKeyHandler 
and its many complex helpers. k.handle_binding method will dispatch key 
events to* specific* key handlers, based on the binding string of the 
event, the present key-handling state, and the presently selected window.


The new code will clarify the distinction between binding strings and the 
characters actually typed. Binding strings should serve only one purpose: 
to dispatch characters to commands. For most commands, the character 
actually typed should be ignored. Conversely, for unbound characters, the 
character itself is all that matters.


Special cases will always be necessary to maintain the illusion of 
simplicity. The new scheme will embed all special cases in binding 
dictionaries. 


This project requires full unit tests. New testing decorators will help 
unit tests verify that the old and new code is equivalent, that is, that 
the new code calls key handlers corresponding to the old key handlers.


Edward

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/leo-editor/89d2c10d-63fa-43ab-bc1e-9070a763a6e7%40googlegroups.com.

Reply via email to