Leo’s Cheat Sheet — Leo 6.8.4 documentation

# Leo’s Cheat Sheet

This Cheat Sheet contains a summary of many of Leo’s important features. Important: The easiest way to find documentation is to search LeoDocs.leo.

## Key bindings

### Selecting outline nodes

When focus is in the outline pane:

    Right-arrow (expand-and-go-right)
    Left-arrow (contract-or-go-left)
    Up-arrow (goto-prev-visible)
    Down-arrow (goto-next-visible)

Regardless of focus:

    Alt-Home (goto-first-visible-node)
    Alt-End (goto-last-visible-node)
    Alt-Right-arrow (expand-and-go-right)
    Alt-Left-arrow (contract-or-go-left)
    Alt-Up-arrow (goto-prev-visible)
    Alt-Down-arrow (goto-next-visible)

### Moving outline nodes

When focus is in the outline:

    Shift-Down-arrow (move-outline-down)
    Shift-Left-arrow (move-outline-left)
    Shift-Right-arrow (move-outline-right)
    Shift-Up-arrow (move-outline-up)

Regardless of focus:

    Alt-Shift-Down-arrow (move-outline-down)
    Alt-Shift-Left-arrow (move-outline-left)
    Alt-Shift-Right-arrow (move-outline-right)
    Alt-Shift-Up-arrow (move-outline-up)
    Ctrl-D (move-outline-down)
    Ctrl-L (move-outline-left)
    Ctrl-R (move-outline-right)
    Ctrl-U (move-outline-up)

### Moving the cursor

When focus is in any of Leo’s text panes (body pane, log pane, headlines):

> Key | Move Cursor
> Arrow keys | one character
> Ctrl-LeftArrow | back one word
> Ctrl-RightArrow | forward one word
> Home | beginning of line
> End | end of line
> Ctrl-Home | beginning of the body
> Ctrl-End | end of body
> PageDown | down one page
> PageUp | up one page

Adding the Shift key modifier to any of the keys above moves the cursor and extends the selected text.

## Executing minibuffer commands

Alt-X puts focus in the minibuffer. Ctrl-G escapes from the minibuffer.

Once there, you can use tab completion to reduce typing. For example, `<Alt-X>open<tab>` shows all commands that start with open.

Hit `<Enter>` to run a complete command.

`Ctrl-P` (`repeat-complex-command`) repeats the last command entered from the minibuffer.

Leo maintains a command history list of all minibuffer commands you have entered.

When focus is in the minibuffer, `UpArrow` shows the previous minibuffer command, and `DnArrow` show the commands before that.

The body text of an `@data history-list` setting node preloads commands into the command history list, ignoring lines starting with `#`. For example:

    run-pylint
    beautify-tree
    cff
    sort-lines
    # show-data
    check-clones
    expand-log-pane
    contract-log-pane

## Frequently used commands

For much more information, see the Commands Reference.

Copy/Paste (text):

    Ctrl-C (copy-text)
    Ctrl-K (kill)
    Ctrl-V (paste-text)
    Ctrl-X (cut-text)
    Ctrl-Y (yank)
    Alt-Y  (yank-pop)
    kill-...

Files:

    Ctrl-N (new)
    Ctrl-O (open-outline)
    Ctrl-S (save-file)
    Ctrl-Q (exit-leo)

Focus:

    Alt-T (focus-to-tree)
    Ctrl-T (toggle-active-pane)
    Ctrl-Tab (tab-cycle-next)

Help:

    Alt-0 (vr-toggle)
    F1 (help)
    F11 (help-for-command)
    F12 (help-for-python)
    show-bindings
    show-settings
    help-for-...

Find/Replace:

    Ctrl-F (search-with-present-options)
    Shift-Ctrl-R (replace-string)
    Ctrl-minus (replace-then-find)
    F3 (find-next)
    F2 (find-previous)

Minibuffer:

    Alt-X (full-command)
    Ctrl-G (keyboard-quit)
    Tab (not a command, completes typing)

Nodes:

    Ctrl-I or Insert (insert-node)
    Ctrl-H (edit-headline)
    <Return> (when editing a headline) (end-edit-headline)
    Ctrl-Shift-C (copy-node)
    Ctrl-Shift-X (cut-node)
    Ctrl-Shift-V (paste-node)
    Ctrl-{ (promote)
    Ctrl-} (demote)
    Ctrl-M (mark)

