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.
