Highlighting Haskell with SHJS

by Nicolas Wu


Posted on 14 October 2010

Tags: Haskell, SHJS


I like to have colourful syntax highlighting, since I find it makes it quicker to read code: a lot of the parsing in my head gets done by simply looking at the colour of some fragment of syntax, and that helps me get the gist of what’s going on quickly.

SHJS

If you want to highlight code for your website, then you might consider using SHJS, a JavaScript program that does syntax highlighting on the client side.

SHJS takes its syntax rules from a GNU Src-Highlite file, and produces JavaScript that marks up your HTML with classes that can then be coloured by a CSS file. This means that any language supported by GNU source-highlight can get highlighted consistently on your website.

There are a number of basic classes that are highlighted by SHJS:

 keyword    type        usertype
 string     regexp      specialchar
 comment    number      preproc
 symbol     function    cbracket
 predef_var predef_func classname
 todo 

Highlighted Haskell

The Haskell definition file supplied by default has the comment “Quick and dirty Haskell highlight rules for GNU Source-highlight and SHJS”, which doesn’t sound like good news to me. As a result, I’ve written my own haskell.lang file that’s a lot more comprehensive.

To demonstrate how SHJS highlights code, here are a few snippets, some of which come from Johan Tibell’s Haskell Style Guide.

Simple code:

> fibs :: [Int]
> fibs = fibs' + fibs''
>   where
>     fibs'  = 1 : fibs''
>     fibs'' = 0 : fibs

Data declarations:

> data Tree a = Branch a (Tree a) (Tree a)
>             | Leaf

Records:

> data Person = Person
>     { firstName :: String  -- ^ First name
>     , lastName  :: String  -- ^ Last name
>     , age       :: Int     -- ^ Age
>     } deriving (Eq, Show)

Some do notation:

> main :: IO ()
> main = do putStrLn "Hello World!"
>           return ()

A type signature:

> div :: Int -> Int -> Int

Language pragmas:

> {-# LANGUAGE pragma #-}
> id :: a -> a
> id x = x
> {-# INLINE id #-}

Class definitions:

> class Functor f where
>   fmap :: (a -> b) -> f a -> f b

Constants:

> True False 1 0x0010 3.1415 "A string!" 'A'

Comments:

> -- None of the following symbols should be parsed as comments:
> --> <-- ->- --<-
> {- multiple
>    line
>    comments {- should allow nesting -}  -}

Hopefully the examples above should cover most of the cases you’ll be interested in, but here’s a summary of features of the script I wrote:

  • Operators are supported (++, !, >>= and any other combination of legal operator characters)
  • Special symbols are recognised (=>, <-, ->, ::, ..)
  • Unicode support for →, ←, ∷, ‥,⇒, ∀, ∃ and others
  • Support for qualified module functions (List.sort, etc)
  • Support for qualified module types
  • Support for literate code (using > and \begin{code} \end{code})
  • Support for literate specifications (using < and \begin{spec} \end{spec})

Setup

Setting up SHJS to work on your website should be really easy. You’ll probably need most of these files:

You should include the CSS file in the header of your HTML, and place the scripts at the end of the body, so that they are the last things to load on a page:

<html>
  <head>
  ...
  <link rel="stylesheet" type="text/css" href="$root/css/sh_style.min.css" />
  ...
  </head>
  <body>
  ...
  <script type="text/javascript" src="$root/js/sh_main.min.js" > </script>
  <script type="text/javascript" src="$root/js/sh_haskell.min.js" > </script>
  <script type="text/javascript" src="$root/js/sh_init.min.js" > </script>
</body>
</html>

I use Pandoc to generate HTML for me from literate Haskell source. The problem is that Pandoc encloses haskell code in <pre class="sourceCode literate haskell"><code> tags, and for SHJS to work, the class should be sh_haskell. To fix this I use the sh_init.min.js script to change the classes as needed. The sh_init.min.js file also includes a line that makes the highlighting start, so if you’re generating your own HTML with the sh_haskell baked in, you won’t need to use sh_init.min.js, and instead, you should include something like the following line in your code:

<body onload="sh_highlightDocument();">

With these in place, all the Haskell code on your website should get highlighted in browsers with JavaScript enabled. If someone doesn’t have JavaScript enabled, then they’ll get a lightweight version of your site that’s not quite as pretty, but that takes up a lot less space than a statically highlighted file.

Comments