Mark Needham

Thoughts on Software Development

Haskell: Colour highlighting when writing to the shell

with 2 comments

I spent a few hours writing a simple front end on top of the Rabin Karp algorithm so that I could show the line of the first occurrence of a pattern in a piece of text on the shell.

I thought it would be quite cool if I could highlight the appropriate text on the line like how grep does when the ‘–color=auto’ flag is supplied.

We can make use of ANSI escape codes to do this.

We need to construct an escape sequence which starts with the ‘ESC’ character, represented by decimal value 27 or hexidecimal value 1b, followed by a left bracket (“[“):

“\x1b[” or “\27[”

In the second part of the sequence we can choose a variety of different options. In this case we use the following:

CSI n [;k] m

SGR – Select Graphic Rendition
Sets SGR parameters, including text color. After CSI can be zero or more parameters separated with ;. With no parameters, CSI m is treated as CSI 0 m (reset / normal), which is typical of most of the ANSI escape sequences.

To change the colour of the text we need to set ‘k’ to one of the following values:

  • 30 – Black
  • 31 – Red
  • 32 – Green
  • 33 – Yellow
  • 34 – Blue
  • 35 – Magenta
  • 36 – Cyan
  • 37 – White

For example, if we want to print all future text in green then we can use the following escape sequence:

> putStrLn $ "\x1b[32m"

Green

Ideally we should then reset the terminal which means passing a ‘k’ value of 0.

So if we just want to highlight one word we’d end up with this:

> putStrLn $ "\x1b[32m" ++ "highlight me" ++ "\x1b[0m" ++ " but not me"

Green2

Be Sociable, Share!

Written by Mark Needham

April 29th, 2012 at 12:01 am

Posted in Haskell

Tagged with

  • http://twitter.com/rjhunter Rob Hunter

    In case you want to get fancy, note that the colors are a bitwise R/G/B addition.

    magenta (5) = red (1) + blue (4)
    cyan (6) = green (2) + blue (4)
    white (7) = all colors
    black (0) = no colors

    Many terminals consider “bold” to be “bright”, so ESC[1;3m is yellow while ESC[22;3m is brown.

    Writing out ANSI codes yourself is theoretically coupling your implementation to ANSI terminals. It’s not such a big deal these days, since the terminal emulation world seems to have largely settled on ANSI and xterm. Most Unix terminal manipulation, including color, relies on the `terminfo` database — try running `toe` to see the various supported terminals and be glad that you don’t have to worry about that variation anymore :-)

  • David Turner

    There’s an ansi-terminal package

    http://hackage.haskell.org/package/ansi-terminal/

    which does all this for you in a strongly-typed fashion. It also works on Windows terminals – your code doesn’t seem to work on my machine.

    Cheers,

    David