packages feed

alms-0.6.5: src/Alt/PrettyPrint.hs

{- | A layer over 'P.Doc' for propagating context information.  (I think
     Template Haskell has a version of this.) -}
module Alt.PrettyPrint (
  -- * Environment-parameterized pretty-printing document
  Doc(..),
  -- ** Environment operations
  mapD, askD, asksD, localD,
  -- * Document combinators
  -- ** Binary operations
  ($$), ($+$), (<+>), (<>),
  -- ** Unary operations
  braces, brackets, doubleQuotes, quotes, parens,
  -- ** List operations
  cat, fcat, fsep, hcat, hsep, sep, vcat,
  -- ** Miscellaneous operations
  nest, hang, punctuate,
  -- ** Nullary operations (documents)
  colon, comma, equals, lbrace, lbrack,
  lparen, rbrace, rbrack, rparen, semi, space,
  -- *** Unary functions returning documents
  char, double, float, int, integer, ptext, rational, text, zeroWidthText,
  -- * Rendering and queries
  toDocIn, isEmptyIn, renderIn, renderStyleIn, fullRenderIn,
  toDoc, isEmpty, render, renderStyle, fullRender,
  -- ** Rendering constants
  P.Mode(..), P.Style(..), P.TextDetails(..), P.style,
  -- * Module exports
  module Data.Monoid,
) where

import qualified Text.PrettyPrint as P
import Control.Applicative
import Data.Monoid hiding ((<>))

-- Document parameterized by type @e@.
newtype Doc e = Doc { unDoc :: e -> P.Doc }

--
-- Environment manipulation
--

mapD     :: (e' -> e) -> Doc e -> Doc e'
mapD f d  = Doc (unDoc d . f)

askD     :: (e -> Doc e) -> Doc e
askD f    = Doc (unDoc <$> f <*> id)

asksD    :: (e -> a) -> (a -> Doc e) -> Doc e
asksD g f = askD (f . g)

localD   :: e' -> Doc e' -> Doc e
localD    = mapD . const

--
-- Lifts
--

liftD0   :: P.Doc -> Doc e
liftD0    = Doc . const

liftD    :: (P.Doc -> P.Doc) -> Doc e -> Doc e
liftD f d = Doc (f <$> unDoc d)

liftD2 :: (P.Doc -> P.Doc -> P.Doc) ->
            Doc e -> Doc e -> Doc e
liftD2 f d1 d2 = Doc (f <$> unDoc d1 <*> unDoc d2)

liftDList :: ([P.Doc] -> P.Doc) -> [Doc e] -> Doc e
liftDList f ds = Doc (\e -> f [ d e | Doc d <- ds ])

--
-- Pretty-printing combinators
--

($$), ($+$), (<+>), (<>) :: Doc e -> Doc e -> Doc e
($$)   = liftD2 (P.$$)
($+$)  = liftD2 (P.$+$)
(<+>)  = liftD2 (P.<+>)
(<>)   = liftD2 (P.<>)

infixl 5 $$, $+$
infixl 6 <+>, <>

braces, brackets, doubleQuotes, parens, quotes :: Doc e -> Doc e
braces       = liftD P.braces
brackets     = liftD P.brackets
doubleQuotes = liftD P.doubleQuotes
quotes       = liftD P.quotes
parens       = liftD P.parens

nest      :: Int -> Doc e -> Doc e
nest         = liftD . P.nest

hang      :: Doc e -> Int -> Doc e -> Doc e
hang d1 n    = liftD2 (flip P.hang n) d1

punctuate :: Doc e -> [Doc e] -> [Doc e]
punctuate _  []     = []
punctuate _  [d]    = [d]
punctuate d1 (d:ds) = d<>d1 : punctuate d1 ds

cat, fcat, fsep, hcat, hsep, sep, vcat :: [Doc e] -> Doc e
cat   = liftDList P.cat
fcat  = liftDList P.fcat
fsep  = liftDList P.fsep
hcat  = liftDList P.hcat
hsep  = liftDList P.hsep
sep   = liftDList P.sep
vcat  = liftDList P.vcat

char            :: Char -> Doc e
double          :: Double -> Doc e
float           :: Float -> Doc e
int             :: Int -> Doc e
integer         :: Integer -> Doc e
ptext           :: String -> Doc e
rational        :: Rational -> Doc e
text            :: String -> Doc e
zeroWidthText   :: String -> Doc e

char             = liftD0 . P.char
double           = liftD0 . P.double
float            = liftD0 . P.float
int              = liftD0 . P.int
integer          = liftD0 . P.integer
ptext            = liftD0 . P.ptext
rational         = liftD0 . P.rational
text             = liftD0 . P.text
zeroWidthText    = liftD0 . P.zeroWidthText

colon, comma, equals, lbrace, lbrack, lparen, rbrace,
  rbrack, rparen, semi, space :: Doc e
colon   = liftD0 P.colon
comma   = liftD0 P.comma
equals  = liftD0 P.equals
lbrace  = liftD0 P.lbrace
lbrack  = liftD0 P.lbrack
lparen  = liftD0 P.lparen
rbrace  = liftD0 P.rbrace
rbrack  = liftD0 P.rbrack
rparen  = liftD0 P.rparen
semi    = liftD0 P.semi
space   = liftD0 P.space

--
-- Rendering and queries
--

toDocIn :: e -> Doc e -> P.Doc
toDocIn  = flip unDoc

isEmptyIn :: e -> Doc e -> Bool
isEmptyIn e = P.isEmpty . toDocIn e

renderIn :: e -> Doc e -> String
renderIn e  = P.render . toDocIn e

renderStyleIn :: e -> P.Style -> Doc e -> String
renderStyleIn e sty = P.renderStyle sty . toDocIn e

fullRenderIn :: e ->
                P.Mode -> Int -> Float ->
                (P.TextDetails -> a -> a) -> a ->
                Doc e -> a
fullRenderIn e mode cols ribbon f z =
  P.fullRender mode cols ribbon f z . toDocIn e

toDoc    :: Monoid e => Doc e -> P.Doc
toDoc     = toDocIn mempty

isEmpty :: Monoid e => Doc e -> Bool
isEmpty  = isEmptyIn mempty

render :: Monoid e => Doc e -> String
render  = renderIn mempty

renderStyle :: Monoid e => P.Style -> Doc e -> String
renderStyle = renderStyleIn mempty

fullRender :: Monoid e =>
              P.Mode -> Int -> Float ->
              (P.TextDetails -> a -> a) -> a ->
              Doc e -> a
fullRender = fullRenderIn mempty

instance Monoid (Doc e) where
  mempty   = liftD0 P.empty
  mappend  = (<>)
  mconcat  = hcat