Have you ever wanted to pretty print more than ASCII text? Perhaps add color, hyperlinks, or other markup in your pretty printed code when rendering to HTML? A new version of the HughesPJ pretty printing library recently uploaded to hackage allows just this.
The library provides one new combinator, one new constructor, and makes Doc a type synonym.
-- The old Doc API
type Doc = MDoc ()
-- The new Doc, with marks
data MDoc a = ...
-- augmented TextDetails
data TextDetails a
= Chr Char
| Str String
| PStr String
| Mark a -- new
-- new combinator; inserts a zero width mark
-- into the output document
mark :: a -> MDoc a
mark m = textBeside_ (Mark m) 0 Empty
A mark is a zero width datum inserted into the output stream, using Mark. For ASCII output, the marks are removed and ignored.
Using this library is straightforward. Here is the test from the package.
module Main where
-- we hide Doc becase we are going to rename it.
import Text.PrettyPrint.MarkedHughesPJ hiding (Doc)
-- simple datastructure
data Exp = App Exp [Exp]
| Var String
| Lit Int
| List [Exp]
-- our pretty printer
pretty :: Exp -> Doc
pretty (App e args) = pretty e <+> sep (map (parens . pretty) args)
pretty (Var v) = text v
pretty (Lit l) = scope BOLD $ scope GREEN $ text (show l)
pretty (List es) = brackets (scope RED (sep (punctuate (text ",") (map pretty es))))
So far, this looks like any other pretty printer, except we have also used a new combinator, scope.
type Doc = MDoc Markup
data Markup = Open MStyle | Close MStyle
data MStyle = BOLD | RED | GREEN
open :: MStyle -> Doc
open = mark . Open
close :: MStyle -> Doc
close = mark . Close
scope :: MStyle -> Doc -> Doc
scope s d = open s <> d <> close s
Here we define scope as an open and a close. We can now provide a function which outputs marked up HTML rather than ASCII (the default).
html_txt (Chr c) r = concatMap escape [c] ++ r
html_txt (Str s) r = concatMap escape s ++ r
html_txt (PStr s) r = concatMap escape s ++ r
html_txt (Mark (Open BOLD)) r = "<b>" ++ r
html_txt (Mark (Close BOLD)) r = "</b>" ++ r
html_txt (Mark (Open RED)) r = "<font color=\"red\">" ++ r
html_txt (Mark (Close RED)) r = "</font>" ++ r
html_txt (Mark (Open GREEN)) r = "<font color=\"green\">" ++ r
html_txt (Mark (Close GREEN)) r = "</font>" ++ r
escape '<' = "<"
escape '>' = ">"
escape c = [c]
Now we use both the default and html_txt render methods.
main = do
putStrLn "<html><body><pre>"
sequence_ [ putStrLn $ render (pretty ex)
| ex <- examples
]
putStrLn $ "<hr>"
sequence_ [ putStrLn $ fullRender PageMode 100 1.5 html_txt "" (pretty ex)
| ex <- examples
]
putStrLn ...
This outputs the original style and new style of markup.
abc
cde
abc (cde)
99
abc (cde) (abc) (99) (cde) (abc) (cde) (99) (cde)
[abc,
cde,
abc (cde),
99,
abc (cde) (abc) (99) (cde) (abc) (cde) (99) (cde)]
abc (cde) ([abc,
cde,
abc (cde),
99,
abc (cde) (abc) (99) (cde) (abc) (cde) (99) (cde)])
99 (99)
abc
cde
abc (cde)
99
abc (cde) (abc) (99) (cde) (abc) (cde) (99) (cde)
[abc,
cde,
abc (cde),
99,
abc (cde) (abc) (99) (cde) (abc) (cde) (99) (cde)]
abc (cde) ([abc,
cde,
abc (cde),
99,
abc (cde) (abc) (99) (cde) (abc) (cde) (99) (cde)])
99 (99)
This is all there is to it - it is easy to add markup inside pretty printed documents. This library is used inside the Haskell Equational Reasoning Assistant, to facilitate the selectability of sub-expressions, make keywords bold, make symbols blue, etc. It is available from hackage, called marked-pretty.