packages feed

pretty-types 0.1.1.0 → 0.2.0.0

raw patch · 4 files changed

+249/−36 lines, 4 filesdep +mtlPVP ok

version bump matches the API change (PVP)

Dependencies added: mtl

API changes (from Hackage documentation)

- Data.Type.Pretty: [PrettySeperated] :: PrettyType -> PrettyType -> PrettyType -> PrettyType
- Data.Type.Pretty: instance (Data.Type.Pretty.PrettyTypeShow sep, Data.Type.Pretty.PrettyTypeShow l, Data.Type.Pretty.PrettyTypeShow r) => Data.Type.Pretty.PrettyTypeShow ('Data.Type.Pretty.PrettySeperated sep l r)
+ Data.Type.Pretty: AlreadyIndented :: PTRenderState
+ Data.Type.Pretty: AtBeginningOfLine :: PTRenderState
+ Data.Type.Pretty: [PrettyAlternative] :: PrettyType -> PrettyType -> PrettyType
+ Data.Type.Pretty: [PrettyIndent] :: Nat -> PrettyType -> PrettyType
+ Data.Type.Pretty: [PrettyInfix] :: PrettyType -> PrettyType -> PrettyType -> PrettyType
+ Data.Type.Pretty: [PrettyPrefix] :: PrettyType -> PrettyType -> PrettyType
+ Data.Type.Pretty: [PrettySuffix] :: PrettyType -> PrettyType -> PrettyType
+ Data.Type.Pretty: data PTRenderState
+ Data.Type.Pretty: instance (Data.Type.Pretty.PrettyTypeShow l, Data.Type.Pretty.PrettyTypeShow r) => Data.Type.Pretty.PrettyTypeShow ('Data.Type.Pretty.PrettyAlternative l r)
+ Data.Type.Pretty: instance (Data.Type.Pretty.PrettyTypeShow r, GHC.TypeLits.KnownNat n) => Data.Type.Pretty.PrettyTypeShow ('Data.Type.Pretty.PrettyIndent n r)
+ Data.Type.Pretty: instance (Data.Type.Pretty.PrettyTypeShow sep, Data.Type.Pretty.PrettyTypeShow l, Data.Type.Pretty.PrettyTypeShow r) => Data.Type.Pretty.PrettyTypeShow ('Data.Type.Pretty.PrettyInfix sep l r)
+ Data.Type.Pretty: instance (Data.Type.Pretty.PrettyTypeShow sep, Data.Type.Pretty.PrettyTypeShow x) => Data.Type.Pretty.PrettyTypeShow ('Data.Type.Pretty.PrettyPrefix sep x)
+ Data.Type.Pretty: instance (Data.Type.Pretty.PrettyTypeShow sep, Data.Type.Pretty.PrettyTypeShow x) => Data.Type.Pretty.PrettyTypeShow ('Data.Type.Pretty.PrettySuffix sep x)
+ Data.Type.Pretty: ptHasContent :: PrettyTypeShow p => proxy p -> PTM Bool
+ Data.Type.Pretty: type Indentation = Int
+ Data.Type.Pretty: type PTM a = RWS Indentation String PTRenderState a
+ Data.Type.Pretty: writeIndented :: String -> PTM ()
- Data.Type.Pretty: class PrettyTypeShow (p :: PrettyType)
+ Data.Type.Pretty: class PrettyTypeShow (p :: PrettyType) where ptHasContent _ = return True
- Data.Type.Pretty: ptShow :: PrettyTypeShow p => proxy p -> String
+ Data.Type.Pretty: ptShow :: PrettyTypeShow p => proxy p -> PTM ()
- Data.Type.Pretty: type (<$$>) l r = PrettySeperated PrettyNewline l r
+ Data.Type.Pretty: type (<$$-->) l r = PrettyInfix PrettyNewline l (PrettyIndent 2 r)

Files

+ ChangeLog.md view
@@ -0,0 +1,8 @@+## Version 0.2.0.0++* Added indentation support+* Added `<:>` combinator++## Version 0.1.0.0++* Initial Version
pretty-types.cabal view
@@ -1,5 +1,5 @@ name:                pretty-types-version:             0.1.1.0+version:             0.2.0.0 synopsis:            A small pretty printing DSL for complex types. description:         Please see README.md homepage:            https://github.com/sheyll/pretty-types#readme@@ -14,12 +14,14 @@                     , stack.yaml                     , .travis.yml                     , .gitignore+                    , ChangeLog.md cabal-version:       >=1.10  library   hs-source-dirs:      src   exposed-modules:     Data.Type.Pretty   build-depends:       base >= 4.9 && < 5+                     , mtl >= 2.2 && < 3   default-language:    Haskell2010   ghc-options:         -Wall -funbox-strict-fields -fno-warn-unused-do-bind   default-extensions:  BangPatterns
spec/PrettyTypesSpec.hs view
@@ -40,7 +40,7 @@       showPretty (PX :: PX ('PrettySymbol ('PrettyPadded 10) ('PrettyPrecision 2) "hello"))         `shouldBe` "        he" -  describe "PrettySeperated" $ do+  describe "PrettyInfix" $ do     it "renders (PutNat 0 <++> PutNat 1) as \"01\"" $       showPretty (PX :: PX (PutNat 0 <++> PutNat 1)) `shouldBe` "01"     it "renders (PutNat 0 <+> PutNat 1) as \"0 1\"" $@@ -66,6 +66,79 @@       showPretty (PX :: PX (PrettyMany (PutNat 777) '[PutStr "."])) `shouldBe` "."     it "renders (PrettyMany (PutNat 777) '[PutStr \".\", PutNat 3, PutNat 4]) as \".77737774\"" $       showPretty (PX :: PX (PrettyMany (PutNat 777) '[PutStr ".", PutNat 3, PutNat 4])) `shouldBe` ".77737774"++  describe "PrettyInfix" $ do+    it "renders the seperator if nested docs contain text" $+      showPretty (PX :: PX ('PrettyInfix 'PrettySpace (PutStr "foo") (PutStr "bar"))) `shouldBe` "foo bar"+    it "renders only the the first document of the second is empty, and no seperator" $+      showPretty (PX :: PX ('PrettyInfix 'PrettySpace (PutStr "foo") (PutStr ""))) `shouldBe` "foo"+    it "renders only the the second document of the first is empty, and no seperator" $+      showPretty (PX :: PX ('PrettyInfix 'PrettySpace (PutStr "") (PutStr "bar"))) `shouldBe` "bar"++  describe "PrettyAlternative" $ do+    it "renders the first document if is not empty" $+      showPretty (PX :: PX ('PrettyAlternative (PutStr "foo") (PutStr "bar"))) `shouldBe` "foo"+    it "renders the second document if the first is empty" $+      showPretty (PX :: PX ('PrettyAlternative (PutStr "") (PutStr "bar"))) `shouldBe` "bar"++  describe "PrettyPrefix" $ do+    it "renders the first and second document if the second is not empty" $+      showPretty (PX :: PX ('PrettyPrefix (PutStr "foo") (PutStr "bar"))) `shouldBe` "foobar"+    it "renders nothing if the second document is empty" $+      showPretty (PX :: PX ('PrettyPrefix (PutStr "foo") (PutStr ""))) `shouldBe` ""++  describe "PrettySuffix" $ do+    it "renders the second and first document if the second is not empty" $+      showPretty (PX :: PX ('PrettySuffix (PutStr "foo") (PutStr "bar"))) `shouldBe` "barfoo"+    it "renders nothing if the second document is empty" $+      showPretty (PX :: PX ('PrettySuffix (PutStr "foo") (PutStr ""))) `shouldBe` ""++  describe "<||>" $ do+    it "is the type alias for 'PrettyAlternative " $+      showPretty (PX :: PX (PutStr "foo" <||> PutStr "bar")) `shouldBe` "foo"+    it "has a lower precedence than <++>" $+      showPretty (PX :: PX (PutStr "foo" <++> PutStr "" <||> PutStr "baz")) `shouldBe` "foo"+    it "has a higher precedence than <$$>" $+      showPretty (PX :: PX (PutStr "foo" <$$> PutStr "" <||> PutStr "baz")) `shouldBe` "foo\nbaz"++  describe "<:>" $ do+    it "renders no label if the label is empty" $+      showPretty (PX :: PX ("" <:> PutStr "bar")) `shouldBe` "bar"+    it "renders a label, a colon followed by a space and the body" $+      showPretty (PX :: PX ("foo" <:> PutStr "bar")) `shouldBe` "foo: bar"+    it "renders a label followed by a colon if the body is empty" $+      showPretty (PX :: PX ("foo" <:> 'PrettyEmpty)) `shouldBe` "foo:"++  describe "<:$$>" $ do+    it "renders no label if the label is empty" $+      showPretty (PX :: PX ("" <:$$> PutStr "bar")) `shouldBe` "bar"+    it "renders a label, a colon followed by the body on the next line" $+      showPretty (PX :: PX ("foo" <:$$> PutStr "bar")) `shouldBe` "foo:\nbar"+    it "renders a label followed by a colon if the body is empty" $+      showPretty (PX :: PX ("foo" <:$$> 'PrettyEmpty)) `shouldBe` "foo:"++  describe "<:$$-->" $ do+    it "renders no label if the label is empty" $+      showPretty (PX :: PX ("" <:$$--> PutStr "bar")) `shouldBe` "  bar"+    it "renders a label, a colon followed by indented the body" $+      showPretty (PX :: PX ("foo" <:$$--> PutStr "bar")) `shouldBe` "foo:\n  bar"+    it "renders a label followed by a colon if the body is empty" $+      showPretty (PX :: PX ("foo" <:$$--> 'PrettyEmpty)) `shouldBe` "foo:"++  describe "PrettyIndent" $ do+    it "renders the indentation" $+      showPretty (PX :: PX (PutStr "foo" <$$--> PutStr "bar")) `shouldBe` "foo\n  bar"+    it "renders the indentation only once per line" $+      showPretty (PX :: PX (PutStr "foo" <$$--> PutStr "bar1" <+> PutStr "bar2" )) `shouldBe` "foo\n  bar1 bar2"+    it "renders the indentation of multi lines and the operator precedence is such that no parens are needed." $+      showPretty (PX :: PX (PutStr "foo" <$$--> PutStr "bar1" <$$> PutStr "bar2")) `shouldBe` "foo\n  bar1\n  bar2"+    it "renders the nested indentation." $+      showPretty (PX :: PX (PutStr "foo" <$$-->+                           (PutStr "bar1" <$$>+                            PutStr "bar2" <$$-->+                            PutStr "bar3" <$$>+                            PutStr "bar4")))+      `shouldBe` "foo\n  bar1\n  bar2\n    bar3\n    bar4"    describe "ToPretty" $ do     it "renders a custom type" $ do
src/Data/Type/Pretty.hs view
@@ -11,7 +11,7 @@ -- for your types by combining the promoted constructors of 'PrettyType'. -- -- If `UndecidableInstances` isn't holding you back, use the type aliases like--- 'PutStr', 'PutNat', 'PrettySeperated', etc in these instance definitions.+-- 'PutStr', 'PutNat', 'PrettyInfix', etc in these instance definitions. -- -- 'ToPretty' is an open type family, that converts a custom type to a -- `PrettyType`.@@ -104,6 +104,8 @@ -- @ module Data.Type.Pretty where +import Control.Monad.RWS hiding (tell)+import qualified Control.Monad.RWS import GHC.TypeLits import Data.Proxy import Text.Printf@@ -116,7 +118,7 @@   :: forall proxy (t :: k) . PrettyTypeShow (ToPretty t)   => proxy t  -- ^ A proxy to the type to print. A 'ToPretty' instance for t must exists.   -> String-showPretty _ = ptShow (Proxy :: Proxy (ToPretty t))+showPretty _ = snd $ evalRWS (ptShow (Proxy :: Proxy (ToPretty t))) 0 AtBeginningOfLine  -- | Create a 'PrettyType' from a type. --@@ -210,18 +212,54 @@  -- ** Composing Pretty Printers +-- | A label followed by a colon and space @: @ followed by another element.+--+-- >>> showPretty (Proxy :: Proxy ("foo" <:> PutStr "bar"))+-- @+-- foo: bar+-- @+type (<:>) label body = 'PrettySuffix (PutStr ":") (PutStr label) <+> body+infixl 5 <:>+-- | Like '<:>' but begin the body on a new line.+--+-- >>> showPretty (Proxy :: Proxy (PutStr "foo" <:$$> PutStr "bar"))+-- @+-- foo:+-- bar+-- @+type (<:$$>) label body = 'PrettySuffix (PutStr ":") (PutStr label) <$$> body+infixl 5 <:$$>++-- | Like '<:$$__>' but indent the body with two spaces.+--+-- >>> showPretty (Proxy :: Proxy (PutStr "foo" <:$$--> PutStr "bar"))+-- @+-- foo:+--   bar+-- @+type (<:$$-->) label body = 'PrettySuffix (PutStr ":") (PutStr label) <$$--> body+infixl 3 <:$$-->+ -- | Concatenate two 'PrettyType'.-type (<++>) l r = 'PrettySeperated 'PrettyEmpty l r+type (<++>) l r = 'PrettyInfix 'PrettyEmpty l r infixl 6 <++>  -- | Concatenate two 'PrettyType' using a 'PrettySpace'.-type (<+>) l r = 'PrettySeperated 'PrettySpace l r+type (<+>) l r = 'PrettyInfix 'PrettySpace l r infixl 5 <+> +-- | Choose the first non-empty from two 'PrettyType's.+type (<||>) l r = 'PrettyAlternative l r+infixl 5 <||>+ -- | Concatenate two 'PrettyType' using a 'PrettyNewline'.-type (<$$>) l r = 'PrettySeperated 'PrettyNewline l r+type (<$$>) l r = 'PrettyInfix 'PrettyNewline l r infixl 4 <$$> +-- | Concatenate two 'PrettyType' using a 'PrettyNewline' and indent the second.+type (<$$-->) l r = 'PrettyInfix 'PrettyNewline l ('PrettyIndent 2 r)+infixl 3 <$$-->+ -- | Surround a pretty with parens type PrettyParens doc = PrettySurrounded (PutStr "(") (PutStr ")") doc @@ -271,10 +309,20 @@ data PrettyType where   PrettyEmpty :: PrettyType   PrettySpace :: PrettyType+  -- | Begin a newline. Always use this otherwise indentation will not work!   PrettyNewline :: PrettyType   PrettySymbol :: PrettyPadded -> PrettyPrecision -> Symbol -> PrettyType   PrettyNat :: PrettyPadded -> PrettyPrecision -> PrettyNatFormat -> Nat -> PrettyType-  PrettySeperated :: PrettyType -> PrettyType -> PrettyType -> PrettyType+  -- | Prefix the second with the first argument, but only if it (the second) has content.+  PrettyPrefix :: PrettyType -> PrettyType -> PrettyType+  -- | Combine the last to arguments with the first in between them, but only if both have content.+  PrettyInfix :: PrettyType -> PrettyType -> PrettyType -> PrettyType+  -- | Add a the first argument as suffix to the second argument, but only if the second has content.+  PrettySuffix :: PrettyType -> PrettyType -> PrettyType+  -- | Indentation. Prefix any line using the given number of 'PrettySpace'.+  PrettyIndent :: Nat -> PrettyType -> PrettyType+  -- | Alternative rendering, if the first document ist empty the second will be rendered.+  PrettyAlternative :: PrettyType -> PrettyType -> PrettyType  -- | Padding for 'PrettyType's 'PrettySymbol' and 'PrettyNat'. data PrettyPadded where@@ -325,45 +373,127 @@ class PrettyTypeShow (p :: PrettyType) where   -- | Given any proxy to a promoted constructor of 'PrettyType', generate a   -- String.-  ptShow :: proxy p -> String+  ptShow :: proxy p -> PTM ()+  -- | Return 'True' if contents would be writting to the output of rendered via 'ptShow'+  ptHasContent :: proxy p -> PTM Bool+  ptHasContent _ = return True +-- | Internal monad used by 'ptShow', the state is a 'Bool' indicating+type PTM a = RWS Indentation String PTRenderState a++-- | Internal; write a possibly indented string, and update the 'PTRenderState' accordingly.+writeIndented :: String -> PTM ()+writeIndented s = do+    st <- get+    case st of+        AtBeginningOfLine -> do+            i <- ask+            Control.Monad.RWS.tell (replicate i ' ')+            put AlreadyIndented+        AlreadyIndented -> return ()+    Control.Monad.RWS.tell s++-- | Internal type of the indentation used by 'ptShow' in 'PTM'+type Indentation = Int++-- | Internal state used by 'ptShow' in 'PTM'+data PTRenderState = AtBeginningOfLine | AlreadyIndented+ -- | Print nothing.-instance PrettyTypeShow 'PrettyEmpty where ptShow _ = ""+instance PrettyTypeShow 'PrettyEmpty where+  ptShow _ = return ()+  ptHasContent _ = return False+ -- | Print a single space character.-instance PrettyTypeShow 'PrettySpace where ptShow _ = " "+instance PrettyTypeShow 'PrettySpace where+  ptShow _ = writeIndented " "+ -- | Print a single newline character.-instance PrettyTypeShow 'PrettyNewline where ptShow _ = "\n"+instance PrettyTypeShow 'PrettyNewline where+    ptShow _ = do+        put AtBeginningOfLine+        Control.Monad.RWS.tell "\n"  -- | Print a 'Symbol' using the 'printf' and the given format parameters.-instance forall t pad prec.-    (KnownSymbol t, PrintfArgModifier pad, PrintfArgModifier prec)-  => PrettyTypeShow ('PrettySymbol pad prec t) where-  ptShow _ = printf ("%" ++ toPrintfArgModifier (Proxy :: Proxy pad)-                         ++ toPrintfArgModifier (Proxy :: Proxy prec)-                         ++ "s")-                    (symbolVal (Proxy :: Proxy t))+instance forall t pad prec . (KnownSymbol t, PrintfArgModifier pad, PrintfArgModifier prec) =>+         PrettyTypeShow ('PrettySymbol pad prec t) where+    ptShow _ = writeIndented $+        printf ("%" +++                    toPrintfArgModifier (Proxy :: Proxy pad)+                        ++ toPrintfArgModifier (Proxy :: Proxy prec)+                            ++ "s")+               (symbolVal (Proxy :: Proxy t))+    ptHasContent _ = return (symbolVal (Proxy :: Proxy t) /= "")  -- | Print a 'Nat' using the 'printf' and the given format parameters.-instance forall fmt x pad prec.-  (KnownNat x, PrintfArgModifier fmt, PrintfArgModifier pad, PrintfArgModifier prec)-  => PrettyTypeShow ('PrettyNat pad prec fmt x) where-  ptShow _ = printf ("%" ++ toPrintfArgModifier (Proxy :: Proxy pad)-                         ++ toPrintfArgModifier (Proxy :: Proxy prec)-                         ++ toPrintfArgModifier (Proxy :: Proxy fmt))-                    (natVal (Proxy :: Proxy x))+instance forall fmt x pad prec . (KnownNat x, PrintfArgModifier fmt, PrintfArgModifier pad, PrintfArgModifier prec) =>+         PrettyTypeShow ('PrettyNat pad prec fmt x) where+    ptShow _ = writeIndented $+        printf ("%" +++                    toPrintfArgModifier (Proxy :: Proxy pad)+                        ++ toPrintfArgModifier (Proxy :: Proxy prec)+                            ++ toPrintfArgModifier (Proxy :: Proxy fmt))+               (natVal (Proxy :: Proxy x))  -- | Concatenate two 'PrettyType's. If one of them is empty print the other -- without any seperation character.-instance forall l r sep .-  (PrettyTypeShow sep, PrettyTypeShow l, PrettyTypeShow r)-  => PrettyTypeShow ('PrettySeperated sep l r) where-  ptShow _ =-    let rstr = ptShow (Proxy :: Proxy r)-        lstr = ptShow (Proxy :: Proxy l)-        sepStr = ptShow (Proxy :: Proxy sep)-    in if lstr == "" then rstr-        else if rstr == "" then lstr-          else lstr ++ sepStr ++ rstr+instance forall l r sep . (PrettyTypeShow sep, PrettyTypeShow l, PrettyTypeShow r) =>+         PrettyTypeShow ('PrettyInfix sep l r) where+    ptShow _ = do+        leftHasContent <- ptHasContent (Proxy :: Proxy l)+        rightHasContent <- ptHasContent (Proxy :: Proxy r)+        when leftHasContent (ptShow (Proxy :: Proxy l))+        when (leftHasContent && rightHasContent) (ptShow (Proxy :: Proxy sep))+        when rightHasContent (ptShow (Proxy :: Proxy r))++    ptHasContent _ =+      (||) <$> ptHasContent (Proxy :: Proxy l)+          <*> ptHasContent (Proxy :: Proxy r)++-- | Prefix a 'PrettyType' to x, but only if 'ptHasContent' of 'x' holds.+instance forall x sep . (PrettyTypeShow sep, PrettyTypeShow x) =>+         PrettyTypeShow ('PrettyPrefix sep x) where+    ptShow _ = do+        hasContent <- ptHasContent (Proxy :: Proxy x)+        when hasContent $ do+          ptShow (Proxy :: Proxy sep)+          ptShow (Proxy :: Proxy x)++    ptHasContent _ = ptHasContent (Proxy :: Proxy x)++-- | Add a 'PrettyType' suffix to x, but only if 'ptHasContent' holds.+instance forall x sep . (PrettyTypeShow sep, PrettyTypeShow x) =>+         PrettyTypeShow ('PrettySuffix sep x) where+    ptShow _ = do+        hasContent <- ptHasContent (Proxy :: Proxy x)+        when hasContent $ do+          ptShow (Proxy :: Proxy x)+          ptShow (Proxy :: Proxy sep)++    ptHasContent _ = ptHasContent (Proxy :: Proxy x)++-- | Render the first document, and if it is empty, the second+instance forall l r . (PrettyTypeShow l, PrettyTypeShow r) =>+         PrettyTypeShow ('PrettyAlternative l r) where+    ptShow _ = do+        leftHasContent <- ptHasContent (Proxy :: Proxy l)+        if leftHasContent+          then ptShow (Proxy :: Proxy l)+          else ptShow (Proxy :: Proxy r)++    ptHasContent _ =+      (||) <$> ptHasContent (Proxy :: Proxy l)+          <*> ptHasContent (Proxy :: Proxy r)+++-- | Render an indented, nested type.+instance forall n r . (PrettyTypeShow r, KnownNat n) =>+         PrettyTypeShow ('PrettyIndent n r) where++    ptShow _ = local (+ (fromIntegral (natVal (Proxy :: Proxy n))))+                     (ptShow (Proxy :: Proxy r))++    ptHasContent _ =  ptHasContent (Proxy :: Proxy r)  -- | Internal 'printf' format generation. Used internally by 'PrettyTypeShow' -- instances to generate the format string piece by piece with the values for