{-# LANGUAGE UnicodeSyntax #-} module Printcess.Combinators where import Control.Lens import qualified Data.Map as M import Printcess.Core import Printcess.Config -- For haddock links to work... -- Basic Combinators ----------------------------------------------------------- -- | Print two 'Pretty' printable things in sequence. -- -- Example: -- -- > pretty defConfig $ "x" +> 1 -- ↪ "x1" -- -- Convenience function, defined as -- -- > a +> b = pp a >> pp b infixr 5 +> (+>) :: (Pretty a, Pretty b) => a → b → PrettyM () a +> b = pp a >> pp b -- | Print two 'Pretty' printable things in sequence, separated by a space. -- -- Example: -- -- > pretty defConfig $ "x" ~> 1 -- ↪ "x 1" -- -- Convenience function, defined as -- -- > a ~> b = a +> " " +> b infixr 4 ~> (~>) :: (Pretty a, Pretty b) => a → b → PrettyM () a ~> b = a +> sp +> b -- | Print two 'Pretty' printable things in sequence, separated by a newline. -- -- Example: -- -- > pretty defConfig $ "x" \> 1 -- ↪ "x -- > 1" -- -- Convenience function, defined as -- -- > a \> b = a +> "\n" +> b infixr 3 \> (\>) :: (Pretty a, Pretty b) => a → b → PrettyM () a \> b = a +> nl +> b -- Composite Combinators ------------------------------------------------------- -- | Print an @a@ between each @b@. -- -- Examples: -- -- > pretty defConfig $ "," `betweenEach` [] -- ↪ "" -- > pretty defConfig $ "," `betweenEach` ["x"] -- ↪ "x" -- > pretty defConfig $ "," `betweenEach` ["x", "y"] -- ↪ "x,y" infixl 6 `betweenEach` betweenEach :: (Pretty a, Pretty b) => a → [b] → PrettyM () betweenEach s as = sepByA_ (map pp as) (pp s) -- | Print an @a@ before each @b@. -- -- Examples: -- -- > pretty defConfig $ "," `beforeEach` [] -- ↪ "" -- > pretty defConfig $ "," `beforeEach` ["x"] -- ↪ ",x" -- > pretty defConfig $ "," `beforeEach` ["x", "y"] -- ↪ ",x,y" infixl 6 `beforeEach` beforeEach :: (Pretty a, Pretty b) => a → [b] → PrettyM () beforeEach a bs = foldl (>>) (pure ()) $ map pp bs `sepByL'` pp a -- | Print an @a@ after each @b@. -- -- Examples: -- -- > pretty defConfig $ "," `afterEach` [] -- ↪ "" -- > pretty defConfig $ "," `afterEach` ["x"] -- ↪ "x," -- > pretty defConfig $ "," `afterEach` ["x", "y"] -- ↪ "x,y," infixl 6 `afterEach` afterEach :: (Pretty a, Pretty b) => a → [b] → PrettyM () afterEach a bs = foldl (>>) (pure ()) $ map pp bs `sepByR'` pp a sepByA :: Applicative f => [f a] → f a → f [a] sepByA [] _ = pure [] sepByA [a] _ = (:[]) <$> a sepByA (a:as) s = (\x y z → x:y:z) <$> a <*> s <*> sepByA as s sepByA_ :: Applicative f => [f a] → f a → f () sepByA_ as s = () <$ sepByA as s sepByL', sepByR' :: [a] → a → [a] sepByL' xs0 s = foldl (\xs x → xs ++ [s,x]) [] xs0 sepByR' xs0 s = foldl (\xs x → xs ++ [x,s]) [] xs0 -- | Print a @[a]@ as a block, meaning that the indentation level is -- increased, and each @a@ is printed on a single line. -- -- Example: -- -- > pretty defConfig $ "do" ~> block ["putStrLn hello", "putStrLn world"] -- > -- ↪ "do -- > -- putStrLn hello -- > -- putStrLn world" block :: Pretty a => [a] → PrettyM () block xs = indented $ nl `beforeEach` xs -- | Same as 'block', but starts the block on the current line. -- -- Example: -- -- > pretty defConfig $ "do" ~> block' ["putStrLn hello", "putStrLn world"] -- > -- ↪ "do putStrLn hello -- > -- putStrLn world" block' :: Pretty a => [a] → PrettyM () block' xs = indentedToCurPos $ nl `betweenEach` xs -- | Print a @[a]@ similar to its 'Show' instance. -- -- Example: -- -- > pretty defConfig $ ppList [ "x", "y" ] -- ↪ "[ x, y ]" ppList :: Pretty a => [a] → PrettyM () ppList ps = "[" ~> ", " `betweenEach` ps ~> "]" -- | Print a list map @[(k,v)]@ as 'ppList', but render @(k,v)@ pairs as @"k → v"@. -- -- Example: -- -- > pretty defConfig $ ppListMap [ ("k1", "v1"), ("k2", "v2") ] -- > -- ↪ "[ k1 → v1, k2 → v2 ]" ppListMap :: (Pretty a, Pretty b) => [(a, b)] → PrettyM () ppListMap = ppList . map (\(a,b) → a ~> "→" ~> b) -- | Print a @Data.Map@ in the same way as 'ppListMap'. ppMap :: (Pretty a, Pretty b) => M.Map a b → PrettyM () ppMap = ppListMap . M.assocs -- | Print a horizontal bar consisting of a 'Char' as long as 'cMaxLineWidth' -- (or 80 if it is @Nothing@). -- -- Example: -- -- > pretty defConfig $ bar '-' -- > -- ↪ "-----------------------------------------…" bar :: Char → PrettyM () bar c = do w ← maybe 80 id <$> use maxLineWidth pp $ replicate w c -- | Print a horizontal bar consisting of a 'Char' as long as 'cMaxLineWidth' -- (or 80 if it is @Nothing@). The horizontal bar has a title 'String' printed -- at column 6. -- -- Example: -- -- > pretty defConfig $ titleBar '-' "Foo" -- > -- ↪ "----- Foo -------------------------------…" titleBar :: Pretty a => Char → a → PrettyM () titleBar c s = do w ← maybe 80 id <$> use maxLineWidth replicate 5 c ~> s ~> replicate (w - (7 + length (pretty (pure ()) s))) c +> "\n" -- | Print a newline (line break). nl :: PrettyM () nl = pp "\n" -- | Print a space. sp :: PrettyM () sp = pp " "