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.

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 .

is very important: it's what I have to insert in the buffer to get the expansion with . Everything after # -- is the snippet body.

is the snippet body. This particular snippet has two fields, in places of $1 and $2 .

and . $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

and 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