Skip to main content
Version: 2.0.0-alpha ๐Ÿšง

Regex triggers

Regex triggers are a powerful alternative to regular ones, with a few extra features:

  • They let you define triggers using a full-blown regex syntax, enabling use-cases that would be inconvenient or even impossible with regular triggers.
  • They accept named groups, which can be used to convenientely pass arguments to expansions or external scripts.
Before starting

This tutorial assumes you already know the basics of Regular expressions. If you don't, these resources can be a good starting point:

Basics

Before diving into the advanced topics, let's recap some of the basics. Let's say we want to create a match that gets expanded to Hello! every time we write :greet2.

With regular triggers, you could do the following:

  - trigger: ":greet2"
replace: "Hello!"

After a while, you decide to extend the previous match to all single-digits, such as :greet1, :greet2, :greet3, etc.

With regular triggers, your only option would be to list all the possible ones:

  - triggers:
- ":greet1"
- ":greet2"
- ":greet3"
- ":greet4"
- ":greet5"
- ":greet6"
- ":greet7"
- ":greet8"
- ":greet9"
- ":greet0"
replace: "Hello!"

As you can see, this approach hardly scales with multiple variations.

On the other hand, Regex triggers let you define the previous match very concisely:

  - regex: ":greet\\d"
replace: "Hello!"

With this match, every time you write :greet1 or :greet7, you'll get Hello!

Escaping regexes

As you can see from the previous example, we had to write :greet\\d instead of :greet\d. That's because we need to escape backslashes for it to be valid YAML.

Although this example showed an interesting use-case for regex triggers, it was only the tip of the iceberg. Regex triggers really start to shine when used to create dynamic triggers, which are explained in the next section.

Arguments

The most significant difference between regular triggers and regex ones is that the latter can be dynamic. For example, the following trigger would only match with :greet(John)

  - trigger: ":greet(John)"
replace: "Hi John!"

whereas we could define a regex trigger to match all names:

  - regex: ":greet\\(.*\\)"
replace: "Hi John!"

The previous match works with any name. For example, both :greet(Bob) and :greet(Mark) cause an expansion. Unfortunately, the match will always output Hi John!, even when you specify other names.

To solve the problem, you'll need to use named groups, a really powerful regex feature that integrates well with Espanso. Let's refactor the previous example to use named groups:

  - regex: ":greet\\((?P<person>.*)\\)"
replace: "Hi {{person}}!"

If you now type :greet(Bob), you'll see Hi Bob! appear!

The key part is the (?P<person>.*) block in the middle, which "captures" the text contained between the two outer parenthesis (the ones we escaped), inside the person group.

The powerful thing is that Espanso automatically converts named groups into variables, so that you can use them in replacements and scripts.

Named group syntax

Although regular expressions are supported by most programming languages, their syntax can slightly vary between implementations. Espanso uses the regex library, which requires you to specify named groups as follows:

(?P<name>exp)

TLDR: Make sure to specify that P, otherwise named groups won't work!

Keep in mind that you don't have to enclose your arguments within parenthesis, as that was only a matter of style. For example, you could also rewrite the previous example as:

  - regex: "greet(?P<person>.*)\\."
replace: "Hi {{person}}!"

Which would expand to Hi Mark! every time you write greetMark.. As you can see, you can go crazy with it!

Advanced examples

As explained in the previous section, Espanso automatically converts named groups into variables. This makes it possible to use the value of a named group inside scripts and shell commands, among other things.

In the next example, we are going to use the expr command line utility to create a match that calculates and expands the sum between two numbers dynamically.

  - regex: "=sum\\((?P<num1>.*?),(?P<num2>.*?)\\)"
replace: "{{result}}"
vars:
- name: result
type: shell
params:
cmd: "expr $ESPANSO_NUM1 + $ESPANSO_NUM2"

If you now type =sum(3,4), Espanso will expand it to 7!

In a nutshell, the two numbers are captured inside the num1 and num2 named groups, which in turn are converted to Espanso variables. As with all variables, you can use them inside the shell extension by reading the appropriate environment variables. In this case we are passing them to the expr command.

If you want to know more about the variable injection logic, please read the Variables section.

windows users

The expr command is not available by default on Windows, so the previous example might not work on that platform. You can still apply the concepts to other commands/scripts.

When NOT to use Regex triggers

Although regex triggers could completely replace regular ones, you should only use them when necessary. In other words, if a regular trigger (or a simple combination of them) could serve your needs, you should prefer them to regex ones.

That's because regex triggers, despite being more powerful, are also less efficient than regular ones. Their performance shouldn't be a problem unless you use thousands of them, but it's important to keep this in mind when defining matches.

Limitations

The current regex implementation is subject to a few limitations:

  • The maximum length of a "regex match" is set to 30 characters, including the captured named groups. For example, if your regex trigger is :greet\\((?P<person>.*)\\) and you type :greet(Bob), you've consumed 11 out of 30 characters. This limitation has been placed to improve performance, but we are planning to make this configurable. Please open an issue on GitHub if this is limiting you :)

  • As explained in the previous sections, Espanso uses a Regex implementation that doesn't support all Regex features. For more information, please see the library documentation: https://docs.rs/regex/1.5.4/regex/

Donate

Hi! I'm Federico, the creator of espanso. I develop espanso in my (little) spare time and I decided to make it open source because I thought it could be useful to many people.

If you liked the project, please consider making a small donation, it really helps :)

A special thanks goes to all the wonderful people who supported espanso along the way

Together, we will make espanso the first universal text expander, open to everyone.

Contributing

Espanso is open source and hosted on GitHub.

Star

If you find a bug or have an idea for a new feature, please open an issue on GitHub.