GeSHi is the php-based syntax highlighter that I use for this blog. The Haskell highlighting included with GeSHi is a bit below par compared to say GitHub, which uses pygments for highlighting.
Not to be outdone, I though I’d delve in to GeSHi’s regular expression features to see if I could improve upon the default. I struggled a bit with things getting highlighted that had already been processed and matched to an alternate regular expression until I dug in to the GeSHi source to and found out how it escapes potential highlights. This led to some lovely regex like the one below as I attempted to work around things like <PIPE>, <SEMI> and other magical GeSHi escapes like <|!REG3XP1!>.
/<[A-Z]+>|<\|!REG3XP\d*!>.*?\|>/
One I figured this out things got a lot easier and I was able to get to a point that I’m pretty happy with. The symbol highlighting is still a bit hacky and I’d like to get to a point where all non-alphanumeric character groups are highlighted as symbols. For now though I’ve just added a few more (<$>, <|>, etc) to the hardcoded list that comes with GeSHi out of the box. So without any further ado, here are the results.
Before
module Main where import Control.Monad.Identity import Text.Parsec (ParseError,parse,char,oneOf,(<?>),(<|>)) import Text.Parsec.ByteString import Text.Parsec.Expr import qualified Data.ByteString.Char8 as S data Expr = Op Char Expr Expr | Value Char deriving Show -- This is a comment main :: IO () main = do xs <- S.getContents let f = S.unlines . translate . (drop 1) . S.lines S.putStr $ f xs return () {- This is a multiline comment -} translate :: [S.ByteString] -> [S.ByteString] translate = map $ f . readExpr where f (Left err) = S.pack $ show err f (Right val) = showRPN val showRPN :: Expr -> S.ByteString showRPN (Value v) = S.singleton v showRPN (Op o a b) = showRPN a `S.append` showRPN b `S.append` S.singleton o table :: [[Operator S.ByteString st Identity Expr]] table = [[op '^', op '/', op '*', op '-', op '+']] where op c = Infix (liftM Op $ char c) AssocLeft term :: Parser Expr term = brackets <|> value <?> "term" brackets :: Parser Expr brackets = char '(' >> expr >>= \x -> char ')' >> return x
After
module Main where import Control.Monad.Identity import Text.Parsec (ParseError,parse,char,oneOf,(<?>),(<|>)) import Text.Parsec.ByteString import Text.Parsec.Expr import qualified Data.ByteString.Char8 as S data Expr = Op Char Expr Expr | Value Char deriving Show -- This is a comment main :: IO () main = do xs <- S.getContents let f = S.unlines . translate . (drop 1) . S.lines S.putStr $ f xs return () {- This is a multiline comment -} translate :: [S.ByteString] -> [S.ByteString] translate = map $ f . readExpr where f (Left err) = S.pack $ show err f (Right val) = showRPN val showRPN :: Expr -> S.ByteString showRPN (Value v) = S.singleton v showRPN (Op o a b) = showRPN a `S.append` showRPN b `S.append` S.singleton o table :: [[Operator S.ByteString st Identity Expr]] table = [[op '^', op '/', op '*', op '-', op '+']] where op c = Infix (liftM Op $ char c) AssocLeft term :: Parser Expr term = brackets <|> value <?> "term" brackets :: Parser Expr brackets = char '(' >> expr >>= \x -> char ')' >> return x
You can download my modifications to the Haskell syntax file for GeSHi here. If I get some more time I’ll probably submit it back to the GeSHi project as a replacement or an alternative for Haskell.
One thing to note is that I’ve stripped out the url linking of Prelude functions. I felt that it wasn’t really that useful as I rarely want to know the details of those functions, it’s all the other ones that I want to know about! I toyed with the idea of linking functions and types to Hoogle or Hayoo!, that might be worthwhile in the future.
Download haskell.php
The syntax file above was built and tested on GeSHi 1.0.8.7, it will probably work on other versions, but you never know :)

Social