diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/Text/PrettyPrint/Compact.hs b/Text/PrettyPrint/Compact.hs
--- a/Text/PrettyPrint/Compact.hs
+++ b/Text/PrettyPrint/Compact.hs
@@ -1,35 +1,83 @@
+{-# LANGUAGE TupleSections #-}
 {-# LANGUAGE OverloadedStrings #-}
-
+-- | Compact pretty-printer.
+--
+-- == Examples
+--
+-- Assume that we want to pretty print S-Expressions, which can either be atom or a list of S-Expressions.
+--
+-- >>> data SExpr = SExpr [SExpr] | Atom String deriving Show
+-- >>> let pretty :: SExpr -> Doc (); pretty (Atom s) = text s; pretty (SExpr xs) = text "(" <> (sep $ map pretty xs) <> text ")"
+--
+-- Using the above representation, the S-Expression @(a b c d)@ has the following encoding:
+--
+-- >>> let abcd = SExpr [Atom "a",Atom "b",Atom "c",Atom "d"]
+--
+-- The legible layouts of the @abcd@ S-Expression defined above would be either
+--
+-- >>> putStrLn $ render $ pretty abcd
+-- (a b c d)
+--
+-- or
+--
+-- >>> putStrLn $ renderWith defaultOptions { optsPageWidth = 5 } $ pretty abcd
+-- (a
+--  b
+--  c
+--  d)
+--
+-- The @testData@ S-Expression is specially crafted to
+-- demonstrate general shortcomings of both Hughes and Wadler libraries.
+--
+-- >>> let abcd4 = SExpr [abcd,abcd,abcd,abcd]
+-- >>> let testData = SExpr [ SExpr [Atom "abcde", abcd4], SExpr [Atom "abcdefgh", abcd4]]
+-- >>> putStrLn $ render $ pretty testData
+-- ((abcde ((a b c d) (a b c d) (a b c d) (a b c d)))
+--  (abcdefgh ((a b c d) (a b c d) (a b c d) (a b c d))))
+--
+-- on 20-column-wide page
+--
+-- >>> putStrLn $ renderWith defaultOptions { optsPageWidth = 20 } $ pretty testData
+-- ((abcde ((a b c d)
+--          (a b c d)
+--          (a b c d)
+--          (a b c d)))
+--  (abcdefgh
+--   ((a b c d)
+--    (a b c d)
+--    (a b c d)
+--    (a b c d))))
+--
+-- Yet, neither Hughes' nor Wadler's library can deliver those results.
+--
+-- === Annotations
+--
+-- For example we can annotate every /car/ element of S-Expressions,
+-- and in the rendering phase emphasise them by rendering them in uppercase.
+--
+-- >>> let pretty' :: SExpr -> Doc Any; pretty' (Atom s) = text s; pretty' (SExpr []) = text "()"; pretty' (SExpr (x:xs)) = text "(" <> (sep $ annotate (Any True) (pretty' x) : map pretty' xs) <> text ")"
+-- >>> let render' = renderWith defaultOptions { optsAnnotate  = \a x -> if a == Any True then map toUpper x else x }
+-- >>> putStrLn $ render' $ pretty' testData
+-- ((ABCDE ((A B C D) (A B C D) (A B C D) (A B C D)))
+--  (ABCDEFGH ((A B C D) (A b c d) (A b c d) (A b c d))))
+--
 module Text.PrettyPrint.Compact (
    -- * Documents
-   Doc, 
+   Doc,
 
    -- * Basic combinators
-   mempty, char, text, (<>), nest, line, linebreak, group, softline,
-   softbreak,
+   module Data.Monoid, text, flush, char,
 
-   -- * Alignment
-   --
-   -- The combinators in this section can not be described by Wadler's
-   -- original combinators. They align their output relative to the
-   -- current output position - in contrast to @nest@ which always
-   -- aligns to the current nesting level. This deprives these
-   -- combinators from being \`optimal\'. In practice however they
-   -- prove to be very useful. The combinators in this section should
-   -- be used with care, since they are more expensive than the other
-   -- combinators. For example, @align@ shouldn't be used to pretty
-   -- print all top-level declarations of a language, but using @hang@
-   -- for let expressions is fine.
-   align, hang, indent, encloseSep, list, tupled, semiBraces,
+   hang, hangWith, encloseSep, list, tupled, semiBraces,
 
    -- * Operators
-   (<+>), (<$>), (</>), (<$$>), (<//>),
+   (<+>), ($$), (</>), (<//>), (<$$>),
 
    -- * List combinators
-   hsep, vsep, fillSep, sep, hcat, vcat, fillCat, cat, punctuate,
+   hsep, sep, hcat, vcat, cat, punctuate,
 
-   -- * Fillers
-   fill, fillBreak,
+   -- * Fill combiantors
+   -- fillSep, fillCat,
 
    -- * Bracketing combinators
    enclose, squotes, dquotes, parens, angles, braces, brackets,
@@ -43,34 +91,44 @@
    bool,
 
    -- * Rendering
+   renderWith,
    render,
+   Options(..),
+   defaultOptions,
 
+   -- * Annotations
+   annotate,
+
    -- * Undocumented
    -- column, nesting, width
    ) where
 
-import Control.Applicative
 import Data.Monoid
 
 import Text.PrettyPrint.Compact.Core as Text.PrettyPrint.Compact
 
-type Indentation = Int
+-- | Render the 'Doc' into 'String' omitting all annotations.
+render :: Annotation a => Doc a -> String
+render = renderWith defaultOptions
 
-infixr 5 </>,<//>,<$$$>,<$$>
-infixr 6 <+>
+defaultOptions :: Options a String
+defaultOptions = Options
+    { optsAnnotate = \_ s -> s
+    , optsPageWidth = 80
+    }
 
 -- | The document @(list xs)@ comma separates the documents @xs@ and
 -- encloses them in square brackets. The documents are rendered
 -- horizontally if that fits the page. Otherwise they are aligned
 -- vertically. All comma separators are put in front of the elements.
-list :: [Doc] -> Doc
+list :: Annotation a => [Doc a] -> Doc a
 list            = encloseSep lbracket rbracket comma
 
 -- | The document @(tupled xs)@ comma separates the documents @xs@ and
 -- encloses them in parenthesis. The documents are rendered
 -- horizontally if that fits the page. Otherwise they are aligned
 -- vertically. All comma separators are put in front of the elements.
-tupled :: [Doc] -> Doc
+tupled :: Annotation a => [Doc a] -> Doc a
 tupled          = encloseSep lparen   rparen  comma
 
 
@@ -78,7 +136,7 @@
 -- semi colons and encloses them in braces. The documents are rendered
 -- horizontally if that fits the page. Otherwise they are aligned
 -- vertically. All semi colons are put in front of the elements.
-semiBraces :: [Doc] -> Doc
+semiBraces :: Annotation a => [Doc a] -> Doc a
 semiBraces      = encloseSep lbrace   rbrace  semi
 
 -- | The document @(enclosure l r sep xs)@ concatenates the documents
@@ -104,12 +162,12 @@
 --      ,200
 --      ,3000]
 -- @
-encloseSep :: Doc -> Doc -> Doc -> [Doc] -> Doc
-encloseSep left right sep ds
-    = case ds of
-        []  -> left <> right
-        [d] -> left <> d <> right
-        _   -> align (cat (zipWith (<>) (left : repeat sep) ds) <> right)
+encloseSep :: Annotation a => Doc a -> Doc a -> Doc a -> [Doc a] -> Doc a
+encloseSep left right separator ds
+    = (<> right) $ case ds of
+        []  -> left
+        [d] -> left <> d
+        (d:ds') -> cat (left <> d:map (separator <>) ds')
 
 -----------------------------------------------------------
 -- punctuate p [d1,d2,...,dn] => [d1 <> p,d2 <> p, ... ,dn]
@@ -139,9 +197,9 @@
 --
 -- (If you want put the commas in front of their elements instead of
 -- at the end, you should use 'tupled' or, in general, 'encloseSep'.)
-punctuate :: Doc -> [Doc] -> [Doc]
-punctuate p []      = []
-punctuate p [d]     = [d]
+punctuate :: Annotation a => Doc a -> [Doc a] -> [Doc a]
+punctuate _p []      = []
+punctuate _p [d]     = [d]
 punctuate p (d:ds)  = (d <> p) : punctuate p ds
 
 
@@ -151,222 +209,166 @@
 
 
 -- | The document @(sep xs)@ concatenates all documents @xs@ either
--- horizontally with @(\<+\>)@, if it fits the page, or vertically with
--- @(\<$\>)@.
+-- horizontally with @(\<+\>)@, if it fits the page, or vertically
+-- with @(\<$$\>)@. Documents on the left of horizontal concatenation
+-- must fit on a single line.
 --
--- > sep xs  = group (vsep xs)
-sep :: [Doc] -> Doc
-sep             = group . vsep
+sep :: Annotation a => [Doc a] -> Doc a
+sep xs = groupingBy " " (map (0,) xs)
 
--- | The document @(fillSep xs)@ concatenates documents @xs@
--- horizontally with @(\<+\>)@ as long as its fits the page, than
--- inserts a @line@ and continues doing that for all documents in
--- @xs@.
---
--- > fillSep xs  = foldr (\<\/\>) empty xs
-fillSep :: [Doc] -> Doc
-fillSep         = foldDoc (</>)
 
+-- -- | The document @(fillSep xs)@ concatenates documents @xs@
+-- -- horizontally with @(\<+\>)@ as long as its fits the page, than
+-- -- inserts a @line@ and continues doing that for all documents in
+-- -- @xs@.
+-- --
+-- -- > fillSep xs  = foldr (\<\/\>) empty xs
+-- fillSep :: Annotation a => [Doc a] -> Doc a
+-- fillSep         = foldDoc (</>)
+
 -- | The document @(hsep xs)@ concatenates all documents @xs@
 -- horizontally with @(\<+\>)@.
-hsep :: [Doc] -> Doc
+hsep :: Annotation a => [Doc a] -> Doc a
 hsep            = foldDoc (<+>)
 
-
--- | The document @(vsep xs)@ concatenates all documents @xs@
--- vertically with @(\<$\>)@. If a 'group' undoes the line breaks
--- inserted by @vsep@, all documents are separated with a space.
---
--- > someText = map text (words ("text to lay out"))
--- >
--- > test     = text "some" <+> vsep someText
---
--- This is layed out as:
---
--- @
--- some text
--- to
--- lay
--- out
--- @
---
--- The 'align' combinator can be used to align the documents under
--- their first element
---
--- > test     = text "some" <+> align (vsep someText)
---
--- Which is printed as:
---
--- @
--- some text
---      to
---      lay
---      out
--- @
-vsep :: [Doc] -> Doc
-vsep            = foldDoc (<$$$>)
-
 -- | The document @(cat xs)@ concatenates all documents @xs@ either
 -- horizontally with @(\<\>)@, if it fits the page, or vertically with
 -- @(\<$$\>)@.
 --
--- > cat xs  = group (vcat xs)
-cat :: [Doc] -> Doc
-cat             = group . vcat
+cat :: Annotation a => [Doc a] -> Doc a
+cat xs = groupingBy "" (map (0,) xs)
 
--- | The document @(fillCat xs)@ concatenates documents @xs@
--- horizontally with @(\<\>)@ as long as its fits the page, than inserts
--- a @linebreak@ and continues doing that for all documents in @xs@.
---
--- > fillCat xs  = foldr (\<\/\/\>) empty xs
-fillCat :: [Doc] -> Doc
-fillCat         = foldDoc (<//>)
+-- -- | The document @(fillCat xs)@ concatenates documents @xs@
+-- -- horizontally with @(\<\>)@ as long as its fits the page, than inserts
+-- -- a @linebreak@ and continues doing that for all documents in @xs@.
+-- --
+-- -- > fillCat xs  = foldr (\<\/\/\>) empty xs
+-- fillCat :: Annotation a => [Doc a] -> Doc a
+-- fillCat         = foldDoc (<//>)
 
 -- | The document @(hcat xs)@ concatenates all documents @xs@
 -- horizontally with @(\<\>)@.
-hcat :: [Doc] -> Doc
+hcat :: Annotation a => [Doc a] -> Doc a
 hcat            = foldDoc (<>)
 
 -- | The document @(vcat xs)@ concatenates all documents @xs@
--- vertically with @(\<$$\>)@. If a 'group' undoes the line breaks
--- inserted by @vcat@, all documents are directly concatenated.
-vcat :: [Doc] -> Doc
-vcat            = foldDoc (<$$>)
+-- vertically with @($$)@.
+vcat :: Annotation a => [Doc a] -> Doc a
+vcat            = foldDoc ($$)
 
-foldDoc :: (Doc -> Doc -> Doc) -> [Doc] -> Doc
-foldDoc f []       = mempty
+foldDoc :: Annotation a => (Doc a -> Doc a -> Doc a) -> [Doc a] -> Doc a
+foldDoc _ []       = mempty
 foldDoc f ds       = foldr1 f ds
 
 -- | The document @(x \<+\> y)@ concatenates document @x@ and @y@ with a
 -- @space@ in between.  (infixr 6)
-(<+>) :: Doc -> Doc -> Doc
+(<+>) :: Annotation a => Doc a -> Doc a -> Doc a
 x <+> y         = x <> space <> y
 
--- | The document @(x \<\/\> y)@ concatenates document @x@ and @y@ with a
--- 'softline' in between. This effectively puts @x@ and @y@ either
--- next to each other (with a @space@ in between) or underneath each
--- other. (infixr 5)
-(</>) :: Doc -> Doc -> Doc
-x </> y         = x <> softline <> y
-
--- | The document @(x \<\/\/\> y)@ concatenates document @x@ and @y@ with
--- a 'softbreak' in between. This effectively puts @x@ and @y@ either
--- right next to each other or underneath each other. (infixr 5)
-(<//>) :: Doc -> Doc -> Doc
-x <//> y        = x <> softbreak <> y
+-- | The document @(x \<\/\> y)@ puts @x@ and @y@ either next to each other
+-- (with a @space@ in between) or underneath each other. (infixr 5)
+(</>) :: Annotation a => Doc a -> Doc a -> Doc a
+x </> y         = hang 0 x y
 
--- | The document @(x \<$\> y)@ concatenates document @x@ and @y@ with a
--- 'line' in between. (infixr 5)
-(<$$$>) :: Doc -> Doc -> Doc
-x <$$$> y         = x <> line <> y
+-- | The document @(x \<\/\/\> y)@ puts @x@ and @y@ either right next
+-- to each other (if @x@ fits on a single line) or underneath each
+-- other. (infixr 5)
+(<//>) :: Annotation a => Doc a -> Doc a -> Doc a
+x <//> y        = hangWith "" 0 x y
 
 -- | The document @(x \<$$\> y)@ concatenates document @x@ and @y@ with
--- a @linebreak@ in between. (infixr 5)
-(<$$>) :: Doc -> Doc -> Doc
-x <$$> y        = x <> linebreak <> y
-
--- | The document @softline@ behaves like 'space' if the resulting
--- output fits the page, otherwise it behaves like 'line'.
---
--- > softline = group line
-softline :: Doc
-softline        = group line
-
--- | The document @softbreak@ behaves like 'empty' if the resulting
--- output fits the page, otherwise it behaves like 'line'.
---
--- > softbreak  = group linebreak
-softbreak :: Doc
-softbreak       = group linebreak
+-- a linebreak in between. (infixr 5)
+(<$$>) :: Annotation a => Doc a -> Doc a -> Doc a
+(<$$>) = ($$)
 
 -- | Document @(squotes x)@ encloses document @x@ with single quotes
 -- \"'\".
-squotes :: Doc -> Doc
+squotes :: Annotation a => Doc a -> Doc a
 squotes         = enclose squote squote
 
 -- | Document @(dquotes x)@ encloses document @x@ with double quotes
 -- '\"'.
-dquotes :: Doc -> Doc
+dquotes :: Annotation a => Doc a -> Doc a
 dquotes         = enclose dquote dquote
 
 -- | Document @(braces x)@ encloses document @x@ in braces, \"{\" and
 -- \"}\".
-braces :: Doc -> Doc
+braces :: Annotation a => Doc a -> Doc a
 braces          = enclose lbrace rbrace
 
 -- | Document @(parens x)@ encloses document @x@ in parenthesis, \"(\"
 -- and \")\".
-parens :: Doc -> Doc
+parens :: Annotation a => Doc a -> Doc a
 parens          = enclose lparen rparen
 
 -- | Document @(angles x)@ encloses document @x@ in angles, \"\<\" and
 -- \"\>\".
-angles :: Doc -> Doc
+angles :: Annotation a => Doc a -> Doc a
 angles          = enclose langle rangle
 
 -- | Document @(brackets x)@ encloses document @x@ in square brackets,
 -- \"[\" and \"]\".
-brackets :: Doc -> Doc
+brackets :: Annotation a => Doc a -> Doc a
 brackets        = enclose lbracket rbracket
 
 -- | The document @(enclose l r x)@ encloses document @x@ between
 -- documents @l@ and @r@ using @(\<\>)@.
-enclose :: Doc -> Doc -> Doc -> Doc
+enclose :: Annotation a => Doc a -> Doc a -> Doc a -> Doc a
 enclose l r x   = l <> x <> r
 
-char :: Char -> Doc
+char :: Annotation a => Char -> Doc a
 char x = text [x]
 
 -- | The document @lparen@ contains a left parenthesis, \"(\".
-lparen :: Doc
+lparen :: Annotation a => Doc a
 lparen          = char '('
 -- | The document @rparen@ contains a right parenthesis, \")\".
-rparen :: Doc
+rparen :: Annotation a => Doc a
 rparen          = char ')'
 -- | The document @langle@ contains a left angle, \"\<\".
-langle :: Doc
+langle :: Annotation a => Doc a
 langle          = char '<'
 -- | The document @rangle@ contains a right angle, \">\".
-rangle :: Doc
+rangle :: Annotation a => Doc a
 rangle          = char '>'
 -- | The document @lbrace@ contains a left brace, \"{\".
-lbrace :: Doc
+lbrace :: Annotation a => Doc a
 lbrace          = char '{'
 -- | The document @rbrace@ contains a right brace, \"}\".
-rbrace :: Doc
+rbrace :: Annotation a => Doc a
 rbrace          = char '}'
 -- | The document @lbracket@ contains a left square bracket, \"[\".
-lbracket :: Doc
+lbracket :: Annotation a => Doc a
 lbracket        = char '['
 -- | The document @rbracket@ contains a right square bracket, \"]\".
-rbracket :: Doc
+rbracket :: Annotation a => Doc a
 rbracket        = char ']'
 
 
 -- | The document @squote@ contains a single quote, \"'\".
-squote :: Doc
+squote :: Annotation a => Doc a
 squote          = char '\''
 -- | The document @dquote@ contains a double quote, '\"'.
-dquote :: Doc
+dquote :: Annotation a => Doc a
 dquote          = char '"'
 -- | The document @semi@ contains a semi colon, \";\".
-semi :: Doc
+semi :: Annotation a => Doc a
 semi            = char ';'
 -- | The document @colon@ contains a colon, \":\".
-colon :: Doc
+colon :: Annotation a => Doc a
 colon           = char ':'
 -- | The document @comma@ contains a comma, \",\".
-comma :: Doc
+comma :: Annotation a => Doc a
 comma           = char ','
 
 -- | The document @dot@ contains a single dot, \".\".
-dot :: Doc
+dot :: Annotation a => Doc a
 dot             = char '.'
 -- | The document @backslash@ contains a back slash, \"\\\".
-backslash :: Doc
+backslash :: Annotation a => Doc a
 backslash       = char '\\'
 -- | The document @equals@ contains an equal sign, \"=\".
-equals :: Doc
+equals :: Annotation a => Doc a
 equals          = char '='
 
 -----------------------------------------------------------
@@ -379,191 +381,57 @@
 -- using @line@ for newline characters and @char@ for all other
 -- characters. It is used instead of 'text' whenever the text contains
 -- newline characters.
-string :: String -> Doc
-string ""       = mempty
-string ('\n':s) = line <> string s
-string s        = case (span (/='\n') s) of
-                    (xs,ys) -> text xs <> string ys
+string :: Annotation a => String -> Doc a
+string = vcat . map text . lines
 
-bool :: Bool -> Doc
+bool :: Annotation a => Bool -> Doc a
 bool b          = text (show b)
 
 -- | The document @(int i)@ shows the literal integer @i@ using
 -- 'text'.
-int :: Int -> Doc
+int :: Annotation a => Int -> Doc a
 int i           = text (show i)
 
 -- | The document @(integer i)@ shows the literal integer @i@ using
 -- 'text'.
-integer :: Integer -> Doc
+integer :: Annotation a => Integer -> Doc a
 integer i       = text (show i)
 
 -- | The document @(float f)@ shows the literal float @f@ using
 -- 'text'.
-float :: Float -> Doc
+float :: Annotation a => Float -> Doc a
 float f         = text (show f)
 
 -- | The document @(double d)@ shows the literal double @d@ using
 -- 'text'.
-double :: Double -> Doc
+double :: Annotation a => Double -> Doc a
 double d        = text (show d)
 
 -- | The document @(rational r)@ shows the literal rational @r@ using
 -- 'text'.
-rational :: Rational -> Doc
+rational :: Annotation a => Rational -> Doc a
 rational r      = text (show r)
 
------------------------------------------------------------
--- semi primitive: fill and fillBreak
------------------------------------------------------------
 
--- | The document @(fillBreak i x)@ first renders document @x@. It
--- than appends @space@s until the width is equal to @i@. If the
--- width of @x@ is already larger than @i@, the nesting level is
--- increased by @i@ and a @line@ is appended. When we redefine @ptype@
--- in the previous example to use @fillBreak@, we get a useful
--- variation of the previous output:
---
--- > ptype (name,tp)
--- >        = fillBreak 6 (text name) <+> text "::" <+> text tp
---
--- The output will now be:
---
--- @
--- let empty  :: Doc
---     nest   :: Indentation -> Doc -> Doc
---     linebreak
---            :: Doc
--- @
-fillBreak :: Indentation -> Doc -> Doc
-fillBreak f x   = width x (\w ->
-                  if (w > f) then nest f linebreak
-                             else spacing (f - w))
 
-
--- | The document @(fill i x)@ renders document @x@. It than appends
--- @space@s until the width is equal to @i@. If the width of @x@ is
--- already larger, nothing is appended. This combinator is quite
--- useful in practice to output a list of bindings. The following
--- example demonstrates this.
---
--- > types  = [("empty","Doc")
--- >          ,("nest","Indentation -> Doc -> Doc")
--- >          ,("linebreak","Doc")]
--- >
--- > ptype (name,tp)
--- >        = fill 6 (text name) <+> text "::" <+> text tp
--- >
--- > test   = text "let" <+> align (vcat (map ptype types))
---
--- Which is layed out as:
---
--- @
--- let empty  :: Doc
---     nest   :: Indentation -> Doc -> Doc
---     linebreak :: Doc
--- @
-fill :: Indentation -> Doc -> Doc
-fill f d        = width d (\w ->
-                  if (w >= f) then mempty
-                              else spacing (f - w))
-
-width :: Doc -> (Indentation -> Doc) -> Doc
-width d f       = column (\k1 -> d <> column (\k2 -> f (k2 - k1)))
-
-
------------------------------------------------------------
--- semi primitive: Alignment and indentation
------------------------------------------------------------
-
--- | The document @(indent i x)@ indents document @x@ with @i@ spaces.
---
--- > test  = indent 4 (fillSep (map text
--- >         (words "the indent combinator indents these words !")))
---
--- Which lays out with a page width of 20 as:
---
--- @
---     the indent
---     combinator
---     indents these
---     words !
--- @
-indent :: Indentation -> Doc -> Doc
-indent i d      = hang i (spacing i <> d)
-
 -- | The hang combinator implements hanging indentation. The document
--- @(hang i x)@ renders document @x@ with a nesting level set to the
--- current column plus @i@. The following example uses hanging
--- indentation for some text:
---
--- > test  = hang 4 (fillSep (map text
--- >         (words "the hang combinator indents these words !")))
---
--- Which lays out on a page with a width of 20 characters as:
---
--- @
--- the hang combinator
---     indents these
---     words !
--- @
---
--- The @hang@ combinator is implemented as:
---
--- > hang i x  = align (nest i x)
-hang :: Indentation -> Doc -> Doc
-hang i d        = align (nest i d)
+-- @(hang i x y)@ either @x@ and @y@ concatenated with @\<+\>@ or @y@
+-- below @x@ with an additional indentation of @i@.
 
--- | The document @(align x)@ renders document @x@ with the nesting
--- level set to the current column. It is used for example to
--- implement 'hang'.
---
--- As an example, we will put a document right above another one,
--- regardless of the current nesting level:
---
--- > x $$ y  = align (x <$$$> y)
---
--- > test    = text "hi" <+> (text "nice" $$ text "world")
---
--- which will be layed out as:
---
--- @
--- hi nice
---    world
--- @
-align :: Doc -> Doc
-align d         = column (\k ->
-                  nesting (\i -> nest (k - i) d))   --nesting might be negative :-)
+hang :: Annotation a => Int -> Doc a -> Doc a -> Doc a
+hang = hangWith " "
 
 
--- | The @line@ document advances to the next line and indents to the
--- current nesting level. Document @line@ behaves like @(text \" \")@
--- if the line break is undone by 'group'.
-line :: Doc
-line            = Line False
-
--- | The @linebreak@ document advances to the next line and indents to
--- the current nesting level. Document @linebreak@ behaves like
--- 'empty' if the line break is undone by 'group'.
-linebreak :: Doc
-linebreak       = Line True
+-- | The hang combinator implements hanging indentation. The document
+-- @(hang separator i x y)@ either @x@ and @y@ concatenated with @\<\>
+-- text separator \<\>@ or @y@ below @x@ with an additional
+-- indentation of @i@.
+hangWith :: Annotation a => String -> Int -> Doc a -> Doc a -> Doc a
+hangWith separator n x y = groupingBy separator [(0,x), (n,y)]
 
--- | The document @(nest i x)@ renders document @x@ with the current
--- indentation level increased by i (See also 'hang', 'align' and
--- 'indent').
---
--- > nest 2 (text "hello" <$$$> text "world") <$$$> text "!"
---
--- outputs as:
---
--- @
--- hello
---   world
--- !
--- @
-nest :: Indentation -> Doc -> Doc
-nest i x        = Nest i x
+space :: Annotation a => Doc a
+space = text " "
 
-column, nesting :: (Indentation -> Doc) -> Doc
-column f        = Column f
-nesting f       = Nesting f
+-- $setup
+-- >>> import Data.Monoid
+-- >>> import Data.Char
diff --git a/Text/PrettyPrint/Compact/Core.hs b/Text/PrettyPrint/Compact/Core.hs
--- a/Text/PrettyPrint/Compact/Core.hs
+++ b/Text/PrettyPrint/Compact/Core.hs
@@ -1,142 +1,250 @@
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE ConstraintKinds #-}
 {-# LANGUAGE RecordWildCards #-}
-module Text.PrettyPrint.Compact.Core(Doc(..),render,group,flatten,space,spacing,text) where
+{-# LANGUAGE ScopedTypeVariables, TypeSynonymInstances, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, ViewPatterns, DeriveFunctor, DeriveFoldable, DeriveTraversable, LambdaCase #-}
+module Text.PrettyPrint.Compact.Core(Annotation,Layout(..),renderWith,Options(..),groupingBy,Doc,($$)) where
 
-import Data.Monoid
+import Prelude ()
+import Prelude.Compat as P
+
+import Data.List.Compat (sortOn,groupBy,minimumBy)
 import Data.Function (on)
-import Data.List (partition,minimumBy,sort)
-import Data.Either (partitionEithers)
+import Data.Semigroup
+import Data.Sequence (singleton, Seq, viewl, viewr, ViewL(..), ViewR(..), (|>))
 import Data.String
+import Data.Foldable (toList)
+import Control.Applicative (liftA2)
+-- | Annotated string, which consists of segments with separate (or no) annotations.
+--
+-- We keep annotated segments in a container (list).
+-- The annotation is @Maybe a@, because the no-annotation case is common.
+--
+-- /Note:/ with @Last x@ annotation, the 'annotate' will overwrite all annotations.
+--
+-- /Note:/ if the list is changed into `Seq` or similar structure
+-- allowing fast viewr and viewl, then we can impose an additional
+-- invariant that there aren't two consequtive non-annotated segments;
+-- yet there is no performance reason to do so.
+--
+data AS a = AS !Int [(a, String)]
+  deriving (Eq,Ord,Show,Functor,Foldable,Traversable)
 
-type Indentation = Int
+-- | Tests the invariants of 'AS'
+_validAs :: AS a -> Bool
+_validAs (AS i s) = lengthInvariant && noNewlineInvariant
+  where
+    lengthInvariant = i == sum (map (length . snd) s)
+    noNewlineInvariant = all (notElem '\n' . snd) s
 
-data Box = Str String | Spacing Indentation | NewLine {-^ not allowed in the input -}
+asLength :: AS a -> Int
+asLength (AS l _) = l
 
-instance IsString Doc where
-  fromString = text
+-- | Make a non-annotated 'AS'.
+mkAS :: Monoid a => String -> AS a
+mkAS s = AS (length s) [(mempty, s)]
 
-data Doc = Empty
-         | Text Box
-         | Line !Bool            -- True <=> when undone by group, do not insert a space
-         | Cat Doc Doc
-         | Nest Indentation Doc
-         | Union Doc Doc           -- invariant: first lines of first doc longer than the first lines of the second doc
-         | Column  (Indentation -> Doc)
-         | Nesting (Indentation -> Doc)
+instance Semigroup (AS a) where
+  AS i xs <> AS j ys = AS (i + j) (xs <> ys)
 
+newtype L a = L (Seq (AS a)) -- non-empty sequence
+  deriving (Eq,Ord,Show,Functor,Foldable,Traversable)
 
-instance Monoid Doc where
-  mempty = Empty
-  mappend = Cat
+instance Monoid a => Semigroup (L a) where
+  L (viewr -> xs :> x) <> L (viewl -> y :< ys) =
+    L (xs <> singleton (x <> y) <> indent ys) where
 
-text :: String -> Doc
-text = Text . Str
+    n      = asLength x
+    pad    = mkAS (P.replicate n ' ')
+    indent = if n == 0 then id else fmap (pad <>)
 
-spacing = Text . Spacing
+  L _ <> L _ = error "<> @L: invariant violated, Seq is empty"
 
-space = spacing 1
+instance Monoid a => Monoid (L a) where
+   mempty = L (singleton (mkAS ""))
 
-group :: Doc -> Doc
-group x         = Union (flatten x) x
+instance Layout L where
+   text = L . singleton . mkAS
+   flush (L xs) = L (xs |> mkAS "")
+   annotate a (L s') = L (fmap annotateAS s')
+      where annotateAS (AS i s) = AS i (fmap annotatePart s)
+            annotatePart (b, s) = (b `mappend` a, s)
 
-flatten :: Doc -> Doc
-flatten (Cat x y)       = Cat (flatten x) (flatten y)
-flatten (Nest i x)      = Nest i (flatten x)
-flatten (Line noSpace)  = if noSpace then Empty else space
-flatten (Union x _y)    = flatten x
-flatten (Column f)      = Column (flatten . f)
-flatten (Nesting f)     = Nesting (flatten . f)
-flatten other           = other                     --Empty,Char,Text
+renderWithL :: (Monoid a, Monoid r) => Options a r -> L a -> r
+renderWithL opts (L xs) = intercalate (toList xs)
+  where
+    f = optsAnnotate opts
+    f' (AS _ s) = foldMap (uncurry f) s
+    sep = f mempty "\n"
 
-len :: Box -> Int
-len (Str s) = length s
-len (Spacing x) = x
-len NewLine = 0
+    intercalate []     = mempty
+    intercalate (y:ys) = f' y `mappend` foldMap (mappend sep . f') ys
 
-data Docs   = Nil
-            | Cons !Indentation Doc Docs
+data Options a r = Options
+    { optsPageWidth :: !Int              -- ^ maximum page width
+    , optsAnnotate  :: a -> String -> r  -- ^ how to annotate the string. /Note:/ the annotation should preserve the visible length of the string.
+    }
 
-data Process = Process {overflow :: Indentation
-                       ,curIndent :: !Indentation -- current indentation
-                       ,numToks :: Int -- total number of non-space tokens produced
-                       ,tokens :: [Box] -- tokens produced, in reverse order
-                       ,rest :: !Docs -- rest of the input document to process
-                       }
--- The ⟨filtering⟩ step takes all process states starting at the current
--- line, and discards those that are dominated.  A process state
--- dominates another one if it was able to produce more tokens while
--- having less indentation. This is implemented by first sorting the
--- process states by following 'measure', and then applying the
--- 'filtering' function.
+class Layout d where
+  text :: Monoid a => String -> d a
+  flush :: Monoid a => d a -> d a
+  -- | `<>` new annotation to the 'Doc'.
+  --
+  -- Example: 'Any True' annotation will transform the rendered 'Doc' into uppercase:
+  --
+  -- >>> let r = putStrLn . renderWith defaultOptions { optsAnnotate = \a x -> if a == Any True then map toUpper x else x }
+  -- >>> r $ text "hello" <$$> annotate (Any True) (text "world")
+  -- hello
+  -- WORLD
+  --
+  annotate :: forall a. Monoid a => a -> d a -> d a
 
-measure :: Process -> (Indentation, Int)
-measure Process{..} = (curIndent, negate numToks)
+-- type parameter is phantom.
+data M a = M {height    :: Int,
+              lastWidth :: Int,
+              maxWidth  :: Int
+              }
+  deriving (Show,Eq,Ord,Functor,Foldable,Traversable)
 
-instance Eq Process where
-  (==) = (==) `on` measure
-instance Ord Process where
-  compare = compare `on` measure
+instance Semigroup (M a) where
+  a <> b =
+    M {maxWidth = max (maxWidth a) (maxWidth b + lastWidth a),
+       height = height a + height b,
+       lastWidth = lastWidth a + lastWidth b}
 
-filtering :: [Process] -> [Process]
-filtering (x:y:xs) | numToks x >= numToks y = filtering (x:xs)
-                   | otherwise = x:filtering (y:xs)
-filtering xs = xs
+instance Monoid a => Monoid (M a) where
+  mempty = text ""
+  mappend = (<>)
 
-renderAll :: Double -> Indentation -> Doc -> [Box]
-renderAll rfrac w doc = reverse $ loop [Process 0 0 0 [] $ Cons 0 doc Nil]
-    where
-      loop ps = case dones of
-        ((_,done):_) -> done -- here we could attempt to do a better choice. Seems to be fine for now.
-        [] -> case conts of
-          (_:_) -> loop $ filtering $ sort $ conts -- See the comment ⟨filtering⟩ above
-          [] -> case conts'over of
-            (_:_) -> loop [minimumBy (compare `on` overflow) conts'over]
-            [] -> case dones'over of
-              ((_,done):_) -> done
-              [] -> [Str "Pretty print: Panic"]
+instance Layout M where
+  text s = M {height = 0, maxWidth = length s, lastWidth = length s}
+  flush a = M {maxWidth = maxWidth a,
+               height = height a + 1,
+               lastWidth = 0}
+  annotate _ M{..} = M{..}
+class Poset a where
+  (≺) :: a -> a -> Bool
 
-        where
-          -- advance all processes by one line (if possible)
-          ps' = concatMap (\Process{..} -> rall numToks tokens curIndent curIndent rest) ps
-          -- Have some processes reached the end? With some overflow?
-          (dones0,conts0) = partitionEithers ps'
-          (conts,conts'over) = partition (\p -> overflow p <= 0) conts0
-          (dones,dones'over) = partition (\(o,_) -> o <= 0) dones0
 
-      -- r :: the ribbon width in characters
-      r  =  max 0 (min w (round (fromIntegral w * rfrac)))
+instance Poset (M a) where
+  M c1 l1 s1 ≺ M c2 l2 s2 = c1 <= c2 && l1 <= l2 && s1 <= s2
 
-      -- Automatically inserted spacing does not count as doing more production.
-      count (Spacing _) = 0
-      count (Str _) = 1
+mergeOn :: Ord b => (a -> b) -> [a] -> [a] -> [a]
+mergeOn m = go
+  where
+    go [] xs = xs
+    go xs [] = xs
+    go (x:xs) (y:ys)
+      | m x <= m y  = x:go xs (y:ys)
+      | otherwise    = y:go (x:xs) ys
 
-      -- Compute the state(s) after reaching the end of line.
-      -- rall :: n = indentation of current line
-      --         k = current column
-      --        (ie. (k >= n) && (k - n == count of inserted characters)
-      rall :: Int -> [Box] -> Indentation -> Indentation -> Docs -> [Either (Indentation,[Box]) Process]
-      rall nts ts n k ds0 = case ds0 of
-         Nil ->  [Left $ (overflow,ts)] -- Done!
-         Cons i d ds -> case d of
-            Empty       -> rall nts ts n k ds
-            Text s      -> let k' = k+len s in seq k' (rall (nts+count s) (s:ts) n k' ds)
-            Line _      -> [Right $ Process overflow i nts (Spacing i:NewLine:ts) ds] -- "yield" when the end of line is reached
-            Cat x y     -> rall nts ts n k (Cons i x (Cons i y ds))
-            Nest j x    -> let i' = i+j in seq i' (rall nts ts n k (Cons i' x ds))
-            Union x y   -> rall nts ts n k (Cons i x ds) ++ rall nts ts n k (Cons i y ds)
-            Column f    -> rall nts ts n k (Cons i (f k) ds)
-            Nesting f   -> rall nts ts n k (Cons i (f i) ds)
-        where overflow = negate $ min (w - k) (r - k + n)
+mergeAllOn :: Ord b => (a -> b) -> [[a]] -> [a]
+mergeAllOn _ [] = []
+mergeAllOn m (x:xs) = mergeOn m x (mergeAllOn m xs)
 
+bestsOn :: forall a b. (Poset b, Ord b)
+      => (a -> b) -- ^ measure
+      -> [[a]] -> [a]
+bestsOn m = paretoOn' m [] . mergeAllOn m
 
-layout :: [Box] -> String
-layout [] = []
-layout (Str s:xs) = s ++ layout xs
-layout (NewLine:xs) = '\n' : layout xs
-layout (Spacing x:xs) = replicate x ' ' ++ layout xs
+-- | @paretoOn m = paretoOn' m []@
+paretoOn' :: Poset b => (a -> b) -> [a] -> [a] -> [a]
+paretoOn' _ acc [] = P.reverse acc
+paretoOn' m acc (x:xs) = if any ((≺ m x) . m) acc
+                            then paretoOn' m acc xs
+                            else paretoOn' m (x:acc) xs
+                            -- because of the ordering, we have that
+                            -- for all y ∈ acc, y <= x, and thus x ≺ y
+                            -- is false. No need to refilter acc.
 
-render :: Double -> Indentation -> Doc -> String
-render rfrac w d = do
-  layout $ renderAll rfrac w d
+-- list sorted by lexicographic order for the first component
+-- function argument is the page width
+newtype ODoc a = MkDoc {fromDoc :: Int -> [(Pair M L a)]}
+  deriving Functor
 
-instance Show Doc where
-  show = render 0.8 80
+instance Monoid a => Semigroup (ODoc a) where
+  MkDoc xs <> MkDoc ys = MkDoc $ \w -> bestsOn frst [ discardInvalid w [x <> y | y <- ys w] | x <- xs w]
+
+discardInvalid w = quasifilter (fits w . frst)
+
+quasifilter _ [] = []
+quasifilter p zs = let fzs = filter p zs
+                   in if null fzs -- in case that there are no valid layouts, we take a narrow one.
+                      then [minimumBy (compare `on` (maxWidth . frst)) zs]
+                      else fzs
+
+instance Monoid a => Monoid (ODoc a) where
+  mempty = text ""
+  mappend = (<>)
+
+fits :: Int -> M a -> Bool
+fits w x = maxWidth x <= w
+
+instance Layout ODoc where
+  text s = MkDoc $ \_ -> [text s]
+  flush (MkDoc xs) = MkDoc $ \w -> fmap flush (xs w)
+  annotate a (MkDoc xs) = MkDoc $ \w -> fmap (annotate a) (xs w)
+
+renderWith :: (Monoid r, Annotation a)
+           => Options a r  -- ^ rendering options
+           -> ODoc a          -- ^ renderable
+           -> r
+renderWith opts d = case xs of
+    [] -> error "No suitable layout found."
+    ((_ :-: x):_) -> renderWithL opts x
+  where
+    pageWidth = optsPageWidth opts
+    xs = discardInvalid pageWidth (fromDoc d pageWidth)
+
+onlySingleLine :: [Pair M L a] -> [Pair M L a]
+onlySingleLine = takeWhile (\(M{..} :-: _) -> height == 0)
+
+spaces :: (Monoid a,Layout l) => Int -> l a
+spaces n = text $ replicate n ' '
+
+
+-- | The document @(x \$$> y)@ concatenates document @x@ and @y@ with
+-- a linebreak in between. (infixr 5)
+($$) :: (Layout d, Monoid a, Semigroup (d a)) => d a -> d a -> d a
+a $$ b = flush a <> b
+
+second f (a,b) = (a, f b)
+
+groupingBy :: Monoid a => String -> [(Int,Doc a)] -> Doc a
+groupingBy _ [] = mempty
+groupingBy separator ms = MkDoc $ \w ->
+  let mws = map (second (($ w) . fromDoc)) ms
+      (_,lastMw) = last mws
+      hcatElems = map (onlySingleLine . snd) (init mws) ++ [lastMw] -- all the elements except the first must fit on a single line
+      vcatElems = map (\(indent,x) -> map (spaces indent <>) x) mws
+      horizontal = discardInvalid w $ foldr1 (liftA2 (\x y -> x <> text separator <> y)) hcatElems
+      vertical = foldr1 (\xs ys -> bestsOn frst [[x $$ y | y <- ys] | x <- xs]) vcatElems
+  in bestsOn frst [horizontal,vertical]
+
+data Pair f g a = (:-:) {frst :: f a, scnd :: g a}
+  deriving (Functor,Foldable,Traversable)
+
+instance (Semigroup (f a), Semigroup (g a)) => Semigroup (Pair f g a) where
+  (x :-: y) <> (x' :-: y') = (x <> x') :-: (y <> y')
+instance (Monoid (f a), Monoid (g a)) => Monoid (Pair f g a) where
+  mempty = mempty :-: mempty
+
+instance (Layout a, Layout b) => Layout (Pair a b) where
+  text s = text s :-: text s
+  flush (a:-:b) = (flush a:-: flush b)
+  annotate x (a:-:b) = (annotate x a:-:annotate x b)
+
+instance Monoid a => IsString (Doc a) where
+  fromString = text
+
+type Annotation a = (Monoid a)
+type Doc = ODoc
+
+-- tt :: Doc ()
+-- tt = groupingBy " " $ map (4,) $ 
+--      ((replicate 4 $ groupingBy " " (map (4,) (map text ["fw"]))) ++
+--       [groupingBy " " (map (0,) (map text ["fw","arstnwfyut","arstin","arstaruf"]))])
+
+-- $setup
+-- >>> import Text.PrettyPrint.Compact
+-- >>> import Data.Monoid
+-- >>> import Data.Char
diff --git a/bench/Benchmark.hs b/bench/Benchmark.hs
new file mode 100644
--- /dev/null
+++ b/bench/Benchmark.hs
@@ -0,0 +1,85 @@
+module Main (main) where
+
+import Prelude ()
+import Prelude.Compat
+
+import Control.DeepSeq (force)
+import Control.Exception (evaluate)
+import Data.Aeson (Value (..), decode)
+import Data.Foldable (toList)
+
+import qualified Criterion.Main as C
+import qualified Data.HashMap.Lazy as H
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.Builder as TLB
+
+import qualified Text.PrettyPrint.Compact  as PC
+import qualified Text.PrettyPrint.HughesPJ as HPJ
+import qualified Text.PrettyPrint.Leijen   as WL
+
+prettiestJSON :: Value -> PC.Doc ()
+prettiestJSON (Bool True)  = PC.text "true"
+prettiestJSON (Bool False) = PC.text "false"
+prettiestJSON (Object o)   = PC.encloseSep (PC.text "{") (PC.text "}") (PC.text ",") (map prettyKV $ H.toList o)
+  where prettyKV (k,v)     = PC.text (show k) PC.<> PC.text ":" PC.<+> prettiestJSON v
+prettiestJSON (String s)   = PC.string (show s)
+prettiestJSON (Array a)    = PC.encloseSep (PC.text "[") (PC.text "]") (PC.text ",") (map prettiestJSON $ toList a)
+prettiestJSON Null         = PC.mempty
+prettiestJSON (Number n)   = PC.text (show n)
+
+pcRenderText :: Monoid a => PC.Doc a -> TL.Text
+pcRenderText = TLB.toLazyText . PC.renderWith PC.defaultOptions
+    { PC.optsAnnotate = \_ -> TLB.fromString }
+
+wlJSON :: Value -> WL.Doc
+wlJSON (Bool True)     = WL.text "true"
+wlJSON (Bool False)    = WL.text "false"
+wlJSON (Object o)      = WL.encloseSep (WL.text "{") (WL.text "}") (WL.text ",") (map prettyKV $ H.toList o)
+  where prettyKV (k,v) = WL.text (show k) WL.<> WL.text ":" WL.<+> wlJSON v
+wlJSON (String s)      = WL.string (show s)
+wlJSON (Array a)       = WL.encloseSep (WL.text "[") (WL.text "]") (WL.text ",") (map wlJSON $ toList a)
+wlJSON Null            = WL.empty
+wlJSON (Number n)      = WL.text (show n)
+
+wlRender :: WL.Doc -> String
+wlRender d = WL.displayS (WL.renderPretty 1 80 d) ""
+
+hpjEncloseSep :: HPJ.Doc -> HPJ.Doc -> HPJ.Doc -> [HPJ.Doc] -> HPJ.Doc
+hpjEncloseSep open close sep list = open HPJ.<> HPJ.sep (HPJ.punctuate sep list) HPJ.<> close
+
+hpjJSON :: Value -> HPJ.Doc
+hpjJSON (Bool True)    = HPJ.text "true"
+hpjJSON (Bool False)   = HPJ.text "false"
+hpjJSON (Object o)     = hpjEncloseSep (HPJ.text "{") (HPJ.text "}") (HPJ.text ",") (map prettyKV $ H.toList o)
+  where prettyKV (k,v) = HPJ.text (show k) HPJ.<> HPJ.text ":" HPJ.<+> hpjJSON v
+hpjJSON (String s)     = HPJ.text (show s)
+hpjJSON (Array a)      = hpjEncloseSep (HPJ.text "[") (HPJ.text "]") (HPJ.text ",") (map hpjJSON $ toList a)
+hpjJSON Null           = HPJ.empty
+hpjJSON (Number n)     = HPJ.text (show n)
+
+readJSONValue :: FilePath -> IO Value
+readJSONValue fname = do
+    contents <- BSL.readFile fname
+    let Just inpJson = decode contents
+    return inpJson
+
+main :: IO ()
+main = do
+    smallValue <- evaluate . force =<< readJSONValue "bench/small.json"
+    bigValue <- evaluate . force =<< readJSONValue "bench/big.json"
+
+    C.defaultMain
+        [ C.bgroup "small"
+            [ C.bench "pretty-compact" $ C.nf (PC.render . prettiestJSON) smallValue
+            , C.bench "pretty-compact Text" $ C.nf (pcRenderText . prettiestJSON) smallValue
+            , C.bench "pretty"         $ C.nf (HPJ.render . hpjJSON) smallValue
+            , C.bench "wl-pprint"      $ C.nf (wlRender . wlJSON) smallValue
+            ]
+        , C.bgroup "big"
+            [ C.bench "pretty-compact" $ C.nf (PC.render . prettiestJSON) bigValue
+            , C.bench "pretty-compact Text" $ C.nf (pcRenderText . prettiestJSON) bigValue
+            , C.bench "pretty"         $ C.nf (HPJ.render . hpjJSON) bigValue 
+            , C.bench "wl-pprint"      $ C.nf (wlRender . wlJSON) bigValue
+            ]
+        ]
diff --git a/pretty-compact.cabal b/pretty-compact.cabal
--- a/pretty-compact.cabal
+++ b/pretty-compact.cabal
@@ -1,5 +1,6 @@
+Cabal-Version: 3.4
 name: pretty-compact
-version: 1.0
+version: 3.1
 synopsis: Pretty-printing library
 description:
   This package contains a pretty-printing library, a set of API's
@@ -9,21 +10,46 @@
   .
   This library produces more compact outputs than both
   Wadler-Leijen or Hughes-PJ algorithms, at the expense of computational ressources.
-  The API is the same as Wadler-Leijen's.
-license: GPL
+  The core API is based on Hughes-PJ, but some combinators of the Leijen API are implemented as well.
+license: LGPL-3.0-only
 license-file: LICENSE
 category: Text
 maintainer: Jean-Philippe Bernardy <jeanphilippe.bernardy@gmail.com>
 build-type: Simple
-Cabal-Version: >= 1.8
 
 source-repository head
                   type: git
                   location: http://github.com/jyp/prettiest.git
 
+
 Library
-        exposed-modules:
-                Text.PrettyPrint.Compact
-                Text.PrettyPrint.Compact.Core
-        build-depends:
-                base >= 3 && < 5
+  exposed-modules:
+    Text.PrettyPrint.Compact
+    Text.PrettyPrint.Compact.Core
+  build-depends:
+    base >= 4.6 && < 5,
+    base-compat >= 0.9.3 && <0.12,
+    containers >= 0 && <666
+
+  if !impl(ghc >= 8.0)
+    build-depends: semigroups >= 0 && <666
+
+  other-extensions:
+    LambdaCase
+
+benchmark pretty-comparison
+  type: exitcode-stdio-1.0
+  hs-source-dirs: bench
+  main-is: Benchmark.hs
+  build-depends:
+    aeson,
+    base,
+    base-compat,
+    bytestring,
+    criterion,
+    deepseq,
+    pretty,
+    pretty-compact,
+    text,
+    unordered-containers,
+    wl-pprint
