Sunday, February 15, 2009

Getting colorful testrun output in Emacs

When I run my RSpec tests from the command line, the output is colorful, but from within Emacs, it's all just plain white text. Something must be done!

The first and most-obvious problem: spec (the testrunner) needs the -c flag before it produces colorful output. Easy fix! Update my test-running function to throw a -c in there, and presto! Beautiful output:

Compilation started at Sun Feb 15 16:35:33

spec spec/thingy.rb -c
.F...

Great. spec emitted a bunch of ANSI color escape sequences (yay!), and Emacs failed to interpret them (boo!).

Googling revealed that compilation-mode is derived from comint-mode, so I should just set up comint-mode to have color output. It's well-documented and easy to do:

(setq ansi-color-for-comint-mode t)
(add-hook 'comint-output-filter-functions 'ansi-color-process-output)

This let me run a shell from within Emacs and have ls --color show color in it. Unfortunately, this did absolutely nothing for my test output, which remained full of raw escape sequences.

The problem is in compile.el. It calls comint-exec, which sets the process's output filter to comint-output-filter, which in turn exposes a variety of useful hooks, including the aforementioned comint-output-filter-functions. Unfortunately, after calling comint-exec, compile sets the output filter to compilation-filter, which only exposes one hook, and that one gets called *after* the string's been inserted, with no arguments. It's not very useful.

Fortunately, Emacs's advice system makes it easy to modify the arguments that compilation-filter gets, so it's easy to turn those escape sequences into colors.

(defadvice compilation-filter (before ansify-compilation-output activate)
(ad-set-arg 1 (ansi-color-apply (ad-get-arg 1))))

Now I run my tests, and I get... no escape sequences or colors? Huh?

It turns out that you can't insert text with color properties (or something) into a buffer with font-lock-mode on. Since I have that on everywhere, I just need to turn off font-lock-mode in the compilation buffer. Fortunately, a process filter's first argument is the process.

(defadvice compilation-filter (before ansify-compilation-output activate)
(with-current-buffer (process-buffer (ad-get-arg 0))
(font-lock-mode -1)
(ad-set-arg 1 (ansi-color-apply (ad-get-arg 1)))))

Now I've got colorful test output in Emacs.

(Why not add something to compilation-mode-hook? It's because compilation-mode unconditionally turns on font-lock-mode after running compilation-mode-hook.)

No comments:

Post a Comment