**Note**: feel free to ignore this post if you like.  Much of this
consists of notes to myself about a horribly complex bug, but here and
there I will say a few words about how to use the Stupendous Aha to do
complex tasks.

The bug 879331: "Redefining a key binding breaks menu items with same
binding" is easily the most complex bug I've ever tried to fix.  For
complexity it rivals the year-long project to fully integrate
LeoUser's minibuffer prototype into Leo's core.

Recall that the so-called "Stupendous Aha" merely says that @test
nodes are an excellent way to organize development.  The work flow
becomes something like, "edit, alt-4, edit, alt-4,..."

This bug is far too complex to attempt a direct fix.  Typically, I
would create a new bzr branch for this bug, and I may in fact do so.
But creating a new branch doesn't actually help fix the bug in any
way: it merely adds a level of protection for the trunk.

So the question is, how can I avoid "eating the elephant in a single
bite"?

A few days ago, I began the project by creating: @test new binding
scheme.

Yesterday, I realized I had to start *somewhere*, so I wrote a few
methods that would dump Leo's bindings-related dictionaries so I could
see them.  The data structures are so complex that I felt I had to
make them more "visible" so that I could start to think concretely
about them.

The top-level of the @test node is (slightly edited)::

    # Run this test with Alt-4: run-marked-unit-tests-externally.
    << about key dicts >>
    @others

    aList = c.config.getMenusList()
    printMenusList(aList)
    printInverseBindingDict(c)
    printMasterBindingsDict(c)

The << about key dicts >> section is a clone of documentation in
leoKeys.py, cloned for handy reference.

Even this trivial start help me to start thinking.  Here are some
thoughts:

1. The essence of the situation is that overriding a *specific*
binding is an extremely complex operation: all *previous* bindings for
that *specific* keystroke (stroke, in Leo's code) should be cleared.

2. Alas, there are *several* ways that keystrokes can be overridden:
some are implicit, some are explicit.

For example, during startup, the bindings for myLeoSettings.leo
*implicitly* override bindings in leoSettings.leo, and the binding for
x.leo override bindings in both myLeoSettings.leo and
leoSettings.leo.  These implicit bindings in turn use Leo's
configuration (g.app.config and c.config) data structures.  The
details are mind-boggling complex.  Note that the config data
structures do *not* now change as the result of overrides: what
changes is the order in which a tree of data structures get searched.

@button nodes, @command nodes and user scripts can explicitly override
bindings.  In fact, overriding key bindings can happen at *any* time.
As I write this, I see that it *might* be possible (and desirable??)
to prohibit key-binding overrides after a commander has been inited.
In effect, the present code improperly assumes this is so.

3. Allowing the user to override bindings at any time would have grave
consequences for Leo's menu code.

At present, Leo constructs menus very late in the init process for
each commander.  This is, you could say, the simplest thing that could
possibly work: Leo constructs menus only once, during startup (and
during the init of each loaded commander).

If we allow shortcuts to be overridden at any time, then Leo must
create each menu **at the time the user invokes the menu**. This would
be the latest of late bindings.

To repeat point 1:  overriding a specific binding is an extremely
complex operation: all *previous* bindings for that *specific*
keystroke (stroke, in Leo's code) should be cleared.

As we can now see, to fix this bug we must at least do two or three
things:

A.  We must correct the bugs in the implicit binding code so that, for
example, bindings in x.leo properly clear corresponding bindings in
myLeoSettings.leo and leoSettings.leo.  This is no easy task: we
probably can *not* change the (complex) dictionaries in leoConfig.py,
but we must arrange things to work *as if* these dictionaries had been
cleared.  It's a puzzle.

B. We must find some way to handle explicit bindings overrides in (at
least) @button and @command nodes.

C. And if we decide that we *do* want to allow after-init overrides,
we must create menus dynamically when the user invokes them.  This
will result in big changes to k.createMenuEntries, one of Leo's most
important and most complex methods.  It will also require an entirely
new infrastructure for creating menus and enabling and disabling menu
items.

Aside: The code that enables and disables menu entries is suspiciously
static.  For example, I suspect it may break if people actually
changed Leo's "Outline" menu.

Summary and strategy
==================

Your head should be swimming at this point.  Mine certainly is.  The
question is, how to take the next step?

I think the Stupendous Aha is crucial here.  We can think of the @test
node as a general-purpose framework for design, implementation and
testing.  The main idea is really quite simple:  we try out all our
ideas in the context of the @test node, leaving Leo's core safely
untouched.

Another way of visualizing the situation is to say that the @test node
provides a safe framework for running as many experiments as we like,
and for writing *provisional* code that attempts to fix various
aspects of the bug.

To work safely, we merely have to ensure that:

A.  We don't use clones in the @test tree,

B. We don't change any clones used as reference.  But this is easy to
do: just check bzr status.

Later, after everything finally appears to work, it may be time to
create a branch for final testing.  That is, we will migrate new code
from the @test node into a new branch, and see what happens.

Edward

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/leo-editor?hl=en.

Reply via email to