Using Emacs yasnippet against repetitive boilerplate code

Yasnippet is an emacs package for snippet expansion into code according to predefined rules. In this post I show how to use it for doing away with common boilerplate code in C.

This blogpost will target the C language but all of its contents can easily be transferred to any other verbose enough language where repetition some times may arise.

I love working with C code, for the simplicity and clarity it gives if written well when compared to the higher-level alternative languages like C++. The other side of the coin for this is that C requires you to write a lot of boilerplate code. You can either go ahead, bite the bullet and write it every time or try to abstract it away in preprocessor macros.

The macros way can go really far if you combine it with preprocessor magic like counting the number of arguments passed to a variadic macro. I have used the macros approach personally in a C project I am working on. It was okay at first but then it became so ugly to look at and messed up with code jumping in any IDE (including Emacs) that I decided to abandon the approach.

Introducing yasnippet

So what is the alternative? I have been experimenting a bit with yasnippet lately and I find it to be a very useful tool for exactly the problem I described above.

Yasnippet works in a quite simple but powerfull manner. It has the definition of a code snippet which is a templated piece of code in a specific language. You can define a keyword after which you can press a particular key, the TAB key by default, and then your keyword will expand into the code snippet following the template rules. It comes with some predefined snippets for various language modes but also allows you to define your own.

You can have yasnippet enabled globally. But if you are like me and only need it in some specific modes you can have something like this in your emacs configuration:

