The Leo Tutorial

Leo is a power tool for people who want to organize, study and work with data, especially complex data like computer programs, books, web sites and data bases. Superficially, Leo may look like other outlining programs, code folding editors or class browsers, but it most certainly is not.

People say Leo is a revolutionary tool, and that Leo is fun to use, even additive. There is a unique “Leo way” of managing data; the term Leonine describes how people treat data in “the world according to Leo”. Leo definitely takes a bit of work to understand. Leo’s users speak of an “Aha” moment, when they see how these pieces fit together: outline structure is significant everywhere. For a more detailed introduction to Leo, see Leo in a nutshell.

Leo is freely available in source or binary form for all major platforms. You may download Leo from http://sourceforge.net/projects/leo/files/Leo/ Leo is Open Software and may be freely distributed.

Leo’s home page contains additional documentation and links to other resources. For another introduction to Leo, open the file quickstart.leo in the leo/doc folder.

This tutorial introduces the reader to the basic concepts and features of Leo. It helps to have Leo running for hands-on experience, but all examples here are self-contained, so the tutorial can be read off-line as well. See Leo’s Installation Guide. for detailed installation instructions. If you have problems installing Leo, please ask for help on Leo’s forum.

This tutorial does not attempt to be comprehensive and cover every single feature of Leo, or even every commonly used feature. Instead, it introduces many of Leo’s most noteworthy features, and will give you a good idea of Leo’s flavor and style. After reading it, you will be able to use Leo in basic ways to create external files, organize data and run simple scripts. You will then be ready to learn more about Leo’s many advanced features.

The Glossary is also worth reading.

Leo’s main window

Let’s start looking at Leo in detail. We’ll start with what you see when you first open Leo, Leo’s main window. Leo’s main window, shown below, represents an entire project. As you can see, the main window contains three panes: the outline pane at the top left, the log pane at the top right, and the body pane at the bottom. The window also contains an icon area at the very top, a status area and a mini-buffer at the very bottom.

leoMainWindow

Outline pane

The outline pane shows your project as an outline. The outline contains all your project’s data. An outline consists of nodes. The icon box is a small icon directly to the left of the headline text. The border of the icon box is black if the node has been changed. Smaller icons within the icon box indicate the status of the node:

A small blue box:   the node has body text.
A red vertical bar: the node is marked.
A circular arrow:   the node is cloned.

If a node contains children, a smaller icon appears to the left of the icon box. This icon contains a ‘+’ or ‘-‘ symbol. Clicking this expansion box expands or contracts the node.

Node

Each outline node has two two parts, a headline and body text. The outline pane shows headlines. Selecting a headline selects the entire node; the node’s body text appears in the body pane. Leo uses standard terminology to describe the relationships of nodes in an outline. We speak of parent nodes, child nodes, ancestor nodes and descendant nodes.

Body pane

The body pane contains the body text of the node selected in the outline pane.

Log pane

The log pane contains informational messages from Leo or your scripts.

Icon area

Depending on what plugins are enabled, the icon area may contain buttons and other widgets that extend what Leo can do. The scripting plugin makes it easy to add buttons to the icon area.

Status area

The status area shows the line and column containing the body text’s cursor, and the UNL (Uniform Node Location), the path from the top of the outline to the selected node. This path will change as you change outline nodes.

Minibuffer

You can type command and search strings in the minibuffer. It works much like the Emacs mini-buffer. To enter a command, type <Alt-x> followed by the command name and then <return>. To type a search string, type <ctrl-f> followed by the search string and then <return>. For full details, see Using Leo’s Commands.

External files and @file nodes

Leo stores outline data on your file system in .leo files. The format of these files is XML. You don’t have to store all your data in .leo files: Leo allows you to store parts of your outline data external files, that is, other files on your file system.

@file nodes create external files. @file nodes have headlines starting with @file followed by a file name. Some examples:

@file leoNodes.py
@file ../../notes.text

The file name can be an absolute path or a relative path to the file that starts at Leo’s load directory, the directory containing the .leo file.

Leo reads and writes external files automatically when you open or save your Leo outline:

  • When you open an outline (.leo file) Leo reads all the external files created by the @file nodes in the outline. If you have changed an external file outside of Leo, Leo will update the corresponding @file tree to reflect those changes when Leo next opens the outline.
  • When you save your outline, Leo writes all dirty @file nodes. An @file is dirty if the node or any of its descendant nodes has changed. Important: When Leo writes an external file, Leo writes all the essential information in the @file tree to the external file, not to the .leo file. The only nodes that gets written to the .leo file are nodes that are not contained in any @file tree.

