<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Tom Moertel's Weblog: Adding Haskell syntax highlighting to the Typo blogging system</title>
    <link>http://blog.moertel.com/articles/2006/11/01/adding-haskell-syntax-highlighting-to-the-typo-blogging-system</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Quality rants on programming theory and stuff geeks like</description>
    <item>
      <title>Adding Haskell syntax highlighting to the Typo blogging system</title>
      <description>&lt;p&gt;Last night on &lt;a href="irc://irc.freenode.net/%23haskell"&gt;#haskell&lt;/a&gt;, &lt;a href="http://www.cse.unsw.edu.au/~dons/"&gt;Don
Stewart&lt;/a&gt; asked if I had seen
&lt;a href="http://www.cs.york.ac.uk/fp/darcs/hscolour/"&gt;HsColour&lt;/a&gt;
for rendering syntax-highlighted Haskell in &lt;span class="caps"&gt;HTML&lt;/span&gt;.  He had
used it recently, he noted in passing, &lt;a href="http://cgi.cse.unsw.edu.au/~dons/blog/2006/09/10#colours"&gt;to add syntax highlighting to planet.haskell.org&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;Now, I can&amp;#8217;t be certain about this, but I suspect that Don&amp;#8217;s question
was cleverly designed to instill in me a subtle case of
syntax-highlighting envy.  For on &lt;a href="http://blog.moertel.com/"&gt;&lt;em&gt;my&lt;/em&gt; blog&lt;/a&gt;, Haskell code snippets
were rendered in dreadfully boring uncolored text.
But on &lt;a href="http://cgi.cse.unsw.edu.au/~dons/blog"&gt;&lt;em&gt;his&lt;/em&gt; blog&lt;/a&gt;, the
snippets dance in joyous polychromatic splendor.&lt;/p&gt;


	&lt;p&gt;Thus I was compelled to add Haskell syntax-highlighting to my blog.&lt;/p&gt;


	&lt;h3&gt; Adding Haskell syntax-highlighting to Typo&lt;/h3&gt;


	&lt;p&gt;My blog runs on the Ruby-on-Rails-powered &lt;a href="http://typosphere.org/"&gt;Typo&lt;/a&gt;
system, which &lt;a href="http://scottstuff.net/blog/articles/2005/08/23/introduction-to-typo-filters"&gt;allows for plug-in text filters&lt;/a&gt;.  One of the included filters, in fact, is a syntax-highlighting filter for snippets of Ruby, &lt;span class="caps"&gt;XML&lt;/span&gt;, and &lt;span class="caps"&gt;YAML&lt;/span&gt; code.  This filter is built upon the Ruby &lt;a href="http://syntax.rubyforge.org/"&gt;Syntax&lt;/a&gt; module, which wasn&amp;#8217;t exactly designed for Haskell syntax analysis.  So I set out to create a new plug-in filter based upon HsColour.&lt;/p&gt;


	&lt;p&gt;This task turned out to be easy.  All I did was duplicate
Typo&amp;#8217;s existing syntax-highlighting filter and swap out its filtering
code for the following:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;IO&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;popen&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;HsColour -css&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;r+&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
  &lt;span class="ident"&gt;pid&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;fork&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;write&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;close&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt; &lt;span class="ident"&gt;exit!&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;close_write&lt;/span&gt;
  &lt;span class="ident"&gt;text&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;read&lt;/span&gt;
  &lt;span class="constant"&gt;Process&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;waitpid&lt;/span&gt; &lt;span class="ident"&gt;pid&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;I also tweaked the post-processing regular expressions so that they
would whittle away the &lt;span class="caps"&gt;HTML&lt;/span&gt; filler before and after the
syntax-highlighted output of HsColour:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gsub!&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;.*&amp;lt;p()re&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;...)&lt;/span&gt;
&lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gsub!&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&amp;lt;&lt;span class="escape"&gt;\/&lt;/span&gt;pre&amp;gt;.*&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;...)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;A few more tweaks and I was done.&lt;/p&gt;


	&lt;p&gt;Now I can wrap my Haskell code in &amp;lt;typo:haskell&amp;gt; tags and it, too, will