Undo:

    Ctrl-Z (undo)
    Ctrl-Shift-Z (redo)

## Gathering find commands

The clone find commands, cfa and cff, move clones of all nodes matching the search pattern under a single organizer node, created as the last top-level node. Flattened searches put all nodes as direct children of the organizer node:

    cfa     clone-find-all
    cff     clone-find-all-flattened

The clone-marked commands move clones of all marked nodes under an organizer node. Especially useful for gathering nodes by hand:

    cfam    clone-find-marked
    cffm    clone-find-flattened-marked

## Leo directives

Directives starting with ‘@ in the leftmost column

See the Directives reference for full details:

    @                       # starts doc part
    @c                      # ends doc part
    @color
    @doc                    # starts doc part
    @killcolor
    @nocolor
    @language python
    @language c
    @language rest          # restructured text
    @language plain         # plain text: no syntax coloring.
    @lineending lineending
    @nosearch               # suppress searching for cff & cfa commands.
    @pagewidth 100
    @tabwidth -4            # use spaces
    @tabwidth 8             # use tabs
    @nowrap
    @wrap

Leading whitespace is allowed (and significant) for:

    @all
    @others

## Settings

For documentation see node: “About this file” in leoSettings.leo:

    Command history:    @history-list
    Key bindings:       @shortcuts
    Visual settings:    @data qt-gui-plugin-style-sheet
    Enabling plugins:   @enabled-plugins

To disable a binding for a key, bind it to do-nothing:

    do-nothing = Insert

This overrides the following default binding in leoSettings.leo:

    insert-node = Insert

## Node types

Supported by Leo’s core:

    @chapter
    @rst, @rst-no-head, @rst-ignore, @rst-ignore-tree
    @settings
    @url

Within @settings trees:

    @bool, @buttons, @color, @commands
    @directory, @encoding
    @enabled-plugins, @float, @font,
    @history-list, @int
    @menus, @menu, @menuat, @item
    @openwith, @path, @shortcuts, @string

Supported by plugins:

    bookmarks.py:       @bookmarks, @bookmark
    at_folder.py:       @folder
    at_produce.py:      @produce
    at_view.py:         @clip, @strip, @view
    expfolder.py:       @expfolder
    mod_scripting.py:   @button, @command, @script
    viewrendered.py:    @graphics-script, @image
                        @html, @movie, @svg

## Notable Plugins

See the docstring of these plugins for more details:

    bookmarks.py        Manages URL's used as bookmarks.
    contextmenu.py      Creates context menus for headlines.
    demo.py             Creates demos and slides.
    mod_scripting.py    Supports @button and @command nodes.
    quicksearch.py      Adds Nav tab for searching.
    todo.py             To-do lists and simple project management.
    viewrendered.py     Creates a rendering pane.
                        Automatically loaded by Leo's help commands.
                        Supports @graphics-script, @image, @html,
                        @movie and @svg nodes.
    viewrendered3.py    An enhanced version of viewrendered3.py.
    vim.py & xemacs.py  Interface with vim and xemacs.

## External files (@<file> nodes)

@<file> nodes create external files:

    @asis <filename>        write only, no sentinels, exact line endings
    @auto <filename>        recommended
    @clean <filename>       recommended
    @edit <filename>        @edit node contains entire file
    @file <filename>        recommended
    @nosent <filename>      write only, no sentinels

This table summarizes the differences between @<file> nodes:

                             Sections &   File data in
    @<file> kind  Sentinels?  @others?    .leo file?    Notes
    ------------  ---------- -----------  ------------  -----
    @asis          no          no          yes
    @auto          no          yes         no           1, 2
    @auto-xx       no          yes         no           1, 2
    @clean         no          yes         yes
    @edit          no          no          no
    @file          yes         yes         no
    @nosent        no          yes         yes

