Custom text snippets with espanso: a text expander written in Rust
I recently discovered a wonderfully useful little program called espanso â a âtext expanderâ that I now use on a daily basis. Text expander programs âexpandâ short text âtriggersâ into longer / more complex strings of text, like this:
There are lots of these programs out there, such as Typinator, or the creatively-named âtextexpanderâ (macOS even has a build-in expander). But I prefer to use open source software whenever possible (the espanso source code is on GitHub), and especially when using software that tracks the text you type (imagine how easy it would be to grab passwords with this type of software). Plus espanso is free đ. Of course if youâd like to support the project you can donate here.
More than anything, I started (and kept) using espanso because I found it to be remarkably simple to use and easy to customize. So far, it has integrated quite nicely into my day-to-day workflow. In fact, I have become such a fan of espanso that I decided to sit down and write a whole post highlighting some of the ways I use it.
Note: Iâm skipping a lot of details about installation, etc. because itâs all there in the espanso documentation. Itâs really simple to get it up and running â check it out.
To set a text expansion, all you have to do is edit the default.yml
file. You can find itâs location with this in the terminal:
/Users/jhelvy/Library/Preferences/espanso
Once youâve opened up the default.yml
file, you can define an expansion like this:
# Simple text replacement
- trigger: ":test"
replace: "Hello World!"
Now anytime I type :test
, the text will expand to Hello World!
. Pretty cool! đ
What I really love about this interface is itâs simplicity. With just a few lines you can define a wide range of custom expansions. After using it for just a few weeks, I have already come up with several dozen expansions that I now regularly use.
Iâll spend the rest of this post highlighting a few use cases, but for reference I also included a copy of most of my settings at the end of this post (I took out some snippets with personal information like the link to my personal Zoom room, etc.)
I canât stand having to type my name, address, email, etc. into online forms. I probably have to do this once a week or so for one reason or another, so coming up with some quick expansions for common personal information is extremely satisfying.
Note: Itâs probably a really bad idea to put passwords you use in your espanso settings, so donât do that!
Here are a few Iâve set up:
Trigger | Expansion |
---|---|
":jp" |
"John Paul Helveston" |
":jh" |
"John Helveston" |
":title" |
"Assistant Professor" |
":dept" |
"Engineering Management and Systems Engineering" |
":gw" |
"George Washington University" |
"@!" |
"jph@gwu.edu" |
I used to rely on bookmarks for lots of common sites I visit, but I often need to copy-paste the actual url to those sites (e.g. if Iâm sending the link to someone else). Before, I had to open up my browser, navigate to the site, then copy the url. Now I can just type a few keys đ! I start each of these with "//"
. For example, to get to my GitHub page, I just type //gh
:
By far my most used url expansion this year was //zoom
to expand to the url to my personal zoom room! đ
This is where things start getting really helpful. I write lots of common code snippets for a wide variety of use cases, and the ability to automate these globally (i.e. not just in a code IDE) on my computer is an incredible help. The time savings donât come from avoiding the extra keystrokes â they come from avoiding typos! Itâs all too easy to miss a tiny symbol or letter in a chunk of code which then results in several minutes (if not longer) of searching for the bug.
As a convention, I decided to start all of my âcodeâ triggers with a double letter of the first letter of the code language. So html expansions all start with hh
, R expansions start with rr
, python with pp
, etc. I use this rather than starting with the default :
symbol because 1) I find it easier to remember, and 2) it doesnât require me to use the Shift key (so itâs fast).
I donât write a lot of html, but occasionally Iâll use it to insert an image in a RMarkdown document. And usually I want that image centered. The basic template I usually start with is:
I use the trigger hhimg
(which I remember as âhtmlâ + âimgâ) to insert this snippet. Hereâs the code to define this expansion:
- trigger: "hhimg"
replace: "<center>\n<img src=\"$|$\" width=100%>\n</center>"
Notice the $|$
symbols inside the quotes following âsrcâ. This sets the location of the cursor once the expansion is complete, allowing me to quickly type in the name of the path to the image I want to insert. Another one of those little bonus features about espanso that is just đ€ *chefâs kiss*.
Since I use R more than any other language, Iâve come up with a lot of R-specific shortcuts. But before I dive into them, a quick side note:
One important modification I learned (thanks to some help on the espanso Reddit community) is that if youâre working in RStudio, the expansions donât quite work correctly on their own (I believe it might have something to do with RStudioâs autofill feature). To fix it, I had to insert the force_clipboard: true
setting into each espanso expansion. For example, in my snippet to read in a CSV file, I inserted this option between the trigger and expansion text like this:
- trigger: "rrcsv"
force_clipboard: true
replace: "read_csv($|$)"
Okay with that in mind, Iâll highlight a few R expansions I use a lot. Iâve organized this based on my common workflow for exploring data:
I usually include a âsetupâ chunk in most of my Rmd files, but itâs really hard to remember all the specific details for various settings, so I made a dedicated expansion just for this. With this expansion, I can insert my whole default setup chunk with rrsetup
:
- trigger: "rrsetup"
force_clipboard: true
replace: |
```{r setup, include=FALSE}
knitr::opts_chunk$set(
warning = FALSE,
message = FALSE,
fig.path = "figs/",
fig.width = 7.252,
fig.height = 4,
comment = "#>",
fig.retina = 3
)
```
I use some simple shortcuts to load individual libraries, like rrtidy
to expand to library(tidyverse)
. But (as shown in the above image) I also set up an expansion to load some of my most often used libraries and settings. By typing rrstart
, I get this expansion:
With my libraries loaded, I then read in my data. I use rrcsv
to expand to read_csv($|$)
, rrex
to expand to read_excel($|$)
, and rrhere
to expand to here::here($|$)
, which I use to define paths to files.
Side note: if you donât already, you should definitely try using the {here} package to define paths.
I usually wrangle my data using tidyverse commands, and I have come up with a few expansions for some common âpipelinesâ. For example, one common sequence is to do grouped summary operations. I use the rrgs
(âgsâ for âgrouped summaryâ) trigger to load the code block:
group_by() %>%
summarise()
Likewise, I use the rrgm
trigger to load the code block for a grouped mutate:
group_by() %>%
mutate()
There are so many of these you could put together!
Since {ggplot2} has such a distinct syntax, I decided to start my ggplot code snippets with gg
rather than rr
. Some of my favoriate {ggplot2} expansions are the scaling shortcuts:
ggsxc
â> scale_x_continuous()
ggsyc
â> scale_y_continuous()
ggsxd
â> scale_x_discrete()
ggsyd
â> scale_y_discrete()
I also use some snippets for common geoms, like ggpoint
:
ggplot() +
geom_point(aes(x = , y = ))
and ggcol
:
ggplot() +
geom_col(aes(x = , y = , fill = ))
This is probably my favorite little hack. Often times, I want to copy-paste a snippet of text from one source to another, but when I go to paste it somewhere else, it retains all of the text formatting from the original source. My previous work around was to paste it first into a text editor (e.g. Sublime Text), then re-copy-paste it from there with the formatting now stripped away. But since espanso has support for working with your âclipboardâ, I found I could simply paste the plain text by using the force_clipboard: true
. I use ppp
as the trigger for this:
- trigger: "ppp"
force_clipboard: true
replace: "{{clipboard}}"
vars:
- name: "clipboard"
type: "clipboard"
You can run terminal commands using an espanso trigger. I havenât found too many ways to use this feature, but it is helpful for opening files or folders that may be buried deep down in some folder structure. For example, I use the :espanso
trigger to open the default.yml
file to edit my espanso settings:
- trigger: ":espanso"
replace: "{{output}}"
vars:
- name: output
type: shell
params:
cmd: "open '/Users/jhelvy/gh/espanso/espanso/default.yml'"
These come from the espanso documentation, but theyâre super useful:
- trigger: ":date"
force_clipboard: true
replace: "{{today}}"
vars:
- name: today
type: date
params:
format: "%Y-%m-%d"
- trigger: ":time"
force_clipboard: true
replace: "{{time}}"
vars:
- name: time
type: date
params:
format: "%H:%M"
There are already a lot of great espanso packages. My favorite so far is the emoji package. Itâs super convenient, but I often forget the name of specific emojis, so I made another expansion to the url to an emoji cheat sheet site:
- trigger: "//emo"
force_clipboard: true
replace: "https://www.webfx.com/tools/emoji-cheat-sheet/"
default.yml
fileThanks for reading, and I hope you check out espanso! Here are some of the expansions I have come up with in my default.yml
file (for nowâŠagain, I removed a few that had some personal information)
# espanso configuration file
# This is the default configuration file, change it as you like it
# You can refer to the official documentation:
# https://espanso.org/docs/
# Matches are the substitution rules, when you type the "trigger" string
# it gets replaced by the "replace" string.
matches:
# Sign offs
- trigger: "cjo"
force_clipboard: true
replace: "Cheers,\nJohn"
- trigger: "bjo"
force_clipboard: true
replace: "Best,\nJohn"
- trigger: "rjo"
force_clipboard: true
replace: "Regards,\nJohn"
- trigger: "cjp"
force_clipboard: true
replace: "Cheers,\nJP"
- trigger: "cpr"
force_clipboard: true
replace: "Cheers,\nProfessor Helveston"
# URLs
- trigger: "//emo"
force_clipboard: true
replace: "https://www.webfx.com/tools/emoji-cheat-sheet/"
- trigger: "//dr"
force_clipboard: true
replace: "https://drive.google.com/drive/u/0/my-drive"
- trigger: "//gh"
force_clipboard: true
replace: "https://github.com/jhelvy/"
- trigger: "//jh"
force_clipboard: true
replace: "https://jhelvy.com/"
- trigger: "//talks"
force_clipboard: true
replace: "https://jhelvy.com/talks"
- trigger: "//pubs"
force_clipboard: true
replace: "https://jhelvy.com/publications"
- trigger: "//gm"
force_clipboard: true
replace: "https://mail.google.com/mail/u/0/"
# GW
- trigger: ":title"
force_clipboard: true
replace: "Assistant Professor"
- trigger: ":gw"
force_clipboard: true
replace: "George Washington University"
- trigger: ":dept"
force_clipboard: true
replace: "Engineering Management and Systems Engineering"
# Names
- trigger: ":jp"
force_clipboard: true
replace: "John Paul Helveston"
- trigger: ":jh"
force_clipboard: true
replace: "John Helveston"
# HTML
- trigger: "hhimg"
force_clipboard: true
replace: "<center>\n<img src=\"$|$\" width=100%>\n</center>"
- trigger: "hhcss"
force_clipboard: true
replace: "<style>\n$|$\n</style>"
- trigger: "hhbr"
force_clipboard: true
replace: "<br>"
# R
- trigger: "rrrm"
force_clipboard: true
replace: "rm(list = ls())"
- trigger: "rrrs"
force_clipboard: true
replace: "rs.restartR()"
- trigger: "rrlib"
force_clipboard: true
replace: "library($|$)"
- trigger: "rrtidy"
force_clipboard: true
replace: "library(tidyverse)\n"
- trigger: "rrdply"
force_clipboard: true
replace: "library(dplyr)\n"
- trigger: "rrcow"
force_clipboard: true
replace: "library(cowplot)\n"
- trigger: "rrops"
force_clipboard: true
replace: "options(dplyr.width = Inf)\n"
- trigger: "rrstart"
force_clipboard: true
replace: "library(tidyverse)\nlibrary(cowplot)\noptions(dplyr.width = Inf)\n"
- trigger: "rrsetup"
force_clipboard: true
replace: |
```{r setup, include=FALSE}
knitr::opts_chunk$set(
warning = FALSE,
message = FALSE,
fig.path = "figs/",
fig.width = 7.252,
fig.height = 4,
comment = "#>",
fig.retina = 3
)
```
- trigger: "rrcsv"
force_clipboard: true
replace: "read_csv($|$)"
- trigger: "rrex"
force_clipboard: true
replace: "read_excel($|$)"
- trigger: "rrhere"
force_clipboard: true
replace: "here::here($|$)"
- trigger: "rread"
force_clipboard: true
replace: "read_csv(here::here($|$))"
- trigger: "rrgs"
force_clipboard: true
replace: "group_by($|$) %>%\n summarise()\n"
- trigger: "rrgm"
force_clipboard: true
replace: "group_by($|$) %>%\n mutate()\n"
- trigger: "filterne"
force_clipboard: true
replace: "filter($|$ != )"
- trigger: "rrin"
force_clipboard: true
replace: " %in% "
- trigger: "ggsxc"
force_clipboard: true
replace: "scale_x_continuous($|$) +"
- trigger: "ggsyc"
force_clipboard: true
replace: "scale_y_continuous($|$) +"
- trigger: "ggsxd"
force_clipboard: true
replace: "scale_x_discrete($|$) +"
- trigger: "ggsyc"
force_clipboard: true
replace: "scale_y_discrete($|$) +"
- trigger: "ggex"
force_clipboard: true
replace: "expand = expansion(mult = c(0, 0.05))"
- trigger: "rrch"
force_clipboard: true
replace: "```{r$|$}\n```"
- trigger: "rrmd"
force_clipboard: true
replace: "rmarkdown::render($|$)"
- trigger: "rrxi"
force_clipboard: true
replace: "xaringan::inf_mr()"
- trigger: "rrgg"
force_clipboard: true
replace: "ggplot($|$) +\n"
- trigger: "ggcol"
force_clipboard: true
replace: "ggplot() +\n geom_col(aes(x = $|$, y = , fill = ))\n"
- trigger: "ggpoint"
force_clipboard: true
replace: "ggplot() +\n geom_point(aes(x = $|$, y = ))\n"
# Dates
- trigger: ":date"
force_clipboard: true
replace: "{{today}}"
vars:
- name: today
type: date
params:
format: "%Y-%m-%d"
- trigger: ":time"
force_clipboard: true
replace: "{{time}}"
vars:
- name: time
type: date
params:
format: "%H:%M"
# Paste clipboard contents as plain text
- trigger: "ppp"
force_clipboard: true
replace: "{{clipboard}}"
vars:
- name: "clipboard"
type: "clipboard"
# Open things
- trigger: ":espanso"
replace: "{{output}}"
vars:
- name: output
type: shell
params:
cmd: "open '/Users/jhelvy/gh/espanso/espanso/default.yml'"
If you see mistakes or want to suggest changes, please create an issue on the source repository.
Text and figures are licensed under Creative Commons Attribution CC BY-SA 4.0. Source code is available at https://github.com/jhelvy/jhelvy_distill, unless otherwise noted. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".
For attribution, please cite this work as
Helveston (2021, May 4). jhelvy.com: Expand your productivity with espanso. Retrieved from https://www.jhelvy.com/posts/2021-05-04-expand-your-productivity-with-espanso/
BibTeX citation
@misc{helveston2021expand, author = {Helveston, John Paul}, title = {jhelvy.com: Expand your productivity with espanso}, url = {https://www.jhelvy.com/posts/2021-05-04-expand-your-productivity-with-espanso/}, year = {2021} }