Luke Melia

software dev

October 3, 2007

Clickable Ruby Stacktraces with iTerm & TextMate

UPDATED: Rev’d the script to strip out “stupid rails /../ crap”. Thanks, Ryan.

When I’m working in Ruby on my delightful new MacBook, I sometimes miss a feature of VisualStudio: the ability to double-click a line of a stacktrace in a test failure and open that file and line number in the editor.

I started thinking about how it might be possible at RubyEast on Friday, and tonight I hacked something together that works for TextMate and iTerm, my tools of choice. Here’s the scoop:

iTerm has a feature where you can Command-Click a URL in terminal window and “follow” it using the app registered for that protocol. TextMate has a txmt:// URL scheme that lets you open files in textmate.

I wrote a ruby script that filters input to convert stacktrace lines to the txmt::// format. So here is a normal test failure (without processing through the filter):

[code]
$ /usr/local/bin/ruby -I.:lib:test -rtest/unit -e "%w[test/unit/some_test.rb].each { |f| require f }"
Loaded suite -e
Started
.....F...
Finished in 0.128739 seconds.

1) Failure:
test_spec {Someone, in general, } 006 [should say something](Someone, in general, )
[/Users/lmelia/devprojects/secret/config/../vendor/plugins/test_spec_on_rails/lib/test/spec/rails/test_spec_ext.rb:6:in `equal'
./test/unit/blog_entry_factory_test.rb:68:in `test_spec {Someone, in general, } 006 [should say something]'
/Users/lmelia/devprojects/secret/config/../vendor/gems/mocha-0.5.3/lib/mocha/test_case_adapter.rb:19:in `__send__'
/Users/lmelia/devprojects/secret/config/../vendor/gems/mocha-0.5.3/lib/mocha/test_case_adapter.rb:19:in `run']:
<"Someone's clever response goes where?"> expected but was
<"Someone's clever response goes here.">.

9 tests, 11 assertions, 1 failures, 0 errors
$
[/code]

And here it is running through the textmate_urls Ruby script:

[code]
$ /usr/local/bin/ruby -I.:lib:test -rtest/unit -e "%w[test/unit/blog_entry_factory_test.rb].each { |f| require f }" | textmate_urls
Loaded suite -e
Started
.....F...
Finished in 0.111423 seconds.

1) Failure:
test_spec {Someone, in general, } 006 [should say something](Someone, in general, )
[txmt://open?url=file:///Users/lmelia/devprojects/secret/vendor/plugins/test_spec_on_rails/lib/test/spec/rails/test_spec_ext.rb&line=6 :in `equal'
txmt://open?url=file:///Users/lmelia/devprojects/secret/test/unit/blog_entry_factory_test.rb&line=68 :in `test_spec {Someone, in general, } 006 [should say something]'
txmt://open?url=file:///Users/lmelia/devprojects/secret/config/../vendor/gems/mocha-0.5.3/lib/mocha/test_case_adapter.rb&line=19 :in `__send__'
txmt://open?url=file:///Users/lmelia/devprojects/secret/config/../vendor/gems/mocha-0.5.3/lib/mocha/test_case_adapter.rb&line=19 :in `run']:
<"Someone's clever response goes where?"> expected but was
<"Someone's clever response goes here.">.

9 tests, 11 assertions, 1 failures, 0 errors
$
[/code]

Now I can mouseover any of the stacktrace lines above, hold down command and click, and I’m automatically switched to TextMate with the specified file and line showing.

Yay!

My next step is to figure out how best to integrate it with autotest. If you have ideas, please leave a comment!

Here’s the script:

[ruby]
#!/usr/local/bin/ruby -ws
#
# textmate_urls – by Luke Melia
#
# usage:
# test.rb | textmate_urls

############################################################

class TextmateUrls

def self.urlize
trap ‘INT’ do exit 1 end
TextmateUrls.new.urlize
end

##
# Scans input looking for stacktrace lines and rewrite them with textmate urls in the output

def urlize(input=ARGF, output=$stdout)
cwd = `pwd`.chomp #remember the current working directory to rewrite relative paths in the stacktrace
old_sync = output.sync
output.sync = true
while line = input.gets
case line
when %r{(^[a-z_]+\([A-Za-z]+\)) \[\.?(/?.*):(\d+)\]:}
line = “#{$1}\ntxmt://open?url=file://#{File.join(cwd, $2)}&line=#{$3}”
line.gsub!(%r|/[^/]+/\.\./|, ‘/’)
when %r{^\s*(\[?)(/.+):(\d+):(.*)$} # a stacktrace line
line = "#{$1}txmt://open?url=file://#{$2}&line=#{$3} #{$4}"
line.gsub!(%r|/[^/]+/\.\./|, ‘/’)
when %r{^\s*(\[?)\.?(/?.+):(\d+):(in .*)$} # a stacktrace line with a relative file reference
line = "#{$1}txmt://open?url=file://#{File.join(cwd, $2)}&line=#{$3} #{$4}"
line.gsub!(%r|/[^/]+/\.\./|, ‘/’)
end
output.puts line
end
output.sync = old_sync
end
end

TextmateUrls.urlize
[/ruby]

Happy stack-walking!

6 Responses to “Clickable Ruby Stacktraces with iTerm & TextMate”

  1. Reinier Balt chimed in:

    you get this for free when using Netbeans :-)

  2. Ryan Davis chimed in:

    Reinier: yeah, but then he’d be running netbeans. My guess is textmate + iterm + rails + the rest of the OS runs within the memory footprint of netbeans…

    Luke: while I’m glad you now have clickable links, isn’t there some way to keep them readable? Maybe even cleaning them up even more over your default (remove full paths and the stupid rails /../ crap)?

    I’ve got all that and autotest integration and more within emacs. It truly is helpful.

  3. Bryan Helmkamp chimed in:

    The same thing works in Leopard’s Terminal.app. If you right click a link, you can choose “Open URL”. I confirmed this works for txmt:// URLs.

  4. Chip chimed in:

    I am new to RoR and this has helped me immensely when using autotest. I am trying to figure out how to hook this into the browser’s stack trace. If anyone has any suggestions, please let me know.

    Thanks again for this killer script!

  5. Dr Nic chimed in:

    Did you ever get this hooked into autotest?

  6. Mark’s Link Blog » links for 2008-12-29 chimed in:

    [...] Luke Melia ยป Clickable Ruby Stacktraces with iTerm & TextMate (tags: ruby textmate autotest iterm testing) [...]

Leave a Reply

LukeMelia.com created 1999. ··· Luke Melia created 1976. ··· Live With Passion!