@auto nodes read files using language-specific importers. By default, the file’s extension determines the importer:

    Extensions                  Importer
    ----------                  --------
    .c, .cc, .c++, .cpp,.cxx    C
    .cs', .c#'                  C Sharp
    .el                         Elisp
    .h, .h++                    C
    .html, .htm                 HTML
    .ini                        Config file
    .ipynb                      Jupyter notebook
    .java                       Java
    .js                         JavaScript
    .md                         Markdown
    .org                        Org Mode
    .otl                        Vim outline
    .pas                        Pascal
    .php                        PHP
    .py, .pyi, .pyw             Python
    .rest, .rst                 reStructuredText
    .ts                         TypeScript
    .xml                        XML

You can also specify importers explicitly as follows:

    @auto-xxx           Importer
    ---------           --------
    @auto-ctext         ctext
    @auto-markdown      markdown
    @auto-md            markdown
    @auto-org           org-mode
    @auto-org-mode      org-mode
    @auto-otl           vimoutline
    @auto-vim-outline   vimoutline
    @auto-rst           reStructuredText

Important: The importers/exporters for markdown, org-mode, reStructuredText and vimoutline files automatically generate section headings corresponding to Leo’s outline level. Body text of the top-level @auto node is ignored.

See the Directives reference for full details.

## Sections

Section names have the form:

    << any text, except double closing angle brackets >>

Section-definition nodes have headlines starting with a section name.

Leo performs expansions for all @<file> nodes except @asis.

Expansion of @all:

- Leo replaces @all by the unexpanded body text of all nodes.

Expansion of section names and @others:

- Leo replaces section names in body text by the expanded text of the corresponding section definition node.

- Leo replaces @others with the expanded text of all nodes that aren’t section-definition nodes.

## Scripting

This section lists the ivars (instance variables), properties, functions and methods most commonly used in Leo scripts.

Very important: use Alt-1 (toggle-autocompleter) and Alt-2 (toggle-calltips) to recreate these lists as you type.

### Pre-defined symbols

execute-script predefines:

    c: The commander of the present outline.
    g: The leo.core.leoGlobals module.
    p: The presently selected position, c.p.

### LeoApp class

Ivars:

    g.app                   A LeoApp instance.
    g.app.gui               A LeoGui instance.
    g.app.pluginsController A LeoPluginsController instance.
    g.app.*                 Leo's global variables.

### Commands class

Ivars:

    c.config                c's configuration object
    c.frame                 c's outer frame, a leoFrame instance.
    c.undoer                c's undo handler.
    c.user_dict             A temporary dict for use of scripts and plugins.

SubCommanders:

    # In leo/core...
    c.atFileCommands
    c.chapterController
    c.fileCommands
    c.findCommands
    c.importCommands
    c.keyHandler = c.k
    c.persistenceController
    c.printingController
    c.rstCommands
    c.shadowController
    c.tangleCommands
    c.testManager
    c.vimCommands

    # In leo/commands...
    c.abbrevCommands
    c.controlCommands
    c.convertCommands
    c.debugCommands
    c.editCommands
    c.editFileCommands
    c.gotoCommands
    c.helpCommands
    c.keyHandlerCommands
    c.killBufferCommands
    c.rectangleCommands
    c.spellCommands

Generators (New in Leo 5.5: All generators yield distinct positions):

    c.all_positions()
    c.all_unique_positions()

Most useful methods:

    c.isChanged()
    c.deletePositionsInList(aList, callback=None)
                            # safely deletes all the positions in aList.
    c.positionExists(p)
    c.redraw(p=None)        # Redraw the screen. Select p if given.
    c.save()                # Save the present outline.
    c.selectPosition()

Using Command names

When learning Leo’s codebase, it may be easier to refer to a command by its ‘command name’. You can therefore use c.doCommandByName(‘command-name’) instead of calling the proper method on the commander. This will return the same value as that method.

Leo Script Return Values

Commands created with @command and @button nodes can return values as well. Since top-level return statements aren’t allowed in scripts executed with exec, you can define a global variable named result, and Leo will return its value:

    global result
    result = 42

Official ivars of any leoFrame f:

    f.c                     is the frame’s commander.
    f.body                  is a leoBody instance.
    f.body.bodyCtl          is a leoQTextEditWidget instance.
    f.body.bodyCtrl.widget  is a LeoQTextBrowser(QTextBrowser) instance.
    f.log                   is a leoLog instance.
    f.tree                  is a leoQtTree instance.
    f.tree.treeWidget       is a LeoQTreeWidget (a QTreeWidget) instance.

