Scripting a better 'cd' and then some

This is a somewhat long winded tale about how I came up with a way to type gprails expr and have a bunch of iTerm tabs open in a project folder matching expr with each tab starting one of mongrel, textmate, tailing of different logs, script/console, and so on. Skip to the end if you just want the script...

I don't know about you but I sometimes think I am collecting programming projects. I have folders and folders of them, for different languages, frameworks, backup copies, open source stuff, branches, what a mess.

So like my Rails projects are in:

/Users/matt/Projects/rails/myproject

or

/Users/matt/Projects/rails/project/branch

but some of them are related to my current company and they are in:

/Users/matt/Projects/topsecret/project

with Ruby stuff in:

/Users/matt/Projects/ruby/apps/project

and so on.

Most of my current activity is related to developing a Rails application. Every time I start work I go through a routine like this:

  • Open iTerm
  • cd to the project folder
  • start TextMate
  • start Diffly
  • start mongrel
  • open a new tab
  • cd to the project folder
  • clear the logs
  • start a tail on the development log
  • open a new tab
  • cd to the project folder
  • start a tail on the request log
  • open a new tab
  • cd to the project folder
  • ....

All those cd's get old fast even with copy & paste.

I've tried various solutions based on aliases, scripting find (this was difficult because find seemingly has no way of halting after the first match), and hard coding paths. All were workable but messy and frustrating. What I wanted was a cd command that would know about the structure of my projects folder and just do the right thing.

A couple of days ago I caved and wrote a ruby script that first indexes my project folders to a specified depth ( weeding out useless stuff like ".svn" folders and so forth) and then searches for a specified substring. If more than one match was found it presents a list of matches and allows me to select the one I want.

Then I hit another snag. There was no way for this ruby process to affect the working directory of the terminal that owned it. I considered writing a shell script to take the output of the ruby script but this wouldn't work if you needed to print out the list of matches to choose from.

That's when Defiler suggested I try scripting iTerm with RubyOSA. It was a neat idea, have the Ruby script using an AppleEvent to tell iTerm to write the "cd" command into the terminal. Except that the terminal would be running a ruby process which would eat the input. Bah!

Still, it was a good idea, and I could live with opening a new tab I guess. So I consulted the Script Dictionary for iTerm and came up with an AppleScript that opened a new terminal tab and changed to a specified directory. Nearly home.

Except RubyOSA absolutely would not play ball with iTerm. Or vice verca. According to RubyOSA all the cool methods for finding the current terminal, making new sessions, and so on were in a class OSAITermApplication but the only thing I could get a reference to was OSAIterm and there appeared to be no way to get from one to the other.

Then Has pointed me at Ruby Appscript which is another bridge to AppleScript that works hard to make even difficult apps like iTerm behave normally w.r.t. AppleScript. He gave me a simple demo and I was able to extend that to do what I needed.

Now I had a script which I called gp for (Goto Project) that would, based on a search expression, open a new tab in the correct folder. Or, if more than folder matched, present me with the choices, let me choose, then open a new tab in the correct folder. Then I added a parameter to say how many new tabs to open so I could get all 5 that I needed at once. This was cool.

Then it occurred to me, why stop at opening a terminal? Now I had a means to run any command in a terminal window I could have it do everything. So I added a simple scripting facility. Here is my rails script:

mate .; diffly .; mongrel_rails start
rake log:clear; tail -f log/development.log
tail -f log/request.log
ruby script/console
irb
#

Each line is executed in it's own terminal. This opens the project source in Textmate, opens the current svn diff in Diffly, starts mongrel, clears the developer log and opens the two logs I care about, then gives me a console and an irb session, plus a spare terminal in the right directory.

Scripts live in a .gp folder in the projects folder alongside the index of project folders. I modified gp to check on the filename used to run it and automatically run a script based on the prefix so:

ln -s /Users/matt/bin/gp /Users/matt/bin/gprails

means I can type:

gprails myproject

and have it do all of the above in the right folder for myproject. I'll probably write a script for working with Cocoa projects too.

I've no idea if this is of any interest or use to anyone else but it's a nice solution for me and if you're interested in scripting iTerm it might be a good place to start from. My 'gp' script is here.

If you decide to play with it you'll want to change where it looks for projects unless you use ~/Projects like I do. Also the first time you run it it will build the index of project folders and write a default rails script for you but you'll want to modify that script unless you use the exact same combination of commands that I do.

Enjoy.

31/10/2007 20:13 by Matt Mower | Permalink | comments:
More about: