wl-pprint-text (empty) → 1.0.0.0
raw patch · 5 files changed
+1697/−0 lines, 5 filesdep +basedep +textsetup-changed
Dependencies added: base, text
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- Text/PrettyPrint/Leijen/Text.hs +1030/−0
- Text/PrettyPrint/Leijen/Text/Monadic.hs +613/−0
- wl-pprint-text.cabal +22/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2010, Ivan Lazar Miljenovic++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Ivan Lazar Miljenovic nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Text/PrettyPrint/Leijen/Text.hs view
@@ -0,0 +1,1030 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.PrettyPrint.Leijen.Text+-- Copyright : Ivan Lazar Miljenovic (c) 2010,+-- Daan Leijen (c) 2000, http://www.cs.uu.nl/~daan+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Ivan.Miljenovic@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- This library is a port of the /wl-pprint/ package to use 'Text' values rather than 'String's.+--+-- Pretty print module based on Philip Wadler's \"prettier printer\"+--+-- @+-- \"A prettier printer\"+-- Draft paper, April 1997, revised March 1998.+-- <http://cm.bell-labs.com/cm/cs/who/wadler/papers/prettier/prettier.ps>+-- @+--+-- PPrint is an implementation of the pretty printing combinators+-- described by Philip Wadler (1997). In their bare essence, the+-- combinators of Wadler are not expressive enough to describe some+-- commonly occurring layouts. The PPrint library adds new primitives+-- to describe these layouts and works well in practice.+--+-- The library is based on a single way to concatenate documents,+-- which is associative and has both a left and right unit. This+-- simple design leads to an efficient and short implementation. The+-- simplicity is reflected in the predictable behaviour of the+-- combinators which make them easy to use in practice.+--+-- A thorough description of the primitive combinators and their+-- implementation can be found in Philip Wadler's paper+-- (1997). Additions and the main differences with his original paper+-- are:+--+-- * The nil document is called empty.+--+-- * The above combinator is called '<$>'. The operator '</>' is used+-- for soft line breaks.+--+-- * There are three new primitives: 'align', 'fill' and+-- 'fillBreak'. These are very useful in practice.+--+-- * Lots of other useful combinators, like 'fillSep' and 'list'.+--+-- * There are two renderers, 'renderPretty' for pretty printing and+-- 'renderCompact' for compact output. The pretty printing algorithm+-- also uses a ribbon-width now for even prettier output.+--+-- * There are two displayers, 'displayT' for 'Text' values and 'displayIO'+-- for file based output.+--+-- * There is a 'Pretty' class.+--+-- * The implementation uses optimised representations and strictness+-- annotations.+--+-- Ways that this library differs from /wl-pprint/ (apart from using+-- 'Text' rather than 'String'):+--+-- * Smarter treatment of 'empty' sub-documents (partially copied over+-- from the /pretty/ library).+-----------------------------------------------------------+module Text.PrettyPrint.Leijen.Text (+ -- * Documents+ Doc,++ -- * Basic combinators+ empty, char, text, (<>), nest, line, linebreak, group, softline,+ softbreak,++ -- * 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,++ -- * Operators+ (<+>), (<$>), (</>), (<$$>), (<//>),++ -- * List combinators+ hsep, vsep, fillSep, sep, hcat, vcat, fillCat, cat, punctuate,++ -- * Fillers+ fill, fillBreak,++ -- * Bracketing combinators+ enclose, squotes, dquotes, parens, angles, braces, brackets,++ -- * Character documents+ lparen, rparen, langle, rangle, lbrace, rbrace, lbracket, rbracket,+ squote, dquote, semi, colon, comma, space, dot, backslash, equals,++ -- * Primitive type documents+ string, int, integer, float, double, rational, bool,++ -- * Position-based combinators+ column, nesting, width,++ -- * Pretty class+ Pretty(..),++ -- * Rendering+ SimpleDoc(..), renderPretty, renderCompact, displayT,+ displayIO, putDoc, hPutDoc++ ) where++import System.IO (Handle,hPutStr,hPutChar,stdout)++import qualified Data.Text.Lazy as T+import Data.Text.Lazy(Text)+import qualified Data.Text.Lazy.IO as T+import qualified Data.Text.Lazy.Builder as B+import Data.Text.Lazy.Builder(Builder)+import Data.Int(Int64)+import Data.Monoid(mempty,mappend)++infixr 5 </>,<//>,<$>,<$$>+infixr 6 <>,<+>+++-----------------------------------------------------------+-- list, tupled and semiBraces pretty print a list of+-- documents either horizontally or vertically aligned.+-----------------------------------------------------------+++-- | 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 = 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 = encloseSep lparen rparen comma++-- | The document @(semiBraces xs)@ separates the documents @xs@ with+-- 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 = encloseSep lbrace rbrace semi++-- | The document @(encloseSep l r sep xs)@ concatenates the documents+-- @xs@ separated by @sep@ and encloses the resulting document by+-- @l@ and @r@. The documents are rendered horizontally if that fits+-- the page. Otherwise they are aligned vertically. All separators+-- are put in front of the elements. For example, the combinator+-- 'list' can be defined with @encloseSep@:+--+-- > list xs = encloseSep lbracket rbracket comma xs+-- > test = text "list" <+> (list (map int [10,200,3000]))+--+-- Which is layed out with a page width of 20 as:+--+-- @+-- list [10,200,3000]+-- @+--+-- But when the page width is 15, it is layed out as:+--+-- @+-- list [10+-- ,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)++-----------------------------------------------------------+-- punctuate p [d1,d2,...,dn] => [d1 <> p,d2 <> p, ... ,dn]+-----------------------------------------------------------+++-- | @(punctuate p xs)@ concatenates all documents in @xs@ with+-- document @p@ except for the last document.+--+-- > someText = map text ["words","in","a","tuple"]+-- > test = parens (align (cat (punctuate comma someText)))+--+-- This is layed out on a page width of 20 as:+--+-- @+-- (words,in,a,tuple)+-- @+--+-- But when the page width is 15, it is layed out as:+--+-- @+-- (words,+-- in,+-- a,+-- tuple)+-- @+--+-- (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 p (d:ds) = (d <> p) : punctuate p ds+++-----------------------------------------------------------+-- high-level combinators+-----------------------------------------------------------+++-- | The document @(sep xs)@ concatenates all documents @xs@ either+-- horizontally with @(\<+\>)@, if it fits the page, or vertically+-- with @(\<$\>)@.+--+-- > sep xs = group (vsep xs)+sep :: [Doc] -> Doc+sep = group . vsep++-- | 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 = fold (</>)++-- | The document @(hsep xs)@ concatenates all documents @xs@+-- horizontally with @(\<+\>)@.+hsep :: [Doc] -> Doc+hsep = fold (<+>)++-- | 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 = fold (<$>)++-- | 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++-- | 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 = fold (<//>)++-- | The document @(hcat xs)@ concatenates all documents @xs@+-- horizontally with @(\<\>)@.+hcat :: [Doc] -> Doc+hcat = fold (<>)++-- | 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 = fold (<$$>)++fold :: (Doc -> Doc -> Doc) -> [Doc] -> Doc+fold f [] = empty+fold f ds = foldr1 f ds++-- | The document @(x \<\> y)@ concatenates document @x@ and document+-- @y@. It is an associative operation having 'empty' as a left and+-- right unit. (infixr 6)+(<>) :: Doc -> Doc -> Doc+x <> y = x `beside` y++-- | The document @(x \<+\> y)@ concatenates document @x@ and @y@ with+-- a @space@ in between. (infixr 6)+(<+>) :: Doc -> Doc -> Doc+Empty <+> y = y+x <+> Empty = x+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+(</>) = splitWithBreak False++-- | 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+(<//>) = splitWithBreak True++splitWithBreak :: Bool -> Doc -> Doc -> Doc+splitWithBreak _ Empty b = b+splitWithBreak _ a Empty = a+splitWithBreak f a b = a <> group (Line f) <> b++-- | The document @(x \<$\> y)@ concatenates document @x@ and @y@ with+-- a 'line' in between. (infixr 5)+(<$>) :: Doc -> Doc -> Doc+(<$>) = splitWithLine False++-- | The document @(x \<$$\> y)@ concatenates document @x@ and @y@+-- with a @linebreak@ in between. (infixr 5)+(<$$>) :: Doc -> Doc -> Doc+(<$$>) = splitWithLine True++splitWithLine :: Bool -> Doc -> Doc -> Doc+splitWithLine _ Empty b = b+splitWithLine _ a Empty = a+splitWithLine f a b = a <> Line f <> b++-- | 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++-- | Document @(squotes x)@ encloses document @x@ with single quotes+-- \"'\".+squotes :: Doc -> Doc+squotes = enclose squote squote++-- | Document @(dquotes x)@ encloses document @x@ with double quotes+-- '\"'.+dquotes :: Doc -> Doc+dquotes = enclose dquote dquote++-- | Document @(braces x)@ encloses document @x@ in braces, \"{\" and+-- \"}\".+braces :: Doc -> Doc+braces = enclose lbrace rbrace++-- | Document @(parens x)@ encloses document @x@ in parenthesis, \"(\"+-- and \")\".+parens :: Doc -> Doc+parens = enclose lparen rparen++-- | Document @(angles x)@ encloses document @x@ in angles, \"\<\" and+-- \"\>\".+angles :: Doc -> Doc+angles = enclose langle rangle++-- | Document @(brackets x)@ encloses document @x@ in square brackets,+-- \"[\" and \"]\".+brackets :: Doc -> Doc+brackets = enclose lbracket rbracket++-- | The document @(enclose l r x)@ encloses document @x@ between+-- documents @l@ and @r@ using @(\<\>)@.+--+-- > enclose l r x = l <> x <> r+enclose :: Doc -> Doc -> Doc -> Doc+enclose l r x = l <> x <> r++-- | The document @lparen@ contains a left parenthesis, \"(\".+lparen :: Doc+lparen = char '('++-- | The document @rparen@ contains a right parenthesis, \")\".+rparen :: Doc+rparen = char ')'++-- | The document @langle@ contains a left angle, \"\<\".+langle :: Doc+langle = char '<'++-- | The document @rangle@ contains a right angle, \">\".+rangle :: Doc+rangle = char '>'++-- | The document @lbrace@ contains a left brace, \"{\".+lbrace :: Doc+lbrace = char '{'++-- | The document @rbrace@ contains a right brace, \"}\".+rbrace :: Doc+rbrace = char '}'++-- | The document @lbracket@ contains a left square bracket, \"[\".+lbracket :: Doc+lbracket = char '['++-- | The document @rbracket@ contains a right square bracket, \"]\".+rbracket :: Doc+rbracket = char ']'++-- | The document @squote@ contains a single quote, \"'\".+squote :: Doc+squote = char '\''++-- | The document @dquote@ contains a double quote, '\"'.+dquote :: Doc+dquote = char '"'++-- | The document @semi@ contains a semi colon, \";\".+semi :: Doc+semi = char ';'++-- | The document @colon@ contains a colon, \":\".+colon :: Doc+colon = char ':'++-- | The document @comma@ contains a comma, \",\".+comma :: Doc+comma = char ','++-- | The document @space@ contains a single space, \" \".+--+-- > x <+> y = x <> space <> y+space :: Doc+space = char ' '++-- | The document @dot@ contains a single dot, \".\".+dot :: Doc+dot = char '.'++-- | The document @backslash@ contains a back slash, \"\\\".+backslash :: Doc+backslash = char '\\'++-- | The document @equals@ contains an equal sign, \"=\".+equals :: Doc+equals = char '='++-----------------------------------------------------------+-- Combinators for prelude types+-----------------------------------------------------------++-- string is like "text" but replaces '\n' by "line"++-- | The document @(string s)@ concatenates all characters in @s@+-- using @line@ for newline characters and @char@ for all other+-- characters. It is used instead of 'text' whenever the text+-- contains newline characters.+string :: Text -> Doc+string str = case T.uncons str of+ Nothing -> empty+ Just ('\n',str') -> line <> string str'+ _ -> case (T.span (/='\n') str) of+ (xs,ys) -> text xs <> string ys++-- | The document @(bool b)@ shows the literal boolean @b@ using+-- 'text'.+bool :: Bool -> Doc+bool b = text' b++-- | The document @(int i)@ shows the literal integer @i@ using+-- 'text'.+int :: Int -> Doc+int i = text' i++-- | The document @(integer i)@ shows the literal integer @i@ using+-- 'text'.+integer :: Integer -> Doc+integer i = text' i++-- | The document @(float f)@ shows the literal float @f@ using+-- 'text'.+float :: Float -> Doc+float f = text' f++-- | The document @(double d)@ shows the literal double @d@ using+-- 'text'.+double :: Double -> Doc+double d = text' d++-- | The document @(rational r)@ shows the literal rational @r@ using+-- 'text'.+rational :: Rational -> Doc+rational r = text' r++text' :: (Show a) => a -> Doc+text' = text . T.pack . show++-----------------------------------------------------------+-- overloading "pretty"+-----------------------------------------------------------++-- | The member @prettyList@ is only used to define the @instance+-- Pretty a => Pretty [a]@. In normal circumstances only the+-- @pretty@ function is used.+class Pretty a where+ pretty :: a -> Doc++ prettyList :: [a] -> Doc+ prettyList = list . map pretty++instance Pretty a => Pretty [a] where+ pretty = prettyList++instance Pretty Doc where+ pretty = id++instance Pretty Text where+ pretty = string++instance Pretty () where+ pretty () = text' ()++instance Pretty Bool where+ pretty b = bool b++instance Pretty Char where+ pretty c = char c++ prettyList s = string $ T.pack s++instance Pretty Int where+ pretty i = int i++instance Pretty Integer where+ pretty i = integer i++instance Pretty Float where+ pretty f = float f++instance Pretty Double where+ pretty d = double d++--instance Pretty Rational where+-- pretty r = rational r++instance (Pretty a,Pretty b) => Pretty (a,b) where+ pretty (x,y) = tupled [pretty x, pretty y]++instance (Pretty a,Pretty b,Pretty c) => Pretty (a,b,c) where+ pretty (x,y,z)= tupled [pretty x, pretty y, pretty z]++instance Pretty a => Pretty (Maybe a) where+ pretty Nothing = empty++ pretty (Just x) = pretty x++-----------------------------------------------------------+-- 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 :: Int -> Doc -> Doc+-- linebreak+-- :: Doc+-- @+fillBreak :: Int -> Doc -> Doc+fillBreak f x = width x (\w ->+ if (w > f)+ then nest f linebreak+ else spaced (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","Int -> 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 :: Int -> Doc -> Doc+-- linebreak :: Doc+-- @+fill :: Int -> Doc -> Doc+fill f d = width d (\w ->+ if (w >= f)+ then empty+ else spaced (f - w)+ )+++width :: Doc -> (Int -> 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 :: Int -> Doc -> Doc+indent _ Empty = Empty+indent i d = hang i (spaced 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 :: Int -> Doc -> Doc+hang i d = align (nest i d)++-- | 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 :-)++-----------------------------------------------------------+-- Primitives+-----------------------------------------------------------++-- | The abstract data type @Doc@ represents pretty documents.+--+-- @Doc@ is an instance of the 'Show' class. @(show doc)@ pretty+-- prints document @doc@ with a page width of 100 characters and a+-- ribbon width of 40 characters.+--+-- > show (text "hello" <$> text "world")+--+-- Which would return the string \"hello\\nworld\", i.e.+--+-- @+-- hello+-- world+-- @+data Doc = Empty+ | Char Char -- invariant: char is not '\n'+ | Text !Int64 Builder -- invariant: text doesn't contain '\n'+ | Line !Bool -- True <=> when undone by group, do not insert a space+ | Cat Doc Doc+ | Nest !Int64 Doc+ | Union Doc Doc -- invariant: first lines of first doc longer than the first lines of the second doc+ | Column (Int64 -> Doc)+ | Nesting (Int64 -> Doc)++-- | The data type @SimpleDoc@ represents rendered documents and is+-- used by the display functions.+--+-- The @Int@ in @SText@ contains the length of the string. The @Int@+-- in @SLine@ contains the indentation for that line. The library+-- provides two default display functions 'displayS' and+-- 'displayIO'. You can provide your own display function by writing+-- a function from a @SimpleDoc@ to your own output format.+data SimpleDoc = SEmpty+ | SChar Char SimpleDoc+ | SText !Int64 Builder SimpleDoc+ | SLine !Int64 SimpleDoc++-- | The empty document is, indeed, empty. Although @empty@ has no+-- content, it does have a \'height\' of 1 and behaves exactly like+-- @(text \"\")@ (and is therefore not a unit of @\<$\>@).+empty :: Doc+empty = Empty++-- | The document @(char c)@ contains the literal character @c@. The+-- character shouldn't be a newline (@'\n'@), the function 'line'+-- should be used for line breaks.+char :: Char -> Doc+char '\n' = line+char c = Char c++-- | The document @(text s)@ contains the literal string @s@. The+-- string shouldn't contain any newline (@'\n'@) characters. If the+-- string contains newline characters, the function 'string' should+-- be used.+text :: Text -> Doc+text s+ | T.null s = Empty+ | otherwise = Text (T.length s) (B.fromLazyText s)++-- | 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++beside :: Doc -> Doc -> Doc+beside Empty r = r+beside l Empty = l+beside l r = Cat l r++-- | 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 :: Int -> Doc -> Doc+nest _ Empty = Empty+nest i x = Nest (fromIntegral i) x++-- | Specifies how to create the document based upon which column it is in.+column :: (Int -> Doc) -> Doc+column f = Column (f . fromIntegral)++-- | Specifies how to nest the document based upon which column it is+-- being nested in.+nesting :: (Int -> Doc) -> Doc+nesting f = Nesting (f . fromIntegral)++-- | The @group@ combinator is used to specify alternative+-- layouts. The document @(group x)@ undoes all line breaks in+-- document @x@. The resulting line is added to the current line if+-- that fits the page. Otherwise, the document @x@ is rendered+-- without any changes.+group :: Doc -> Doc+group x = Union (flatten x) x++flatten :: Doc -> Doc+flatten (Cat x y) = Cat (flatten x) (flatten y)+flatten (Nest i x) = Nest i (flatten x)+flatten (Line break) = if break then Empty else Text 1 (B.singleton ' ')+flatten (Union x y) = flatten x+flatten (Column f) = Column (flatten . f)+flatten (Nesting f) = Nesting (flatten . f)+flatten other = other --Empty,Char,Text++-----------------------------------------------------------+-- Renderers+-----------------------------------------------------------++-----------------------------------------------------------+-- renderPretty: the default pretty printing algorithm+-----------------------------------------------------------++-- list of indentation/document pairs; saves an indirection over [(Int,Doc)]+data Docs = Nil+ | Cons !Int64 Doc Docs++-- | This is the default pretty printer which is used by 'show',+-- 'putDoc' and 'hPutDoc'. @(renderPretty ribbonfrac width x)@+-- renders document @x@ with a page width of @width@ and a ribbon+-- width of @(ribbonfrac * width)@ characters. The ribbon width is+-- the maximal amount of non-indentation characters on a line. The+-- parameter @ribbonfrac@ should be between @0.0@ and @1.0@. If it+-- is lower or higher, the ribbon width will be 0 or @width@+-- respectively.+renderPretty :: Float -> Int -> Doc -> SimpleDoc+renderPretty rfrac w x+ = best 0 0 (Cons 0 x Nil)+ where+ -- r :: the ribbon width in characters+ r = max 0 (min w64 (round (fromIntegral w * rfrac)))++ w64 = fromIntegral w++ -- best :: n = indentation of current line+ -- k = current column+ -- (ie. (k >= n) && (k - n == count of inserted characters)+ best n k Nil = SEmpty+ best n k (Cons i d ds)+ = case d of+ Empty -> best n k ds+ Char c -> let k' = k+1 in seq k' $ SChar c (best n k' ds)+ Text l s -> let k' = k+l in seq k' $ SText l s (best n k' ds)+ Line _ -> SLine i (best i i ds)+ Cat x y -> best n k (Cons i x (Cons i y ds))+ Nest j x -> let i' = i+j in seq i' (best n k (Cons i' x ds))+ Union x y -> nicest n k (best n k $ Cons i x ds)+ (best n k $ Cons i y ds)+ Column f -> best n k (Cons i (f k) ds)+ Nesting f -> best n k (Cons i (f i) ds)++ --nicest :: r = ribbon width, w = page width,+ -- n = indentation of current line, k = current column+ -- x and y, the (simple) documents to chose from.+ -- precondition: first lines of x are longer than the first lines of y.+ nicest n k x y+ | fits width x = x+ | otherwise = y+ where+ width = min (w64 - k) (r - k + n)++fits :: Int64 -> SimpleDoc -> Bool+fits w x | w < 0 = False+fits w SEmpty = True+fits w (SChar c x) = fits (w - 1) x+fits w (SText l s x) = fits (w - l) x+fits w (SLine i x) = True++-----------------------------------------------------------+-- renderCompact: renders documents without indentation+-- fast and fewer characters output, good for machines+-----------------------------------------------------------++-- | @(renderCompact x)@ renders document @x@ without adding any+-- indentation. Since no \'pretty\' printing is involved, this+-- renderer is very fast. The resulting output contains fewer+-- characters than a pretty printed version and can be used for+-- output that is read by other programs.+renderCompact :: Doc -> SimpleDoc+renderCompact x+ = scan 0 [x]+ where+ scan k [] = SEmpty+ scan k (d:ds)+ = case d of+ Empty -> scan k ds+ Char c -> let k' = k+1 in seq k' (SChar c (scan k' ds))+ Text l s -> let k' = k+l in seq k' (SText l s (scan k' ds))+ Line _ -> SLine 0 (scan 0 ds)+ Cat x y -> scan k (x:y:ds)+ Nest j x -> scan k (x:ds)+ Union x y -> scan k (y:ds)+ Column f -> scan k (f k:ds)+ Nesting f -> scan k (f 0:ds)++-----------------------------------------------------------+-- Displayers: displayS and displayIO+-----------------------------------------------------------+++-- | @(displayB simpleDoc)@ takes the output @simpleDoc@ from a+-- rendering function and transforms it to a 'Builder' type (for+-- further manipulation before converting to a lazy 'Text').+displayB :: SimpleDoc -> Builder+displayB SEmpty = mempty+displayB (SChar c x) = c `consB` displayB x+displayB (SText _ s x) = s `mappend` displayB x+displayB (SLine i x) = '\n' `consB` (indentation i `mappend` displayB x)++consB :: Char -> Builder -> Builder+c `consB` b = B.singleton c `mappend` b++-- | @(displayT simpleDoc)@ takes the output @simpleDoc@ from a+-- rendering function and transforms it to a lazy 'Text' value.+--+-- > showWidth :: Int -> Doc -> Text+-- > showWidth w x = displayT (renderPretty 0.4 w x)+displayT :: SimpleDoc -> Text+displayT = B.toLazyText . displayB++-- | @(displayIO handle simpleDoc)@ writes @simpleDoc@ to the+-- file handle @handle@. This function is used for example by+-- 'hPutDoc':+--+-- > hPutDoc handle doc = displayIO handle (renderPretty 0.4 100 doc)+displayIO :: Handle -> SimpleDoc -> IO ()+displayIO handle simpleDoc+ = display simpleDoc+ where+ display SEmpty = return ()+ display (SChar c x) = hPutChar handle c >> display x+ display (SText l s x) = T.hPutStr handle (B.toLazyText s) >> display x+ display (SLine i x) = T.hPutStr handle newLine >> display x+ where+ newLine = B.toLazyText $ '\n' `consB` indentation i++-----------------------------------------------------------+-- default pretty printers: show, putDoc and hPutDoc+-----------------------------------------------------------++instance Show Doc where+ showsPrec d doc = showsPrec d (displayT $ renderPretty 0.4 80 doc)++-- | The action @(putDoc doc)@ pretty prints document @doc@ to the+-- standard output, with a page width of 100 characters and a ribbon+-- width of 40 characters.+--+-- > main :: IO ()+-- > main = do{ putDoc (text "hello" <+> text "world") }+--+-- Which would output+--+-- @+-- hello world+-- @+putDoc :: Doc -> IO ()+putDoc doc = hPutDoc stdout doc++-- | @(hPutDoc handle doc)@ pretty prints document @doc@ to the file+-- handle @handle@ with a page width of 100 characters and a ribbon+-- width of 40 characters.+--+-- > main = do handle <- 'openFile' "MyFile" 'WriteMode'+-- > 'hPutDoc' handle ('vcat' ('map' 'text'+-- > ['T.pack' "vertical", 'T.pack' "text"]))+-- > 'hClose' handle+hPutDoc :: Handle -> Doc -> IO ()+hPutDoc handle doc = displayIO handle (renderPretty 0.4 80 doc)++-----------------------------------------------------------+-- insert spaces+-- "indentation" used to insert tabs but tabs seem to cause+-- more trouble than they solve :-)+-----------------------------------------------------------+spaces :: Int64 -> Builder+spaces n+ | n <= 0 = mempty+ | otherwise = B.fromLazyText $ T.replicate n (T.singleton ' ')++spaced :: Int -> Doc+spaced l = Text l' $ spaces l'+ where+ l' = fromIntegral l++-- An alias for readability purposes+indentation :: Int64 -> Builder+indentation = spaces++-- LocalWords: PPrint combinators Wadler Wadler's encloseSep
+ Text/PrettyPrint/Leijen/Text/Monadic.hs view
@@ -0,0 +1,613 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.PrettyPrint.Leijen.Text.Monadic+-- Copyright : Ivan Lazar Miljenovic (c) 2010,+-- Daan Leijen (c) 2000, http://www.cs.uu.nl/~daan+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Ivan.Miljenovic@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- This module provides a version of+-- "Text.PrettyPrint.Leijen.Text" where the combinators have been+-- lifted into a 'Monad'. The main usage for this is for state-based+-- pretty-printing.+-----------------------------------------------------------+module Text.PrettyPrint.Leijen.Text.Monadic (+ -- * Documents+ Doc, -- putDoc, hPutDoc,++ -- * Basic combinators+ empty, char, text, (<>), nest, line, linebreak, group, softline,+ softbreak,++ -- * 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,++ -- * Operators+ (<+>), (<$>), (</>), (<$$>), (<//>),++ -- * List combinators+ hsep, vsep, fillSep, sep, hcat, vcat, fillCat, cat, punctuate,++ -- * Fillers+ fill, fillBreak,++ -- * Bracketing combinators+ enclose, squotes, dquotes, parens, angles, braces, brackets,++ -- * Character documents+ lparen, rparen, langle, rangle, lbrace, rbrace, lbracket, rbracket,+ squote, dquote, semi, colon, comma, space, dot, backslash, equals,++ -- * Primitive type documents+ string, int, integer, float, double, rational, bool,++ -- * Position-based combinators+ column, nesting, width,++ -- * Pretty class+ Pretty(..), prettyM,++ -- * Rendering+ SimpleDoc(..), renderPretty, renderCompact, displayT,+ displayIO, putDoc, hPutDoc++ ) where++import qualified Text.PrettyPrint.Leijen.Text as PP+import Text.PrettyPrint.Leijen.Text( Doc, SimpleDoc(..), renderPretty, renderCompact+ , displayT, displayIO, putDoc, hPutDoc, Pretty(..))++import Control.Monad(liftM, liftM2, liftM3, liftM4)+import Data.Text.Lazy(Text)+import Data.Int(Int64)++infixr 5 </>,<//>,<$>,<$$>+infixr 6 <>,<+>++-----------------------------------------------------------++-- | 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 :: (Monad m) => m [Doc] -> m Doc+list = liftM PP.list++-- | 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 :: (Monad m) => m [Doc] -> m Doc+tupled = liftM PP.tupled++-- | The document @(semiBraces xs)@ separates the documents @xs@ with+-- 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 :: (Monad m) => m [Doc] -> m Doc+semiBraces = liftM PP.semiBraces++-- | The document @(encloseSep l r sep xs)@ concatenates the documents+-- @xs@ separated by @sep@ and encloses the resulting document by+-- @l@ and @r@. The documents are rendered horizontally if that fits+-- the page. Otherwise they are aligned vertically. All separators+-- are put in front of the elements. For example, the combinator+-- 'list' can be defined with @encloseSep@:+--+-- > list xs = encloseSep lbracket rbracket comma xs+-- > test = text "list" <+> (list (map int [10,200,3000]))+--+-- Which is layed out with a page width of 20 as:+--+-- @+-- list [10,200,3000]+-- @+--+-- But when the page width is 15, it is layed out as:+--+-- @+-- list [10+-- ,200+-- ,3000]+-- @+encloseSep :: (Monad m) => m Doc -> m Doc -> m Doc -> m [Doc] -> m Doc+encloseSep = liftM4 PP.encloseSep++-- | @(punctuate p xs)@ concatenates all documents in @xs@ with+-- document @p@ except for the last document.+--+-- > someText = map text ["words","in","a","tuple"]+-- > test = parens (align (cat (punctuate comma someText)))+--+-- This is layed out on a page width of 20 as:+--+-- @+-- (words,in,a,tuple)+-- @+--+-- But when the page width is 15, it is layed out as:+--+-- @+-- (words,+-- in,+-- a,+-- tuple)+-- @+--+-- (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 :: (Monad m) => m Doc -> m [Doc] -> m [Doc]+punctuate = liftM2 PP.punctuate++-- | The document @(sep xs)@ concatenates all documents @xs@ either+-- horizontally with @(\<+\>)@, if it fits the page, or vertically+-- with @(\<$\>)@.+--+-- > sep xs = group (vsep xs)+sep :: (Monad m) => m [Doc] -> m Doc+sep = liftM PP.sep++-- | 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 :: (Monad m) => m [Doc] -> m Doc+fillSep = liftM PP.fillSep++-- | The document @(hsep xs)@ concatenates all documents @xs@+-- horizontally with @(\<+\>)@.+hsep :: (Monad m) => m [Doc] -> m Doc+hsep = liftM PP.hsep++-- | 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 :: (Monad m) => m [Doc] -> m Doc+vsep = liftM PP.vsep++-- | 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 :: (Monad m) => m [Doc] -> m Doc+cat = liftM PP.cat++-- | 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 :: (Monad m) => m [Doc] -> m Doc+fillCat = liftM PP.fillCat++-- | The document @(hcat xs)@ concatenates all documents @xs@+-- horizontally with @(\<\>)@.+hcat :: (Monad m) => m [Doc] -> m Doc+hcat = liftM PP.hcat++-- | 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 :: (Monad m) => m [Doc] -> m Doc+vcat = liftM PP.vcat++-- | The document @(x \<\> y)@ concatenates document @x@ and document+-- @y@. It is an associative operation having 'empty' as a left and+-- right unit. (infixr 6)+(<>) :: (Monad m) => m Doc -> m Doc -> m Doc+(<>) = liftM2 (PP.<>)++-- | The document @(x \<+\> y)@ concatenates document @x@ and @y@ with+-- a @space@ in between. (infixr 6)+(<+>) :: (Monad m) => m Doc -> m Doc -> m Doc+(<+>) = liftM2 (PP.<+>)++-- | 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)+(</>) :: (Monad m) => m Doc -> m Doc -> m Doc+(</>) = liftM2 (PP.</>)++-- | 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)+(<//>) :: (Monad m) => m Doc -> m Doc -> m Doc+(<//>) = liftM2 (PP.<//>)++-- | The document @(x \<$\> y)@ concatenates document @x@ and @y@ with+-- a 'line' in between. (infixr 5)+(<$>) :: (Monad m) => m Doc -> m Doc -> m Doc+(<$>) = liftM2 (PP.<$>)++-- | The document @(x \<$$\> y)@ concatenates document @x@ and @y@+-- with a @linebreak@ in between. (infixr 5)+(<$$>) :: (Monad m) => m Doc -> m Doc -> m Doc+(<$$>) = liftM2 (PP.<$$>)++-- | The document @softline@ behaves like 'space' if the resulting+-- output fits the page, otherwise it behaves like 'line'.+softline :: (Monad m) => m Doc+softline = return PP.softline++-- | The document @softbreak@ behaves like 'empty' if the resulting+-- output fits the page, otherwise it behaves like 'line'.+softbreak :: (Monad m) => m Doc+softbreak = return PP.softbreak++-- | Document @(squotes x)@ encloses document @x@ with single quotes+-- \"'\".+squotes :: (Monad m) => m Doc -> m Doc+squotes = liftM PP.squotes++-- | Document @(dquotes x)@ encloses document @x@ with double quotes+-- '\"'.+dquotes :: (Monad m) => m Doc -> m Doc+dquotes = liftM PP.dquotes++-- | Document @(braces x)@ encloses document @x@ in braces, \"{\" and+-- \"}\".+braces :: (Monad m) => m Doc -> m Doc+braces = liftM PP.braces++-- | Document @(parens x)@ encloses document @x@ in parenthesis, \"(\"+-- and \")\".+parens :: (Monad m) => m Doc -> m Doc+parens = liftM PP.parens++-- | Document @(angles x)@ encloses document @x@ in angles, \"\<\" and+-- \"\>\".+angles :: (Monad m) => m Doc -> m Doc+angles = liftM PP.angles++-- | Document @(brackets x)@ encloses document @x@ in square brackets,+-- \"[\" and \"]\".+brackets :: (Monad m) => m Doc -> m Doc+brackets = liftM PP.brackets++-- | The document @(enclose l r x)@ encloses document @x@ between+-- documents @l@ and @r@ using @(\<\>)@.+--+-- > enclose l r x = l <> x <> r+enclose :: (Monad m) => m Doc -> m Doc -> m Doc -> m Doc+enclose = liftM3 PP.enclose++-- | The document @lparen@ contains a left parenthesis, \"(\".+lparen :: (Monad m) => m Doc+lparen = return PP.lparen++-- | The document @rparen@ contains a right parenthesis, \")\".+rparen :: (Monad m) => m Doc+rparen = return PP.rparen++-- | The document @langle@ contains a left angle, \"\<\".+langle :: (Monad m) => m Doc+langle = return PP.langle++-- | The document @rangle@ contains a right angle, \">\".+rangle :: (Monad m) => m Doc+rangle = return PP.rangle++-- | The document @lbrace@ contains a left brace, \"{\".+lbrace :: (Monad m) => m Doc+lbrace = return PP.lbrace++-- | The document @rbrace@ contains a right brace, \"}\".+rbrace :: (Monad m) => m Doc+rbrace = return PP.rbrace++-- | The document @lbracket@ contains a left square bracket, \"[\".+lbracket :: (Monad m) => m Doc+lbracket = return PP.lbracket++-- | The document @rbracket@ contains a right square bracket, \"]\".+rbracket :: (Monad m) => m Doc+rbracket = return PP.rbracket++-- | The document @squote@ contains a single quote, \"'\".+squote :: (Monad m) => m Doc+squote = return PP.squote++-- | The document @dquote@ contains a double quote, '\"'.+dquote :: (Monad m) => m Doc+dquote = return PP.dquote++-- | The document @semi@ contains a semi colon, \";\".+semi :: (Monad m) => m Doc+semi = return PP.semi++-- | The document @colon@ contains a colon, \":\".+colon :: (Monad m) => m Doc+colon = return PP.colon++-- | The document @comma@ contains a comma, \",\".+comma :: (Monad m) => m Doc+comma = return PP.comma++-- | The document @space@ contains a single space, \" \".+--+-- > x <+> y = x <> space <> y+space :: (Monad m) => m Doc+space = return PP.space++-- | The document @dot@ contains a single dot, \".\".+dot :: (Monad m) => m Doc+dot = return PP.dot++-- | The document @backslash@ contains a back slash, \"\\\".+backslash :: (Monad m) => m Doc+backslash = return PP.backslash++-- | The document @equals@ contains an equal sign, \"=\".+equals :: (Monad m) => m Doc+equals = return PP.equals++-----------------------------------------------------------+-- Combinators for prelude types+-----------------------------------------------------------++-- | The document @(string s)@ concatenates all characters in @s@+-- using @line@ for newline characters and @char@ for all other+-- characters. It is used instead of 'text' whenever the text+-- contains newline characters.+string :: (Monad m) => Text -> m Doc+string = return . PP.string++-- | The document @(bool b)@ shows the literal boolean @b@ using+-- 'text'.+bool :: (Monad m) => Bool -> m Doc+bool = return . PP.bool++-- | The document @(int i)@ shows the literal integer @i@ using+-- 'text'.+int :: (Monad m) => Int -> m Doc+int = return . PP.int++-- | The document @(integer i)@ shows the literal integer @i@ using+-- 'text'.+integer :: (Monad m) => Integer -> m Doc+integer = return . PP.integer++-- | The document @(float f)@ shows the literal float @f@ using+-- 'text'.+float :: (Monad m) => Float -> m Doc+float = return . PP.float++-- | The document @(double d)@ shows the literal double @d@ using+-- 'text'.+double :: (Monad m) => Double -> m Doc+double = return . PP.double++-- | The document @(rational r)@ shows the literal rational @r@ using+-- 'text'.+rational :: (Monad m) => Rational -> m Doc+rational = return . PP.rational++-- | A monadic version of 'pretty'; this is to allow you to use the+-- 'Pretty' class without having to create extra instances.+-- Alternatively, you may wish to make a variant of 'Pretty' using+-- the actual 'Monad' to be used.+prettyM :: (Pretty a, Monad m) => a -> m Doc+prettyM = return . pretty++-- | 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","Int -> 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 :: Int -> Doc -> Doc+-- linebreak :: Doc+-- @+fill :: (Monad m) => Int -> m Doc -> m Doc+fill = liftM . PP.fill+++width :: (Monad m) => m Doc -> m (Int -> Doc) -> m Doc+width = liftM2 PP.width++-- | 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 :: Int -> Doc -> Doc+-- linebreak+-- :: Doc+-- @+fillBreak :: (Monad m) => Int -> m Doc -> m Doc+fillBreak = liftM . PP.fillBreak++-- | 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 :: (Monad m) => Int -> m Doc -> m Doc+indent = liftM . PP.indent++-- | 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 :: (Monad m) => Int -> m Doc -> m Doc+hang = liftM . PP.hang++-- | 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 :: (Monad m) => m Doc -> m Doc+align = liftM PP.align++-- | The empty document is, indeed, empty. Although @empty@ has no+-- content, it does have a \'height\' of 1 and behaves exactly like+-- @(text \"\")@ (and is therefore not a unit of @\<$\>@).+empty :: (Monad m) => m Doc+empty = return PP.empty++-- | The document @(char c)@ contains the literal character @c@. The+-- character shouldn't be a newline (@'\n'@), the function 'line'+-- should be used for line breaks.+char :: (Monad m) => Char -> m Doc+char = return . PP.char++-- | The document @(text s)@ contains the literal string @s@. The+-- string shouldn't contain any newline (@'\n'@) characters. If the+-- string contains newline characters, the function 'string' should+-- be used.+text :: (Monad m) => Text -> m Doc+text = return . PP.text++-- | 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 :: (Monad m) => m Doc+line = return PP.line++-- | 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 :: (Monad m) => m Doc+linebreak = return PP.linebreak++-- | 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 :: (Monad m) => Int -> m Doc -> m Doc+nest = liftM . PP.nest++-- | Specifies how to create the document based upon which column it is in.+column :: (Monad m) => m (Int -> Doc) -> m Doc+column = liftM PP.column++-- | Specifies how to nest the document based upon which column it is+-- being nested in.+nesting :: (Monad m) => m (Int -> Doc) -> m Doc+nesting = liftM PP.nesting++-- | The @group@ combinator is used to specify alternative+-- layouts. The document @(group x)@ undoes all line breaks in+-- document @x@. The resulting line is added to the current line if+-- that fits the page. Otherwise, the document @x@ is rendered+-- without any changes.+group :: (Monad m) => m Doc -> m Doc+group = liftM PP.group+
+ wl-pprint-text.cabal view
@@ -0,0 +1,22 @@+Name: wl-pprint-text+Version: 1.0.0.0+Synopsis: A Wadler/Leijen Pretty Printer for Text values+Description: A clone of wl-pprint for use with the text library.+License: BSD3+License-file: LICENSE+Author: Ivan Lazar Miljenovic+Maintainer: Ivan.Miljenovic@gmail.com+Copyright: 2007 Daan Leijen, 2010 Ivan Lazar Miljenovic+Category: Text+Build-type: Simple+Cabal-version: >=1.6++Source-Repository head+ type: darcs+ location: http://code.haskell.org/~ivanm/wl-pprint-text++Library+ Exposed-modules: Text.PrettyPrint.Leijen.Text,+ Text.PrettyPrint.Leijen.Text.Monadic+ Build-depends: base < 5,+ text >= 0.11.0.0 && < 0.12.0.0