Saturday, November 8, 2008

Emacs - TextMate like snippets

I love Emacs, and while I'm no guru, I know my way around it.

At Adobe I had the option of using a MacBook Pro, so naturally, being a dynamic languages freak I've had the opportunity of experimenting with Textmate, as all the cool kinds use it in screencasts.

The coolest feature of Textmate is its intuitive and powerful snippets mechanism. The syntax is easy to understand and use and with it you can automate boring boilerplate code. If you don't know what I mean, take a look at the screencasts from rubyonrails.org. So yeah, I felt jelousy that my favorite text editor, Emacs, doesn't have it.

But then I found this wonderful Emacs plugin: Yasnippet. You can also watch a YouTube introductory screencast to whet your appetite. It uses the same Textmate syntax, with a notable difference: for text filters you can use elisp syntax.

Installing it is easy ... just download the latest version, extract the files to your "site-lisp" directory, which usually is "~/.emacs.d/site-lisp" on Linux. If you cannot find your "site-lisp" directory, just create one in "emacs.d/site-lisp" (or whatever) and add this line to your ".emacs" configuration file:
(setq load-path (cons "~/emacs.d/site-lisp" load-path))
Then you need to add to the same ".emacs" configuration file this line:
(require 'yasnippet)
(yas/initialize)
(yas/load-directory "~/emacs.d/site-lisp/snippets")
Be careful to change the specified directory ("~/emacs.d/site-lisp/snippets") to match your own setup.

Defining snippets is easy. It implies creating for every snippet a file containing specific syntax. Snippets are context-dependent ... you can define snippets that only work in html-mode, or only in java-mode (as in Textmate). Editing modes in Emacs have an inheritance model, as in ... java-mode inherits cc-mode, which in turn inherits text-mode. The "snippets" directory has a similar structure, so if you want to define a snippet for java-mode, you can define it in "cc-mode" and it will be inherited by all derived modes, like c++-mode and java-mode, or you can define it for java-mode.

To take an example, to define a snippet named "bean" for java-mode, you need to edit the file "snippets/text-mode/cc-mode/java-mode/bean". And when you'll type "bean" with a TAB following, it will expand. Such a snippet could have the following syntax:
private ${2:String} ${1:varname};

public void set${1:$(upcase-initials text)}(${2:String} new${1:$(upcase-initials text)}) {
${1:fieldName} = new${1:$(upcase-initials text)};
}

public ${2:String} get${1:$(upcase-initials text)}() {
return ${1:fieldName};
}

${N} is a hole in the snippet where you can type text. It can have a default, and you can also have filters for the typed text, by using elisp functions, as in the above example where the typed text for the variable name has the initial upper-cased (according to Java conventions).

Now for my next problem: I'm experimenting with Catalyst web-framework, which uses by default the Template Toolkit. I don't mind using html-mode for editing TT files, but it's a problem for snippets because I used and will use lots of other template managers with html-mode, so snippets names may clash, and I don't want to prefix the names I use for snippet-expansion.

But emacs is wonderful right? So why not define our own editing mode, inherited from html-mode?

To create an inherited mode from html-mode, I created a file named "site-lisp/template-toolkit-mode.el" with the following code:
;; specifies that this script provides an emacs module
(provide 'template-toolkit-mode)

;; defines a derived editing mode, inheriting from html-mode
(define-derived-mode template-toolkit-mode html-mode "Template Toolkit2" nil)
And in our ".emacs" configuration file we can initialize this mode automatically for files ending with ".tt" (the file extension):
(autoload 'template-toolkit-mode "template-toolkit-mode" nil t)
(add-to-list 'auto-mode-alist '("\\.tt$" . template-toolkit-mode))
That's it. Now we can define snippets only for template-toolkit file types. For example this is a snippet creating a foreach loop:
[% FOREACH ${1:item} IN ${2:items} %]
$0
[% END -%]
Btw, if your interested in my emacs configuration (nothing special about it, but may give you some hints), you can find it here: http://code.google.com/p/my-emacs-d/

Enjoy ~

3 comments:

Anonymous said...

I discovered recently there is an anything.el (http://www.emacswiki.org/cgi-bin/wiki/Anything) binding for yasnippet:

http://d.hatena.ne.jp/IMAKADO/20080323/1206280112


Pretty handy.

DanielDBarber said...

Excellent write up. Thanks for sharing.

mapleoin said...

awesome plugin. I'm just starting out with emacs. Now the migration from vim will be a lot easier.