;;; init-yasnippet --- my yasnippet configuration
;;; Commentary:
;;; Code snippets expansion
;;; https://capitaomorte.github.io/yasnippet/
;;;
;;; Code:
(require 'yasnippet)
(yas-reload-all)

(add-hook 'c-mode-common-hook #'yas-minor-mode)
(provide 'init-yasnippet)
;;; init-yasnippet.el ends here

The templated language for yasnippet is really simple. You can describe how you would like the code to look like and insert variables in the form of $n where n is a number that refers to the number of tab completions you want to do when filling in the snippet. At the expansion of the snippet you will be prompted for each of these variables. A variable can also have a default value which is specified like this: ${1:object}.

Using yasnippet for boilerplate code

Yasnippet has some snippets already predefined for C code which you can find under c-mode in the yasnippet, snippets directory but I personally find them lacking. As an example here is a snippet to expand pr into printf taken from the predefined snippets

# -*- mode: snippet -*-
# name: printf
# key: pr
# --
printf("${1:format string}"${2: ,a0,a1});

A simple example for C structs

But since we can create our own snippets let’s get something useful! For those of you who have programmed long in C you must already be quite familiar with the 4 much needed functions to:

  • Allocate a new object of a structure.
  • Initialize a new object of a structure.
  • Destroy an object of a structure.
  • Deinitialize an object of a structure.

Most of the times the code for the above is very repetitive and is the perfect candidate for a snippet. Below you can see a yasnippet that accomplishes exactly that.

# name : C struct Creation and destruction functions
# key : crtdstr
# --

bool ${1:prefix}_${2:name}_init(struct $1_$2 *${3:obj})
{
    return true;
}

struct $1_$2 *$1_$2_create()
{
    struct $1_$2 *ret;
    RF_MALLOC(ret, sizeof(*ret), return NULL);
    if (!$1_$2_init(ret)) {
        free(ret);
        ret = NULL;
    }
    return ret;
}

void $1_$2_deinit(struct $1_$2 *$3)
{

}

void $1_$2_destroy(struct $1_$2 *$3)
{
    $1_$2_deinit($3);
    free($3);
}

In order to use it, just add it under one of the directoris defined in yas-snippet-dirs under a subdirectory called c-mode.

The above snippet has 3 template variables. One for the prefix of the object, one for its name and one for the name of the object variable. These will be filled in with 3 consecutive TAB completions.

The prefix is usually used as a poor man’s way to namespace things in C, so it’s usually the name of the module or library.

An example of the above snippet in action can be seen below

yasnippet1.gif

The demo starts with the keyword crtdstr we defined in the snippet. Then the steps are:

  1. <Tab> -> Expand the snippet and prompt for first variable
  2. Input value for first variable, press <Tab> to continue
  3. Input value for second variable, press <Tab> to continue
  4. Input value for third variable, press <Tab> to finish the snippet

And here is the created C code. RF_MALLOC() is a macro I am using for malloc(), plus error reporting.

bool rf_string_init(struct rf_string *str)
{
    return true;
}

struct rf_string *rf_string_create()
{
    struct rf_string *ret;
    RF_MALLOC(ret, sizeof(*ret), return NULL);
    if (!rf_string_init(ret)) {
        free(ret);
        ret = NULL;
    }
    return ret;
}

void rf_string_deinit(struct rf_string *str)
{

}

void rf_string_destroy(struct rf_string *str)
{
    rf_string_deinit(str);
    free(str);
}

A more complete example for C code

We can also see a more complete version of the above example where we will do the same thing as above but form a full C file and infer the name of the structure from the filename.

# -*- mode: snippet -*-
# name : C full definition of a file with include guards and functions
# key : cfullfile
# --
#ifndef ${1:$(upcase yas-text)}_${2:`(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`_H}
#define ${1:$(upcase yas-text)}_$2

struct ${1:prefix}_${3:`(downcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`} {
$0
};

bool $1_$3_init(struct $1_$3 *${4:obj})
{
    return true;
}

struct $1_$3 *$1_$3_create()
{
    struct $1_$3 *ret;
    RF_MALLOC(ret, sizeof(*ret), return NULL);
    if (!$1_$3_init(ret)) {
        free(ret);
        ret = NULL;
    }
    return ret;
}

void $1_$3_deinit(struct $1_$3 *$4)
{

}

void $1_$3_destroy(struct $1_$3 *$4)
{
    $1_$3_deinit($4);
    free($4);
}
#endif /* ${1:$(upcase yas-text)}_$2 */

Add this to a new file under the c-mode snippets. Below you can see an example of the snippet expansion in action ran on a file named tree.c.

yasnippet2.gif

From the filename we infer all the needed information and we simply provide the rf prefix as required. After that we just <Tab> over the other variables confirming their values. We could also change them if we wanted. The resulting C code can be seen below

#ifndef RF_TREE_H
#define RF_TREE_H

struct rf_tree {
    // here fill in the struct
};

bool rf_tree_init(struct rf_tree *t)
{
    return true;
}

struct rf_tree *rf_tree_create()
{
    struct rf_tree *ret;
    RF_MALLOC(ret, sizeof(*ret), return NULL);
    if (!rf_tree_init(ret)) {
        free(ret);
        ret = NULL;
    }
    return ret;
}

void rf_tree_deinit(struct rf_tree *t)
{

}

void rf_tree_destroy(struct rf_tree *t)
{
    rf_tree_deinit(t);
    free(t);
}
#endif /* RF_TREE_H */

How is that achieved? If you take a careful look at the snippet you can see that we have used embedded elisp code in it. For example in order to get the value that should go to the #ifndef guards we used the following.

${1:$(upcase yas-text)}_${2:`(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`_H}

Remember that variables are numbered for a reason. This may be in the beginning of the snippet but assumes that $1 is already set and uppercases it. Following that it gets the filename and uppercases that. This, as usual in C code, forms the name of the #ifndef guard.

A bit further down we see the following:

struct ${1:prefix}_${3:`(downcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`} {
$0
};

Here is where $1 is first prompted for insertion and where we acquire the prefix. That in combination with similar code to the #ifndef guards form the name of the structure inferred from the file name.

A final thing to note is the special variable $0 in the body of the structure. This is where the cursor of the user will end up after the snippet expansion is finished.

Conclusion

This was just a simple example of using yasnippet to automate and simplify repetitive boilerplate code. If you put enough time into it you can come up with quite more powerful and empowering snippets. To do that important references would be:

2 thoughts on “Using Emacs yasnippet against repetitive boilerplate code”

  1. Thank you for the article! The code to include the filename in yasnippets made my day.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.