dance in joyous polychromatic splendor:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;constructTable&lt;/span&gt; &lt;span class='varid'&gt;tspecs&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;ecolspecs&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;during&lt;/span&gt; &lt;span class='str'&gt;"argument evaluation"&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='varid'&gt;toNvps&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;concat&lt;/span&gt; &lt;span class='varop'&gt;=&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='varid'&gt;mapM&lt;/span&gt; &lt;span class='varid'&gt;splice&lt;/span&gt; &lt;span class='varid'&gt;tspecs&lt;/span&gt;
    &lt;span class='keyword'&gt;let&lt;/span&gt; &lt;span class='varid'&gt;names&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;map&lt;/span&gt; &lt;span class='varid'&gt;fst&lt;/span&gt; &lt;span class='varid'&gt;ecolspecs&lt;/span&gt;
    &lt;span class='keyword'&gt;let&lt;/span&gt; &lt;span class='varid'&gt;evecs&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;map&lt;/span&gt; &lt;span class='varid'&gt;snd&lt;/span&gt; &lt;span class='varid'&gt;ecolspecs&lt;/span&gt;
    &lt;span class='varid'&gt;vecs&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;argof&lt;/span&gt; &lt;span class='varid'&gt;nm&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;mapM&lt;/span&gt; &lt;span class='varid'&gt;evalVector&lt;/span&gt; &lt;span class='varid'&gt;evecs&lt;/span&gt;
    &lt;span class='keyword'&gt;let&lt;/span&gt; &lt;span class='varid'&gt;vlens&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;map&lt;/span&gt; &lt;span class='varid'&gt;vlen&lt;/span&gt; &lt;span class='varid'&gt;vecs&lt;/span&gt;
    &lt;span class='keyword'&gt;if&lt;/span&gt; &lt;span class='varid'&gt;length&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;group&lt;/span&gt; &lt;span class='varid'&gt;vlens&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;==&lt;/span&gt; &lt;span class='num'&gt;1&lt;/span&gt;
        &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='conid'&gt;VTable&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;mkTable&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;zip&lt;/span&gt; &lt;span class='varid'&gt;names&lt;/span&gt; &lt;span class='varid'&gt;vecs&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
        &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='varid'&gt;throwError&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt;
             &lt;span class='str'&gt;"table columns must be non-empty vectors of equal length"&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;nm&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='str'&gt;"table(...) constructor"&lt;/span&gt;
    &lt;span class='varid'&gt;splice&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;TCol&lt;/span&gt; &lt;span class='varid'&gt;envp&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;  &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='varid'&gt;envp&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
    &lt;span class='varid'&gt;splice&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;TSplice&lt;/span&gt; &lt;span class='varid'&gt;e&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;  &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;eval&lt;/span&gt; &lt;span class='varid'&gt;e&lt;/span&gt;
        &lt;span class='keyword'&gt;case&lt;/span&gt; &lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='keyword'&gt;of&lt;/span&gt;
            &lt;span class='conid'&gt;VTable&lt;/span&gt; &lt;span class='varid'&gt;t&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;zipWith&lt;/span&gt; &lt;span class='varid'&gt;mkNVP&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;tcnames&lt;/span&gt; &lt;span class='varid'&gt;t&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;elems&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;tvecs&lt;/span&gt; &lt;span class='varid'&gt;t&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
            &lt;span class='conid'&gt;VList&lt;/span&gt; &lt;span class='varid'&gt;gl&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class='varid'&gt;liftM&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;zipWith&lt;/span&gt; &lt;span class='varid'&gt;mkNVP&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;map&lt;/span&gt; &lt;span class='varid'&gt;name&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;elems&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;glnames&lt;/span&gt; &lt;span class='varid'&gt;gl&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt;
                &lt;span class='varid'&gt;mapM&lt;/span&gt; &lt;span class='varid'&gt;asVectorNull&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;elems&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;glvals&lt;/span&gt; &lt;span class='varid'&gt;gl&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
            &lt;span class='keyword'&gt;_&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;throwError&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt;
                &lt;span class='str'&gt;"can't construct table columns from ("&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt;
                &lt;span class='varid'&gt;show&lt;/span&gt; &lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='str'&gt;")"&lt;/span&gt;
    &lt;span class='varid'&gt;mkNVP&lt;/span&gt; &lt;span class='varid'&gt;n&lt;/span&gt; &lt;span class='varid'&gt;vec&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;NVP&lt;/span&gt; &lt;span class='varid'&gt;n&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;mkNoPosExpr&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='conid'&gt;EVal&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='conid'&gt;VVector&lt;/span&gt; &lt;span class='varid'&gt;vec&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
    &lt;span class='varid'&gt;name&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt;     &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='str'&gt;"NA"&lt;/span&gt;
    &lt;span class='varid'&gt;name&lt;/span&gt; &lt;span class='varid'&gt;n&lt;/span&gt;      &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;If you want the filter code, here it is: &lt;a href="http://community.moertel.com/~thor/blog/haskell_controller.rb.txt"&gt;haskell_controller.rb&lt;/a&gt;.  Just drop it into &lt;code&gt;components/plugins/textfilters&lt;/code&gt; and restart Typo.  The corresponding &lt;span class="caps"&gt;CSS&lt;/span&gt; styles can be found in my &lt;a href="http://blog.moertel.com/stylesheets/user-styles.css"&gt;user-styles.css&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Wed, 01 Nov 2006 17:01:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:62648231-3b46-4d96-a657-69565f7ee784</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2006/11/01/adding-haskell-syntax-highlighting-to-the-typo-blogging-system</link>
      <category>haskell</category>
      <category>ruby</category>
      <category>typo</category>
      <category>typo</category>
      <category>ruby</category>
      <category>haskell</category>
      <category>hscolour</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/207</trackback:ping>
    </item>
  </channel>
</rss>
