(or emacs irrelevant)

Re-introducing auto-yasnippet

I wonder, when the code isn't touched in a long time, is it good (no need for changes) or bad (became obsolete)? Let's find out. I'll explain here auto-yasnippet, my second package in MELPA out of more than a dozen currently that saw almost no changes since the initial commit two years ago, and see if you like it.

Short description of yasnippet

YASnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates. Bundled language templates include: C, C++, C#, Perl, Python, Ruby, SQL, LaTeX, HTML, CSS and more.

Snippet step-by-step

Here's one of the snippets that I use for emacs-lisp-mode:

# -*- mode: snippet -*-
# name: function
# key: d
# --
(defun $1 ($2)
$0)
  • The name of the snippet, function is more like a comment than anything else.
  • On the other hand, key is very important: it's what I have to insert in the buffer to get the expansion with M-x yas-expand.
  • Everything after # -- is the snippet body.
  • This particular snippet has two fields, in places of $1 and $2.
  • $0 is where the point will be when the snippet expansion is finished

As I expand, pressing TAB will move from field to field until the expansion is finished.

Snippets are mode-local

Here's the corresponding snippet for clojure-mode:

# -*- mode: snippet -*-
# name: defn
# key: d
# --
(defn $1 [$2]
$0)

As you see, the key here is the same; you're allowed to overload them based on the current major-mode.

Even after quite a few posts, I still keep forgetting Jekyll's syntax for the post header. This is my reminder:

# -*- mode: snippet -*-
# name: post
# key: post
# --
---
layout: post
title: $0
---

Mirrors in snippets

This simple snippet introduces a powerful concept, and an important yasnippet feature that auto-yasnippet uses:

# -*- mode: snippet -*-
#name : class ... { ... }
# --
class $1$2
{
public:
 $1($0)
};

This is a snippet for a class declaration in c++-mode; $1, the name of the class, is mirrored in the name of the constructor. This way, you don't have to enter it twice.

What auto-yasnippet does

All the snippets listed above are pre-configured, persistent and very rarely changed. They are like plain functions in the source code. Each of them needs their own file and so on.

What auto-yasnippet provides are throw-away lambdas, that don't need a file and aren't persistent.

Basic install of auto-yasnippet

To get a usable install, you just need to bind aya-create, which is similar in spirit to M-w (kill-ring-save):

(global-set-key (kbd "H-w") 'aya-create)

and aya-expand, which is similar to C-y (yank):

(global-set-key (kbd "H-y") 'aya-expand)

I also like to bind:

(global-set-key (kbd "C-o") 'aya-open-line)

I'm using C-o to do all of these:

  • expand-abbrev
  • yas-expand and yas-next-field-or-maybe-expand
  • open-line

Example 1: JavaScript

Let's say that you have this code and want to generate more like it:

field1 = document.getElementById("field1");

Let's even assume that you know how auto-yasnippet works and wrote down a slightly modified code beforehand:

field~1 = document.getElementById("field~1");

Here, ~ are meant to represent yasnippet's mirrors, they will be consistent across every expansion. Now you type H-w (aya-create), which works on the current line when there's no region. Your code becomes the initial one without ~, and aya-current variable now holds:

aya-current
;; => "field$1 = document.getElementById(\"field$1\");"

By typing e.g. H-y 2 C-o RET, H-y 3 C-o RET, H-y Final C-o RET you get:

field2 = document.getElementById("field2");
field3 = document.getElementById("field3");
fieldFinal = document.getElementById("fieldFinal");

Note again, that there would be little point to saving this snippet in a file, since the situation where you need to use it may not come up again.

Example 2: Java

Here's the starting code, with fields and mirrors already in place (note one mirror named ~On, one field named ~on and one field named ~true):

class Light~On implements Runnable {
  public Light~On() {}
  public void run() {
    System.out.println("Turning ~on lights");
    light = ~true;
  }
}

Since the code spans multiple lines, as is often the case with Java, you need to mark it with a region before H-w.

Here's the final result:

class LightOn implements Runnable {
  public LightOn() {}
  public void run() {
    System.out.println("Turning on lights");
    light = true;
  }
}
class LightOff implements Runnable {
  public LightOff() {}
  public void run() {
    System.out.println("Turning off lights");
    light = false;
  }
}

No need for AbstractLightFactoryAdapterProvider when we can just copy-paste stuff with auto-yasnippet. In fact, I should probably emphasize that when describing auto-yasnippet: it's just an advanced copy-paste tool.

Example 3: C++

Suppose that I want to generate curl from the grad. I can start with this template:

const Point<3> curl(grad[~2][~1] - grad[~1][~2],

Here, I need less than a line, so region needs to be marked again. The result:

    const Point<3> curl(grad[2][1] - grad[1][2],
                        grad[0][2] - grad[2][0],
                        grad[1][0] - grad[0][1]);

Note how annoying it would be to triple check that the indices match. This time, I just had to check the first line.

Example 4: taking ~ out of the equation

This works only for one-line snippets with a single mirror parameter. In the JavaScript example, you can leave a $ instead of each occurrence of $1, and with the point in place of the last occurrence you call H-w (aya-create). Here, | represents the point:

field$ = document.getElementById("|");

The final result is the same:

field1 = document.getElementById("field1");
field2 = document.getElementById("field2");
field3 = document.getElementById("field3");
fieldFinal = document.getElementById("fieldFinal");

Outro

I hope that this package will lessen your suffering when dealing with verbose programming languages and repetitive text, and that a day will come when repetition is no longer needed and auto-yasnippet will become obsolete.