2011-07-08

Scala, Emacs and Etags

I've been using Emacs exclusively as my Scala IDE for the last few months. The emacs major mode for Scala is pretty good. It comes with the standard Scala distribution under misc/scala-tool-support/emacs. To load this in to your Emacs environment, I put the following two lines in my .emacs file:
(add-to-list 'load-path "/path/to/scala/misc/scala-tool-support/emacs")
(require 'scala-mode-auto)
I have a couple of gripes about formatting issues, and I may look at "fixing" them some time, but they are very small things, and I can work around them.

UPDATE: The Emacs major mode for Scala is now provided in a separate scala-tool-support archive from the downloads page. Also, a special thanks to whoever fixed the gripes about formatting issues I had!

At some point I decided to get Etags working with my Scala development. It's sort of an old fashioned equivalent to the typical IDE features "go to definition of this class/method/etc" and "find usages of this class/method/etc". It's a little less context-sensitive than the IDE equivalent, you need to configure it a bit yourself, and you need to manually regenerate your tags every once in a while to keep it up to date with changes to your codebase. I got started with Scala Etags by following instructions in this blog post:
I put the following lines in my .ctags file (in my home directory) to get things going:
--langdef=Scala
--langmap=Scala:.scala
--regex-Scala=/^[^\*\/]*class[ \t]*([a-zA-Z0-9_]+)/\1/c,classes/
--regex-Scala=/^[^\*\/]*object[ \t]*([a-zA-Z0-9_]+)/\1/o,objects/
--regex-scala=/^[^\*\/]*trait[ \t]*([a-zA-Z0-9_]+)/\1/t,traits/
--regex-Scala=/^[^\*\/]*case[ \t]*class[ \t]*([a-zA-Z0-9_]+)/\1/q,case-classes/
--regex-Scala=/^[^\*\/]*abstract[ \t]*class[ \t]*([a-zA-Z0-9_]+)/\1/a,abstract-classes/
--regex-Scala=/^[^\*\/]*def[ \t]*([a-zA-Z0-9_]+)/\1/m,methods/
Now, in the root of my project, I run the following command to update my tags:
etags -R
To jump to a class or method definition, I place my point over the symbol name, and type M-. (that's emacs talk for holding down the ALT key and pressing '.'). To iterate through uses of the symbol, I type M-0 M-. to get to the next symbol, and M-* to start over again. Nice!

I recently started getting a warning from the etags -R command which made me realize that Etags was traversing through my target directories!
etags: Warning: ignoring null tag in server/target/scala_2.8.1/doc/main/api/lib/jquery-ui.js
I fixed this problem by adding the following line to my .ctags file, which tells Etags to ignore anything in any target directories:
--exclude='*/target/*'
That's nice.

Yesterday I was embarking on yet another major refactoring of my code, which was awesome. Today I am faced with a recurring problem: a whole bunch of TODO comments in my code that I need to address, (mostly for missing Scaladoc comments that need to be filled in), before I check in. My standard way of handling this with with a recursive grep. Something like:
grep -C2 -ir todo */src | grep -v svn
Quick aside here. Yes, I am using Subversion! That's okay. I'm liking git a lot, but Subversion is what we use on our team. So the pipe into grep -v svn is used to filter out results on .svn-base files and the like.

Anyway, back to the point. This morning I had the idea that I could use Etags to help me cycle through my TODO comments. So I added the following line to my .ctags file, and regenerated my TAGS file:
--regex-Scala=/(TODO)/\1/d,todo/
It's a little cheesy that I have this as a Scala-specific pattern, but I really don't mind. Now, in emacs, I can M-. TODO, and the M-0 M-. to cycle through all my TODO comments. Nice!

It's amazing what you can get away with using old-fashioned language-independent Emacs tricks to substitute for the IDE features we have grown so accustomed to. It's like paying for cable. We think we "need" it, but then we stop paying the bill, and we never miss it. The prime example of this is Emacs metaslash (M-/), which autocompletes for me about as agreeably as Eclipse ever did. (It's much more responsive as well.) If you use Emacs and don't know metaslash, check it out!

1 comment: