Matches Basics
Matches are one of the Espanso's core concepts and define the replacements that will take place.
Static Matchesβ
In their most basic form, Matches are pairs that associate a trigger with a replacement text.
For example, we can define a match that will expand every occurrence of hello
with world
while we are typing. Using the YAML syntax, it can be expressed as:
- trigger: hello
replace: world
These kind of expansions are simple text replacements and can be considered static. Quote-marks are not necessary for trigger
or replace
in simple cases, but some people prefer to use them for consistency.
Multi-line expansionsβ
To replace the original text with a multi-line expansion, we can either use the \n
line terminator character, such as:
- trigger: hello
replace: "line1\nline2"
Note that strings using
\n
as the line terminator character, or\t
for tab-spacing, or beginning with a YAML special character (' " [] {} > | * & ! % # ` @
), must be quoted.
or values can span multiple lines using |
or >
. Spanning multiple lines using a Literal Block Scalar |
will include the newlines and any trailing spaces. Using a Folded Block Scalar >
will fold newlines to spaces; itβs used to make what would otherwise be a very long line easier to read and edit. In either case the indentation will be ignored. Examples are:
- trigger: include newlines
replace: |
exactly as you see
will appear these three
lines of poetry
- trigger: fold newlines
replace: >
this is really a
single line of text
despite appearances
As you can see, no quotes are needed in these cases.
tip
If you want more information about the YAML syntax for multiline strings, please check out this website: https://yaml-multiline.info/
Dynamic Matchesβ
Static matches are suitable for many tasks, but can be problematic when we need an expansion that changes dynamically. For these situations, Espanso introduces the concepts of variables and extensions.
Variables can be used in the replace clause of a Match to include the output of a dynamic component, the extension. To make things more clear, let's see an example:
We want to create a match that every time we type :now
gets expanded to include the current time, like:
It's 11:29
Let's add the following match to your configuration, such as the match/base.yml
file
- trigger: :now
replace: It's {{mytime}}
vars:
- name: mytime
type: date
params:
format: "%H:%M"
After a while, Espanso should pick up the new configuration.
At this point, every time we type :now
, we should see something like It's 09:33
appear!
Let's analyze the match step by step:
- trigger: :now
In the first line we declare the trigger :now
, that must be typed by the user to expand the match.
replace: It's {{mytime}}
In the second line, we declare the replace text as usual, but this time we include the mytime
variable,
that will contain the output of the extension used below.
vars:
- name: mytime
type: date
In the following lines, we defined the mytime
variable as type date. The type of a variable defines
the extension that will be executed to calculate its value.
In this case, we use the Date Extension.
params:
format: "%H:%M"
In the remaining lines we declared the parameters used by the extension, in this case the date format.
The ":" prefix to triggers is useful to avoid unwanted triggering of expansions during typing, because it is unlikely to occur as a word prefix. It's not required, or may be changed to other character(s) at any position in the
trigger
item.
Injection mechanismβ
Espanso follows the backend
value specified in default.yml
, the default for which (auto
) is to use the Inject mechanism for short replacements, and Clipboard for longer ones.
If expansions aren't working (e.g. no replacements, missing characters, or just "v" appearing), adding the force_mode: clipboard
or force_mode: keys
properties to a trigger will override the backend in order to test the two mechanisms.
Once triggers are working, remove the force_mode:
lines, in favour of a global configuration change to the backend
value in default.yml
, and/or in app-specific configurations for individual programs, so that all triggers work in each environment.
Global Variablesβ
Global variables are variables that can be used across multiple matches. You can define them above your matches, and they will be available across all matches defined in that file and it's children.
For example, if you add the following into your match/base.yml
file:
global_vars:
- name: greet
type: echo
params:
echo: Hey
You can then use greet
in the following match:
- trigger: :hello
replace: "{{greet}} Jon"
Typing :hello
will result in Hey Jon
to be expanded.
Word Triggersβ
If you ever considered using Espanso as an autocorrection tool for typos, you may have experienced this problem:
Let's say you occasionally type ther
instead of there
. Before the introduction of word triggers,
you could have used espanso like this:
- trigger: ther
replace: there
This would correctly replace ther
with there
, but it would also expand
other
into othere
, making it unusable.
With word triggers you can now add the word: true
property to a match, telling espanso
to only trigger that match if surrounded by word separators ( such as spaces, commas and newlines).
So in this case it becomes:
- trigger: ther
replace: there
word: true
At this point, espanso will only expand ther
into there
when used as a standalone word.
For instance:
Before | After |
---|---|
Is ther anyone else? | Is there anyone else? |
I have other interests | I have other interests |
The related properties, left_word: true
and right_word: true
, ensure a match will only occur at the beginning or end of words respectively, and not in the middle.
The configuration option word_separators
may be used to customise which characters qualify as word separators.
Special charactersβ
replace
can inject hex and Unicode characters with strings such as "\xC4"
, "\u0105"
and "\U00000105"
, and combine them with plain text. For example:
- trigger: :euro
replace: "\u20ac"
is equivalent to:
- trigger: :euro
replace: β¬
Case propagationβ
Espanso also supports case-propagation, which makes it possible to expand a match preserving the trigger casing.
For example, imagine you want to speedup writing the word although
. You can define a word match as:
- trigger: alh
replace: although
word: true
As of now, this trigger will only be able to be expanded to the lowercase although
. If we now add propagate_case: true
to the match:
- trigger: alh
replace: although
propagate_case: true
word: true
we are now able to propagate the case style to the match:
- If you write
alh
, the match will be expanded toalthough
. - If you write
Alh
, the match will be expanded toAlthough
. - If you write
ALH
, the match will be expanded toALTHOUGH
.
Multi-word capitalization
When using multi-word replacements, the default behavior is to only capitalize the first word. For example, the following match:
- trigger: ;ols
replace: ordinary least squares
propagate_case: true
gets expanded to Ordinary least squares
when typing ;Ols
.
If you want to capitalize each word, you can use the uppercase_style: capitalize_words
option:
- trigger: ;ols
replace: ordinary least squares
uppercase_style: capitalize_words
propagate_case: true
In this case, typing ;Ols
gets expanded to Ordinary Least Squares
.
Cursor Hintsβ
Let's say you want to use espanso to expand some HTML code snippets, such as:
- trigger: :div
replace: <div></div>
With this match, any time you type :div
you get the <div></div>
expansion, with the cursor at the end.
While being useful, this snippet would have been much more convenient if the cursor was positioned
between the tags, such as <div>|</div>
.
To solve this problem, Espanso supports cursor hints, a way to control the position of the cursor after the expansion.
Using them is very simple, just insert $|$
where you want the cursor to be positioned, in this case:
- trigger: :div
replace: <div>$|$</div>
If you now type :div
, you get the <div></div>
expansion, with the cursor between the tags!
Things to keep in mind
- You can only define one cursor hint per match. Multiple hints will be ignored. If you need multiple hints, a decent replacement would be to use Forms.
- This feature should be used with care in multiline expansions, as it may yield
unexpected results when using it in code editors that support auto indenting.
This is due to the way the feature is implemented: espanso simulates a series of
left arrow
key-presses to position the cursor in the correct position. This works perfectly in single line replacements or in non-autoindenting fields, but can be problematic in code editors, as they automatically insert indentations that modify the number of required presses in a way espanso is not capable to detect.
Match Disambiguationβ
By defining the following match, Espanso will inject
"Every moment is a fresh beginning." as soon as you type :quote
- trigger: :quote
replace: Every moment is a fresh beginning.
This mechanism works as long as you provide a unique trigger to each match, but what happens if multiple matches share the same trigger? In such cases, Espanso will use match disambiguation to let you choose the appropriate one.
For example, let's expand the previous example by adding two more matches
with :quote
as trigger:
- trigger: :quote
replace: Every moment is a fresh beginning.
- trigger: :quote
replace: Everything you can imagine is real.
- trigger: :quote
replace: Whatever you do, do it well.
As you can see, all three matches share the same trigger.
If you now type :quote
, Espanso will display a selection dialog
to let you choose the desired one:
This feature is particularly useful when multiple choices are needed for the same trigger. For example, you might define multiple snippets for your signatures and then use match disambiguation to choose between them:
- trigger: :sig
replace: |
Best Regards,
John
- trigger: :sig
replace: |
All the best,
John
Search Labelsβ
When using the Search Bar, matches are displayed with their replacement text as description by default. While this works for basic use-cases, the resulting description might become less intuitive as you start including variables.
For example, given these two matches:
- trigger: :tomorrow
replace: "{{mytime}}"
vars:
- name: mytime
type: date
params:
format: "%v"
offset: 86400
- trigger: :yesterday
replace: "{{mytime}}"
vars:
- name: mytime
type: date
params:
format: "%v"
offset: -86400
the Search bar would display them with {{mytime}}
as description, which might not be very intuitive:
For this reason, Espanso supports the label
match field to override the default description,
making the search UI more intuitive. For example, adding the labels to our previous example:
- trigger: :tomorrow
replace: "{{mytime}}"
label: Insert tomorrow's date, such as 5-Jan-2022
vars:
- name: mytime
type: date
params:
format: "%v"
offset: 86400
- trigger: :yesterday
replace: "{{mytime}}"
label: Insert yesterday's date, such as 5-Jan-2022
vars:
- name: mytime
type: date
params:
format: "%v"
offset: -86400
would be displayed as follows in the Search bar:
Additional words associated with a trigger and available to find using the Search Bar, may be defined with a list following the search_terms
property, e.g.:
- trigger: :meat
replace: π₯©
search_terms:
- steak
- t-bone
Multiple triggersβ
Sometimes it's useful to expand a snippet using various aliases.
Because of this, Espanso supports multi-trigger matches, which allows the user to specify multiple triggers to expand the same match.
To use the feature, simply specify a list of triggers in the triggers
field (instead of trigger
):
- triggers: [hello, hi]
replace: world
Now typing either hello
or hi
will be expanded to world
.
Rich Textβ
Rich text can now be specified as markdown and HTML replacements:
- trigger: :rich
markdown: This *text* is **very rich**!
- trigger: :ric2
html: |
<p>
But <span style="color: #ce181e;"><span style="font-size: x-large;">
this</span></span> one is <span style="color: #81d41a;"><span
style="font-family: Arial, sans-serif;">even richer</span></span>!
</p>
The paragraph: true
option may be added to markdown replacements to avoid injecting a new-line and new paragraph.
Image Matchesβ
Espanso also supports expanding matches into images. This can be useful in many situations, such as when writing emails or chatting.
The syntax is pretty similar to the standard one, except you have to specify image_path
instead of replace
. This will be the path to your image.
- trigger: :cat
image_path: "/path/to/image.png"
Format remarks
On Windows and macOS, the most commonly used formats (such as PNG, JPEG and GIF) should work as expected. On Linux, you should generally use PNG as it's the most compatible.
Path conventionβ
While you can use any valid path in the image_path
field, there are times in which it proves limited.
For example, if you are synchronizing your configuration across different machines, you could have problems
creating the same path on each of them.
In those cases, the best solution is to create a folder into the espanso configuration directory and put all
your images there.
At this point, you can use the $CONFIG
variable in image_path
to avoid hard-coding the path. For example:
Create the images
folder inside the espanso configuration directory (the one which contains the match
and config
directories),
and store all your images there. Let's say I stored the cat.png
image. We can now create a Match with:
- trigger: :cat
image_path: "$CONFIG/images/cat.png"
Nested Matchesβ
Nested matches makes it possible to include the output of a match inside another one.
- trigger: :one
replace: nested
- trigger: :nested
replace: This is a {{output}} match
vars:
- name: output
type: match
params:
trigger: :one
At this point, if you type :nested
you'll see This is a nested match
appear.
Keyboard Triggersβ
Espanso can respond to CTRL-key triggers by using their hex-codes (but not ALT- or META-). For example:
- trigger: "\x05" # <ctrl-e>
replace: testing
force_mode: keys
However, CTRL-character combinations are likely to conflict with editor menu shortcuts, and the force_mode: keys
property may be needed to prevent over-backspacing. A list of the hex-codes may be found at https://ss64.com/ascii.html
Formsβ
Espanso is capable of creating arbitrarily complex input forms.
These open up a world of possibilities, allowing the user to create matches with many arguments, as well as injecting those values into custom Scripts or Shell commands.
For more information, visit the Forms section.