7 min read

Open_rec : Espanso

*#textExpander #productivity*
Get it done FOSS !

espanso?

espanso is the best free and open-source text expander I have seen. It can replace pre-defined triggers with pre-define snippets. For instance, :mt with:

Please provide me the source code for a Markdown table explaining ____

or :br with:

best regards,
Ron

espanso saves me a dozen or so keystrokes each time I interact with GPTs or write an email. It is essential to my workflow, not just because it saves time, but more importantly because reducing tedious yet necessary repetitions to a minimum helps me concentrate on the creative part.

Why espanso? espanso is much more than a text expander.

It supports emojis, images, dynamic injections, and scripts, and can be set up as an emoji+GIF input method, datetime injector, and shortcuts to various scripts, among numerous other possibilities.

espanso also supports context-aware replacement. Different sets of triggers can be set up for different apps. For instance, :br for best regards can be limited to email clients, freeing up the trigger for different use in another context. Therefore, all my triggers can stay really short. None of my triggers are longer than 4 characters.

Last but not the least, the espanso community is just amazing. You meet the nicest people with espanso. Many write and share extensions and packages: Jobie Wong, for instance, created a complete open-source emoji v.15 library for espanso; Many help others out on the Discord channel: The channel is more responsive than most commercial support, getting back to questions within a few hours.

💻 OS:

Linux Windows macOS Android iOS

We focus on Linux, but many of the applications we recommend are cross-platform.

: 10 - 40 minutes

For those:

  • Comfortable with basic command line operations
  • Looking to speed up their typing

Good to Know

Codium:

Our code editor of choice is VSCodium. You will see BASH scripts like codium <file> in this tutorial. You can substitute codium with nano (the default GNU text editor) or your code editor of choice.

espanso

Download

See here for instructions and troubleshooting guide.

Start up

espanso is command line. If you have taken the effort to move to Linux, it is the extra hundred meter after a mile to learn basic BASH command line syntax.
A good place to start with BASH on Linux

# Check status, start if not running
if espanso status | grep -q "espanso is running"; then
    echo "✅ Espanso is running."
else
    echo "❌ Espanso is not running."
    echo "🔄 Attempting to restart..."
    
    espanso
    sleep 2  

    if espanso status | grep -q "espanso is running"; then
        echo "✅ Espanso restarted successfully!"
    else
        echo "⚠️ Failed to start Esvpanso. Try running: espanso"
    fi
fi

Static match

The basic building block of espanso is match.

matches:
  # Simple text replacement
  - trigger: ":espanso"
    replace: "Hi there!"

Whenever the trigger is typed, espanso replaces it with replace.

.yml

Matches are stored in .yml (Yet Another Markup Language) files.

An example .yml file:

# espanso match file

# For a complete introduction, visit the official docs at: https://espanso.org/docs/

# You can use this file to define the base matches (aka snippets)
# that will be available in every application when using espanso.

# Matches are substitution rules: when you type the "trigger" string
# it gets replaced by the "replace" string.
matches:
  # Simple text replacement
  - trigger: ":espanso"
    replace: "Hi there!"

Configuration directory

espanso .yml configuration files are stored in directory $CONFIG. You can find out the path of $CONFIG by bash espanso path.

Run the following script to jump to the configuration directory and store the path as the CONFIG variable:

CONFIG=$(espanso path | awk '/Config:/ {print $2}') 
cd "$CONFIG" && echo -e "\$CONFIG is now set to $CONFIG\nCurrent directory is changed to $CONFIG" || echo -e "Error. Verify espanso is running."

Run ls and you will see two folders:

config\ match\

Continue cd and ls around you quickly discover the configuration file structure of espanso:

  • match
    • base.yml
    • packages/
  • config
    • default.yml

Now run codium $CONFIG/match/base.yml and what do you see?

codium starts the code editor VSCodium. Use nano or another text editor if you do not have VSCodium.

match/

match/ folder tells espanso WAHT to do. You can add custom matches in base.yml, or create your own .ymls. I recommend the latter approach to preserve the base as something to revert back to if things goes wrong. Consider creating separate custom .ymls for different contexts, such as one for Javascript coding, one for responding to emails, and one for AI prompts, etc. with this syntax:

matches:
    
  - trigger: ":br"
    replace: "Regards,\nRon" # `\n` is interpreted by espanso by default as the newline character

  - trigger: ":l"
    replace: "Linux"
  ...

YML is indentation-sensitive. Prefer a single SPACE to TAB.

Special characters can be represented by their unicode, such as \u20ac for €.

config/

config/ folder tells espanso HOW and WHEN to activate.

HOW:

You can toggle options with the format <option>: <value> in default.yml. For instance, you can write auto_restart: false to disable automatic refreshment upon detection of changes to the configuration. A complete list of available options can be found here.

WHEN:

You can also specify in what apps each match sheet in match/ is loaded. For instance, you can specify certain matches are only loaded in email clients.

We will discuss in more depth in App specific Configurations

Search bar and Opps! button

When you have a bazellion matches, which is not impossible with large packages such as emoji packs installed, you will inevitably start to lose track.

ALT+SPACE by default brings up the search bar for matches. Typing > at the beginning of the search bar brings up available espanso commands.

ALT+SPACE may create conflict with system shortcuts on Linux. You can change it by writing search_shortcut: <shortcut> in $CONFIG/config/default.yml

Example: search_shortcut: CTRL+ALT+SPACE

Available keys:

ALT, CTRL, CMD, SHIFT, ENTER, TAB, SPACE, META, OPTION, INSERT, DOWN, LEFT, RIGHT, UP, END, HOME, PAGEDOWN, PAGEUP, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, NUMPAD0, NUMPAD1, NUMPAD2, NUMPAD3, NUMPAD4, NUMPAD5, NUMPAD6, NUMPAD7, NUMPAD8, NUMPAD9
Not all keys are tested. Bug reports are highly appreciated.

You can also set up a search trigger by writing search_trigger: "<trigger>" in $CONFIG/config/default.yml.

BACKSPACE immediately after a trigger undos the trigger. Useful in case of accidents.

❗From time to time espanso would get stuck and not respond to keyboard shortcuts. Run espanso restart and try again.

Case propagation

  - trigger: alh
    replace: although
    word: true
    propagate_case: true

word: true: Replacement is only triggered by alh, not words containing alh, for instance, alh
propagate_case: alh -> although; AlH -> Although; ALH -> ALTHOUGH

Image substitution

  - trigger: :dog
    image_path: "/path/to/image.png"

The recommended image format for Linux is .png

Rich text substitution is also available.

Dynamic matches

In addition to static matches,espanso also supports dynamic matches.

Datetime

In dynamic matches, replace contains one or more variables:

   - trigger: :now
    replace: "{{datetime}}" 
    vars:
      - name: datetime
        type: date
        params:
          format: "%Y-%m-%dT%H:%M%z"

datetime is a custom variable name created for reference in replace.

date is an espanso expansion that return system datetime. It is based on chrono

%z displays the timezone offset relative to UTC+0. The extension accepts parameter format to specify how the datetime should be displayed. For more info on this extension, see espanso date extension guide

Choice

As you add more and more matches, you may run out of simple triggers. Luckily espanso supports the choice structure:

  - trigger: ":os"
    replace: "{{output}}"
    vars:
      - name: output
        type: choice
        params:
          values:
           - "Linux"
           - "macOS"
           - "Windows"

With this :os triggers a selection box. Try it out.

Echo

global_vars:
    vars:
      - name: var
        type: echo
        params:
          echo: "value"

echo is used to declare global variables in espanso, similar to var variable=value in Javascript.

Global variables can be used in any subsequent replaces.

Script activation

  - trigger: ":pyscript"
    replace: "{{output}}"
    vars:
      - name: output
        type: script
        params:
          args:
            - python
            - /path/to/your/script.py

This executes the python script at /path/to/your/script.py.

The espanso .config folder can be substituted by %CONFIG%. For instance, if the script is located at /home/<user>/.config/espanso/script.py, then the path can be passed as %CONFIG%/script.py

❗ Limit execution to fast-running scripts to avoid lag.

A possible value for param is debug:true to display additional technical info,

Shell integration

You can set up trigger to execute shell commands:

  - trigger: ":ip"
    replace: "{{output}}"
    vars:
      - name: output
        type: shell
        params:
          cmd: "curl 'https://api.ipify.org'"
          shell: bash

Use shell: pwsh for powershell core.

For a complete list of extensions, see espanso extensions

Regex triggers

In Dynamic matches, the triggers remained fixed.

Dynamic triggers are possible with Regex:

  - trigger: ":#(?P<num>\\d)"
    replace: "{{output}}"
    vars:
      - name: output
        type: shell
        params:
          cmd: "printf '%*s' $ESPANSO_num | tr ' ' '#'"
          shell: bash

The trigger uses the named capture group (num) to capture a single integer (\d).

In the regex variant used by espanso, named capture groups are specified with (?P<name>expr.). \d must be escaped with \ because \ is a special character in yml.

The captured groups are stored as espanso variables, which can be recalled with $ESPANSO_name in bash scripts. The example bash scripts prints # n times for Markdown usage.

See more here

App-specific configurations

_.*.yml

All .ymls in match/, except those with names beginning in _, are loaded automatically by espanso startup.

Create _<context>.yml for substitions active in particular apps. For instance, _emailClient.yml to store email addresses and signatures for use in email clients only.

_.* is regex.

config/

espanso is not yet smart enough to automatically interpret what you mean by email client. You tell it what it means in config/.

In config/, you can either edit default.yml or create new .ymls.

filter_class: "Mail"

extra_includes:
  - "../match/_email.yml"
  - "textEditing.yml

In general, email clients are in the Mail class. You can find out the class of each Windows with xprop.

in addition to extra_include, include,extra_exclude are some possible values.

With extra_include, the default matches (those written in [^_].*.yml) are implicitedly included, while with include only the .ymls explicitly specified are enabled. extra_exclude can be used to exclude certain .ymls loaded by default.

Other filters are available

Packages

espanso supports packages, which are essentially packaged .yml match sheets written by the community.

espanso path packages #find package storage location
espanso install <package-name> (--version <version> --force) #--force overwrites any existing package of the same name.
espanso package update <package-name> | espanso package update all
espanso uninstall <package-name>
espanso package list

You can find many official packages here

Updating espanso configurations

As you type, you will discover more and more potential matches worth settting up. Run these commands to quickly jump to the match/ directory and add new matches!

CONFIG=$(espanso path | awk '/Config:/ {print $2}') 
cd "$CONFIG/match" && echo -e "\$CONFIG is now set to $CONFIG\nCurrent directory is changed to $CONFIG/match" || echo -e "Error. Verify espanso is running."
echo -e "Available match files:\n$(ls)"