Use autocompletion to explore these objects!

### Undoing commands

If you want to make a command undoable, you must create “before” and “after” snapshots of the parts of the outline that may change. Here are some examples. Leo’s source code contains many other examples.

#### Undoably changing body text

To undo a single change to body text:

    command = 'my-command-name'
    b = c.undoer.beforeChangeNodeContents(p, oldYScroll=ypos)
    # Change p's body text.
    c.undoer.afterChangeNodeContents(p,
        command=command, bunch=b, dirtyVnodeList=[])

#### Undoably changing multiple nodes

If your command changes multiple nodes, the pattern is:

    u, undoType = c.undoer, 'command-name'
    u.beforeChangeGroup(c.p, undoType)
    dirtyVnodeList = []
    changed = False
    # For each change, do something like the following:
    for p in to_be_changed_nodes:
        # Change p.
        dirtyVnodeList.append(p.v)
        u.afterChangeNodeContents(p, undoType, bunch)
        changed = True
    if changed:
        u.afterChangeGroup(c.p, undoType,
            reportFlag=False,
            dirtyVnodeList=dirtyVnodeList)

### VNode class

Ivars:

    v.b:    v's body text.
    v.gnx   v's gnx.
    v.h:    v's headline text.
    v.u:    v.unknownAttributes, a persistent Python dictionary.

v.u (uA’s or unknownAttributes or userAttributes) allow plugins or scripts to associate persistent data with vnodes. For details see the section about userAttributes in the Customizing Leo chapter.

Important: Generally speaking, vnode properties are fast, while the corresponding position properties are much slower. Nevertheless, scripts should usually use position properties rather than vnode properties because the position properties handle recoloring and other details. Scripts should use vnode properties only when making batch changes to vnodes.

### Position class

Properties

    p.b: same as p.v.b.  *Warning*: p.b = s is expensive.
    p.h: same as p.v.h.  *Warning*: p.h = s is expensive.
    p.u: same as p.v.u.

Generators (New in Leo 5.5: All generators yield distinct positions)

    p.children()
    p.parents()
    p.self_and_parents()
    p.self_and_siblings()
    p.following_siblings()
    p.subtree()
    p.self_and_subtree()

Getters These return new positions

    p.back()
    p.children()
    p.copy()
    p.firstChild()
    p.hasBack()
    p.hasChildren()
    p.hasNext()
    p.hasParent()
    p.hasThreadBack()
    p.hasThreadNext()
    p.isAncestorOf(p2)
    p.isAnyAtFileNode()
    p.isAt...Node()
    p.isCloned()
    p.isDirty()
    p.isExpanded()
    p.isMarked()
    p.isRoot()
    p.isVisible()
    p.lastChild()
    p.level()
    p.next()
    p.nodeAfterTree()
    p.nthChild()
    p.numberOfChildren()
    p.parent()
    p.parents()
    p.threadBack()
    p.threadNext()
    p.visBack()
    p.visNext()

Setters

    p.setDirty()  *Warning*: p.setDirty() is expensive.
    p.setMarked()

Operations on nodes

    p.clone()
    p.contract()
    p.doDelete(new_position)
    p.expand()
    p.insertAfter()
    p.insertAsNthChild(n)
    p.insertBefore()
    p.moveAfter(p2)
    p.moveToFirstChildOf(parent,n)
    p.moveToLastChildOf(parent,n)
    p.moveToNthChildOf(parent,n)
    p.moveToRoot(oldRoot=None)
        # oldRoot **must** be the old root position if it exists.

Moving positions

The following move positions themselves: they change the node to which a position refers. They do not change outline structure in any way! Use these when generators are not flexible enough:

    p.moveToBack()
    p.moveToFirstChild()
    p.moveToLastChild()
    p.moveToLastNode()
    p.moveToNext()
    p.moveToNodeAfterTree(p2)
    p.moveToNthChild(n)
    p.moveToParent()
    p.moveToThreadBack()
    p.moveToThreadNext()
    p.moveToVisBack(c)
    p.moveToVisNext(c)

### leo.core.leoGlobals module

For full details, see @file leoGlobals.py in LeoPyRef.leo.