Creating external files from outlines

We come now to one of Leo’s most important and unusual features. When Leo writes an external file, it does so in a flexible manner, directed by outline-based markup. This markup tells Leo exactly how to create the external file from an @file node.

The obvious way to write an external file would be to write the @file node itself followed by all the descendant nodes in outline order (the order in which nodes appear in the outline). But Leo does not write external files exactly this way.

Yes, Leo does indeed start by writing the @file node itself. But Leo writes the @file node’s descendants only when it sees one of three kinds of Leo markup: section references, the @others directive and the @all directive. We’ll discuss these three kinds of markup in the next section.

Section references and the @others and @all directives tell Leo to write the expansion of one or more descendant nodes to the external file. Programmers will recognize this process as akin to macro expansion. The following sections will explain this process in detail.

Section references

A section reference is a line of body text of the form:

<< a section name >>

Here, “a section name” can be any descriptive text not containing “>>”. When Leo encounters a section reference, Leo searches all the descendants of the node containing the reference looking for a node whose headline matches the section reference. That is, Leo looks for a descendant node whose headline starts with:

<< a section name >>

We call such nodes named nodes. Leo doesn’t require an exact match. Leo ignores whitespace and the case of letters when comparing headlines to section reference. Also, Leo ignores anything that may follow the section name in a named node. For example, the following headline will match the section reference above:

<< A Section Name >> (to do)

If Leo does find a match, Leo replaces the section reference (“<< a section name>>”) by the expansion of the body text of the matched node. That is, Leo replaces the section reference by the body text of the matched node, but Leo expands all markup in the matched node before making the replacement. The entire expansion of the matched node replaces the original section reference. Programmers will recognize this process as recursive macro expansion.

We have just discussed what happens if Leo does find a descendant named node that matches the section reference. If no such match is found the section reference is said to be undefined and Leo does not write any data to the external file. This is not a serious error: Leo will will save the erroneous @<file> tree in the .leo file instead of the external file. No information is lost. By the way, Leo’s syntax coloring will indicate undefined section reference by underlining the section name.

Important: the indentation of section references matters. When expanding a section reference, Leo indents every line of the expansion by the leading whitespace that occurs before the section reference. Note also that you can’t write something after a section reference and expect it to end up on the same line after expansion–Leo always writes a newline after the expansion.

The @others directive

The @others directive is the second (and most common) way of including descendant nodes in an external files. When Leo encounters the @others directive it replaces the @others directive by the expansion of all unnamed descendant nodes. As with section references, Leo replaces all markup in the descendant nodes, and the entire expansion replaces the @others directive.

In short, section references write named nodes; @others directives write all unnamed nodes. By the way, no node may contain more than one @others directive because there would be no way to “apportion” descendant nodes to more than one @others directive. However, nodes may contain as many section references as you like.

As with section references, the indentation of the @others directive matters. This allows Leo to handle Python source code properly. For example, the following is a common way of representing a Python class:

class myClass:
    '''a docstring'''
    @others

When Leo writes this node to an external file, Leo will write the first two lines to the external file, with the indentation in effect for the node. Leo will then write all descendant nodes to the external files, with additional indentation equal to the leading whitespace appearing before the @others directive.

The @all directive

The @all directive is the third, simplest (and least common) way of including descendant nodes. This directive causes Leo to write all descendant nodes in outline order, regardless of whether they are named or not. Furthermore, the @all directive does not expand any markup in descendant nodes. This results in Leo writing the external file in the “obvious” way. That is, Leo writes all descendant nodes in outline order.

Use the all directive if your external file contains unrelated nodes. For example, I use an external file to store programming notes. These notes typically contain snippets of programming source code, but there is no real relationships between the snippets–the file is simply a grab bag of information. The @all directive is designed for this situation.

Choosing between @others and sections

Newcomers to Leo frequently ask when to use the @others directive and when to use sections. It is good style to use section references only when the order of text within a external file matters. For example, Python programmers put docstrings and imports at the start of files. So the body text of @file nodes typically look something like this:

<< docstring >>
\@language python
\@tabwidth -4
<< imports >>
@others

