Replacing Autokey on Wayland

Snippets are bits of text you use frequently. Boilerplate email responses, code blocks, and whatever else you regularly need to type. My general rule is, if I type it more than twice, I save it as a snippet.

I have a lot of little snippets of text and code from years of doing this. When I used the i3 desktop (and X11) I used Autokey to invoke shortcuts and paste these snippets where I need them. In Autokey you define a shortcut for your longer chunk of text, and then whenever you type that shortcut Autokey “expands” it to your longer text.

It’s a great app, but I switched to a Wayland-based desktop (Sway) and Autokey doesn’t work in Wayland yet. It’s unclear to me whether it’s even possible to have an Autokey-like app work within Wayland’s security model (Hawck claims to, but I have not tested it).

Instead, after giving it some thought, I came up with a way to do everything I need in a way like even better, using tools that I already have installed.

Rolling Your Own Text Snippet Manager

Autokey is modeled on the idea of typing shortcuts and having them replaced with a larger chuck of text. It works to a point, but has the mental overhead of needing to remember all those keystroke combos.

Dedicating memory to digital stuff feels like we’re doing it wrong. Why not search for a snippet instead of trying to remember some key combo? If the searching is fast and seamless there’s no loss of “flow,” or switching contexts, and no need to remember some obtuse shortcut.

To work though the search must be fast. Fortunately there’s a great little command line app that offers lighting-fast search: fzf, a command line “fuzzy” finder. fzf is a find-as-you-type search interface that’s incredibly fast, especially when you pair it with ripgrep instead of find.

I already use fzf as a DIY application launcher, so I thought why not use it to search for snippets? This way I can keep my snippets in a simple text file, parse them into an array, pass that to fzf, search, and then pass the selected result on to the clipboard.

I combined Alacritty, a Python script, fzf, sed, and some Sway shortcuts to make a snippet manager I can call up and search through with a single keystroke.

Python

It may be possible to do this entirely in a bash script, but I’m not that great at bash scripting so I did the text parsing in Python, which I know well enough.

I wanted to keep all my snippets in a single text file, with the option to do multiline snippets for readability (in other words I didn’t want to be writing \n characters just because that’s easier to parse). I picked --- as a delimiter because… no reason really.

The other thing I wanted was the ability to use tags to simplify searching. Tags become a way of filtering searches. For example, all the snippets I use writing for Wired can be tagged wired and I can see them all in one view by typing “wired” in fzf.

So my snippet files looks something like this:

<div class="cluster">
    <span class="row-2">
    </span>
</div>
tags:html cluster code

---
```python

```
tags: python code

---

Another goal, which you may notice above, is that I didn’t want any format constraints. The snippets can take just about any ascii character. The tags line can have a space, not a have space, have commas, semicolons, doesn’t matter because either way fzf can search it, and the tags will be stripped out before it hits the clipboard.

Here’s the script I cobbled together to parse this text file into an array I can pass to fzf:

import re
with open('~/.textsnippets.txt', 'r') as f:
    data = f.read()
snips = re.split("---", data)
for snip in snips:
    # strip the blank line at the end
    s = '\n'.join(snip.split('\n')[1:-1])
    #make sure we output the newlines, but no string wrapping single quotes
    print(repr(s.strip()).strip('\''))

All this script does is open a file, read the contents into a variable, split those contents on ---, strip any extra space and then return the results to stdout.

The only tricky part is the last line. We need to preserve the linebreaks and to do that I used repr, but that means Python literally prints the string, with the single quotes wrapping it. So the last .strip('\'') gets rid of those.

I saved that file to ~/bin which is already on my $PATH.

Shell Scripting

The next thing we need to do is call this script, and pass the results to fzf so we can search them.

To do that I just wrote a bash script.

#!/usr/bin/env bash
selected="$(python ~/bin/snippet.py | fzf -i -e )"
#strip tags and any trailing space before sending to wl-copy
echo -e "$selected"| sed -e 's/tags\:\.\*\$//;$d' | wl-copy

What happens here is the Python script gets called, parses the snippets file into chunks of text, and then that is passed to fzf. After experimenting with some fzf options I settled on case-insensitive, exact match (-i -e) searching as the most efficient means of finding what I want.

Once I search for and find the snippet I want, that selected bit of text is stored in a variable called, creatively, selected. The next line prints that variable, passes it to sed to strip out the tags, along with any space after that, and then sends that snippet of text the clipboard via wl-copy.

I saved this file in a folder on my PATH (~/bin) and called it fzsnip. At this point in can run fzsnip in a terminal and everything works as I’d expect. As a bonus I have my snippets in a plain text file I can access to copy and paste snippets on my phone, tablet, and any other device where I can run NextCloud.

That’s cool, but on my laptop I don’t want to have to switch to the terminal every time I need to access a snippet. Instead I invoke a small terminal window wherever I am. To do that, I set up a keybinding in my Sway config file like this:

bindsym $mod+s exec alacritty --class 'smsearch' --command bash -c 'fzsnip | xargs -r swaymsg -t command exec'

This is very similar to how I launch apps and search passwords, which I detailed in my post on switching from i3 to Sway. The basic idea is whatever virtual desktop I happen to be on, launch a new instance of Alacritty, with the class smsearch. Assigning that class gives the new instance some styling I’ll show below. The rest of the line fires off that shell script fzsnip. This allows me to hit Alt+s and get a small terminal window with a list of my snippets displayed. I search for the name of the snippet, hit return, the Alacritty window closes and the snippet is on my clipboard, ready to paste wherever I need it.

This line in my Sway config file styles the window class launcher:

for_window [app_id="^smsearch$"] floating enable, border none, resize set width 80 ppt height 60 ppt, move position 0 px 0 px

That puts the window in the upper left corner of the screen and makes it about 1/3 the width of my screen. You can adjust the width and height to suite your tastes.

If you don’t use Alacritty, adjust the command to use the terminal app you prefer. If you don’t use Sway, you’ll need to use whatever system-wide shortcut tool your window manager or desktop environment offers. Another possibility it is using Guake which might be able to this for GNOME users, but I’ve never used it.

Conclusion

I hope this gives anyone searching for a way to replace Autokey on Wayland some ideas. If you have any questions for run into problems, don’t hesitate to drop a comment below.

Is it as nice as Autokey? I actually like this far better now. I often had trouble remembering my Autokey shortcuts, now I can search instead.

As I said above, if I were a better bash scripter I get rid of the Python file and just use a bash loop. That would make it easy to wrap it in a neat package and distribute it, but as it is it has too many moving parts to make it more than some cut and paste code.

Shoulders Stood Upon

  • Using fzf instead of dmenu — This is the post that got me thinking about ways I could use tools I already use (fzf, Alacritty) to accomplish more tasks.