Re-introducing auto-yasnippet
30 Jan 2015I 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-xyas-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
andyas-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.