This ensures that the docstring is first in the file, followed by imports, followed by everything else. Note that the order in which functions are defined in a file, or methods defined within a class, typically does not matter. Thus, it is good style to define classes like this:

class myClass:
      << class attributes >>
      @others

It would be bad style to define a class like this:

class myClass:
      << class attributes >>
      << method 1 >>
      << method 2 >>
      ...

Not only does this over-specify the order in which methods are defined, but it requires lots of extra typing. Not only must you add a line for each method, but headlines must contain section names such as << method 1 >>, <<method 2>>, etc. When using @others it is good style simply to put the name of each method in the headline.

Organizing programs as outlines

A few more words about style:

  • It is good style to put each class, function or method in its own node. This makes it easy to see the shape of your code.

  • It is good style to use organizer nodes to group related functions or methods. An organizer node has no content except maybe for comments. Like this:

    + myClass
        + birth and death
            + __init__
            etc.
        + getters
            etc.
        + setters
            etc.
        + misc methods
            etc.

    (In this notation, ‘+’ denotes a headline.) This organization is far superior to using hideous comments like:

    ###########
    # Getters #
    ###########
    
  • It is bad style to use @others in organizer nodes. There is no need to do so.

  • It is bad style to use @others when order does matter. The reason is that it is very easy to move nodes in a tree by mistake, say by alphabetizing nodes. One wants to make the meaning of a external file immune from such movements.

One last word about style. The world won’t end if you happen to use bad style by mistake: you just might cause a bit more work for yourself than was strictly necessary. Feel free to invent your own style of using Leo. Still, it would be wise to “know the rules before you break them.”

Clones & views

A clone is a node that appears in more than one place in a Leo outline. Clones are marked with a small red arrow in the icon box. All clones of a node are actually the same node, so any change to one clone affects all clones. For example, inserting, moving or deleting any child of a clone will change all other clones on the screen.

Please take a few moments to experiment with clones. Create a node whose headline is A. Clone node A using the Clone Node command in Leo’s Outline menu. Type some text into the body of either clone of A. The same text appears in the bodies of all other clones of A. Now insert a node, say B, as a child of any of the A nodes. All the A nodes now have a B child. See what happens if you clone B. See what happens if you insert, delete or move nodes that are children of A. Verify that when you delete the penultimate clone, the last clone becomes a regular node again.

Clones are much more than a cute feature. Clones allow multiple views of data to exist within a single outline. With Leo, there is no such thing as a single, “correct” view of data. You can have as many views of data as you like.

To create a new view of the data in your outline, just do the following:

  1. Create an ordinary node, that will represent the view. We call these nodes view nodes merely to indicate they represent a view.
  2. Clone all the nodes from the outline that you want the view to contain. Move these clones so they become children of the view node.
  3. (Optional) You can add regular nodes as children of the view node too.

For example, when I fix a bug in Leo, I create an ordinary node to represent the bug. This bug node is my view of all the data in Leo’s source code that relates to the bug. As I discover code related to the bug, I clone their nodes and move them under the bug node. I’ll also add ordinary nodes as children of the bug node. These nodes contain the original bug report, descriptions of how I fixed the bug, test data, or any other notes I might want to keep.

Once I have created the bug node, I concentrate only on that node and its children. I can examine the bug node and its children without having to jump around the outline. Everything I need is in one place. When I get around to actually fixing the bug I can do so by changing the clones. Again, I do not have to jump around the outline. It doesn’t matter how big or complex the entire outline is: I am only dealing with the bug node and its children. This extremely narrow focus makes it much easier to fix bugs.

By the way, I never have to remember to save external files. When I change any clone, Leo marks all instances of that clone throughout the entire outline as dirty (changed). When I save the Leo outline, Leo automatically writes all the external files that contain dirty nodes.

Views have an unlimited number of uses. Use them whenever you want to focus your attention on some smaller set of nodes. For example, I often create view nodes when studying other people’s code. The view node helps me concentrate on just the part of the code that interests me at the moment.

More about directives

Leo’s directives control such things as syntax coloring, line wrapping within the body pane and the width of tabs. Leo directives may appear in headlines or body text. Leo directives start with ‘@’, followed by the name of the directive.

Note: Leo handles Python decorators properly, providing they don’t conflict with Leo’s directives.

Here are some of Leo’s directives:

@language python
@tabwidth -4
@wrap
@nowrap
@color
@nocolor
@killcolor

