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.
At last we move on to real documentation. Let's begin by looking at
leoEditCommands.py. I am starting with this module for several
reasons. First, it's neither overly complex nor completely simple.
Second, there are interesting but not overly complex interactions
between this module and others. Finally, Ville has legitimate
questions and reservations about this module that deserve to be
discussed sooner rather than later.
===== Purpose
The leoEditCommands.py module defines many, but not all, of Leo's
commands. As the name implies, this module defines commands that find
or modify body text. However, leoCommands.py also defines Leo
commands. The location of commands is a bit of an historical accident:
all of Leo's early commands resided in leoCommands.py or
leoFileCommands.py. This could be cleaned up. However, putting edit
commands in this file is not *completely* arbitrary. Indeed, the
commands in this module have access to (and many do in fact use), the
baseEditCommandsClass that will be discussed later.
So the “big picture” of this module is that it defines dozens of
commands. Many commands are completely self-contained. Others use a
common base class to hold state information used by a set of related
commands.
The following sections discuss "What I remember/What you must know".
I'll also discuss items that I didn't remember if they are
interesting: they show how you can discover details for yourself.
===== Shape
Let's look at the top level outline for @thin leoEditCommands.py.
BTW, I did not remember this organization explicitly, but that's the
beauty of Leo: it constantly reminds me of the big picture.
What we see, besides the usual << imports >> section, are:
- the baseEditCommandsClass. I didn't remember this, but you can see
that baseEditCommandsClass class defines a few methods shared by
several base classes. The ctor for each subclass of
baseEditCommandsClass inits the base class and then defines data used
only by the command of that subclass. A very simple pattern. Note:
as I write this I see that it would be better style to put the shared
methods in a subclass, which would then become the parent class of all
classes that need these shared methods. But the present code isn't
likely to change much, so this small lack of organization doesn't
really matter.
- Some top-level stuff used for initialization.
- A series of subclasses of baseEditCommandsClass, one for each kind
of edit command.
- Two other sets of classes, one for search classes and one for spell
classes. A quick look shows us that these classes are not (all)
subclasses of baseEditCommandsClass. These classes are complex. I'll
devote considerable attention to them in a later post.
- And (significantly) last, we see this section reference: << define
classesList >>. We know order is significant because otherwise I
would not have used a section reference. This is another advantage of
Leo, or rather noweb.
===== Defining commands.
The most important thing you need to know about this module is this::
** Every subclass of baseEditCommandsClass has a getPublicCommands
method **
This method returns a dict associating a command name with a method.
Usually the method is method of the class, but it doesn't have to be.
This getPublicCommands method is the most common way of defining
command names. k.registerCommand is another way.
Several subclasses are merely wrappers for code in other parts of
Leo's core. In other words, these classes exist simply to define
minibuffer command names for other code. These classes are:
- chapterCommandsClass: wrappers for code in leoChapters.py.
- keyHandlerCommandsClass: wrappers for code in leoKeys.py.
- leoCommandsClass: wrappers for commands in various other parts of
Leo's core.
This last class is noteworthy: besides creating the command name for
each command, it creates a proper entry in k.inverseCommandsDict for
that command. That way Leo's command-dispatch code works for code
that is not defined in leoEditCommands.py.
===== Initialization
Initialization is almost always tricky, but this module is unique. The
reference to << define classesList >> at the end of the code should be
all the reminder you need that initing this module is unusual.
Initing this module must be handled by top-level code. What I
remember about this code is:
- It is tricky, but it “just works”, provided that each subclass
defines a getPublicCommands method.
- The init code defines entries in several global dicts (defined in
leoKeys.py) that associate command names with code (and vice versa).
- pychecker (and possibly pylint) improperly complain about << define
classesList >>.
- I haven't found a way to eliminate << define classesList >> :-)
Let's see how I (and you) can "relearn" the details of this process.
This is important: if you know how to relearn the code, you can be
comfortable knowing only vague generalities like those above.
Looking at the top-level code I see that there are three methods,
createEditCommanders, finishCreateEditCommanders and
initAllEditCommanders. A quick check shows that each is driven by the
classesList table. Thus, to create a new subclass of
baseEditCommandsClass one would have to create a new entry in
classesList. Each of these methods helps create a “subcommander” (an
ivar of c, the Commander for the window.)
I don't remember how Leo uses these subcommanders, so let's find out.
Take any key of the classesList dict, say “abbrevCommands”. Searching
Leo's core for c,abbrevCommands, we see it is used in leoKeys.py.
Let's take another key, say “c.editCommands”. Searching Leo's core,
we see that it is used in a few places to find the code for particular
commands. We can assume this pattern hold throughout. It's no big
deal: you could forget all about it without harm, just as I have :-)
This illustrates the fact that you do *not* have to remember much
about Leo's code in order to understand it to any level of detail you
like. If you don't understand some ivar or other, just search for
it. I use the clone-find-all command to create a permanent record of
such searches.
===== A buried body: the edit widget.
Almost all commands start with something like the following code::
c = self.c ; w = self.editWidget(event)
if not w: return
This (supposedly) ensures that the command will not be executed unless
the “presently selected” widget is an appropriate kind of widget,
typically a text widget. This works *provided* that self.editWidget
actually returns a reasonable widget. That's not always so easy:
baseEditCommandsClass.editWidget switches the requested widget to the
body pane under some circumstances. That's a dubious idea, but what
else can the code do? Subclasses are free to override this hack. A
simple search shows that only the minibufferFind class does so.
Related code: The minibuffer code uses ivars created by
k.defineSingleLineCommands and k.defineMultiLineCommands to determine
which commands can be executed in various contexts. For example, only
the commands in k.singleLineCommandList may be executed on the text of
the minibuffer itself, or on the text of headlines.
k.multiLineCommandList doesn't seem to be used at in the sources. I
don't remember what it's for. No, it's not in the unit tests
either :-)
Edward
P.S. The following topics are coming:
- Text wrappers: not much of nuisance!
- A complexity: interface with the minibuffer.
- Class-by-class notes.
- (Maybe, if not covered above) Noteworthy and especially complex
code.
EKR
--
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.