Customizing Leo¶
This chapter discusses how to customize Leo using the plugins and other means. See Using settings for a description of how to change Leo’s settings.
Using settings¶
Leo stores options in @settings trees, outlines whose headline is @settings
. When opening a .leo file, Leo looks for @settings
trees not only in the outline being opened but also in various leoSettings.leo
files. This scheme allows for the following kinds of settings:
Per-installation or per-machine settings.
Per-user settings.
Per-folder settings.
Per-file settings.
There are four kinds of settings files:
Default settings files, named leoSettings.leo. Although they can be used in other ways, they typically contain default settings.
Personal settings files, named myLeoSettings.leo. They provide a way of ensuring that your customized settings are not altered when updating Leo from git or while installing a new version of Leo. The
myLeoSettings.leo
file acts much like Python’ssite-customize.py
file.myLeoSettings.leo
will never be part of any Leo distribution, and it will never exist in Leo’s cvs repository. This solution is much better than trying to updateleoSettings.leo
with scripts.Machine settings files, named <prefix>myLeoSettings.leo, where prefix comes from os.getenv(‘HOSTNAME’)or os.getenv(‘COMPUTERNAME’).
Theme files. These are .leo files containing stylesheets and other appearance-related settings.
The following sections describe the kinds of nodes in @settings
trees.
Configuration directories¶
Settings files can be found in the following directories:
homeDir, the
HOME/.leo
directory. HOME is given by Python’s HOME environment variable, or byos.expanduser('~')
if no HOME environment variable exists.configDir, Leo’s configuration directory:
leo/config
.machineDir, the
HOME/.leo/MACHINE
directory. MACHINE is given by Python’sHOSTNAME
environment variable, or by Python’sCOMPUTERNAME
environment variable if there is noHOSTNAME
variable, or by the value returned bysocket.gethostname()
if neither environment variable exists.localDir, the directory containing the .leo file being loaded.
Leo reports in the Log pane window on startup and when opening .leo files what HOME dir is used and which settings files are read.
Search order for settings files¶
When reading a .leo file, Leo looks for settings in default settings files first, then settings in personal settings files, and finally settings in local settings files. The exact search order is:
First: Default settings files:
configDir/leoSettings.leo
homeDir/leoSettings.leo
localDir/leoSettings.leo
Second: Personal settings files:
configDir/myLeoSettings.leo
homeDir/myLeoSettings.leo
homeDir/<machine-name>LeoSettings.leo (note capitalization)
localDir/myLeoSettings.leo
Last: Local settings files: the file being loaded.
Settings that appear later in this list override settings that appear earlier in this list. This happens on a setting-by-setting basis, not on a file-by-file basis. In other words, each individual setting overrides only the corresponding setting in previously-read files. Reading a setting file does not reset all previous settings. Note that the same file might appear several times in the search list. Leo detects such duplicate file names and only loads each settings file once. Leo remembers all the settings in settings files and does not reread those settings when reading another .leo file.
Caution: This search order offers almost too much flexibility. This can be confusing, even for power users. It’s important to choose the “simplest configuration scheme that could possibly work”. Something like:
Use a single
leoSettings.leo
file for installation-wide defaults.Use a single
myLeoSettings.leo
files for personal defaults.Use local settings sparingly.
Note: it is good style to limit settings placed in myLeoSettings.leo
to those settings that differ from default settings.
Safe rules for local settings¶
You should use special care when placing default or personal settings files in local directories, that is, directories other than homeDir, configDir or machineDir. In particular, the value of localDir can change when Leo reads additional files. This can result in Leo finding new default and personal settings files. The values of these newly-read settings files will, as always, override any previously-read settings.
Let us say that a setting is volatile if it is different from a default setting. Let us say that settings file A.leo covers settings file if B.leo if all volatile settings in B.leo occur in A.leo. With these definitions, the safe rule for placing settings files in local directories is:
Settings files in local directories should
cover all other settings files.
Following this rule will ensure that the per-directory defaults specified in the local settings file will take precedence over all previously-read default and personal settings files. Ignore this principle at your peril.
Organizer nodes¶
Organizer nodes have headlines that do no start with @
. Organizer nodes may be inserted freely without changing the meaning of an @setting
tree.
@ignore and @if nodes¶
Leo ignores any subtree of an @settings
tree whose headline starts with @ignore
.
You can use several other kinds of nodes to cause Leo to ignore parts of an @settings
tree:
@ifenv <list of environments>
True if sys.env matches any of given list of environments.
@ifplatform <platform>
True if sys.platform matches the give platform name.
@ifhostname <host>
True if os.getenv(‘HOSTNAME’) or os.getenv(‘COMPUTERNAME’) matches the given host.
@ifhostname !<host>
True if @ifhostname <host>
doesn’t match.
Simple settings nodes¶
Simple settings nodes have headlines of the form @<type> name = val
.
These settings set the value of name to val, with the indicated type:
<type> Valid values
------ ------------
@bool True, False, 0, 1
@color A Qt color name or value, such as 'red' or 'xf2fddff'
(without the quotes)
@directory A path to a directory
@float A floating point number of the form nn.ff.
@int An integer
@ints[list] An integer (must be one of the ints in the list).
Example: @ints meaningOfLife[0,42,666]=42
@keys[name] Gives a name to a set of bindings.
(For the Check Bindings script in leoSettings.leo.)
@path A path to a directory or file
@ratio A floating point number between 0.0 and 1.0, inclusive.
@string A string
@strings[list] A string (must be one of the strings in the list).
Example: @strings tk_relief['flat','groove','raised']='groove'
Note: For a list of Tk color specifiers see:
Important: you can use the show-colors
command to guide you in making these settings.
Complex settings nodes¶
Complex settings nodes have headlines of the form @<type> description
:
@<type> Valid values
------- ------------
@buttons Child @button nodes create global buttons.
@commands Child @command nodes create global buttons.
@command-history Body is a list of commands pre-loaded into history list.
@data Body is a list of strings, one per line.
@enabled-plugins Body is a list of enabled plugins.
@font Body is a font description.
@menus Child @menu and @item nodes create menus and menu items.
@menuat Child @menu and @item nodes modify menu trees
created by @menus.
@mode [name] Body is a list of shortcut specifiers.
@recentfiles Body is a list of file paths.
@shortcuts Body is a list of shortcut specifies.
Complex nodes specify settings in their body text. See the following sections for details.
@commands¶
An @commands
tree in a settings file defines global commands. All @command
nodes in the @commands
tree create global commands. All @command
nodes outside the @commands
tree create commands local to the settings file.
@command-history¶
The body text contains a list of commands, one per line, to be preloaded into Leo’s command history. You access command history using the up and down arrow keys in Leo’s minibuffer.
@data¶
The body text contains a list of strings, one per line. Lines starting with #
are ignored.
@enabled-plugins¶
The body text of the @enabled-plugins
node contains a list of enabled plugins, one per line. Comment lines starting with ‘#’ are ignored. Leo loads plugins in the order they appear. Important: Leo handles @enabled-plugins
nodes a differently from other kinds of settings. To avoid confusion, please read the following carefully.
As always, Leo looks for @enabled-plugins
nodes in settings files in the order specified by Search order for settings files. Leo will enable all plugins found in the @enabled-plugins
node it finds last in the search order. Leo does not enable plugins found in any other @enabled-plugins
node. In particular, you can not specify a list of default plugins by placing that list in a settings file that appears early in the search list. Instead, the last @enabled-plugins
node found in the search list specifies all and only the plugins that will be enabled.
Let us distinguish two different situations. First, what Leo does when loading a file, say x.leo
. Second, what Leo does when loading a second file, say y.leo
, from x.leo
. When loading the first .leo file, Leo enables plugins from the
@enabled-plugins
node it finds last in the search order. But after plugins
have already been loaded and enabled, there is no way to disable previously
loaded-and-enabled plugins. But local settings files can enable additional
plugins.
To avoid confusion, I highly recommend following another kind of safe rule. We say that an @enabled-plugin
node in file A.leo
covers an @enabled-plugin
node in file B.leo
if all plugins specified in B’s @enabled-plugins
node appear A’s @enabled-plugins
node. The safe rule for plugins is:
@enabled-plugin nodes in settings files in local directories
should cover @enabled-plugins nodes in all other settings files.
@font¶
The body text contains a list of settings for a font. For example:
body_text_font_family = Courier New
body_text_font_size = None
body_text_font_slant = None
body_text_font_weight = None
Note: you can use the show-fonts
command to guide you in making these settings.
@mode¶
Leo allows you to specify input modes. You enter mode x with the enter-x-mode
command. The purpose of a mode is to create different bindings for keys within a mode. Often plain keys are useful in input modes.
You can specify modes with @mode
nodes in leoSettings.leo
. @mode
nodes work just like @shortcuts
nodes, but in addition they have the side effect of creating the enter-<mode name>-mode command.
The form of this node is:
@mode <mode name>
The body text contains a list of shortcut specifiers. @mode
nodes work just like @shortcuts
nodes, but in addition they have the side effect of creating the enter-<mode name>-mode
command.
Notes:
You can exit any mode using the
keyboard-quit
(Control-g
) command. This is the only binding that is automatically created in each mode. All other bindings must be specified in the@mode
node. In particular, the bindings specified in@shortcuts
nodes are not in effect in mode (again, except for thekeyboard-quit
binding).Leo supports something akin to tab completion within modes: if you type a key that isn’t bound in a mode a ‘Mode’ tab will appear in the log pane. This tab shows all the keys that you can type and the commands to which they are bound. The
mode-help
command does the same thing.@shortcuts
nodes specify the bindings for what might be called the ‘top-level’ mode. These are the bindings in effect when no internal state is present, for example, just after executing thekeyboard-quit
command.The
top_level_unbound_key_action
setting determines what happens to unbound keys in the top-level mode. Leo ignores unbound keys in all other modes. The possibilities are ‘insert’, ‘replace’ and ‘ignore’.The
set-insert-mode
,set-overwrite-mode
andset-ignore-mode
commands alter what happens to unbound keys in the top-level mode.If the
@mode
headline contains::
, everything following the::
is the mode prompt. For example:@mode abc :: xyz
Creates the enter-abc-mode
command, but the prompt for the command is xyz
.
With all these options it should be possible to emulate the keyboard behavior of any other editor.
@rclick¶
For each @button
node, Leo adds right-click menu items for:
@rclick
nodes directly following the@button
.@rclick
nodes that are children of the@button
node, provided that the@button
node has no@others
directive.
Standard rclick items: Leo adds two standard right-click menu items for
each @button
node: Remove Button
and Goto Script
. Leo adds the
indicator text only to buttons that contain right-click menu items in
addition to these two standard right-click menu items.
The headline of the @rclick
node gives the menu title. The body contains a
Leo script to execute when the user selects the menu item.
Related Setting:
@string mod_scripting_subtext = ▼
This setting specifies indicator text that indicates that an @button
button has right-click menu items created by @rclick
nodes.
Unicode chars like ▼
, ▾
and …
are typical choices for this text.
@recentfiles¶
The body text contains a list of paths of recently opened files, one path per line. Leo writes the list of recent files to .leoRecentFiles.txt
in Leo’s config directory, again one file per line.
@shortcuts¶
The body text contains a list of shortcut specifiers.
Controlling syntax coloring¶
Several settings control Leo’s syntax colorer.
You may specify default fonts and colors. You may also specify fonts and colors on a language-by-language basis.
Color settings¶
To specify a color, say for comment1, for all languages, create an @color node:
@color comment1 = blue
To specify a color for a particular language, say Python, prepend the setting name with the language name. For example:
@color python_comment1 = pink
Here are Leo’s legacy (jEdit-based) @color settings:
@color comment1 @color comment2 @color comment3 @color comment4 @color doc-part @color function @color keyword1 @color keyword2 @color keyword3 @color keyword4 @color keyword5 @color label @color leo-keyword @color link @color literal1 @color literal2 @color literal3 @color literal4 @color markup @color name @color name-brackets @color null @color operator @color show-invisibles-space @color tab @color trailing-whitespace @color url
In addition, the pygments colorizer uses these settings:
@color comment
@color comment.hashbang
@color comment.multiline
@color comment.preproc
@color comment.single
@color comment.special
@color error
@color generic
@color generic.deleted
@color generic.emph
@color generic.error
@color generic.heading
@color generic.inserted
@color generic.output
@color generic.prompt
@color generic.strong
@color generic.subheading
@color generic.traceback
@color keyword
@color keyword.constant
@color keyword.declaration
@color keyword.namespace
@color keyword.pseudo
@color keyword.reserved
@color keyword.type
@color literal
@color literal.date
@color name.attribute
@color name.builtin
@color name.builtin.pseudo
@color name.class
@color name.constant
@color name.decorator
@color name.entity
@color name.exception
@color name.function
@color name.function.magic
@color name.label
@color name.namespace
@color name.other
@color name.pygments
@color name.tag
@color name.variable
@color name.variable.class
@color name.variable.global
@color name.variable.instance
@color name.variable.magic
@color number
@color number.bin
@color number.float
@color number.hex
@color number.integer
@color number.integer.long
@color number.oct
@color operator.word
@color other
@color punctuation
@color string
@color string.affix
@color string.backtick
@color string.char
@color string.delimiter
@color string.doc
@color string.double
@color string.escape
@color string.heredoc
@color string.interpol
@color string.other
@color string.regex
@color string.single
@color string.symbol
@color whitespace
@color xt
Note: all @color settings may optionally end with the _color suffix.
Font settings¶
To specify a font, say for keyword_font, to be used as the default font for all languages, put the following in the body text of an @font node in leoSettings.leo:
# keyword_font_family = None
keyword_font_size = 16
keyword_font_slant = roman
# roman, italic
keyword_font_weight = bold
# normal, bold
Comments are allowed and undefined settings are set to reasonable defaults. At present, comments can not follow a setting: comments must start a line.
You can specify per-language settings by preceding the settings names by a prefix x. Such settings affect only colorizing for language x (i.e., all the modes in modes/x.py when using the new colorizer). For example, to specify a font for php (only), put the following in the body text of an @font node in leoSettings.leo:
# php_keyword_font_family = None
php_keyword_font_size = 16
php_keyword_font_slant = roman
# roman, italic
php_keyword_font_weight = bold
# normal, bold
Customizing the rst3 command¶
This section explains how to use user filters to alter the output of
the rst3
command.
Background
The rst3
command converts @rst
trees containing reStructuredText (rST)
or Sphinx markup to an intermediate string. Depending on user
options, rst3
will:
write the intermediate string to an intermediate file.
send the intermediate string to docutils for conversion to HTML, PDF, LaTeX, etc.
User filters
Plugins or scripts may define two functions: a headline filter and a body filter:
rst3
calls the body filter for all nodes except descendants of@rst-ignore
or@rst-ignore-tree
nodes.rst3
calls the headline filter for all nodes except:rst-no-head nodes or descendants of @rst-ignore or @rst-ignore-tree nodes.
plugins or scripts register filters as follows:
c.rstCommands.register_body_filter(body_filter) c.rstCommands.register_headline_filter(headline_filter)
The signature of all filters is
filter-name (c, p)
where c and p are defined as usual.
Example filters
Do-nothing filters would be defined like this:
def body_filter(c, p):
return p.b
def headline_filter(c, p):
return p.h
The following filters would simulate the frequently-requested “half clone” feature.
That is, within any @rst
tree, the following filters would skip the children of all clones:
def has_cloned_parent(c, p):
"""Return True if p has a cloned parent within the @rst tree."""
root = c.rstCommands.root # The @rst node.
p = p.parent()
while p and p != root:
if p.isCloned():
return True
p.moveToParent()
return False
def body_filter(c, p):
return '' if has_cloned_parent(c, p) else p.b
def headline_filter(c, p):
return '' if has_cloned_parent(c, p) else p.h
Folks, this might send a shiver down your spine. Both filters ignore all
descendants of clones in the @rst
tree. We have the effect of half clones,
with no changes to Leo’s core!
Note: has_cloned_parent
stops the scan at the @rst
node itself, ensuring
that the rst3
command includes “top-level” clones themselves.
See leo/plugins/example_rst_filter.py
for a complete example.
Filters can do practically anything
Filters can use the data in p.b, p.h, p.u, or p.gnx, but filters are not limited to the data in p. The has_cloned_parent filter shows that filters can access:
Any ancestor or descendant of node p.
Any data accessible from c, that is, all the data in the outline, including cached data!
Indeed, has_cloned_parent gets the current @rst
node using root = c.rstCommands.root
.
Moreover, filters can define special conventions, including:
Special-format comments embedded in p.b,
Special-purpose headlines.
Summary
Plugins and scripts can define headline and body filters that alter the intermediate string.
The leo/plugins/example_rst_filter.py
plugin shows how to set up plugins that define custom filters.
Filters have easy access to all data within the outline, including:
c.rstCommands.root
, the@rst
node containing p.All ancestors or descendants of p.
All data contained anywhere in the outline.
The example filters shown above simulate the often-requested “half clone” feature.
Theme files¶
A theme file is a .leo file containing appearance-related settings. The leo/themes folder contains several examples.
The open-theme-file
command will show you the effect of a theme file without changing Leo in any way.
To enable a theme, put the following setting in myLeoSettings.leo
:
@string theme-name = <name of theme file>
Important: When using themes, it’s best to disable all other theme-related settings in myLeoSettings.leo
.
Leo looks for the named .leo file in the following order:
~ (The users home directory)
~/themes
~/.leo
~/.leo/themes
leo/themes
Theme files should contain three settings that describe the theme:
@bool color_theme_is_dark # True for dark themes
@string color_theme = name # Used to find icons
@string theme_name # Used to find other graphics elements.
Input modes¶
Leo now allows you to specify input modes. You enter mode x with the enter-x-mode
command. The purpose of a mode is to create different bindings for keys within a mode. Often plain keys are useful in input modes.
You can specify modes with @mode
nodes in leoSettings.leo
. @mode
nodes work just like @shortcuts
nodes, but in addition they have the side effect of creating the enter-<mode name>-mode
command.
Notes:
You can exit any mode using the
keyboard-quit
(Control-g
) command. This is the only binding that is automatically created in each mode. All other bindings must be specified in the@mode
node. In particular, the bindings specified in@shortcuts
nodes are not in effect in mode (again, except for thekeyboard-quit
binding).Leo supports something akin to tab completion within modes: if you type a key that isn’t bound in a mode a ‘Mode’ tab will appear in the log pane. This tab shows all the keys that you can type and the commands to which they are bound. The
mode-help
command does the same thing.@shortcuts
nodes specify the bindings for what might be called the ‘top-level’ mode. These are the bindings in effect when no internal state is present, for example, just after executing thekeyboard-quit
command.The top_level_unbound_key_action setting determines what happens to unbound keys in the top-level mode. Leo ignores unbound keys in all other modes. The possibilities are ‘insert’, ‘replace’ and ‘ignore’.
The
set-insert-mode
,set-overwrite-mode
andset-ignore-mode
commands alter what happens to unbound keys in the top-level mode.If the
@mode
headline contains::
, everything following the::
is the mode prompt. For example:@mode abc :: xyz
Creates the enter-abc-mode
command, but the prompt for the command is xyz
.
With all these options it should be possible to emulate the keyboard behavior of any other editor.
uA’s: extensible attribues of nodes¶
Leo’s .leo file format is extensible. The basis for extending .leo files are the v.unknownAttributes ivars of vnodes, also know as user attributes, uA’s for short. Leo translates between uA’s and xml attributes in the corresponding <v> elements in .leo files. Plugins may also use v.tempAttributes ivars to hold temporary information that will not be written to the .leo file. These two ivars are called attribute ivars.
Attribute ivars must be Python dictionaries, whose keys are names of plugins and whose values are other dictionaries, called inner dictionaries, for exclusive use of each plugin.
The v.u Python property allows plugins to get and set v.unknownAttributes easily:
d = v.u # gets uA (the outer dict) for v
v.u = d # sets uA (the outer dict) for v
For example:
plugin_name = 'xyzzy'
d = v.u # Get the outer dict.
inner_d = d.get(plugin_name,{}) # Get the inner dict.
inner_d ['duration']= 5
inner_d ['notes'] "This is a note."
d [plugin_name] = inner_d
v.u = d
No corresponding Python properties exist for v.tempAttributes, so the corresponding example would be:
plugin_name = 'xyzzy'
# Get the outer dict.
if hasattr(p.v,'tempAttributes'): d = p.v.tempAttributes
else: d = {}
inner_d = d.get(plugin_name,{}) # Get the inner dict.
inner_d ['duration'] = 5
inner_d ['notes'] = "This is a note."
d [plugin_name] = inner_d
p.v.tempAttributes = d
Important: All members of inner dictionaries should be picklable: Leo uses Python’s Pickle module to encode all values in these dictionaries. Leo will discard any attributes that can not be pickled. This should not be a major problem to plugins. For example, instead of putting a tnode into these dictionaries, a plugin could put the tnode’s gnx (a string) in the dictionary.
Note: Leo does not pickle members of inner dictionaries whose name (key) starts with str_. The values of such members should be a Python string. This convention allows strings to appear in .leo files in a more readable format.
Here is how Leo associates uA’s with <v>
elements in .leo
files:
Native xml attributes are the attributes of
<v>
elements that are known (treated specially) by Leo’s read/write code. The native attributes of<v>
elements area
,t
,vtag
,tnodeList
,marks
,expanded
, anddescendentTnodeUnknownAttributes
. All other attributes of<v>
and<t>
elements are foreign xml attributes.When reading a
.leo
file, Leo will create v.unknownAttributes ivars for any vnode whose corresponding<v>
or<t>
element contains a foreign xml attribute.When writing a
.leo
file, Leo will write foreign xml attributes in<v>
elements if the corresponding vnode contains an unknownAttributes ivar.Leo performs the usual xml escapes on these strings when reading or writing the unknownAttributes ivars.
Decluttering headlines¶
Decluttering replaces controls custom formatting of headlines, including:
Hiding or changing headline text,
Adding icons to headlines,
Changing the styling of headlines.
Decluttering is inactive when you are editing a headline.
Decluttering is completely optional. To enable decluttering, use:
@bool tree-declutter = True
Decluttering is controlled by decluttering rulesets. You specify decluttering rulesets in the body text of:
@data tree-declutter-patterns
As usual with @data
nodes:
Blank lines and lines starting with
#
are ignored.You may organize the text of the
@data
node using child nodes.
Each ruleset consists of a list of lines:
The first line is a rule line, containing a find pattern.
The second line is a replacement line.
The ruleset ends with zero or more style lines.
Find patterns are regular expressions. Decluttering affects only those headlines that match a rule pattern.
The following section shows some example rulesets. Later sections discuss decluttering commands, patterns and styles in more detail.
Examples¶
Here are some examples of decluttering rulesets:
# Hide org-mode tags and bold the headline.
RULE :([\w_@]+:)+\s*$
REPLACE-HEAD
WEIGHT Bold
# Replace @clean with an icon
RULE ^@clean (.*)
REPLACE \1
ICON file_icons/file_clean.png
# Show the last part of long filenames
RULE ^.{1,1000}([/\\])(.{25})
REPLACE …\1\2
Rule & replacement lines¶
All rulesets start with a rule line of the form:
RULE <regular expression>
The ruleset matches a headline if and only if the regular expression matches. Matches can start anywhere in the headline. Leo first attempts to a match using re.match. If that doesn’t work, Leo tries re.search.
A replacement line must follow the rule line. Here are the valid forms:
REPLACE <substitution expression>
REPLACE-HEAD
REPLACE-TAIL
REPLACE-REST
REPLACE
replaces the headline by the value of the substitution expression. For example:REPLACE \1
matches replaces the headline by the first matched regex group.
REPLACE-HEAD
replaces replace the headline by the text that precedes the matched text.REPLACE-TAIL
replaces the headline by the text that follows the matched text.REPLACE-REST
replaces the headline by everything except the matched text.
Style lines¶
Leo applies style lines only if they appear in a ruleset that matches a headline. Style lines do the following…
Add an icon to the headline:
ICON path/to/icon
Set the background or foreground color to a color number or names:
BG #FF8800
FG @solarized-magenta
Set the font to a given font name:
Font Times
Set the font size in pixels (PX) or points (PT):
PX 40
PT 16
Enable or disable italics:
ITALIC 0
ITALIC 1
Set the font weight to one of Light, Normal, DemiBold, Bold, Black:
WEIGHT DemiBold