On May 10, 10:11 am, "Edward K. Ream" <[email protected]> wrote:
> In this thread I shall attempt to explain everything my successor will
> need know about Leo in order to carry on after I am gone.


Encapsulation of complexity.

Leo's design is driven by "encapsulation of complexity."  This is
*not* a direct result of using modules and objects.  On the contrary,
it is the most import factor in designing Leo's modules and objects.

The essence of this kind of encapsulation are the following design
principle:

   ** Modules and classes should hide internal details and complexity
as much as possible. **

Leo follows this principle everywhere, which is why (at the design
level) Leo is a successful software project.

This principle has many implications, but its major effect is on the
public api's presented by objects and modules.  You could say that
this principle makes the following demands on the api:

- The api should not depend on any internal data structures.

- The api should not expose any other kind of internal complexity.

- The complexity of a module or class should not in any other way
"bleed" into the rest of Leo.

- The functions of the api should be callable in any order.
(Initialization is an obvious exception to this rule, which is what
makes initialization so tricky.)

I can say that these principles are a spectacular success, for the
following reasons:

- Leo's modules have remained stable for 15+ years.

- I can quickly, easily and reliably change almost any part of Leo's
code without those changes having unintended consequences.

- When fixing bugs or adding features, I can instantly identify the
code that will have to change without remember *any* details of the
code.

- I can make major modifications to complex classes with little or no
changes to the code that uses those classes.

I can not overstate how important these principles are.  For example,
I *do* remember that some parts of the code are more complex than
others, but that is (no kidding) all I remember.  When I go to change
complex code, I must always "relearn" the code.  But this relearning
process is easy because it is almost invariably a *local* project.

Many parts of Leo are indeed quite complex.  That complexity is often
an inherent part of the work to be done.  But the encapsulation of the
complexity means that complexity in the various parts of Leo *do not
matter*.  That is, the internal complexity of a module does not in any
way complicate the rest of Leo.


Where the bodies are buried

What I remember most about the code (again, I'm not kidding) are the
places where:

a) the code is unavoidably complex or

b) the encapsulation of complexity breaks down for one reason or
another.

I think of this as "knowing where the bodies are buried."  An
essential part of these documents will be explaining why some things
are, and likely must be, complex.  Here is a brief overview:

- Initialization: as I said earlier, initialization violates the rule
that code should be called in any order.  For most modules and
objects, it would be easier to init the object if all other modules
and objects were already inited.  Obviously, some modules and objects
must be inited before others.  As a workaround, classes often contain
a finishCreate method that finishes the initing of an object at a
later time.  c.finishCreate contains calls to many other finishCreate
methods.  Order matters greatly here, and I see no reasonable
alternative to the code as it is.

- leoTree.select: The present Python version of Leo is the fourth or
fifth version of Leo.  In every version, the code that switches
between nodes is exceptionally tricky.  Once again, the precise order
of code matters greatly.  This can't be helped--we are in the midst of
changing the meaning of the current position and the current node.

- colorizing: The internal colorizing code must be complex because of
severe performance constraints.  The api is simple, but great care
must be taken to call the colorizer entry points when needed, and
*only* when needed.  Colorizing in Leo is *much* harder than
colorizing in flat editors because the text to be colorized keeps
changing.  For most editors, the setup code for colorizing gets
executed only when loading a file for the first time.  In Leo, it
happens every time the current node changes.

- key handling: This code is complex because it has extremely
ambitious goals, namely to allow the user to use *any* kind of key
bindings, be they vim-like or emacs-like or any other.  To my
knowledge, no other editor allows Leo's flexibility.  And more is
coming.  Key handling is indeed complex, but I have very little
trouble understanding it because all the complexity is encapsulated in
k.masterKeyHandler and its helpers.  When I have to relearn what is
happening, I simply set trace = True in these methods and see what
happens.

- Qt and Tk tree redraw.  This code is complex because it can be
driven either from the user (typing or mouse clicks) or from Leo's
core (commands, including @button/@command commands).  As a result, a
complex series of interlocks are required to ensure that asynchronous
user inputs do not interrupt the drawing process.  There are other
complexities as well, which I intend to discuss thoroughly.

This complexity is, imo, unavoidable. We could substantially simplify
this code only by eliminating Leo's minibuffer and all the commands
(including @button/@command commands) that can be called from the
minibuffer.

- The api for tree/body redraw.  I think of this is a separate issue
from the internals of redrawing just discussed.  The api exposed to
Leo's core is more complicated than one might naively expect.  This is
an important optimization that is fully justified by the performance
gains.  Again, I'll discuss the rationale in detail in later posts.
I'll probably discuss old schemes as well.

Summary

Leo is designed so complexities in one part of the code do not bleed
into the rest of the code.  This is the fundamental reason why Leo can
keep growing without "feeling" more complex. When fixing bugs or
adding features I almost never need to concern myself with more than
just pieces of code.  Conversely, Leo's design allows me to make huge
changes to complex modules without breaking the rest of Leo.  This is
the reason that I can contemplate adding support for vim bindings:
changes to leoKeys.py won't affect the rest of Leo.

Yes, there are tricky parts to Leo's code.  Most likely, they can not
be simplified further.  These docs will discuss these parts in detail
so that future maintainers will be spared missteps.

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