g vars

    g.app
    g.app.gui
    g.app.windowlist
    g.unitTesting
    g.user_dict  # a temporary dict for use of scripts and plugins.

g decorator

    @g.command(command-name)

g functions (the most interesting: there are many more in leoGlobals.py)

    g.angleBrackets()
    g.app.commanders()
    g.app.gui.guiName()
    g.es(*args,**keys)
    g.es_print(*args,**keys)
    g.es_exception()
    g.getScript(c,p,
        useSelectedText=True,
        forcePythonSentinels=True,
        useSentinels=True)
    g.openWithFileName(fileName,old_c=None,gui=None)
    g.os_path_... # Wrappers for os.path methods.
    g.pdb(message='')
    g.toEncodedString(s,encoding='utf-8',reportErrors=False)
    g.toUnicode(s, encoding='utf-8',reportErrors=False)
    g.trace(*args,**keys)
    g.warning(*args,**keys)

### Performance

The Position methods `p.b`, `p.h`, and `p.setDirty()` are slow.

The VNode methods `p.v.b`, `p.v.b`, and `p.v.setDirty()` are fast.

However, it’s usually better to use the Position methods, despite their cost, because Position methods update Leo’s outline completely.

Use the VNode methods only for repetitive commands, like `cff`, `replace-all` and recursive import.

### Prompting for command arguments

#### Example 1: Prompt for one arg

    @g.command('i1')
    def i1_command(event):
        c = event.get('c')
        if not c: return

        def callback(args, c, event):
            g.trace(args)
            c.bodyWantsFocus()

        c.interactive(callback, event, prompts=['Prompt: '])

#### Example 2: Prompt for 2 args

    @g.command('i2')
    def i2_command(event):
        c = event.get('c')
        if not c: return

        def callback(args, c, event):
            g.trace(args)
            c.bodyWantsFocus()

        c.interactive(callback, event,
            prompts=['Find: ', ' Replace: '])

#### Example 3: Prompt for 3 args

    @g.command('i3')
    def i3_command(event):
        c = event.get('c')
        if not c: return

        def callback(args, c, event):
            g.trace(args)
            c.bodyWantsFocus()

        c.interactive(callback, event,
            prompts=['One: ', ' Two: ', ' Three: '])

### Naming conventions in Leo’s core

`LeoPyRef.leo` contains all of Leo’s core source code.

Leo’s code uses the following conventions throughout:

    c:  a commander.
    ch: a character.
    d:  a dialog or a dict.
    f:  an open file.
    fn: a file name.
    g:  the leoGlobals module.
    i, j, k: indices into a string.
    p:  a Position.
    s:  a string.
    t:  a text widget.
    u:  an undoer.
    w:  a gui widget.
    v:  a Vnode
    z:  a local temp.

In more limited contexts, the following conventions apply:

    si:     a g.ShortcutInfo object.
    ks:     a g.KeyStroke object
    stroke: a KeyStroke object.

    btw:    leoFrame.BaseTextWrapper
    stw:    leoFrame.StringTextWrapper

    bqtw:   qt_text.BaseQTextWrapper
    lqtb:   qt_text.LeoQTextBrowser
    qhlw:   qt_text.QHeadlineWrapper
    qmbw:   qt_text.QMinibufferWrapper
    qlew:   qt_text.QLineEditWrapper
    qsciw:  qt_text.QScintiallaWrapper
    qtew:   qt_text.QTextEditWrapper

Names defined in Leo’s core are unlikely to change, especially names used outside their defining module. This includes virtually everything in `leoGlobals.py`, and many names in `leoCommands.py` and other files.

#### Official ivars

The following ‘official’ ivars (instance vars) will always exist:

    c.frame                 The frame containing the log,body,tree, etc.
    c.frame.body            The body pane.
    c.frame.body.widget     The gui widget for the body pane.
    c.frame.body.wrapper    The high level interface for the body widget.
    c.frame.iconBar         The icon bar.
    c.frame.log             The log pane.
    c.frame.log.widget      The gui widget for the log pane.
    c.frame.log.wrapper     The high-level interface for the log pane.
    c.frame.tree            The tree pane.

The following were official ivars that no longer exist:

    c.frame.body.bodyCtrl   Use c.frame.body.wrapper instead.
    c.frame.log.logCtrl     Use c.frame.log.wrapper instead.

