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.
