This chapter describes an important new feature that debuted in Leo 4.5 b2: @shadow trees. These trees combine the benefits of @auto, @file and @nosent trees:
@shadow trees are often useful for studying or editing source files from projects that don’t use Leo. In such situations, it is convenient to import the @shadow tree from the (public) sources. As discussed below, Leo can import @shadow trees automatically, using the same algorithms used by @auto trees.
The crucial ideas and algorithms underlying @shadow trees are the invention of Bernhard Mulder.
Contents
Using @shadow trees is the best choice when you want to have the full power of Leo’s outlines, but wish to retain the source files in their original format, without Leo sentinels (markup) in comments in the source file.
Leo’s @file trees create external files containing comments called sentinels. These sentinel lines allow Leo to recreate the outlines structure of @file trees. Alas, many people and organizations find these added sentinel lines unacceptable. @nosent nodes create external files without sentinels, but at a cost: Leo can not update @nosent trees when the corresponding external file is changed outside of Leo.
@shadow trees provide a way around this dilemma. When Leo saves an @shadow tree, it saves two copies of the tree: a public file without sentinels, and a private file containing sentinels. Using Bernhard Mulder’s brilliant update algorithm, Leo is able to update @shadow trees in the Leo outline based solely on changes to public files.
Leo writes private files to a subfolder of the folder containing the public file: by default this folder is called .leo_shadow. You can change the name of this folder using the @string shadow_subdir setting. Note that private files need not be known to source code control systems such as bzr or cvs.
That’s almost all there is to it. The following sections discuss important details:
The first step in creating an @shadow tree is to create a node whose headline is @shadow <filename>.
Thus, you can create an @shadow node and save your outline, regardless of whether the original file exists. The next time Leo reads the @shadow node, Leo will create the entire @shadow tree using the same logic as for @auto trees. You can cause Leo to read the @shadow node in two ways: 1) by closing and reloading the Leo outline or 2) by selecting the @shadow node and executing the File:Read/Write:Read @shadow Node command.
Important: Leo imports the private file into the @shadow tree only if
Thus, Leo will import code into each @shadow node at most once. After the first import, updates are made using the update algorithm.
Note: just as for @auto, Leo will never read (import) or write an @shadow tree if the @shadow node is under the influence of an @ignore directive.
Suppose our @shadow tree is @shadow a.py. When Leo writes this tree it creates a public file, a.py, and a private file, .leo_shadow/xa.p (or just xa.p for short). Public files might can committed to a source code control system such as cvs or bzr. Private files should not be known to cvs or bzr.
Now suppose a.py has been changed outside of Leo, say as the result of a bzr merge. The corresponding private file, xa.p, will not have been changed. (Private files should never change outside of Leo.
When Leo reads the new (and possibly updated) public file it does the following:
Important: The update algorithm never changes sentinels. This means that the update algorithm never inserts or deletes nodes. The user is responsible for creating nodes to hold new lines, or for deleting nodes that become empty as the result of deleting lines.
Step 3 is the clever part. To see all the details of how the algorithm works, please study the x.propagate_changed_lines method in leoShadow.py. This code is heavily commented.
There are several boundary cases that the update algorithm can not resolve. For example, if a line is inserted at the boundary between nodes, the updated algorithm can not determine whether the line should be inserted at the end of one node of the start of the next node.
Happily, the inability of the update algorithm to distinguish between these two cases does not matter, for three very important reasons:
Understanding these three reasons finally convinced me that @shadow could be made to work reliably.