### Widgets and wrappers

A widget is an actual Qt widget. Leo’s core seldom accesses widgets directly. Instead, a wrapper class defines a standard api that hides the details of the underlying gui text widgets.

Leo’s core uses the wrapper api almost exclusively. That is, Leo’s core code treats wrappers as if they were only text widgets there are!

A back door exists for special cases. All wrapper classes define an official `widget` ivar, so core or plugin code can gain access to the real Qt widget using wrapper.widget. Searching for `wrapper.widget` will find all gui-dependent snippets of code in Leo’s core.

## Command-line options

    usage: launchLeo.py [options] file1, file2, ...

    options:
      -h, --help            show this help message and exit
      -b, --black-sentinels write black-compatible sentinel comments
      --diff                use Leo as an external git diff
      --fail-fast           stop unit tests after the first failure
      --fullscreen          start fullscreen
      --gui=GUI             specify gui: browser,console,curses,qt,text,null
      --listen-to-log       start log_listener.py on startup
      --maximized           start maximized
      --minimized           start minimized
      --no-plugins          disable all plugins
      --no-splash           disable the splash screen
      --quit                quit immediately after loading
      --save-session        always save session data when Leo closes
      --script=PATH         execute a script and then exit
      --script-window       execute script using default gui
      --select=ID           headline or gnx of node to select
      --silent              disable all log messages
      --theme=NAME          use the named theme file
      --trace=LIST          add one or more strings to g.app.debug

            A comma-separated list. Valid values are:
            abbrev, beauty, cache, coloring, drawing, events, focus, git, gnx,
            importers, keys, layouts, plugins, save, select, sections,
            shutdown, size, speed, startup, themes, undo, verbose, zoom.

      --trace-binding=KEY   trace commands bound to a key
      --trace-setting=NAME  trace where named setting is set
      --window-size=SIZE    initial window size: (height x width)
      --window-spot=SPOT    initial window position: (top x left)
      -v, --version         print version number and exit

## Learning to be a Leo developer

### Code academy

Leo’s Code Academy posts discuss how to do useful things in Leo. The following are distilled from online discussions about Leo’s scripting.

#### CA: uA’s

uA’s (user Attributes) associate arbitrary data with any vnode. uA’s are dictionaries of dictionaries–an outer dictionary and zero or more inner dictionaries. The outer dictionary associates plugin names (or Leo’s core) with inner dictionaries. The inner dictionaries carry the actual data.

The v.u or p.v properties get and set uA’s. You can think of p.u as a synonym for p.v.unknownAttributes on both sides of an assignment. For example:

    plugin_name = 'test_plugin'
    d = p.u.get(plugin_name,{})
    d ['n'] = 8
    p.u [plugin_name] = d

p.u is the outer dictionary. p.u.get.(plugin_name, {}) is the inner dictionary. The last line is all that is needed to update the outer dictionary!

It is easy to search for particular uA’s. The following script prints all the keys in the outer-level uA dictionaries:

    for p in c.all_unique_positions():
        if p.u:
            print(p.h, sorted(p.u.keys()))

This is a typical usage of Leo’s generators. Generators visit each position (or node) quickly. Even if you aren’t going to program much, you should be aware of how easy it is to get and set the data in each node. In fact, now would be a great time to read Leo’s Scripting Tutorial again :-) This will allow you to “dream bigger” with Leo.

The following script creates a list of all positions having an icon, that is, an outer uA dict with a ‘icon’ key:

    aList = [p.copy() for p in c.all_unique_positions() if 'icon' in p.u]
    print('\n'.join([p.h for p in aList]))

Important: If you don’t understand these lines, please study Python’s list comprehensions. They are incredibly useful. ‘n’.join(aList) is a great idiom to know. str.join is one of python’s most useful string methods. It converts between lists and strings.

#### CA: icons

This script inserts three icons in the current outline node. Running the script again will insert three more:

    table = (
        'edittrash.png',
        'connect_no.png',
        'error.png',
    )
    for icon in table:
        fn = g.os_path_finalize_join(g.app.loadDir,
            '..', 'Icons', 'Tango', '16x16', 'status', icon)
        if g.os_path_exists(fn):
            c.editCommands.insertIconFromFile(path=fn)