Most directives must start with the ‘@’ in the leftmost column, but whitespace may appear before the '@others‘ and '@all‘ directives. As we have seen, such whitespace is significant.

Directives apply until overridden in a subtree. All of these directives apply to the node they are contained in, and also to the entire tree of descendant nodes, unless over-ridden by a similar directive in a descendant node. For example, the directive:

@language python

tells Leo to syntax color the node and all descendant nodes as Python code. However, some descendant node might contain:

@language rest

which tells Leo to color that node and all of its descendants as reStructureText. This principle applies to almost all of Leo’s directives: the directive is in effect throughout a tree, unless overridden in some subtree.

@color, @nocolor and @killcolor

These directives control how Leo colors body text. You can mix @nocolor and @color directives in a single node. This directives affect descendant nodes unless a node contains both @color and @color. Such ambiguous nodes do not affect the coloring of descendant nodes.

@first

This directive forces a lines to appear before the first sentinel of a external file. Here is a common way to start a Python file:

@first #! /usr/bin/env python @first # -- coding: utf-8 --

@language

Sets the language in effect for a tree. This affects how Leo colors body text. It also sets the comment delimiters used in external files. Leo supports dozens of languages. See Leo’s reference for a complete list. Here are a few:

@language python
@language c
@language rest # restructured text
@language plain # plain text: no syntax coloring.

@pagewidth <n>

Sets the page width used to format break doc:

@pagewidth 100

@path <path>

This directive is a convenience. Rather than specifying long paths in @file nodes, you can specify a path in an ancestor @path node. For example, suppose three nodes have the following headlines:

@path a
    @path b
        @file c/d.py

Because of the ancestor @path nodes, the @file node creates the file a/b/c/d.py

Within @path and @<file> paths, {{exp}} gets evaluated with the following symbols known: c, g, p, os and sys. For example:

@file {{os.path.abspath(os.curdir)}}/abc.py

refers to the file abc.py in (absolute path of) the current directory.

@tabwidth

Sets the width of tabs. Negative tab widths cause Leo to convert tabs to spaces and are highly recommended for Python programming.

@wrap and @nowrap.

These enable or disable line wrapping the Leo’s body pane.

Scripting, extending and customizing Leo

Leo is fully scriptable using the Python language. Leo can execute any body text as a Python script. To run the entire body text as a script, simply choose the node and execute the Execute Script command (Ctrl+B). If text is selected, the Execute Script command will run just the selected text as the script.

The Execute Script command preprocesses the script before executing it, in exactly the same way that Leo writes external files. Leo expands section references and processes @others directives before executing the script. This allows you to use all of Leo’s outlining capabilities to organize your scripts.

Your Python scripts can easily access data in an outline. Leo’s execute-script (Ctrl-B) command predefines three variables, c, g and p, that scripts can use to easily access any part of any Leo outline, and Leo’s own source code. For example, the following script will print all the headlines in an outline:

for p in c.all_positions():
    print ' '*p.level(),p.h

The example above is only the beginning of what scripts can do. See Scripting Leo with Python for a complete discussion of scripting Leo.

Plugins are Python modules that change how Leo works. Leo’s user have contributed dozens of plugins that have extended Leo’s capabilities in many new directions. The file leoPlugins.leo contains all plugins that are included in Leo distributions.

Plugins and other parts of Leo can get options from @settings trees. @settings trees allow plugins to get options without any further support from Leo’s core code. For a full discussion of @settings trees, see Customizing Leo.

Summary

Using Leo quickly becomes second nature:

  • You can use Leo like any ordinary outliner, as a filing cabinet, but Leo’s clones makes this filing cabinet much more flexible and useful than usual.
  • You create external files using @file trees. Within @file trees, you use section references and the @others directive to tell Leo how to write nodes to the external file. Directives such as @tabwidth and @language provide other information to Leo. Leo’s @file trees allow you to organize your scripts and programs with Leo’s outline structure.
  • You can execute Python scripts from any node in a Leo outline. Leo scripts have full, easy, access to all the information in the outline. Using scripts and plugins, you can easily add new features to Leo.

Further study

LeoPyRef.leo (in the core subdirectory of the leo folder) contains almost all of Leo’s source code. It provides hundreds of examples of everything discussed here. This file will repay close study. For full details on all aspects of Leo see LeoDocs.leo.

Previous topic

Installing Leo

Next topic

Using Outlines