Last night on #haskell, Don Stewart asked if I had seen HsColour for rendering syntax-highlighted Haskell in HTML. He had used it recently, he noted in passing, to add syntax highlighting to planet.haskell.org.
Now, I can’t be certain about this, but I suspect that Don’s question was cleverly designed to instill in me a subtle case of syntax-highlighting envy. For on my blog, Haskell code snippets were rendered in dreadfully boring uncolored text. But on his blog, the snippets dance in joyous polychromatic splendor.
Thus I was compelled to add Haskell syntax-highlighting to my blog.
Adding Haskell syntax-highlighting to Typo
My blog runs on the Ruby-on-Rails-powered Typo system, which allows for plug-in text filters. One of the included filters, in fact, is a syntax-highlighting filter for snippets of Ruby, XML, and YAML code. This filter is built upon the Ruby Syntax module, which wasn’t exactly designed for Haskell syntax analysis. So I set out to create a new plug-in filter based upon HsColour.
This task turned out to be easy. All I did was duplicate Typo’s existing syntax-highlighting filter and swap out its filtering code for the following:
IO.popen("HsColour -css", "r+") do |f|
= fork { f.write text; f.close; exit! 0 }
pid .close_write
f= f.read
text Process.waitpid pid
end
I also tweaked the post-processing regular expressions so that they would whittle away the HTML filler before and after the syntax-highlighted output of HsColour:
.gsub!(/.*<p()re>/m, ...)
text.gsub!(/<\/pre>.*/m, ...) text
A few more tweaks and I was done.
Now I can wrap my Haskell code in <typo:haskell> tags and it, too, will dance in joyous polychromatic splendor:
= do
constructTable tspecs <- during "argument evaluation" $ do
ecolspecs . concat =<< mapM splice tspecs
toNvps let names = map fst ecolspecs
let evecs = map snd ecolspecs
<- argof nm $ mapM evalVector evecs
vecs let vlens = map vlen vecs
if length (group vlens) == 1
then return . VTable $ mkTable (zip names vecs)
else throwError $
"table columns must be non-empty vectors of equal length"
where
= "table(...) constructor"
nm TCol envp) = return [envp]
splice (TSplice e) = do
splice (<- eval e
val case val of
VTable t ->
return $ zipWith mkNVP (tcnames t) (elems (tvecs t))
VList gl ->
zipWith mkNVP (map name . elems $ glnames gl)) $
liftM (mapM asVectorNull (elems $ glvals gl)
-> throwError $
_ "can't construct table columns from (" ++
show val ++ ")"
= NVP n (mkNoPosExpr . EVal $ VVector vec)
mkNVP n vec "" = "NA"
name = n name n
If you want the filter code, here it is:
haskell_controller.rb.
Just drop it into components/plugins/textfilters
and
restart Typo. The corresponding CSS styles can be found in my
user-styles.css.