This deletes all icons of the node at position p:

    c.editCommands.deleteNodeIcons(p=p)

#### CA: using git

Using Leo’s latest sources from GitHub is highly recommended. Once git is installed, the following gets the latest Leo sources:

    git clone https://github.com/leo-editor/leo-editor.git

Thereafter, you can update Leo’s sources with:

    git pull

Git is great in tracking history and reverting unwanted changes. And it has many other benefits.

Using git is very similar to using bzr or hg or any other SCCS. To change Leo, you add files, you commit files, and you push files. That’s about it.

#### CA: finding nodes with c.cloneFindByPredicate

c.cloneFindByPredicate is a powerful new addition to Leo. Here is its docstring:

    Traverse the tree given using the generator, cloning all positions for
    which predicate(p) is True. Undoably move all clones to a new node, created
    as the last top-level node. Returns the newly-created node. Arguments:

    generator,      The generator used to traverse the tree.
    predicate,      A function of one argument p returning true if
                    p should be included.
    failMsg=None,   Message given if nothing found. Default is no message.
    flatten=False,  True: Move all node to be parents of the root node.
    iconPath=None,  Full path to icon to attach to all matches.
    redraw=True,    True: redraw the screen.
    undo_type=None, The undo/redo name shown in the Edit:Undo menu.
                    The default is 'clone-find-predicate'

For example, clone-find-all-marked command is essentially:

    @cmd('clone-find-all-marked')
    def cloneFindMarked(self, flatten):

        def isMarked(p):
            return p.isMarked()

        self.cloneFindByPredicate(
            generator = self.all_unique_positions,
            predicate = isMarked,
            failMsg = 'nothing found',
            flatten = flatten,
            undoType = 'clone-find-marked',
        )

The predicate could filter on an attribute or combination of attributes. For example, the predicate could return p has attributes A and B but not attribute C. This instantly gives Leo full database query capabilities. If we then hoist the resulting node we see all and only those nodes satisfying the query.

These following position methods make it easy to skip @ignore trees or @<file> trees containing @all:

    p.is_at_all()          True if p is an @<file> node containing an @all directive.
    p.in_at_all()          True if p is in an @<file> tree whose root contains @all.
    p.is_at_ignore()       True if p is an @ignore node
    p.in_at_ignore_tree()  True if p is in an @ignore tree.

For example, here is how to gather only those marked nodes that lie outside any @ignore tree:

    def isMarked(p):
        return p.isMarked() and not p.in_at_ignore_tree()

    c.cloneFindByPredicate(
        generator = self.all_unique_positions,
        predicate = isMarked,
        flatten = flatten,
        undoType = 'gather-marked',
    )

### Leo University

Leo University is a project devoted to help people become Leo developers.

Here is the main page. It contains links to individual lessons. https://github.com/leo-editor/leo-editor/issues/816

### Architecture

Leo uses a model/view/controller architecture.

- Controller: The Commands class and its helpers in leoCommands.py and leoEditCommands.py.

- Model: The VNode and Position classes in leoNodes.py.

- View: The gui-independent base classes are in the node “Gui Base Classes”. The Qt-Specific subclasses are in the node “Qt gui”.

Important: The general organization of these classes have changed hardly at all in Leo’s 20+ year history. The reason is that what each class does is fairly obvious. How the gets the job done may have changed drastically, but that’s an internal implementation detail of the class itself. This is the crucial design principle that allows Leo’s code to remain stable. Classes do not know or meddle in the internal details of other classes. As a result, nobody, including EKR, needs to remember internal details.

## Clickable links

Leo syntax colors clickable links in the body pane. For example:

Leo’s home page: https://leo-editor.github.io/leo-editor/

The status area shows the UNL (Universal Node Locator) for each node.

Control-clicking a UNL will take you to its target node, even if the target is in another Leo file! gnx-based UNLs won’t break even if you move or rename the target node.

Try these examples!

The “Key bindings” node in this file: unl:gnx://CheatSheet.leo#ekr.20131019184243.16685

The “6.7.5 release notes” node in LeoDocs.leo. unl:gnx://LeoDocs.leo#ekr.20230703101804.1