packages feed

marked-pretty 0.1 → 1.1.2.1

raw patch · 8 files changed

+1209/−1043 lines, 8 filesdep +deepseqdep +ghc-primdep +marked-prettydep ~basesetup-changednew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: deepseq, ghc-prim, marked-pretty

Dependency ranges changed: base

API changes (from Hackage documentation)

- Text.PrettyPrint.MarkedHughesPJ: instance Show (MDoc a)
+ Text.PrettyPrint.MarkedHughesPJ: first :: MDoc a -> MDoc a -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_0MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_0Mode
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_0Style
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_0TextDetails
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_1MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_1Mode
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_1TextDetails
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_2MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_2Mode
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_2TextDetails
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_3MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_3Mode
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_3TextDetails
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_4MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_5MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_6MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Constructor C1_7MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Datatype D1MDoc
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Datatype D1Mode
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Datatype D1Style
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Datatype D1TextDetails
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Eq (MDoc a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Eq Mode
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Eq Style
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Eq a => Eq (TextDetails a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Generic (MDoc a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Generic (TextDetails a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Generic Mode
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Generic Style
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] IsString (MDoc a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Monoid (MDoc a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] NFData a => NFData (MDoc a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] NFData a => NFData (TextDetails a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Selector S1_0_0Style
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Selector S1_0_1Style
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Selector S1_0_2Style
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Show (MDoc a)
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Show Mode
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Show Style
+ Text.PrettyPrint.MarkedHughesPJ: instance [safe] Show a => Show (TextDetails a)
+ Text.PrettyPrint.MarkedHughesPJ: maybeBraces :: Bool -> MDoc a -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJ: maybeBrackets :: Bool -> MDoc a -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJ: maybeDoubleQuotes :: Bool -> MDoc a -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJ: maybeParens :: Bool -> MDoc a -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJ: maybeQuotes :: Bool -> MDoc a -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJ: reduceDoc :: MDoc a -> RDoc a
+ Text.PrettyPrint.MarkedHughesPJ: sizedText :: Int -> String -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJ: zeroWidthText :: String -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJClass: PrettyLevel :: Int -> PrettyLevel
+ Text.PrettyPrint.MarkedHughesPJClass: class Pretty a where pPrintPrec _ _ = pPrint pPrint = pPrintPrec prettyNormal 0 pPrintList l = brackets . fsep . punctuate comma . map (pPrintPrec l 0)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] (Pretty a, Pretty b) => Pretty (Either a b)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] (Pretty a, Pretty b) => Pretty (a, b)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] (Pretty a, Pretty b, Pretty c) => Pretty (a, b, c)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] (Pretty a, Pretty b, Pretty c, Pretty d) => Pretty (a, b, c, d)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e) => Pretty (a, b, c, d, e)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e, Pretty f) => Pretty (a, b, c, d, e, f)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e, Pretty f, Pretty g) => Pretty (a, b, c, d, e, f, g)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e, Pretty f, Pretty g, Pretty h) => Pretty (a, b, c, d, e, f, g, h)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Eq PrettyLevel
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Ord PrettyLevel
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty ()
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty Bool
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty Char
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty Double
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty Float
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty Int
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty Integer
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty Ordering
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty a => Pretty (Maybe a)
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Pretty a => Pretty [a]
+ Text.PrettyPrint.MarkedHughesPJClass: instance [safe] Show PrettyLevel
+ Text.PrettyPrint.MarkedHughesPJClass: newtype PrettyLevel
+ Text.PrettyPrint.MarkedHughesPJClass: pPrint :: Pretty a => a -> MDoc b
+ Text.PrettyPrint.MarkedHughesPJClass: pPrintList :: Pretty a => PrettyLevel -> [a] -> MDoc b
+ Text.PrettyPrint.MarkedHughesPJClass: pPrintPrec :: Pretty a => PrettyLevel -> Rational -> a -> MDoc b
+ Text.PrettyPrint.MarkedHughesPJClass: prettyNormal :: PrettyLevel
+ Text.PrettyPrint.MarkedHughesPJClass: prettyParen :: Bool -> MDoc a -> MDoc a
+ Text.PrettyPrint.MarkedHughesPJClass: prettyShow :: Pretty a => a -> String
- Text.PrettyPrint.MarkedHughesPJ: Chr :: Char -> TextDetails a
+ Text.PrettyPrint.MarkedHughesPJ: Chr :: {-# UNPACK #-} !Char -> TextDetails a

Files

+ CHANGELOG.md view
@@ -0,0 +1,8 @@+# 1.1.2.1+* Redesigned to be based off of `pretty-1.1.2.1`++# 0.1+* Initial commit+* AJG: Adding mark, which adds markup the output of the pretty printed text.+  This make no difference to the output ASCII text, but allow sub-expressions+  to be denoted, and other markup to be added. So, for example, keywords can be bold. 
+ README.md view
@@ -0,0 +1,3 @@+# marked-pretty [![Hackage version](https://img.shields.io/hackage/v/marked-pretty.svg?style=flat)](http://hackage.haskell.org/package/marked-pretty) [![Build Status](https://img.shields.io/travis/ku-fpg/marked-pretty.svg?style=flat)](https://travis-ci.org/ku-fpg/marked-pretty)++Pretty-printing library, with scoping, based on `pretty`.
Setup.hs view
@@ -1,6 +1,2 @@-module Main (main) where- import Distribution.Simple--main :: IO () main = defaultMain
− Text/PrettyPrint/MarkedHughesPJ.hs
@@ -1,1017 +0,0 @@--------------------------------------------------------------------------------- |--- Module      :  Text.PrettyPrint.MarkedHughesPJ--- Copyright   :  (c) The University of Glasgow 2001--- License     :  BSD-style (see the file libraries/base/LICENSE)--- --- Maintainer  :  andygill@ku.edu--- Stability   :  provisional--- Portability :  portable------ John Hughes's and Simon Peyton Jones's Pretty Printer Combinators--- --- Based on /The Design of a Pretty-printing Library/--- in Advanced Functional Programming,--- Johan Jeuring and Erik Meijer (eds), LNCS 925--- <http://www.cs.chalmers.se/~rjmh/Papers/pretty.ps>------ Heavily modified by Simon Peyton Jones, Dec 96------ Marking added by Andy Gill, Oct 08.-----------------------------------------------------------------------------------{--Version 3.0     28 May 1997-  * Cured massive performance bug.  If you write--        foldl <> empty (map (text.show) [1..10000])--    you get quadratic behaviour with V2.0.  Why?  For just the same-    reason as you get quadratic behaviour with left-associated (++)-    chains.--    This is really bad news.  One thing a pretty-printer abstraction-    should certainly guarantee is insensivity to associativity.  It-    matters: suddenly GHC's compilation times went up by a factor of-    100 when I switched to the new pretty printer.--    I fixed it with a bit of a hack (because I wanted to get GHC back-    on the road).  I added two new constructors to the Doc type, Above-    and Beside:--         <> = Beside-         $$ = Above--    Then, where I need to get to a "TextBeside" or "NilAbove" form I-    "force" the Doc to squeeze out these suspended calls to Beside and-    Above; but in so doing I re-associate. It's quite simple, but I'm-    not satisfied that I've done the best possible job.  I'll send you-    the code if you are interested.--  * Added new exports:-        punctuate, hang-        int, integer, float, double, rational,-        lparen, rparen, lbrack, rbrack, lbrace, rbrace,--  * fullRender's type signature has changed.  Rather than producing a-    string it now takes an extra couple of arguments that tells it how-    to glue fragments of output together:--        fullRender :: Mode-                   -> Int                       -- Line length-                   -> Float                     -- Ribbons per line-                   -> (TextDetails -> a -> a)   -- What to do with text-                   -> a                         -- What to do at the end-                   -> Doc-                   -> a                         -- Result--    The "fragments" are encapsulated in the TextDetails data type:--        data TextDetails = Chr  Char-                         | Str  String-                         | PStr FAST_STRING--    The Chr and Str constructors are obvious enough.  The PStr-    constructor has a packed string (FAST_STRING) inside it.  It's-    generated by using the new "ptext" export.--    An advantage of this new setup is that you can get the renderer to-    do output directly (by passing in a function of type (TextDetails-    -> IO () -> IO ()), rather than producing a string that you then-    print.---Version 2.0     24 April 1997-  * Made empty into a left unit for <> as well as a right unit;-    it is also now true that-        nest k empty = empty-    which wasn't true before.--  * Fixed an obscure bug in sep that occassionally gave very weird behaviour--  * Added $+$--  * Corrected and tidied up the laws and invariants--======================================================================-Relative to John's original paper, there are the following new features:--1.  There's an empty document, "empty".  It's a left and right unit for -    both <> and $$, and anywhere in the argument list for-    sep, hcat, hsep, vcat, fcat etc.--    It is Really Useful in practice.--2.  There is a paragraph-fill combinator, fsep, that's much like sep,-    only it keeps fitting things on one line until it can't fit any more.--3.  Some random useful extra combinators are provided.  -        <+> puts its arguments beside each other with a space between them,-            unless either argument is empty in which case it returns the other---        hcat is a list version of <>-        hsep is a list version of <+>-        vcat is a list version of $$--        sep (separate) is either like hsep or like vcat, depending on what fits--        cat  behaves like sep,  but it uses <> for horizontal conposition-        fcat behaves like fsep, but it uses <> for horizontal conposition--        These new ones do the obvious things:-                char, semi, comma, colon, space,-                parens, brackets, braces, -                quotes, doubleQuotes--4.  The "above" combinator, $$, now overlaps its two arguments if the-    last line of the top argument stops before the first line of the-    second begins.--        For example:  text "hi" $$ nest 5 (text "there")-        lays out as-                        hi   there-        rather than-                        hi-                             there--        There are two places this is really useful--        a) When making labelled blocks, like this:-                Left ->   code for left-                Right ->  code for right-                LongLongLongLabel ->-                          code for longlonglonglabel-           The block is on the same line as the label if the label is-           short, but on the next line otherwise.--        b) When laying out lists like this:-                [ first-                , second-                , third-                ]-           which some people like.  But if the list fits on one line-           you want [first, second, third].  You can't do this with-           John's original combinators, but it's quite easy with the-           new $$.--        The combinator $+$ gives the original "never-overlap" behaviour.--5.      Several different renderers are provided:-                * a standard one-                * one that uses cut-marks to avoid deeply-nested documents -                        simply piling up in the right-hand margin-                * one that ignores indentation (fewer chars output; good for machines)-                * one that ignores indentation and newlines (ditto, only more so)--6.      Numerous implementation tidy-ups-        Use of unboxed data types to speed up the implementation---}--{--        AJG: Adding mark, which adds markup the output of the pretty printed text.-        This make no difference to the output ASCII text, but allow sub-expressions-        to be denoted, and other markup to be added. So, for example, keywords can be bold. --}--module Text.PrettyPrint.MarkedHughesPJ (--	-- * The document type-        Doc,            -- type synonym-        MDoc,		-- Abstract--	-- * Constructing documents-	-- ** Converting values into documents-        char, text, ptext,-        int, integer, float, double, rational,--	-- ** Simple derived documents-        semi, comma, colon, space, equals,-        lparen, rparen, lbrack, rbrack, lbrace, rbrace,--	-- ** Wrapping documents in delimiters-        parens, brackets, braces, quotes, doubleQuotes,--	-- ** Combining documents-        empty,-        (<>), (<+>), hcat, hsep, -        ($$), ($+$), vcat, -        sep, cat, -        fsep, fcat, -	nest,-        hang, punctuate,-        -	-- * Predicates on documents-	isEmpty,--	-- * Rendering documents--	-- ** Default rendering-	render, --	-- ** Rendering with a particular style-	Style(..),-	style,-        renderStyle,--	-- ** General rendering-        fullRender,-        Mode(..), TextDetails(..),--        -- ** Markup extension-        mark-  ) where---import Prelude--infixl 6 <> -infixl 6 <+>-infixl 5 $$, $+$---- ------------------------------------------------------------------------------ The interface---- The primitive Doc values--isEmpty :: MDoc a    -> Bool;  -- ^ Returns 'True' if the document is empty---- | The empty document, with no height and no width.--- 'empty' is the identity for '<>', '<+>', '$$' and '$+$', and anywhere--- in the argument list for 'sep', 'hcat', 'hsep', 'vcat', 'fcat' etc.-empty   :: MDoc a--semi	:: MDoc a;			-- ^ A ';' character-comma	:: MDoc a;			-- ^ A ',' character-colon	:: MDoc a;			-- ^ A ':' character-space	:: MDoc a;			-- ^ A space character-equals	:: MDoc a;			-- ^ A '=' character-lparen	:: MDoc a;			-- ^ A '(' character-rparen	:: MDoc a;			-- ^ A ')' character-lbrack	:: MDoc a;			-- ^ A '[' character-rbrack	:: MDoc a;			-- ^ A ']' character-lbrace	:: MDoc a;			-- ^ A '{' character-rbrace	:: MDoc a;			-- ^ A '}' character---- | A document of height and width 1, containing a literal character.-char 	 :: Char     -> MDoc a---- | A document of height 1 containing a literal string.--- 'text' satisfies the following laws:------ * @'text' s '<>' 'text' t = 'text' (s'++'t)@------ * @'text' \"\" '<>' x = x@, if @x@ non-empty------ The side condition on the last law is necessary because @'text' \"\"@--- has height 1, while 'empty' has no height.-text	 :: String   -> MDoc a---- | An obsolete function, now identical to 'text'.-ptext	 :: String   -> MDoc a--int      :: Int      -> MDoc a;	-- ^ @int n = text (show n)@-integer  :: Integer  -> MDoc a;	-- ^ @integer n = text (show n)@-float    :: Float    -> MDoc a;	-- ^ @float n = text (show n)@-double   :: Double   -> MDoc a;	-- ^ @double n = text (show n)@-rational :: Rational -> MDoc a;	-- ^ @rational n = text (show n)@--parens       :: MDoc a -> MDoc a; 	-- ^ Wrap document in @(...)@-brackets     :: MDoc a -> MDoc a;  	-- ^ Wrap document in @[...]@-braces	     :: MDoc a -> MDoc a;   	-- ^ Wrap document in @{...}@-quotes	     :: MDoc a -> MDoc a;	-- ^ Wrap document in @\'...\'@-doubleQuotes :: MDoc a -> MDoc a;	-- ^ Wrap document in @\"...\"@---- Combining @MDoc a@ values---- | Beside.--- '<>' is associative, with identity 'empty'.-(<>)   :: MDoc a -> MDoc a -> MDoc a---- | Beside, separated by space, unless one of the arguments is 'empty'.--- '<+>' is associative, with identity 'empty'.-(<+>)  :: MDoc a -> MDoc a -> MDoc a---- | Above, except that if the last line of the first argument stops--- at least one position before the first line of the second begins,--- these two lines are overlapped.  For example:------ >    text "hi" $$ nest 5 (text "there")------ lays out as------ >    hi   there------ rather than------ >    hi--- >         there------ '$$' is associative, with identity 'empty', and also satisfies------ * @(x '$$' y) '<>' z = x '$$' (y '<>' z)@, if @y@ non-empty.----($$)   :: MDoc a -> MDoc a -> MDoc a---- | Above, with no overlapping.--- '$+$' is associative, with identity 'empty'.-($+$)   :: MDoc a -> MDoc a -> MDoc a--hcat   :: [MDoc a] -> MDoc a;          -- ^List version of '<>'.-hsep   :: [MDoc a] -> MDoc a;          -- ^List version of '<+>'.-vcat   :: [MDoc a] -> MDoc a;          -- ^List version of '$$'.--cat    :: [MDoc a] -> MDoc a;          -- ^ Either 'hcat' or 'vcat'.-sep    :: [MDoc a] -> MDoc a;          -- ^ Either 'hsep' or 'vcat'.-fcat   :: [MDoc a] -> MDoc a;          -- ^ \"Paragraph fill\" version of 'cat'.-fsep   :: [MDoc a] -> MDoc a;          -- ^ \"Paragraph fill\" version of 'sep'.---- | Nest (or indent) a document by a given number of positions--- (which may also be negative).  'nest' satisfies the laws:------ * @'nest' 0 x = x@------ * @'nest' k ('nest' k' x) = 'nest' (k+k') x@------ * @'nest' k (x '<>' y) = 'nest' k z '<>' 'nest' k y@------ * @'nest' k (x '$$' y) = 'nest' k x '$$' 'nest' k y@------ * @'nest' k 'empty' = 'empty'@------ * @x '<>' 'nest' k y = x '<>' y@, if @x@ non-empty------ The side condition on the last law is needed because--- 'empty' is a left identity for '<>'.-nest   :: Int -> MDoc a -> MDoc a---- GHC-specific ones.---- | @hang d1 n d2 = sep [d1, nest n d2]@-hang :: MDoc a -> Int -> MDoc a -> MDoc a---- | @punctuate p [d1, ... dn] = [d1 \<> p, d2 \<> p, ... dn-1 \<> p, dn]@-punctuate :: MDoc a -> [MDoc a] -> [MDoc a]----- Displaying @MDoc a@ values. --instance Show (MDoc a) where-  showsPrec prec doc cont = showDoc doc cont---- | Renders the document as a string using the default 'style'.-render     :: MDoc a -> String---- | The general rendering interface.-fullRender :: Mode			   -- ^Rendering mode-           -> Int                          -- ^Line length-           -> Float                        -- ^Ribbons per line-           -> (TextDetails b  -> a -> a)   -- ^What to do with text-           -> a                            -- ^What to do at the end-           -> MDoc b		  	   -- ^The document-           -> a                            -- ^Result---- | Render the document as a string using a specified style.-renderStyle  :: Style -> MDoc a -> String---- | A rendering style.-data Style- = Style { mode           :: Mode     -- ^ The rendering mode- 	 , lineLength     :: Int      -- ^ Length of line, in chars-         , ribbonsPerLine :: Float    -- ^ Ratio of ribbon length to line length-         }---- | The default style (@mode=PageMode, lineLength=100, ribbonsPerLine=1.5@).-style :: Style-style = Style { lineLength = 100, ribbonsPerLine = 1.5, mode = PageMode }---- | Rendering mode.-data Mode = PageMode            -- ^Normal -          | ZigZagMode          -- ^With zig-zag cuts-          | LeftMode            -- ^No indentation, infinitely long lines-          | OneLineMode         -- ^All on one line---- ------------------------------------------------------------------------------ The Doc calculus---- The Doc combinators satisfy the following laws:--{--Laws for $$-~~~~~~~~~~~-<a1>    (x $$ y) $$ z   = x $$ (y $$ z)-<a2>    empty $$ x      = x-<a3>    x $$ empty      = x--        ...ditto $+$...--Laws for <>-~~~~~~~~~~~-<b1>    (x <> y) <> z   = x <> (y <> z)-<b2>    empty <> x      = empty-<b3>    x <> empty      = x--        ...ditto <+>...--Laws for text-~~~~~~~~~~~~~-<t1>    text s <> text t        = text (s++t)-<t2>    text "" <> x            = x, if x non-empty--Laws for nest-~~~~~~~~~~~~~-<n1>    nest 0 x                = x-<n2>    nest k (nest k' x)      = nest (k+k') x-<n3>    nest k (x <> y)         = nest k z <> nest k y-<n4>    nest k (x $$ y)         = nest k x $$ nest k y-<n5>    nest k empty            = empty-<n6>    x <> nest k y           = x <> y, if x non-empty--** Note the side condition on <n6>!  It is this that-** makes it OK for empty to be a left unit for <>.--Miscellaneous-~~~~~~~~~~~~~-<m1>    (text s <> x) $$ y = text s <> ((text "" <> x)) $$ -                                         nest (-length s) y)--<m2>    (x $$ y) <> z = x $$ (y <> z)-        if y non-empty---Laws for list versions-~~~~~~~~~~~~~~~~~~~~~~-<l1>    sep (ps++[empty]++qs)   = sep (ps ++ qs)-        ...ditto hsep, hcat, vcat, fill...--<l2>    nest k (sep ps) = sep (map (nest k) ps)-        ...ditto hsep, hcat, vcat, fill...--Laws for oneLiner-~~~~~~~~~~~~~~~~~-<o1>    oneLiner (nest k p) = nest k (oneLiner p)-<o2>    oneLiner (x <> y)   = oneLiner x <> oneLiner y --You might think that the following verion of <m1> would-be neater:--<3 NO>  (text s <> x) $$ y = text s <> ((empty <> x)) $$ -                                         nest (-length s) y)--But it doesn't work, for if x=empty, we would have--        text s $$ y = text s <> (empty $$ nest (-length s) y)-                    = text s <> nest (-length s) y--}---- ------------------------------------------------------------------------------ Simple derived definitions--semi  = char ';'-colon = char ':'-comma = char ','-space = char ' '-equals = char '='-lparen = char '('-rparen = char ')'-lbrack = char '['-rbrack = char ']'-lbrace = char '{'-rbrace = char '}'--int      n = text (show n)-integer  n = text (show n)-float    n = text (show n)-double   n = text (show n)-rational n = text (show n)--- SIGBJORN wrote instead:--- rational n = text (show (fromRationalX n))--quotes p        = char '\'' <> p <> char '\''-doubleQuotes p  = char '"' <> p <> char '"'-parens p        = char '(' <> p <> char ')'-brackets p      = char '[' <> p <> char ']'-braces p        = char '{' <> p <> char '}'---hcat = foldr (<>)  empty-hsep = foldr (<+>) empty-vcat = foldr ($$)  empty--hang d1 n d2 = sep [d1, nest n d2]--punctuate p []     = []-punctuate p (d:ds) = go d ds-                   where-                     go d [] = [d]-                     go d (e:es) = (d <> p) : go e es---- ------------------------------------------------------------------------------ The Doc data type---- An Doc is a Marked Doc (MDoc) with no interesting markings.-type Doc = MDoc ()---- An MDoc represents a *set* of layouts.  A Doc with--- no occurrences of Union or NoDoc represents just one layout.---- | The abstract type of documents.--- The 'Show' instance is equivalent to using 'render'.-data MDoc a- = Empty                                      -- empty- | NilAbove (MDoc a)                          -- text "" $$ x- | TextBeside (TextDetails a) !Int (MDoc a)   -- text s <> x  - | Nest !Int (MDoc a)                         -- nest k x- | Union (MDoc a) (MDoc a)                    -- ul `union` ur- | NoDoc                                      -- The empty set of documents- | Beside (MDoc a) Bool (MDoc a)              -- True <=> space between- | Above  (MDoc a) Bool (MDoc a)              -- True <=> never overlap--type RDoc a = MDoc a         -- RDoc is a "reduced Doc", guaranteed not to have a top-level Above or Beside---reduceDoc :: MDoc a -> RDoc a-reduceDoc (Beside p g q) = beside p g (reduceDoc q)-reduceDoc (Above  p g q) = above  p g (reduceDoc q)-reduceDoc p              = p--data TextDetails a-                 = Chr  Char-                 | Str  String-                 | PStr String-                 | Mark a--space_text = Chr ' '-nl_text    = Chr '\n'--{--  Here are the invariants:-  -  * The argument of NilAbove is never Empty. Therefore-    a NilAbove occupies at least two lines.-  -  * The arugment of @TextBeside@ is never @Nest@.-  -  -  * The layouts of the two arguments of @Union@ both flatten to the same -    string.-  -  * The arguments of @Union@ are either @TextBeside@, or @NilAbove@.-  -  * The right argument of a union cannot be equivalent to the empty set-    (@NoDoc@).  If the left argument of a union is equivalent to the-    empty set (@NoDoc@), then the @NoDoc@ appears in the first line.-  -  * An empty document is always represented by @Empty@.  It can't be-    hidden inside a @Nest@, or a @Union@ of two @Empty@s.-  -  * The first line of every layout in the left argument of @Union@ is-    longer than the first line of any layout in the right argument.-    (1) ensures that the left argument has a first line.  In view of-    (3), this invariant means that the right argument must have at-    least two lines.--}--        -- Arg of a NilAbove is always an RDoc-nilAbove_ p = NilAbove p--        -- Arg of a TextBeside is always an RDoc-textBeside_ s sl p = TextBeside s sl p--        -- Arg of Nest is always an RDoc-nest_ k p = Nest k p--        -- Args of union are always RDocs-union_ p q = Union p q----- Notice the difference between--- 	   * NoDoc (no documents)--- 	   * Empty (one empty document; no height and no width)--- 	   * text "" (a document containing the empty string;--- 		      one line high, but has no width)----- ------------------------------------------------------------------------------ @empty@, @text@, @nest@, @union@--empty = Empty--isEmpty Empty = True-isEmpty _     = False--char  c = textBeside_ (Chr c) 1 Empty-text  s = case length s of {sl -> textBeside_ (Str s)  sl Empty}-ptext s = case length s of {sl -> textBeside_ (PStr s) sl Empty}--nest k  p = mkNest k (reduceDoc p)        -- Externally callable version---- mkNest checks for Nest's invariant that it doesn't have an Empty inside it-mkNest k       _           | k `seq` False = undefined-mkNest k       (Nest k1 p) = mkNest (k + k1) p-mkNest k       NoDoc       = NoDoc-mkNest k       Empty       = Empty-mkNest 0       p           = p                  -- Worth a try!-mkNest k       p           = nest_ k p---- mkUnion checks for an empty document-mkUnion Empty q = Empty-mkUnion p q     = p `union_` q---- ------------------------------------------------------------------------------ Vertical composition @$$@--above_ :: MDoc a -> Bool -> MDoc a -> MDoc a-above_ p _ Empty = p-above_ Empty _ q = q-above_ p g q = Above p g q--p $$  q = above_ p False q-p $+$ q = above_ p True q--above :: MDoc a -> Bool -> RDoc a -> RDoc a-above (Above p g1 q1)  g2 q2 = above p g1 (above q1 g2 q2)-above p@(Beside _ _ _) g  q  = aboveNest (reduceDoc p) g 0 (reduceDoc q)-above p g q                  = aboveNest p             g 0 (reduceDoc q)--aboveNest :: RDoc a -> Bool -> Int -> RDoc a -> RDoc a--- Specfication: aboveNest p g k q = p $g$ (nest k q)--aboveNest _                   _ k _ | k `seq` False = undefined-aboveNest NoDoc               g k q = NoDoc-aboveNest (p1 `Union` p2)     g k q = aboveNest p1 g k q `union_` -                                      aboveNest p2 g k q-                                -aboveNest Empty               g k q = mkNest k q-aboveNest (Nest k1 p)         g k q = nest_ k1 (aboveNest p g (k - k1) q)-                                  -- p can't be Empty, so no need for mkNest-                                -aboveNest (NilAbove p)        g k q = nilAbove_ (aboveNest p g k q)-aboveNest (TextBeside s sl p) g k q = k1 `seq` textBeside_ s sl rest-                                    where-                                      k1   = k - sl-                                      rest = case p of-                                                Empty -> nilAboveNest g k1 q-                                                other -> aboveNest  p g k1 q---nilAboveNest :: Bool -> Int -> RDoc a -> RDoc a--- Specification: text s <> nilaboveNest g k q ---              = text s <> (text "" $g$ nest k q)--nilAboveNest _ k _           | k `seq` False = undefined-nilAboveNest g k Empty       = Empty    -- Here's why the "text s <>" is in the spec!-nilAboveNest g k (Nest k1 q) = nilAboveNest g (k + k1) q--nilAboveNest g k q           | (not g) && (k > 0)        -- No newline if no overlap-                             = textBeside_ (Str (spaces k)) k q-                             | otherwise                        -- Put them really above-                             = nilAbove_ (mkNest k q)---- ------------------------------------------------------------------------------ Horizontal composition @<>@--beside_ :: MDoc a -> Bool -> MDoc a -> MDoc a-beside_ p _ Empty = p-beside_ Empty _ q = q-beside_ p g q = Beside p g q--p <>  q = beside_ p False q-p <+> q = beside_ p True  q--beside :: MDoc a -> Bool -> RDoc a -> RDoc a--- Specification: beside g p q = p <g> q- -beside NoDoc               g q   = NoDoc-beside (p1 `Union` p2)     g q   = (beside p1 g q) `union_` (beside p2 g q)-beside Empty               g q   = q-beside (Nest k p)          g q   = nest_ k (beside p g q)       -- p non-empty-beside p@(Beside p1 g1 q1) g2 q2 -           {- (A `op1` B) `op2` C == A `op1` (B `op2` C)  iff op1 == op2 -                                                 [ && (op1 == <> || op1 == <+>) ] -}-         | g1 == g2              = beside p1 g1 (beside q1 g2 q2)-         | otherwise             = beside (reduceDoc p) g2 q2-beside p@(Above _ _ _)     g q   = beside (reduceDoc p) g q-beside (NilAbove p)        g q   = nilAbove_ (beside p g q)-beside (TextBeside s sl p) g q   = textBeside_ s sl rest-                               where-                                  rest = case p of-                                           Empty -> nilBeside g q-                                           other -> beside p g q---nilBeside :: Bool -> RDoc a -> RDoc a--- Specification: text "" <> nilBeside g p ---              = text "" <g> p--nilBeside g Empty      = Empty  -- Hence the text "" in the spec-nilBeside g (Nest _ p) = nilBeside g p-nilBeside g p          | g         = textBeside_ space_text 1 p-                       | otherwise = p---- ------------------------------------------------------------------------------ Separate, @sep@, Hughes version---- Specification: sep ps  = oneLiner (hsep ps)---                         `union`---                          vcat ps--sep = sepX True         -- Separate with spaces-cat = sepX False        -- Don't--sepX x []     = empty-sepX x (p:ps) = sep1 x (reduceDoc p) 0 ps----- Specification: sep1 g k ys = sep (x : map (nest k) ys)---                            = oneLiner (x <g> nest k (hsep ys))---                              `union` x $$ nest k (vcat ys)--sep1 :: Bool -> RDoc a -> Int -> [MDoc a] -> RDoc a -sep1 g _                   k ys | k `seq` False = undefined-sep1 g NoDoc               k ys = NoDoc-sep1 g (p `Union` q)       k ys = sep1 g p k ys-                                  `union_`-                                  (aboveNest q False k (reduceDoc (vcat ys)))--sep1 g Empty               k ys = mkNest k (sepX g ys)-sep1 g (Nest n p)          k ys = nest_ n (sep1 g p (k - n) ys)--sep1 g (NilAbove p)        k ys = nilAbove_ (aboveNest p False k (reduceDoc (vcat ys)))-sep1 g (TextBeside s sl p) k ys = textBeside_ s sl (sepNB g p (k - sl) ys)---- Specification: sepNB p k ys = sep1 (text "" <> p) k ys--- Called when we have already found some text in the first item--- We have to eat up nests--sepNB g (Nest _ p)  k ys  = sepNB g p k ys--sepNB g Empty k ys        = oneLiner (nilBeside g (reduceDoc rest))-                                `mkUnion` -                            nilAboveNest False k (reduceDoc (vcat ys))-                          where-                            rest | g         = hsep ys-                                 | otherwise = hcat ys--sepNB g p k ys            = sep1 g p k ys---- ------------------------------------------------------------------------------ @fill@--fsep = fill True-fcat = fill False---- Specification: ---   fill []  = empty---   fill [p] = p---   fill (p1:p2:ps) = oneLiner p1 <#> nest (length p1) ---                                          (fill (oneLiner p2 : ps))---                     `union`---                      p1 $$ fill ps--fill g []     = empty-fill g (p:ps) = fill1 g (reduceDoc p) 0 ps---fill1 :: Bool -> RDoc a -> Int -> [MDoc a] -> MDoc a-fill1 g _                   k ys | k `seq` False = undefined-fill1 g NoDoc               k ys = NoDoc-fill1 g (p `Union` q)       k ys = fill1 g p k ys-                                   `union_`-                                   (aboveNest q False k (fill g ys))--fill1 g Empty               k ys = mkNest k (fill g ys)-fill1 g (Nest n p)          k ys = nest_ n (fill1 g p (k - n) ys)--fill1 g (NilAbove p)        k ys = nilAbove_ (aboveNest p False k (fill g ys))-fill1 g (TextBeside s sl p) k ys = textBeside_ s sl (fillNB g p (k - sl) ys)--fillNB g _           k ys | k `seq` False = undefined-fillNB g (Nest _ p)  k ys  = fillNB g p k ys-fillNB g Empty k []        = Empty-fillNB g Empty k (y:ys)    = nilBeside g (fill1 g (oneLiner (reduceDoc y)) k1 ys)-                             `mkUnion` -                             nilAboveNest False k (fill g (y:ys))-                           where-                             k1 | g         = k - 1-                                | otherwise = k--fillNB g p k ys            = fill1 g p k ys----- ------------------------------------------------------------------------------ Selecting the best layout--best :: Mode-     -> Int             -- Line length-     -> Int             -- Ribbon length-     -> RDoc a-     -> RDoc a            -- No unions in here!--best OneLineMode w r p-  = get p-  where-    get Empty               = Empty-    get NoDoc               = NoDoc-    get (NilAbove p)        = nilAbove_ (get p)-    get (TextBeside s sl p) = textBeside_ s sl (get p)-    get (Nest k p)          = get p             -- Elide nest-    get (p `Union` q)       = first (get p) (get q)--best mode w r p-  = get w p-  where-    get :: Int          -- (Remaining) width of line-        -> MDoc a -> MDoc a-    get w _ | w==0 && False   = undefined-    get w Empty               = Empty-    get w NoDoc               = NoDoc-    get w (NilAbove p)        = nilAbove_ (get w p)-    get w (TextBeside s sl p) = textBeside_ s sl (get1 w sl p)-    get w (Nest k p)          = nest_ k (get (w - k) p)-    get w (p `Union` q)       = nicest w r (get w p) (get w q)--    get1 :: Int         -- (Remaining) width of line-         -> Int         -- Amount of first line already eaten up-         -> MDoc a      -- This is an argument to TextBeside => eat Nests-         -> MDoc a      -- No unions in here!--    get1 w _ _ | w==0 && False = undefined-    get1 w sl Empty               = Empty-    get1 w sl NoDoc               = NoDoc-    get1 w sl (NilAbove p)        = nilAbove_ (get (w - sl) p)-    get1 w sl (TextBeside t tl p) = textBeside_ t tl (get1 w (sl + tl) p)-    get1 w sl (Nest k p)          = get1 w sl p-    get1 w sl (p `Union` q)       = nicest1 w r sl (get1 w sl p) -                                                   (get1 w sl q)--nicest w r p q = nicest1 w r 0 p q-nicest1 w r sl p q | fits ((w `minn` r) - sl) p = p-                   | otherwise                   = q--fits :: Int     -- Space available-     -> MDoc a-     -> Bool    -- True if *first line* of Doc fits in space available- -fits n p    | n < 0 = False-fits n NoDoc               = False-fits n Empty               = True-fits n (NilAbove _)        = True-fits n (TextBeside _ sl p) = fits (n - sl) p--minn x y | x < y    = x-         | otherwise = y---- @first@ and @nonEmptySet@ are similar to @nicest@ and @fits@, only simpler.--- @first@ returns its first argument if it is non-empty, otherwise its second.--first p q | nonEmptySet p = p -          | otherwise     = q--nonEmptySet NoDoc           = False-nonEmptySet (p `Union` q)      = True-nonEmptySet Empty              = True-nonEmptySet (NilAbove p)       = True           -- NoDoc always in first line-nonEmptySet (TextBeside _ _ p) = nonEmptySet p-nonEmptySet (Nest _ p)         = nonEmptySet p---- @oneLiner@ returns the one-line members of the given set of @Doc@s.--oneLiner :: MDoc a -> MDoc a-oneLiner NoDoc               = NoDoc-oneLiner Empty               = Empty-oneLiner (NilAbove p)        = NoDoc-oneLiner (TextBeside s sl p) = textBeside_ s sl (oneLiner p)-oneLiner (Nest k p)          = nest_ k (oneLiner p)-oneLiner (p `Union` q)       = oneLiner p----- ------------------------------------------------------------------------------ Displaying the best layout--renderStyle style doc -  = fullRender (mode style)-               (lineLength style)-	       (ribbonsPerLine style)-	       string_txt-	       ""-	       doc--render doc       = showDoc doc ""-showDoc doc rest = fullRender PageMode 100 1.5 string_txt rest doc--string_txt (Chr c)    s  = c:s-string_txt (Str s1)   s2 = s1 ++ s2-string_txt (PStr s1)  s2 = s1 ++ s2-string_txt (Mark _)   s2 = s2--fullRender OneLineMode _ _ txt end doc = easy_display space_text txt end (reduceDoc doc)-fullRender LeftMode    _ _ txt end doc = easy_display nl_text    txt end (reduceDoc doc)--fullRender mode line_length ribbons_per_line txt end doc-  = display mode line_length ribbon_length txt end best_doc-  where -    best_doc = best mode hacked_line_length ribbon_length (reduceDoc doc)--    hacked_line_length, ribbon_length :: Int-    ribbon_length = round (fromIntegral line_length / ribbons_per_line)-    hacked_line_length = case mode of { ZigZagMode -> maxBound; other -> line_length }--display mode page_width ribbon_width txt end doc-  = case page_width - ribbon_width of { gap_width ->-    case gap_width `quot` 2 of { shift ->-    let-        lay k _            | k `seq` False = undefined-        lay k (Nest k1 p)  = lay (k + k1) p-        lay k Empty        = end-    -        lay k (NilAbove p) = nl_text `txt` lay k p-    -        lay k (TextBeside s sl p)-            = case mode of-                    ZigZagMode |  k >= gap_width-                               -> nl_text `txt` (-                                  Str (multi_ch shift '/') `txt` (-                                  nl_text `txt` (-                                  lay1 (k - shift) s sl p)))--                               |  k < 0-                               -> nl_text `txt` (-                                  Str (multi_ch shift '\\') `txt` (-                                  nl_text `txt` (-                                  lay1 (k + shift) s sl p )))--                    other -> lay1 k s sl p-    -        lay1 k _ sl _ | k+sl `seq` False = undefined-        lay1 k s sl p = Str (indent k) `txt` (s `txt` lay2 (k + sl) p)-    -        lay2 k _ | k `seq` False = undefined-        lay2 k (NilAbove p)        = nl_text `txt` lay k p-        lay2 k (TextBeside s sl p) = s `txt` (lay2 (k + sl) p)-        lay2 k (Nest _ p)          = lay2 k p-        lay2 k Empty               = end-    in-    lay 0 doc-    }}--cant_fail = error "easy_display: NoDoc"-easy_display nl_text txt end doc -  = lay doc cant_fail-  where-    lay NoDoc               no_doc = no_doc-    lay (Union p q)         no_doc = {- lay p -} (lay q cant_fail)              -- Second arg can't be NoDoc-    lay (Nest k p)          no_doc = lay p no_doc-    lay Empty               no_doc = end-    lay (NilAbove p)        no_doc = nl_text `txt` lay p cant_fail      -- NoDoc always on first line-    lay (TextBeside s sl p) no_doc = s `txt` lay p no_doc---- OLD version: we shouldn't rely on tabs being 8 columns apart in the output.--- indent n | n >= 8 = '\t' : indent (n - 8)---          | otherwise      = spaces n-indent n = spaces n--multi_ch 0 ch = ""-multi_ch n       ch = ch : multi_ch (n - 1) ch---- (spaces n) generates a list of n spaces------ It should never be called with 'n' < 0, but that can happen for reasons I don't understand--- Here's a test case:---	ncat x y = nest 4 $ cat [ x, y ]---	d1 = foldl1 ncat $ take 50 $ repeat $ char 'a'---	d2 = parens $  sep [ d1, text "+" , d1 ]---	main = print d2--- I don't feel motivated enough to find the Real Bug, so meanwhile we just test for n<=0-spaces n | n <= 0    = ""-	 | otherwise = ' ' : spaces (n - 1)--{- Comments from Johannes Waldmann about what the problem might be:--   In the example above, d2 and d1 are deeply nested, but `text "+"' is not, -   so the layout function tries to "out-dent" it.-   -   when I look at the Doc values that are generated, there are lots of-   Nest constructors with negative arguments.  see this sample output of-   d1 (obtained with hugs, :s -u)-   -   tBeside (TextDetails_Chr 'a') 1 Doc_Empty) (Doc_NilAbove (Doc_Nest-   (-241) (Doc_TextBeside (TextDetails_Chr 'a') 1 Doc_Empty)))))-   (Doc_NilAbove (Doc_Nest (-236) (Doc_TextBeside (TextDetails_Chr 'a') 1-   (Doc_NilAbove (Doc_Nest (-5) (Doc_TextBeside (TextDetails_Chr 'a') 1-   Doc_Empty)))))))) (Doc_NilAbove (Doc_Nest (-231) (Doc_TextBeside-   (TextDetails_Chr 'a') 1 (Doc_NilAbove (Doc_Nest (-5) (Doc_TextBeside-   (TextDetails_Chr 'a') 1 (Doc_NilAbove (Doc_Nest (-5) (Doc_TextBeside-   (TextDetails_Chr 'a') 1 Doc_Empty))))))))))) (Doc_NilAbove (Doc_Nest--}------------------------------------------------------------------------------------- | mark inserts a zero width mark into the output document--mark :: a -> MDoc a-mark m = textBeside_ (Mark m) 0 Empty-
marked-pretty.cabal view
@@ -1,23 +1,36 @@-name:		marked-pretty-version:	0.1-Cabal-Version:  >= 1.2-license:	BSD3-license-file:	LICENSE-category:	Text-maintainer:	andygill@ku.edu-synopsis:	Pretty-printing library, with scoping, based on pretty-1.0.0.0-description:-	This package contains a pretty-printing library, with scoping.-build-type: Simple--Library-  Build-Depends:        base-  Exposed-modules:-     Text.PrettyPrint.MarkedHughesPJ+name:                marked-pretty+version:             1.1.2.1+synopsis:            Pretty-printing library, with scoping, based on pretty.+description:         This package contains a pretty-printing library, with scoping.+homepage:            https://github.com/ku-fpg/marked-pretty+bug-reports:         https://github.com/ku-fpg/marked-pretty/issues+license:             BSD3+license-file:        LICENSE+category:            Text+author:              Andy Gill+maintainer:          Andy Gill <andygill@ku.edu>+build-type:          Simple+stability:           Stable+extra-source-files:  CHANGELOG.md, README.md+cabal-version:       >= 1.8 -Executable test-  Main-Is:        Main.hs-  Hs-Source-Dirs: ., test-  buildable: False+source-repository head+  type:                git+  location:            git://github.com/ku-fpg/marked-pretty.git +library+  build-depends:       base    >= 4.5 && < 5+                     , deepseq >= 1.1+                     , ghc-prim+  exposed-modules:     Text.PrettyPrint.MarkedHughesPJ+                       Text.PrettyPrint.MarkedHughesPJClass+  hs-source-dirs:      src+  ghc-options:         -Wall +test-suite test+  type:                exitcode-stdio-1.0+  main-is:             Main.hs+  build-depends:       base          >= 4.5 && < 5+                     , marked-pretty == 1.1.2.1+  hs-source-dirs:      test+  ghc-options:         -Wall
+ src/Text/PrettyPrint/MarkedHughesPJ.hs view
@@ -0,0 +1,999 @@+{-# LANGUAGE BangPatterns, CPP #-}+#if __GLASGOW_HASKELL__ >= 701+{-# LANGUAGE Safe #-}+{-# LANGUAGE DeriveGeneric #-}+#endif++-----------------------------------------------------------------------------+-- |+-- Module      :  Text.PrettyPrint.MarkedHughesPJ+-- Copyright   :  (c) The University of Glasgow 2001+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Andy Gill <andygill@ku.edu>+-- Stability   :  stable+-- Portability :  portable+--+-- Provides a collection of pretty printer combinators, a set of API's+-- that provides a way to easily print out text in a consistent format+-- of your choosing.+--+-- Originally designed by John Hughes's and Simon Peyton Jones's.+-- +-- Marking added by Andy Gill, Oct 08.+-- +-- For more information you can refer to the+-- <http://belle.sourceforge.net/doc/hughes95design.pdf original paper> that+-- serves as the basis for this libraries design:+-- /The Design -- of a Pretty-printing Library/ by John Hughes, in Advanced+-- Functional Programming, 1995+-- +-----------------------------------------------------------------------------++module Text.PrettyPrint.MarkedHughesPJ (++        -- * The document type+        Doc,  -- Type synonym+        MDoc, -- Abstract+        TextDetails(..),++        -- * Constructing documents++        -- ** Converting values into documents+        char, text, ptext, sizedText, zeroWidthText,+        int, integer, float, double, rational,++        -- ** Simple derived documents+        semi, comma, colon, space, equals,+        lparen, rparen, lbrack, rbrack, lbrace, rbrace,++        -- ** Wrapping documents in delimiters+        parens, brackets, braces, quotes, doubleQuotes,+        maybeParens, maybeBrackets, maybeBraces, maybeQuotes, maybeDoubleQuotes,++        -- ** Combining documents+        empty,+        (<>), (<+>), hcat, hsep,+        ($$), ($+$), vcat,+        sep, cat,+        fsep, fcat,+        nest,+        hang, punctuate,++        -- * Predicates on documents+        isEmpty,++        -- * Utility functions for documents+        first, reduceDoc,++        -- * Rendering documents++        -- ** Default rendering+        render,++        -- ** Rendering with a particular style+        Style(..),+        style,+        renderStyle,+        Mode(..),++        -- ** General rendering+        fullRender,++        -- ** Markup extension+        mark+    ) where++import Control.DeepSeq ( NFData(rnf) )+import Data.Function   ( on )+#if __GLASGOW_HASKELL__ < 709+import Data.Monoid     ( Monoid(mempty, mappend)  )+#endif+import Data.String     ( IsString(fromString) )++import GHC.Generics++-- ---------------------------------------------------------------------------+-- The MDoc calculus++{-+Laws for $$+~~~~~~~~~~~+<a1>    (x $$ y) $$ z   = x $$ (y $$ z)+<a2>    empty $$ x      = x+<a3>    x $$ empty      = x++        ...ditto $+$...++Laws for <>+~~~~~~~~~~~+<b1>    (x <> y) <> z   = x <> (y <> z)+<b2>    empty <> x      = empty+<b3>    x <> empty      = x++        ...ditto <+>...++Laws for text+~~~~~~~~~~~~~+<t1>    text s <> text t        = text (s++t)+<t2>    text "" <> x            = x, if x non-empty++** because of law n6, t2 only holds if x doesn't+** start with `nest'.+++Laws for nest+~~~~~~~~~~~~~+<n1>    nest 0 x                = x+<n2>    nest k (nest k' x)      = nest (k+k') x+<n3>    nest k (x <> y)         = nest k x <> nest k y+<n4>    nest k (x $$ y)         = nest k x $$ nest k y+<n5>    nest k empty            = empty+<n6>    x <> nest k y           = x <> y, if x non-empty++** Note the side condition on <n6>!  It is this that+** makes it OK for empty to be a left unit for <>.++Miscellaneous+~~~~~~~~~~~~~+<m1>    (text s <> x) $$ y = text s <> ((text "" <> x) $$+                                         nest (-length s) y)++<m2>    (x $$ y) <> z = x $$ (y <> z)+        if y non-empty+++Laws for list versions+~~~~~~~~~~~~~~~~~~~~~~+<l1>    sep (ps++[empty]++qs)   = sep (ps ++ qs)+        ...ditto hsep, hcat, vcat, fill...++<l2>    nest k (sep ps) = sep (map (nest k) ps)+        ...ditto hsep, hcat, vcat, fill...++Laws for oneLiner+~~~~~~~~~~~~~~~~~+<o1>    oneLiner (nest k p) = nest k (oneLiner p)+<o2>    oneLiner (x <> y)   = oneLiner x <> oneLiner y++You might think that the following verion of <m1> would+be neater:++<3 NO>  (text s <> x) $$ y = text s <> ((empty <> x)) $$+                                         nest (-length s) y)++But it doesn't work, for if x=empty, we would have++        text s $$ y = text s <> (empty $$ nest (-length s) y)+                    = text s <> nest (-length s) y+-}++-- ---------------------------------------------------------------------------+-- Operator fixity++infixl 6 <>+infixl 6 <+>+infixl 5 $$, $+$++-- ---------------------------------------------------------------------------+-- The MDoc data type++-- An Doc is a Marked Doc (MDoc) with no interesting markings.+type Doc = MDoc ()++-- | The abstract type of documents.+-- An MDoc represents a *set* of layouts.  A MDoc with+-- no occurrences of Union or NoDoc represents just one layout.+data MDoc a+  = Empty                                                     -- empty+  | NilAbove (MDoc a)                                         -- text "" $$ x+  | TextBeside !(TextDetails a) {-# UNPACK #-} !Int (MDoc a)  -- text s <> x+  | Nest {-# UNPACK #-} !Int (MDoc a)                         -- nest k x+  | Union (MDoc a) (MDoc a)                                   -- ul `union` ur+  | NoDoc                                                     -- The empty set of documents+  | Beside (MDoc a) Bool (MDoc a)                             -- True <=> space between+  | Above (MDoc a) Bool (MDoc a)                              -- True <=> never overlap+#if __GLASGOW_HASKELL__ >= 701+  deriving (Generic)+#endif++{-+Here are the invariants:++1) The argument of NilAbove is never Empty. Therefore+   a NilAbove occupies at least two lines.++2) The argument of @TextBeside@ is never @Nest@.++3) The layouts of the two arguments of @Union@ both flatten to the same+   string.++4) The arguments of @Union@ are either @TextBeside@, or @NilAbove@.++5) A @NoDoc@ may only appear on the first line of the left argument of an+   union. Therefore, the right argument of an union can never be equivalent+   to the empty set (@NoDoc@).++6) An empty document is always represented by @Empty@.  It can't be+   hidden inside a @Nest@, or a @Union@ of two @Empty@s.++7) The first line of every layout in the left argument of @Union@ is+   longer than the first line of any layout in the right argument.+   (1) ensures that the left argument has a first line.  In view of+   (3), this invariant means that the right argument must have at+   least two lines.++Notice the difference between+   * NoDoc (no documents)+   * Empty (one empty document; no height and no width)+   * text "" (a document containing the empty string;+              one line high, but has no width)+-}+++-- | RDoc is a "reduced GDoc", guaranteed not to have a top-level Above or Beside.+type RDoc a = MDoc a++-- | The TextDetails data type+--+-- A TextDetails represents a fragment of text that will be+-- output at some point.+data TextDetails a = Chr  {-# UNPACK #-} !Char -- ^ A single Char fragment+                   | Str  String -- ^ A whole String fragment+                   | PStr String -- ^ Used to represent a Fast String fragment+                                 --   but now deprecated and identical to the+                                 --   Str constructor.+                   | Mark a+#if __GLASGOW_HASKELL__ >= 701+                 deriving (Show, Eq, Generic)+#endif++-- Combining @MDoc@ values+instance Monoid (MDoc a) where+    mempty  = empty+    mappend = (<>)++instance IsString (MDoc a) where+    fromString = text++instance Show (MDoc a) where+  showsPrec _ doc cont = fullRender (mode style) (lineLength style)+                                    (ribbonsPerLine style)+                                    txtPrinter cont doc++instance Eq (MDoc a) where+  (==) = (==) `on` render++instance NFData a => NFData (MDoc a) where+  rnf Empty               = ()+  rnf (NilAbove d)        = rnf d+  rnf (TextBeside td i d) = rnf td `seq` rnf i `seq` rnf d+  rnf (Nest k d)          = rnf k  `seq` rnf d+  rnf (Union ur ul)       = rnf ur `seq` rnf ul+  rnf NoDoc               = ()+  rnf (Beside ld s rd)    = rnf ld `seq` rnf s `seq` rnf rd+  rnf (Above ud s ld)     = rnf ud `seq` rnf s `seq` rnf ld++instance NFData a => NFData (TextDetails a) where+  rnf (Chr c)    = rnf c+  rnf (Str str)  = rnf str+  rnf (PStr str) = rnf str+  rnf (Mark a)   = rnf a++-- ---------------------------------------------------------------------------+-- Values and Predicates on GDocs and TextDetails++-- | A document of height and width 1, containing a literal character.+char :: Char -> MDoc a+char c = textBeside_ (Chr c) 1 Empty++-- | A document of height 1 containing a literal string.+-- 'text' satisfies the following laws:+--+-- * @'text' s '<>' 'text' t = 'text' (s'++'t)@+--+-- * @'text' \"\" '<>' x = x@, if @x@ non-empty+--+-- The side condition on the last law is necessary because @'text' \"\"@+-- has height 1, while 'empty' has no height.+text :: String -> MDoc a+text s = case length s of {sl -> textBeside_ (Str s)  sl Empty}++-- | Same as @text@. Used to be used for Bytestrings.+ptext :: String -> MDoc a+ptext s = case length s of {sl -> textBeside_ (PStr s) sl Empty}++-- | Some text with any width. (@text s = sizedText (length s) s@)+sizedText :: Int -> String -> MDoc a+sizedText l s = textBeside_ (Str s) l Empty++-- | Some text, but without any width. Use for non-printing text+-- such as a HTML or Latex tags+zeroWidthText :: String -> MDoc a+zeroWidthText = sizedText 0++-- | The empty document, with no height and no width.+-- 'empty' is the identity for '<>', '<+>', '$$' and '$+$', and anywhere+-- in the argument list for 'sep', 'hcat', 'hsep', 'vcat', 'fcat' etc.+empty :: MDoc a+empty = Empty++-- | Returns 'True' if the document is empty+isEmpty :: MDoc a -> Bool+isEmpty Empty = True+isEmpty _     = False++-- | Produce spacing for indenting the amount specified.+--+-- an old version inserted tabs being 8 columns apart in the output.+indent :: Int -> String+indent !n = replicate n ' '++{-+Q: What is the reason for negative indentation (i.e. argument to indent+   is < 0) ?++A:+This indicates an error in the library client's code.+If we compose a <> b, and the first line of b is more indented than some+other lines of b, the law <n6> (<> eats nests) may cause the pretty+printer to produce an invalid layout:++doc       |0123345+------------------+d1        |a...|+d2        |...b|+          |c...|++d1<>d2    |ab..|+         c|....|++Consider a <> b, let `s' be the length of the last line of `a', `k' the+indentation of the first line of b, and `k0' the indentation of the+left-most line b_i of b.++The produced layout will have negative indentation if `k - k0 > s', as+the first line of b will be put on the (s+1)th column, effectively+translating b horizontally by (k-s). Now if the i^th line of b has an+indentation k0 < (k-s), it is translated out-of-page, causing+`negative indentation'.+-}+++semi   :: MDoc a -- ^ A ';' character+comma  :: MDoc a -- ^ A ',' character+colon  :: MDoc a -- ^ A ':' character+space  :: MDoc a -- ^ A space character+equals :: MDoc a -- ^ A '=' character+lparen :: MDoc a -- ^ A '(' character+rparen :: MDoc a -- ^ A ')' character+lbrack :: MDoc a -- ^ A '[' character+rbrack :: MDoc a -- ^ A ']' character+lbrace :: MDoc a -- ^ A '{' character+rbrace :: MDoc a -- ^ A '}' character+semi   = char ';'+comma  = char ','+colon  = char ':'+space  = char ' '+equals = char '='+lparen = char '('+rparen = char ')'+lbrack = char '['+rbrack = char ']'+lbrace = char '{'+rbrace = char '}'++spaceText, nlText :: TextDetails a+spaceText = Chr ' '+nlText    = Chr '\n'++int      :: Int      -> MDoc a -- ^ @int n = text (show n)@+integer  :: Integer  -> MDoc a -- ^ @integer n = text (show n)@+float    :: Float    -> MDoc a -- ^ @float n = text (show n)@+double   :: Double   -> MDoc a -- ^ @double n = text (show n)@+rational :: Rational -> MDoc a -- ^ @rational n = text (show n)@+int      n = text (show n)+integer  n = text (show n)+float    n = text (show n)+double   n = text (show n)+rational n = text (show n)++parens       :: MDoc a -> MDoc a -- ^ Wrap document in @(...)@+brackets     :: MDoc a -> MDoc a -- ^ Wrap document in @[...]@+braces       :: MDoc a -> MDoc a -- ^ Wrap document in @{...}@+quotes       :: MDoc a -> MDoc a -- ^ Wrap document in @\'...\'@+doubleQuotes :: MDoc a -> MDoc a -- ^ Wrap document in @\"...\"@+quotes p       = char '\'' <> p <> char '\''+doubleQuotes p = char '"' <> p <> char '"'+parens p       = char '(' <> p <> char ')'+brackets p     = char '[' <> p <> char ']'+braces p       = char '{' <> p <> char '}'++-- | Apply 'parens' to 'MDoc' if boolean is true.+maybeParens :: Bool -> MDoc a -> MDoc a+maybeParens False = id+maybeParens True = parens++-- | Apply 'brackets' to 'MDoc' if boolean is true.+maybeBrackets :: Bool -> MDoc a -> MDoc a+maybeBrackets False = id+maybeBrackets True = brackets++-- | Apply 'braces' to 'MDoc' if boolean is true.+maybeBraces :: Bool -> MDoc a -> MDoc a+maybeBraces False = id+maybeBraces True = braces++-- | Apply 'quotes' to 'MDoc' if boolean is true.+maybeQuotes :: Bool -> MDoc a -> MDoc a+maybeQuotes False = id+maybeQuotes True = quotes++-- | Apply 'doubleQuotes' to 'MDoc' if boolean is true.+maybeDoubleQuotes :: Bool -> MDoc a -> MDoc a+maybeDoubleQuotes False = id+maybeDoubleQuotes True = doubleQuotes++-- ---------------------------------------------------------------------------+-- Structural operations on GDocs++-- | Perform some simplification of a built up @GDoc@.+reduceDoc :: MDoc a -> RDoc a+reduceDoc (Beside p g q) = beside p g (reduceDoc q)+reduceDoc (Above  p g q) = above  p g (reduceDoc q)+reduceDoc p              = p++-- | List version of '<>'.+hcat :: [MDoc a] -> MDoc a+hcat = snd . reduceHoriz . foldr (\p q -> Beside p False q) empty++-- | List version of '<+>'.+hsep :: [MDoc a] -> MDoc a+hsep = snd . reduceHoriz . foldr (\p q -> Beside p True q)  empty++-- | List version of '$$'.+vcat :: [MDoc a] -> MDoc a+vcat = snd . reduceVert . foldr (\p q -> Above p False q) empty++-- | Nest (or indent) a document by a given number of positions+-- (which may also be negative).  'nest' satisfies the laws:+--+-- * @'nest' 0 x = x@+--+-- * @'nest' k ('nest' k' x) = 'nest' (k+k') x@+--+-- * @'nest' k (x '<>' y) = 'nest' k z '<>' 'nest' k y@+--+-- * @'nest' k (x '$$' y) = 'nest' k x '$$' 'nest' k y@+--+-- * @'nest' k 'empty' = 'empty'@+--+-- * @x '<>' 'nest' k y = x '<>' y@, if @x@ non-empty+--+-- The side condition on the last law is needed because+-- 'empty' is a left identity for '<>'.+nest :: Int -> MDoc a -> MDoc a+nest k p = mkNest k (reduceDoc p)++-- | @hang d1 n d2 = sep [d1, nest n d2]@+hang :: MDoc a -> Int -> MDoc a -> MDoc a+hang d1 n d2 = sep [d1, nest n d2]++-- | @punctuate p [d1, ... dn] = [d1 \<> p, d2 \<> p, ... dn-1 \<> p, dn]@+punctuate :: MDoc a -> [MDoc a] -> [MDoc a]+punctuate _ []     = []+punctuate p (x:xs) = go x xs+                   where go y []     = [y]+                         go y (z:zs) = (y <> p) : go z zs++-- mkNest checks for Nest's invariant that it doesn't have an Empty inside it+mkNest :: Int -> MDoc a -> MDoc a+mkNest k _ | k `seq` False = undefined+mkNest k (Nest k1 p)       = mkNest (k + k1) p+mkNest _ NoDoc             = NoDoc+mkNest _ Empty             = Empty+mkNest 0 p                 = p+mkNest k p                 = nest_ k p++-- mkUnion checks for an empty document+mkUnion :: MDoc a -> MDoc a -> MDoc a+mkUnion Empty _ = Empty+mkUnion p q     = p `union_` q++data IsEmpty = IsEmpty | NotEmpty++reduceHoriz :: MDoc a -> (IsEmpty, MDoc a)+reduceHoriz (Beside p g q) = eliminateEmpty Beside (snd (reduceHoriz p)) g (reduceHoriz q)+reduceHoriz doc            = (NotEmpty, doc)++reduceVert :: MDoc a -> (IsEmpty, MDoc a)+reduceVert (Above  p g q) = eliminateEmpty Above  (snd (reduceVert p)) g (reduceVert q)+reduceVert doc            = (NotEmpty, doc)++{-# INLINE eliminateEmpty #-}+eliminateEmpty ::+  (MDoc a -> Bool -> MDoc a -> MDoc a) ->+  MDoc a -> Bool -> (IsEmpty, MDoc a) -> (IsEmpty, MDoc a)+eliminateEmpty _    Empty _ q          = q+eliminateEmpty cons p     g q          =+  (NotEmpty,+   -- We're not empty whether or not q is empty, so for laziness-sake,+   -- after checking that p isn't empty, we put the NotEmpty result+   -- outside independent of q. This allows reduceAB to immediately+   -- return the appropriate constructor (Above or Beside) without+   -- forcing the entire nested MDoc. This allows the foldr in vcat,+   -- hsep, and hcat to be lazy on its second argument, avoiding a+   -- stack overflow.+   case q of+     (NotEmpty, q') -> cons p g q'+     (IsEmpty, _) -> p)++nilAbove_ :: RDoc a -> RDoc a+nilAbove_ = NilAbove++-- Arg of a TextBeside is always an RDoc+textBeside_ :: TextDetails a -> Int -> RDoc a -> RDoc a+textBeside_ = TextBeside++nest_ :: Int -> RDoc a -> RDoc a+nest_ = Nest++union_ :: RDoc a -> RDoc a -> RDoc a+union_ = Union+++-- ---------------------------------------------------------------------------+-- Vertical composition @$$@++-- | Above, except that if the last line of the first argument stops+-- at least one position before the first line of the second begins,+-- these two lines are overlapped.  For example:+--+-- >    text "hi" $$ nest 5 (text "there")+--+-- lays out as+--+-- >    hi   there+--+-- rather than+--+-- >    hi+-- >         there+--+-- '$$' is associative, with identity 'empty', and also satisfies+--+-- * @(x '$$' y) '<>' z = x '$$' (y '<>' z)@, if @y@ non-empty.+--+($$) :: MDoc a -> MDoc a -> MDoc a+p $$  q = above_ p False q++-- | Above, with no overlapping.+-- '$+$' is associative, with identity 'empty'.+($+$) :: MDoc a -> MDoc a -> MDoc a+p $+$ q = above_ p True q++above_ :: MDoc a -> Bool -> MDoc a -> MDoc a+above_ p _ Empty = p+above_ Empty _ q = q+above_ p g q     = Above p g q++above :: MDoc a -> Bool -> RDoc a -> RDoc a+above (Above p g1 q1)  g2 q2 = above p g1 (above q1 g2 q2)+above p@(Beside{})     g  q  = aboveNest (reduceDoc p) g 0 (reduceDoc q)+above p g q                  = aboveNest p             g 0 (reduceDoc q)++-- Specfication: aboveNest p g k q = p $g$ (nest k q)+aboveNest :: RDoc a -> Bool -> Int -> RDoc a -> RDoc a+aboveNest _                   _ k _ | k `seq` False = undefined+aboveNest NoDoc               _ _ _ = NoDoc+aboveNest (p1 `Union` p2)     g k q = aboveNest p1 g k q `union_`+                                      aboveNest p2 g k q++aboveNest Empty               _ k q = mkNest k q+aboveNest (Nest k1 p)         g k q = nest_ k1 (aboveNest p g (k - k1) q)+                                  -- p can't be Empty, so no need for mkNest++aboveNest (NilAbove p)        g k q = nilAbove_ (aboveNest p g k q)+aboveNest (TextBeside s sl p) g k q = textBeside_ s sl rest+                                    where+                                      !k1  = k - sl+                                      rest = case p of+                                                Empty -> nilAboveNest g k1 q+                                                _     -> aboveNest  p g k1 q+aboveNest (Above {})          _ _ _ = error "aboveNest Above"+aboveNest (Beside {})         _ _ _ = error "aboveNest Beside"++-- Specification: text s <> nilaboveNest g k q+--              = text s <> (text "" $g$ nest k q)+nilAboveNest :: Bool -> Int -> RDoc a -> RDoc a+nilAboveNest _ k _           | k `seq` False = undefined+nilAboveNest _ _ Empty       = Empty+                               -- Here's why the "text s <>" is in the spec!+nilAboveNest g k (Nest k1 q) = nilAboveNest g (k + k1) q+nilAboveNest g k q           | not g && k > 0      -- No newline if no overlap+                             = textBeside_ (Str (indent k)) k q+                             | otherwise           -- Put them really above+                             = nilAbove_ (mkNest k q)+++-- ---------------------------------------------------------------------------+-- Horizontal composition @<>@++-- We intentionally avoid Data.Monoid.(<>) here due to interactions of+-- Data.Monoid.(<>) and (<+>).  See+-- http://www.haskell.org/pipermail/libraries/2011-November/017066.html++-- | Beside.+-- '<>' is associative, with identity 'empty'.+(<>) :: MDoc a -> MDoc a -> MDoc a+p <>  q = beside_ p False q++-- | Beside, separated by space, unless one of the arguments is 'empty'.+-- '<+>' is associative, with identity 'empty'.+(<+>) :: MDoc a -> MDoc a -> MDoc a+p <+> q = beside_ p True  q++beside_ :: MDoc a -> Bool -> MDoc a -> MDoc a+beside_ p _ Empty = p+beside_ Empty _ q = q+beside_ p g q     = Beside p g q++-- Specification: beside g p q = p <g> q+beside :: MDoc a -> Bool -> RDoc a -> RDoc a+beside NoDoc               _ _   = NoDoc+beside (p1 `Union` p2)     g q   = beside p1 g q `union_` beside p2 g q+beside Empty               _ q   = q+beside (Nest k p)          g q   = nest_ k $! beside p g q+beside p@(Beside p1 g1 q1) g2 q2+         | g1 == g2              = beside p1 g1 $! beside q1 g2 q2+         | otherwise             = beside (reduceDoc p) g2 q2+beside p@(Above{})         g q   = let !d = reduceDoc p in beside d g q+beside (NilAbove p)        g q   = nilAbove_ $! beside p g q+beside (TextBeside s sl p) g q   = textBeside_ s sl $! rest+                               where+                                  rest = case p of+                                           Empty -> nilBeside g q+                                           _     -> beside p g q++-- Specification: text "" <> nilBeside g p+--              = text "" <g> p+nilBeside :: Bool -> RDoc a -> RDoc a+nilBeside _ Empty         = Empty -- Hence the text "" in the spec+nilBeside g (Nest _ p)    = nilBeside g p+nilBeside g p | g         = textBeside_ spaceText 1 p+              | otherwise = p+++-- ---------------------------------------------------------------------------+-- Separate, @sep@++-- Specification: sep ps  = oneLiner (hsep ps)+--                         `union`+--                          vcat ps++-- | Either 'hsep' or 'vcat'.+sep  :: [MDoc a] -> MDoc a+sep = sepX True   -- Separate with spaces++-- | Either 'hcat' or 'vcat'.+cat :: [MDoc a] -> MDoc a+cat = sepX False  -- Don't++sepX :: Bool -> [MDoc a] -> MDoc a+sepX _ []     = empty+sepX x (p:ps) = sep1 x (reduceDoc p) 0 ps+++-- Specification: sep1 g k ys = sep (x : map (nest k) ys)+--                            = oneLiner (x <g> nest k (hsep ys))+--                              `union` x $$ nest k (vcat ys)+sep1 :: Bool -> RDoc a -> Int -> [MDoc a] -> RDoc a+sep1 _ _                   k _  | k `seq` False = undefined+sep1 _ NoDoc               _ _  = NoDoc+sep1 g (p `Union` q)       k ys = sep1 g p k ys `union_`+                                  aboveNest q False k (reduceDoc (vcat ys))++sep1 g Empty               k ys = mkNest k (sepX g ys)+sep1 g (Nest n p)          k ys = nest_ n (sep1 g p (k - n) ys)++sep1 _ (NilAbove p)        k ys = nilAbove_+                                  (aboveNest p False k (reduceDoc (vcat ys)))+sep1 g (TextBeside s sl p) k ys = textBeside_ s sl (sepNB g p (k - sl) ys)+sep1 _ (Above {})          _ _  = error "sep1 Above"+sep1 _ (Beside {})         _ _  = error "sep1 Beside"++-- Specification: sepNB p k ys = sep1 (text "" <> p) k ys+-- Called when we have already found some text in the first item+-- We have to eat up nests+sepNB :: Bool -> MDoc a -> Int -> [MDoc a] -> MDoc a+sepNB g (Nest _ p) k ys+  = sepNB g p k ys -- Never triggered, because of invariant (2)+sepNB g Empty k ys+  = oneLiner (nilBeside g (reduceDoc rest)) `mkUnion`+    -- XXX: TODO: PRETTY: Used to use True here (but GHC used False...)+    nilAboveNest False k (reduceDoc (vcat ys))+  where+    rest | g         = hsep ys+         | otherwise = hcat ys+sepNB g p k ys+  = sep1 g p k ys+++-- ---------------------------------------------------------------------------+-- @fill@++-- | \"Paragraph fill\" version of 'cat'.+fcat :: [MDoc a] -> MDoc a+fcat = fill False++-- | \"Paragraph fill\" version of 'sep'.+fsep :: [MDoc a] -> MDoc a+fsep = fill True++-- Specification:+--+-- fill g docs = fillIndent 0 docs+--+-- fillIndent k [] = []+-- fillIndent k [p] = p+-- fillIndent k (p1:p2:ps) =+--    oneLiner p1 <g> fillIndent (k + length p1 + g ? 1 : 0)+--                               (remove_nests (oneLiner p2) : ps)+--     `Union`+--    (p1 $*$ nest (-k) (fillIndent 0 ps))+--+-- $*$ is defined for layouts (not Docs) as+-- layout1 $*$ layout2 | hasMoreThanOneLine layout1 = layout1 $$ layout2+--                     | otherwise                  = layout1 $+$ layout2++fill :: Bool -> [MDoc a] -> RDoc a+fill _ []     = empty+fill g (p:ps) = fill1 g (reduceDoc p) 0 ps++fill1 :: Bool -> RDoc a -> Int -> [MDoc a] -> MDoc a+fill1 _ _                   k _  | k `seq` False = undefined+fill1 _ NoDoc               _ _  = NoDoc+fill1 g (p `Union` q)       k ys = fill1 g p k ys `union_`+                                   aboveNest q False k (fill g ys)+fill1 g Empty               k ys = mkNest k (fill g ys)+fill1 g (Nest n p)          k ys = nest_ n (fill1 g p (k - n) ys)+fill1 g (NilAbove p)        k ys = nilAbove_ (aboveNest p False k (fill g ys))+fill1 g (TextBeside s sl p) k ys = textBeside_ s sl (fillNB g p (k - sl) ys)+fill1 _ (Above {})          _ _  = error "fill1 Above"+fill1 _ (Beside {})         _ _  = error "fill1 Beside"++fillNB :: Bool -> MDoc a -> Int -> [MDoc a] -> MDoc a+fillNB _ _           k _  | k `seq` False = undefined+fillNB g (Nest _ p)  k ys   = fillNB g p k ys+                              -- Never triggered, because of invariant (2)+fillNB _ Empty _ []         = Empty+fillNB g Empty k (Empty:ys) = fillNB g Empty k ys+fillNB g Empty k (y:ys)     = fillNBE g k y ys+fillNB g p k ys             = fill1 g p k ys+++fillNBE :: Bool -> Int -> MDoc a -> [MDoc a] -> MDoc a+fillNBE g k y ys+  = nilBeside g (fill1 g ((elideNest . oneLiner . reduceDoc) y) k' ys)+    -- XXX: TODO: PRETTY: Used to use True here (but GHC used False...)+    `mkUnion` nilAboveNest False k (fill g (y:ys))+  where k' = if g then k - 1 else k++elideNest :: MDoc a -> MDoc a+elideNest (Nest _ d) = d+elideNest d          = d+++-- ---------------------------------------------------------------------------+-- Selecting the best layout++best :: Int     -- Line length+     -> Int     -- Ribbon length+     -> RDoc a+     -> RDoc a  -- No unions in here!+best w0 r = get w0+  where+    get w _ | w == 0 && False = undefined+    get _ Empty               = Empty+    get _ NoDoc               = NoDoc+    get w (NilAbove p)        = nilAbove_ (get w p)+    get w (TextBeside s sl p) = textBeside_ s sl (get1 w sl p)+    get w (Nest k p)          = nest_ k (get (w - k) p)+    get w (p `Union` q)       = nicest w r (get w p) (get w q)+    get _ (Above {})          = error "best get Above"+    get _ (Beside {})         = error "best get Beside"++    get1 w _ _ | w == 0 && False  = undefined+    get1 _ _  Empty               = Empty+    get1 _ _  NoDoc               = NoDoc+    get1 w sl (NilAbove p)        = nilAbove_ (get (w - sl) p)+    get1 w sl (TextBeside t tl p) = textBeside_ t tl (get1 w (sl + tl) p)+    get1 w sl (Nest _ p)          = get1 w sl p+    get1 w sl (p `Union` q)       = nicest1 w r sl (get1 w sl p)+                                                   (get1 w sl q)+    get1 _ _  (Above {})          = error "best get1 Above"+    get1 _ _  (Beside {})         = error "best get1 Beside"++nicest :: Int -> Int -> MDoc a -> MDoc a -> MDoc a+nicest !w !r = nicest1 w r 0++nicest1 :: Int -> Int -> Int -> MDoc a -> MDoc a -> MDoc a+nicest1 !w !r !sl p q | fits ((w `min` r) - sl) p = p+                      | otherwise                 = q++fits :: Int  -- Space available+     -> MDoc a+     -> Bool -- True if *first line* of MDoc fits in space available+fits n _ | n < 0           = False+fits _ NoDoc               = False+fits _ Empty               = True+fits _ (NilAbove _)        = True+fits n (TextBeside _ sl p) = fits (n - sl) p+fits _ (Above {})          = error "fits Above"+fits _ (Beside {})         = error "fits Beside"+fits _ (Union {})          = error "fits Union"+fits _ (Nest {})           = error "fits Nest"++-- | @first@ returns its first argument if it is non-empty, otherwise its second.+first :: MDoc a -> MDoc a -> MDoc a+first p q | nonEmptySet p = p -- unused, because (get OneLineMode) is unused+          | otherwise     = q++nonEmptySet :: MDoc a -> Bool+nonEmptySet NoDoc              = False+nonEmptySet (_ `Union` _)      = True+nonEmptySet Empty              = True+nonEmptySet (NilAbove _)       = True+nonEmptySet (TextBeside _ _ p) = nonEmptySet p+nonEmptySet (Nest _ p)         = nonEmptySet p+nonEmptySet (Above {})         = error "nonEmptySet Above"+nonEmptySet (Beside {})        = error "nonEmptySet Beside"++-- @oneLiner@ returns the one-line members of the given set of @GDoc@s.+oneLiner :: MDoc a -> MDoc a+oneLiner NoDoc               = NoDoc+oneLiner Empty               = Empty+oneLiner (NilAbove _)        = NoDoc+oneLiner (TextBeside s sl p) = textBeside_ s sl (oneLiner p)+oneLiner (Nest k p)          = nest_ k (oneLiner p)+oneLiner (p `Union` _)       = oneLiner p+oneLiner (Above {})          = error "oneLiner Above"+oneLiner (Beside {})         = error "oneLiner Beside"+++-- ---------------------------------------------------------------------------+-- Rendering++-- | A rendering style.+data Style+  = Style { mode           :: Mode  -- ^ The rendering mode+          , lineLength     :: Int   -- ^ Length of line, in chars+          , ribbonsPerLine :: Float -- ^ Ratio of line length to ribbon length+          }+#if __GLASGOW_HASKELL__ >= 701+  deriving (Show, Eq, Generic)+#endif++-- | The default style (@mode=PageMode, lineLength=100, ribbonsPerLine=1.5@).+style :: Style+style = Style { lineLength = 100, ribbonsPerLine = 1.5, mode = PageMode }++-- | Rendering mode.+data Mode = PageMode     -- ^ Normal+          | ZigZagMode   -- ^ With zig-zag cuts+          | LeftMode     -- ^ No indentation, infinitely long lines+          | OneLineMode  -- ^ All on one line+#if __GLASGOW_HASKELL__ >= 701+          deriving (Show, Eq, Generic)+#endif++-- | Render the @MDoc@ to a String using the default @Style@.+render :: MDoc a -> String+render = fullRender (mode style) (lineLength style) (ribbonsPerLine style)+                    txtPrinter ""++-- | Render the @MDoc@ to a String using the given @Style@.+renderStyle :: Style -> MDoc a -> String+renderStyle s = fullRender (mode s) (lineLength s) (ribbonsPerLine s)+                txtPrinter ""++-- | Default TextDetails printer+txtPrinter :: TextDetails a -> String -> String+txtPrinter (Chr c)   s  = c:s+txtPrinter (Str s1)  s2 = s1 ++ s2+txtPrinter (PStr s1) s2 = s1 ++ s2+txtPrinter (Mark _)  s2 = s2++-- | The general rendering interface.+fullRender :: Mode                       -- ^ Rendering mode+           -> Int                        -- ^ Line length+           -> Float                      -- ^ Ribbons per line+           -> (TextDetails b -> a -> a)  -- ^ What to do with text+           -> a                          -- ^ What to do at the end+           -> MDoc b                     -- ^ The document+           -> a                          -- ^ Result+fullRender OneLineMode _ _ txt end doc+  = easyDisplay spaceText (\_ y -> y) txt end (reduceDoc doc)+fullRender LeftMode    _ _ txt end doc+  = easyDisplay nlText first txt end (reduceDoc doc)++fullRender m lineLen ribbons txt rest doc+  = display m lineLen ribbonLen txt rest doc'+  where+    doc' = best bestLineLen ribbonLen (reduceDoc doc)++    bestLineLen, ribbonLen :: Int+    ribbonLen   = round (fromIntegral lineLen / ribbons)+    bestLineLen = case m of+                      ZigZagMode -> maxBound+                      _          -> lineLen++easyDisplay :: TextDetails b+             -> (MDoc b -> MDoc b -> MDoc b)+             -> (TextDetails b -> a -> a)+             -> a+             -> MDoc b+             -> a+easyDisplay nlSpaceText choose txt end+  = lay+  where+    lay NoDoc              = error "easyDisplay: NoDoc"+    lay (Union p q)        = lay (choose p q)+    lay (Nest _ p)         = lay p+    lay Empty              = end+    lay (NilAbove p)       = nlSpaceText `txt` lay p+    lay (TextBeside s _ p) = s `txt` lay p+    lay (Above {})         = error "easyDisplay Above"+    lay (Beside {})        = error "easyDisplay Beside"++display :: Mode -> Int -> Int -> (TextDetails b -> a -> a) -> a -> MDoc b -> a+display m !page_width !ribbon_width txt end doc+  = case page_width - ribbon_width of { gap_width ->+    case gap_width `quot` 2 of { shift ->+    let+        lay k _            | k `seq` False = undefined+        lay k (Nest k1 p)  = lay (k + k1) p+        lay _ Empty        = end+        lay k (NilAbove p) = nlText `txt` lay k p+        lay k (TextBeside s sl p)+            = case m of+                    ZigZagMode |  k >= gap_width+                               -> nlText `txt` (+                                  Str (replicate shift '/') `txt` (+                                  nlText `txt`+                                  lay1 (k - shift) s sl p ))++                               |  k < 0+                               -> nlText `txt` (+                                  Str (replicate shift '\\') `txt` (+                                  nlText `txt`+                                  lay1 (k + shift) s sl p ))++                    _ -> lay1 k s sl p+        lay _ (Above {})   = error "display lay Above"+        lay _ (Beside {})  = error "display lay Beside"+        lay _ NoDoc        = error "display lay NoDoc"+        lay _ (Union {})   = error "display lay Union"++        lay1 !k s !sl p    = let !r = k + sl+                             in Str (indent k) `txt` (s `txt` lay2 r p)++        lay2 k _ | k `seq` False   = undefined+        lay2 k (NilAbove p)        = nlText `txt` lay k p+        lay2 k (TextBeside s sl p) = s `txt` lay2 (k + sl) p+        lay2 k (Nest _ p)          = lay2 k p+        lay2 _ Empty               = end+        lay2 _ (Above {})          = error "display lay2 Above"+        lay2 _ (Beside {})         = error "display lay2 Beside"+        lay2 _ NoDoc               = error "display lay2 NoDoc"+        lay2 _ (Union {})          = error "display lay2 Union"+    in+    lay 0 doc+    }}++------------------------------------------------------------------------------++-- | mark inserts a zero width mark into the output document++mark :: a -> MDoc a+mark m = textBeside_ (Mark m) 0 Empty
+ src/Text/PrettyPrint/MarkedHughesPJClass.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE CPP #-}+#if __GLASGOW_HASKELL__ >= 701+{-# LANGUAGE Safe #-}+#endif++-----------------------------------------------------------------------------+-- |+-- Module      :  Text.PrettyPrint.HughesPJClass+-- Copyright   :  (c) Lennart Augustsson 2014+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  David Terei <code@davidterei.com>+-- Stability   :  stable+-- Portability :  portable+--+-- Pretty printing class, simlar to 'Show' but nicer looking. +--+-- Note that the precedence level is a 'Rational' so there is an unlimited+-- number of levels. This module re-exports 'Text.PrettyPrint.HughesPJ'.+--+-----------------------------------------------------------------------------++module Text.PrettyPrint.MarkedHughesPJClass (+    -- * Pretty typeclass+    Pretty(..),++    PrettyLevel(..), prettyNormal,+    prettyShow, prettyParen,++    -- re-export HughesPJ+    module Text.PrettyPrint.MarkedHughesPJ+  ) where++import Text.PrettyPrint.MarkedHughesPJ++-- | Level of detail in the pretty printed output.+-- Level 0 is the least detail.+newtype PrettyLevel = PrettyLevel Int+  deriving (Eq, Ord, Show)++-- | The "normal" (Level 0) of detail.+prettyNormal :: PrettyLevel+prettyNormal = PrettyLevel 0++-- | Pretty printing class. The precedence level is used in a similar way as in+-- the 'Show' class. Minimal complete definition is either 'pPrintPrec' or+-- 'pPrint'.+class Pretty a where+  pPrintPrec :: PrettyLevel -> Rational -> a -> MDoc b+  pPrintPrec _ _ = pPrint++  pPrint :: a -> MDoc b+  pPrint = pPrintPrec prettyNormal 0++  pPrintList :: PrettyLevel -> [a] -> MDoc b+  pPrintList l = brackets . fsep . punctuate comma . map (pPrintPrec l 0)++#if __GLASGOW_HASKELL__ >= 708+  {-# MINIMAL pPrintPrec | pPrint #-}+#endif++-- | Pretty print a value with the 'prettyNormal' level.+prettyShow :: (Pretty a) => a -> String+prettyShow = render . pPrint++pPrint0 :: (Pretty a) => PrettyLevel -> a -> MDoc b+pPrint0 l = pPrintPrec l 0++appPrec :: Rational+appPrec = 10++-- | Parenthesize an value if the boolean is true.+{-# DEPRECATED prettyParen "Please use 'maybeParens' instead" #-}+prettyParen :: Bool -> MDoc a -> MDoc a+prettyParen = maybeParens++-- Various Pretty instances+instance Pretty Int where pPrint = int++instance Pretty Integer where pPrint = integer++instance Pretty Float where pPrint = float++instance Pretty Double where pPrint = double++instance Pretty () where pPrint _ = text "()"++instance Pretty Bool where pPrint = text . show++instance Pretty Ordering where pPrint = text . show++instance Pretty Char where+  pPrint = char+  pPrintList _ = text . show++instance (Pretty a) => Pretty (Maybe a) where+  pPrintPrec _ _ Nothing = text "Nothing"+  pPrintPrec l p (Just x) =+    prettyParen (p > appPrec) $ text "Just" <+> pPrintPrec l (appPrec+1) x++instance (Pretty a, Pretty b) => Pretty (Either a b) where+  pPrintPrec l p (Left x) =+    prettyParen (p > appPrec) $ text "Left" <+> pPrintPrec l (appPrec+1) x+  pPrintPrec l p (Right x) =+    prettyParen (p > appPrec) $ text "Right" <+> pPrintPrec l (appPrec+1) x++instance (Pretty a) => Pretty [a] where+  pPrintPrec l _ = pPrintList l++instance (Pretty a, Pretty b) => Pretty (a, b) where+  pPrintPrec l _ (a, b) =+    parens $ fsep $ punctuate comma [pPrint0 l a, pPrint0 l b]++instance (Pretty a, Pretty b, Pretty c) => Pretty (a, b, c) where+  pPrintPrec l _ (a, b, c) =+    parens $ fsep $ punctuate comma [pPrint0 l a, pPrint0 l b, pPrint0 l c]++instance (Pretty a, Pretty b, Pretty c, Pretty d) => Pretty (a, b, c, d) where+  pPrintPrec l _ (a, b, c, d) =+    parens $ fsep $ punctuate comma+      [pPrint0 l a, pPrint0 l b, pPrint0 l c, pPrint0 l d]++instance (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e) => Pretty (a, b, c, d, e) where+  pPrintPrec l _ (a, b, c, d, e) =+    parens $ fsep $ punctuate comma+      [pPrint0 l a, pPrint0 l b, pPrint0 l c, pPrint0 l d, pPrint0 l e]++instance (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e, Pretty f) => Pretty (a, b, c, d, e, f) where+  pPrintPrec l _ (a, b, c, d, e, f) =+    parens $ fsep $ punctuate comma+      [pPrint0 l a, pPrint0 l b, pPrint0 l c,+        pPrint0 l d, pPrint0 l e, pPrint0 l f]++instance (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e, Pretty f, Pretty g) =>+         Pretty (a, b, c, d, e, f, g) where+  pPrintPrec l _ (a, b, c, d, e, f, g) =+    parens $ fsep $ punctuate comma+      [pPrint0 l a, pPrint0 l b, pPrint0 l c,+        pPrint0 l d, pPrint0 l e, pPrint0 l f, pPrint0 l g]++instance (Pretty a, Pretty b, Pretty c, Pretty d, Pretty e, Pretty f, Pretty g, Pretty h) =>+         Pretty (a, b, c, d, e, f, g, h) where+  pPrintPrec l _ (a, b, c, d, e, f, g, h) =+    parens $ fsep $ punctuate comma+      [pPrint0 l a, pPrint0 l b, pPrint0 l c,+        pPrint0 l d, pPrint0 l e, pPrint0 l f, pPrint0 l g, pPrint0 l h]
test/Main.hs view
@@ -1,4 +1,4 @@-module Main where+module Main (main) where  import Text.PrettyPrint.MarkedHughesPJ hiding (Doc) @@ -29,15 +29,31 @@ scope :: MStyle -> Doc -> Doc scope s d = open s <> d <> close s +example1 :: Exp example1 = Var "abc"++example2 :: Exp example2 = Var "cde"++example3 :: Exp example3 = App example1 [example2]++example4 :: Exp example4 = Lit 99++example5 :: Exp example5 = App example3 [example1,example4,example2,example1,example2,example4,example2]++example6 :: Exp example6 = List [example1,example2,example3,example4,example5]++example7 :: Exp example7 = App example3 [example6]++example8 :: Exp example8 = App example4 [example4] +examples :: [Exp] examples = [ example1            , example2            , example3@@ -48,6 +64,7 @@            , example8            ] +main :: IO () main = do   putStrLn "<html><body><pre>"   sequence_ [ putStrLn $ render (pretty ex)@@ -59,6 +76,7 @@             ]   putStrLn "</pre></body></html>" +html_txt :: TextDetails Markup -> String -> String html_txt (Chr c) r      = concatMap escape [c] ++ r html_txt (Str s) r      = concatMap escape s ++ r html_txt (PStr s) r     = concatMap escape s ++ r@@ -69,7 +87,7 @@ html_txt (Mark (Open GREEN)) r   = "<font color=\"green\">" ++ r  html_txt (Mark (Close GREEN)) r  = "</font>" ++ r +escape :: Char -> String escape '<' = "&lt;" escape '>' = "&gt;" escape c   = [c]-