Navigating Your Projects in Emacs

Posted by Doug Wed, 07 Nov 2007 16:04:00 GMT

One of the things that’s really nice about TextMate is the Cmd-T navigation of files in your project. It pops up this little dialog with fancy pattern matching input to select one of the files in your project and then jumps to that file. There’s a similar command once your in a file to jump to symbols in that file. I’ve tried a couple things to achieve that behavior in emacs that I’d like to talk about.

The first thing I tried was a method called find-file-in-project. It was originally implemented by Phil Hagelberg as part of his “Another Ruby on Rails Mode” (arorem) and then also ported over to the current Rinari Is Not a Rails IDE. Basically, it indexes all the files in a “project” and then provides a nice interactive completing read to switch between them. It basically works just like Cmd-T in TextMat. I did quite a bit of work optimizing the completing read so that it would behave nicely. The problem is that emacs is slow to index the files in your project.

What I’m using now is the tried and true find-tag method which is part of etags.el. ETags depends on an external TAGS file as an index of all the symbols in your project. When you invoke find-tag (by default bound to M-.) it prompts you with completing read for the symbol to find. It then jumps directly to the file and location where that symbol is defined. It’s basically combining the find-file-in-project with find-symbol-in-buffer. It’s also very, very fast.

As an added bonus, you can use tags-search to search through your project finding places that tag is used. This is similar to TextMate’s slowly grep all the files and render a buffer with those results in it, but much faster.

The bad news is you have to manually build the TAGS file periodically. Emacs is pretty good about continuing to work when you’ve made modifications to the files, but if you add new methods, new files, or refactor significantly where thing are located then it gets confused. When that happens, simply rebuild the TAGS file.

Jim Weirich gave me a nice little rake task to do the job:

module Tags
  RUBY_FILES = FileList['**/*.rb'].exclude("pkg")
end

namespace "tags" do
  task :emacs => Tags::RUBY_FILES do
    puts "Making Emacs TAGS file"
    sh "xctags -e #{Tags::RUBY_FILES}", :verbose => false
  end
end

task :tags => ["tags:emacs"]

Just put that in lib/tasks/tags.rake and then run rake tags:emacs when you invoke find-tag and it can’t find it. That shouldn’t happen very often. I’ve found it’s also very fast to build the TAGS file. I might consider putting that tags task as part of the normal run tests task, but I’m not sure that’s necessary.

The other catch here is that the rake task above calls out the exuberant ctags rather than the ctags that comes with emacs. The exuberant ctags knows how to parse ruby files whereas the ctags that comes with emacs can’t.

I’ve installed exuberant ctags from MacPorts. It is xctags even though MacPorts doesn’t install it that way. So (for better or worse) I’ve renamed the ctags files installed with ports to xctags. This also gets around the conflict that MacPorts thinks there is between the ctags installed with emacs and the exuberant ctags.

Give this a spin and let me know what you think. I’ve found it to be very accurate and very fast.

UPDATE: I’ve added a github repository for my find-file-in-project. Feel free to do with it as you will.

Posted in , , , ,  | Tags , , , , ,  | 4 comments

Ventruing into Lisp

Posted by Doug Tue, 20 May 2003 15:16:00 GMT

I’ve been writing Emacs:EmacsLisp for a couple of years now (and using emacs for many, many years). I like the style of Lisp. I’ve read lots of Paul Graham. He writes over and over again how things that are “impossible” in other languages are possible in Lisp. He’s claimed for years that working in Lisp is a competitive advanatage because of the power and flexibility it gives you more than any other language. Of course, as an elitist programmer, I want to believe this. The problem is I’ve never gottem much chance to experiment with it in real life applications outside of extending emacs (I’m trying to collect as much of that type stuff on my CategoryEmacs page).

I’ve recently been presented with a problem that lends itself particularly well to Lisp. One of my clients is “subscribing” to some data from a large vender. That vendor is providing it as two 15MB XML feeds. My first thought was to use Perl’s XML::Parser. For better or for worse, XML::Parser choked on it. I’m sure I could have made it work if I had fiddled with some of the more advanced features. What I wanted though was to simply $my parser = new XML::Parser; $parser->parserfromfile("foo.xml"); and move on to using the data. Proponents of Lisp have claimed that XML is Lisp for people afraid of parentheses. It seems Lisp should be a natural win for parsing XML.

Indeed it is. I’ve been struggling quite a bit though. Once I got some hand-holding I was able to use James Anderson’s CL-XML loaded into Steel Bank Common Lisp it was only about five minutes later I got (setf my-xml-data (xmlp:document-parser "foo.xml")) to work on the first of my 15MB files. The hard part has been knowing how to work in Lisp. How do you compile Lisp? How do you load libraries? How do I make sense of these compiler errors? These are things I intimately know for other languages but that I’m having to learn from scratch with Lisp. The other big problem is that cl-xml doesn’t seem to be documented very well to users like me. It makes heavy use of the Common Lisp Object System (which I don’t know). So, this whole process has been a baptism by fire.

The good news is that I’ve made some progress (Marc if you’re reading this that’s good news for you). I’ve managed to generate SQL for a big piece of the XML file. There XML data is broken up into “markets”. Each market has four main objects: offers, boltons, features, and promotions. I’ve managed to generate the SQL for inserting the offer data from a single market. I think this code is going to be very similar for the other three objects. So, I’m going to try and take advantage of Lisp’s ability to write code that generates code here. I’ve written just over 100 lines of Lisp so far. I don’t actually think it will take much more than this to do the whole thing. I’ll keep you posted.

Posted in  | no comments

Copyright 2001 - 2005 by Lathi.net and Doug Alcorn

Creative Commons, Some Rights Reserved Ruby on Rails Developer Powered by Debian GNU/Linux Powered by Typo