Fuzzy search snippets for the desktop.

The problem

During everyday computer use, there are several non-trivial things that must be typed fairly often. Things like your email address or ¯\_(ツ)_/¯. Usually this means typing it out in full, or navigating to somewhere like https://textfac.es/ and copying emojis from there. In the spirit of automating everything and efficiency, let’s see if we can improve on this!

The idea

So, what if I could press a key sequence, fuzzy search for a “snippet” by name, and instantly have it typed or copied to the clipboard?

The solution

After some fiddling around with bash and python scripts, I managed to work out a solution that seems to work pretty well! Simply put, it does the following:

  1. Load in a yaml file containing name: snippet entries.
  2. Launch rofi with the snippet names for fuzzy search.
  3. Copy the corresponding snippet to the clipboard or use xdotool to type it out.

screenshot view of fuzzy searching snippets with rofi

The script can be called with python scriptname.py copy|type path/to/database.yml. This can be set up with a keybinding for launching, for example in i3:

bindsym Mod4+z exec clip-snippet.py type ~/Documents/snippets.yml
bindsym Mod4+Shift+z exec clip-snippet.py copy ~/Documents/snippets.yml

Now the workflow becomes: press Mod4+z, type a couple of characters from the snippet name, press Enter, and it gets instantly typed into the active window! Alternatively, Mod4+Shift+z to copy the snippet to the clipboard.

The code

For reference, the code (current version) and an example snippets file is below. Note: the code is also available on GitHub.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2017 Samuel Walladge
# fuzzy search for text snippets to copy to clipboard
# depends on PyYaml, rofi, xsel, xdotool, and a yaml file containing key:snippet pairs

import yaml
import sys
import subprocess

COPY = 'copy'
TYPE = 'type'

if len(sys.argv) < 3:
    print('Usage: clip-snippet.py copy|type YAML_DB_FILENAME')
    sys.exit()

mode = sys.argv[1]
db_filename = sys.argv[2]

if mode not in (COPY, TYPE):
    print('Invalid mode! Must be either copy or type.')
    sys.exit(-1)


try:
    # { 'name': 'snippet' }
    db = yaml.load(open(db_filename))
except Exception as e:
    print('Failed to load database file ({!r})!'.format(db_filename))
    print('Ensure that it exists and is a valid yaml file.\n')
    print(e)
    sys.exit(-1)


keys = '\n'.join(db.keys())
result = subprocess.run(['rofi', '-dmenu', '-no-custom', '-p', 'snippet:', '-matching', 'fuzzy'],
        input=keys, stdout=subprocess.PIPE, encoding='utf-8')
if result.returncode != 0:
    sys.exit(-1)

key = result.stdout.strip()
snippet = db.get(key, None)
if snippet is None:
    sys.exit(-1)

# copy to primary and clipboard selection
if mode == COPY:
    subprocess.run(['xsel', '-ip'], encoding='utf-8', input=str(snippet))
    subprocess.run(['xsel', '-ib'], encoding='utf-8', input=str(snippet))
elif mode == TYPE:
    subprocess.run(['xdotool', 'type', '--', snippet], encoding='utf-8')

Example snippets file. I chose yaml because it’s simple and way more flexible that json for hand editing.

# snippets file for clip-snippet.py
# keys are snippet names (able to be fuzzy searched on)
# values are the snippets themselves - values can be multiline if required too

shrug: ¯\_(ツ)_/¯
email: test@example.com
koala emoji: ʕ•ᴥ•ʔ
disapproval emoji: ಠ_ಠ
table flip: (╯°□°)╯︵ ┻━┻

It’s all pretty hacky and simple, but it works nicely. Hope this will be useful to someone! Comments, feedback, or suggestions for improvement welcome!