-- |
-- Module      :  Text.PrettyPrint.Mainland
-- Copyright   :  (c) 2006-2011 Harvard University
--                (c) 2011-2012 Geoffrey Mainland
--                (c) 2015-2017 Drexel University
-- License     :  BSD-style
-- Maintainer  :  mainland@drexel.edu
--
-- Stability   :  provisional
-- Portability :  portable
--
-- This module is based on /A Prettier Printer/ by Phil Wadler in
-- /The Fun of Programming/, Jeremy Gibbons and Oege de Moor (eds)
-- <http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf>
--
-- At the time it was originally written I didn't know about Daan Leijen's
-- pretty printing module based on the same paper. I have since incorporated
-- many of his improvements. This module is geared towards pretty printing
-- source code; its main advantages over other libraries are the ability to
-- automatically track the source locations associated with pretty printed
-- values and output appropriate #line pragmas and the use of
-- 'Data.Text.Lazy.Text' for output.

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}

module Text.PrettyPrint.Mainland (
    -- * The document type
    Doc,

    -- * Constructing documents
    -- ** Converting values into documents
    text, bool, char, string, int, integer, float, double, rational,
    strictText, lazyText,

    -- ** Simple documents documents
    star, colon, comma, dot, equals, semi, space, spaces,
    backquote, squote, dquote,
    langle, rangle, lbrace, rbrace, lbracket, rbracket, lparen, rparen,

    -- ** Basic document combinators
    empty,
    srcloc, line, softline, softbreak,
    (<|>), (<+>), (</>), (<+/>), (<//>),
    group, flatten,

    -- ** Wrapping documents in delimiters
    enclose, squotes, dquotes, angles, backquotes, braces, brackets, parens,
    parensIf,

    -- * Combining lists of documents
    folddoc, spread, stack, cat, sep,
    punctuate, commasep, semisep,
    enclosesep, tuple, list,

    -- ** Alignment and indentation
    align, hang, indent,
    nest, column, nesting,
    width, fill, fillbreak,

    -- ** Utilities
    faildoc, errordoc,

    -- * The rendered document type
    RDoc(..),

    -- * Document rendering
    render, renderCompact,
    displayS, prettyS, pretty, prettyCompactS, prettyCompact,
    displayPragmaS, prettyPragmaS, prettyPragma,
    displayLazyText, prettyLazyText,
    displayPragmaLazyText, prettyPragmaLazyText,

    -- * Document output
    putDoc, putDocLn, hPutDoc, hPutDocLn
  ) where

import Data.Loc (L(..),
                 Loc(..),
                 Located(..),
                 Pos(..),
                 posFile,
                 posLine)
import qualified Data.Map as Map
#if !(MIN_VERSION_base(4,9,0))
import Data.Monoid (Monoid(..), (<>))
#endif /* !(MIN_VERSION_base(4,9,0)) */
#if MIN_VERSION_base(4,9,0) && !(MIN_VERSION_base(4,11,0))
import Data.Semigroup (Semigroup(..))
#endif
import qualified Data.Set as Set
import Data.String (IsString(..))
import qualified Data.Text as T
import qualified Data.Text.Lazy.IO as TIO
import qualified Data.Text.Lazy as L
import qualified Data.Text.Lazy.Builder as B
import System.IO (Handle)

-- | The abstract type of documents.
data Doc -- | The empty document
         =  Empty
         -- | A single character
         | Char {-# UNPACK #-} !Char
         -- | 'String' with associated length (to avoid recomputation)
         | String {-# UNPACK #-} !Int String
         -- | 'T.Text'
         | Text T.Text
         -- | 'L.Text'
         | LazyText L.Text
         -- | Newline
         | Line
         -- | Indented document
         | Nest {-# UNPACK #-} !Int Doc
         -- | Tag output with source location
         | SrcLoc Loc
         -- | Document concatenation
         | Doc `Cat` Doc
         -- | Provide alternatives. Invariant: both arguments must flatten to
         -- the same document.
         | Doc `Alt` Doc
         -- | Calculate document based on current column
         | Column  (Int -> Doc)
         -- | Calculate document based on current nesting
         | Nesting (Int -> Doc)

#if MIN_VERSION_base(4,9,0)
instance Semigroup Doc where
    <> :: Doc -> Doc -> Doc
(<>) = Doc -> Doc -> Doc
Cat
#endif

instance Monoid Doc where
    mempty :: Doc
mempty  = Doc
empty
#if !(MIN_VERSION_base(4,11,0))
    mappend = Cat
#endif

instance IsString Doc where
    fromString :: FilePath -> Doc
fromString FilePath
s = FilePath -> Doc
string FilePath
s

-- | The document @'text' s@ consists of the string @s@, which should not
-- contain any newlines. For a string that may include newlines, use 'string'.
text :: String -> Doc
text :: FilePath -> Doc
text FilePath
s = Int -> FilePath -> Doc
String (FilePath -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length FilePath
s) FilePath
s

-- | The document @bool b@ is equivalent to @text (show b)@.
bool :: Bool -> Doc
bool :: Bool -> Doc
bool Bool
b = FilePath -> Doc
text (Bool -> FilePath
forall a. Show a => a -> FilePath
show Bool
b)

-- | The document @'char' c@ consists the single character @c@.
char :: Char -> Doc
char :: Char -> Doc
char Char
'\n' = Doc
line
char Char
c    = Char -> Doc
Char Char
c

-- | The document @'string' s@ consists of all the characters in @s@ but with
-- newlines replaced by 'line'.
string :: String -> Doc
string :: FilePath -> Doc
string FilePath
""         = Doc
empty
string (Char
'\n' : FilePath
s) = Doc
line Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> FilePath -> Doc
string FilePath
s
string FilePath
s          = case (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\n') FilePath
s of
                      (FilePath
xs, FilePath
ys) -> FilePath -> Doc
text FilePath
xs Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> FilePath -> Doc
string FilePath
ys

-- | The document @int i@ is equivalent to @text (show i)@.
int :: Int -> Doc
int :: Int -> Doc
int Int
i = FilePath -> Doc
text (Int -> FilePath
forall a. Show a => a -> FilePath
show Int
i)

-- | The document @integer i@ is equivalent to @text (show i)@.
-- 'text'.
integer :: Integer -> Doc
integer :: Integer -> Doc
integer Integer
i = FilePath -> Doc
text (Integer -> FilePath
forall a. Show a => a -> FilePath
show Integer
i)

-- | The document @float f@ is equivalent to @text (show f)@.
float :: Float -> Doc
float :: Float -> Doc
float Float
f = FilePath -> Doc
text (Float -> FilePath
forall a. Show a => a -> FilePath
show Float
f)

-- | The document @double d@ is equivalent to @text (show d)@.
double :: Double -> Doc
double :: Double -> Doc
double Double
d = FilePath -> Doc
text (Double -> FilePath
forall a. Show a => a -> FilePath
show Double
d)

-- | The document @rational r@ is equivalent to @text (show r)@.
rational :: Rational -> Doc
rational :: Rational -> Doc
rational Rational
r = FilePath -> Doc
text (Rational -> FilePath
forall a. Show a => a -> FilePath
show Rational
r)

-- | The document @'strictText' s@ consists of the 'T.Text' @s@, which should
-- not contain any newlines.
strictText :: T.Text -> Doc
strictText :: Text -> Doc
strictText = Text -> Doc
Text

-- | The document @'lazyText' s@ consists of the 'L.Text' @s@, which should
-- not contain any newlines.
lazyText :: L.Text -> Doc
lazyText :: Text -> Doc
lazyText = Text -> Doc
LazyText

-- | The document @star@ consists of an asterisk, @\"*\"@.
star :: Doc
star :: Doc
star = Char -> Doc
char Char
'*'

-- | The document @colon@ consists of a colon, @\":\"@.
colon :: Doc
colon :: Doc
colon = Char -> Doc
char Char
':'

-- | The document @comma@ consists of a comma, @\",\"@.
comma :: Doc
comma :: Doc
comma = Char -> Doc
char Char
','

-- | The document @dot@ consists of a period, @\".\"@.
dot :: Doc
dot :: Doc
dot = Char -> Doc
char Char
'.'

-- | The document @equals@ consists of an equals sign, @\"=\"@.
equals :: Doc
equals :: Doc
equals = Char -> Doc
char Char
'='

-- | The document @semi@ consists of a semicolon, @\";\"@.
semi :: Doc
semi :: Doc
semi = Char -> Doc
char Char
';'

-- | The document @space@ consists of a space, @\" \"@.
space :: Doc
space :: Doc
space = Char -> Doc
char Char
' '

-- | The document @'space' n@ consists of n spaces.
spaces :: Int -> Doc
spaces :: Int -> Doc
spaces Int
n = FilePath -> Doc
text (Int -> Char -> FilePath
forall a. Int -> a -> [a]
replicate Int
n Char
' ')

-- | The document @backquote@ consists of a backquote, @\"`\"@.
backquote :: Doc
backquote :: Doc
backquote = Char -> Doc
char Char
'`'

-- | The document @squote@ consists of a single quote, @\"\\'\"@.
squote :: Doc
squote :: Doc
squote = Char -> Doc
char Char
'\''

-- | The document @dquote@ consists of a double quote, @\"\\\"\"@.
dquote :: Doc
dquote :: Doc
dquote = Char -> Doc
char Char
'"'

-- | The document @langle@ consists of a less-than sign, @\"<\"@.
langle :: Doc
langle :: Doc
langle = Char -> Doc
char Char
'<'

-- | The document @rangle@ consists of a greater-than sign, @\">\"@.
rangle :: Doc
rangle :: Doc
rangle = Char -> Doc
char Char
'>'

-- | The document @lbrace@ consists of a left brace, @\"{\"@.
lbrace :: Doc
lbrace :: Doc
lbrace = Char -> Doc
char Char
'{'

-- | The document @rbrace@ consists of a right brace, @\"}\"@.
rbrace :: Doc
rbrace :: Doc
rbrace = Char -> Doc
char Char
'}'

-- | The document @lbracket@ consists of a right brace, @\"[\"@.
lbracket :: Doc
lbracket :: Doc
lbracket = Char -> Doc
char Char
'['

-- | The document @rbracket@ consists of a right brace, @\"]\"@.
rbracket :: Doc
rbracket :: Doc
rbracket = Char -> Doc
char Char
']'

-- | The document @lparen@ consists of a right brace, @\"(\"@.
lparen :: Doc
lparen :: Doc
lparen = Char -> Doc
char Char
'('

-- | The document @rparen@ consists of a right brace, @\")\"@.
rparen :: Doc
rparen :: Doc
rparen = Char -> Doc
char Char
')'

-- | The empty document.
empty :: Doc
empty :: Doc
empty = Doc
Empty

-- | The document @'srcloc' x@ tags the current line with @'locOf' x@. Only
-- shown when running 'prettyPragma' and friends.
srcloc :: Located a => a -> Doc
srcloc :: forall a. Located a => a -> Doc
srcloc a
x = Loc -> Doc
SrcLoc (a -> Loc
forall a. Located a => a -> Loc
locOf a
x)

-- | The document @'line'@ advances to the next line and indents to the current
-- indentation level. When undone by 'group', it behaves like 'space'.
line :: Doc
line :: Doc
line = Doc
Line

-- | Becomes 'space' if there is room, otherwise 'line'.
--
-- > pretty 11 $ text "foo" <+/> text "bar" <+/> text "baz" =="foo bar baz"
-- > pretty  7 $ text "foo" <+/> text "bar" <+/> text "baz" == "foo bar\nbaz"
-- > pretty  6 $ text "foo" <+/> text "bar" <+/> text "baz" == "foo\nbar\nbaz"
softline :: Doc
softline :: Doc
softline = Doc
space Doc -> Doc -> Doc
`Alt` Doc
line

-- | Becomes 'empty' if there is room, otherwise 'line'.
softbreak :: Doc
softbreak :: Doc
softbreak = Doc
empty Doc -> Doc -> Doc
`Alt` Doc
line

#if !MIN_VERSION_base(4,5,0)
infixr 6 <>
#endif /* !MIN_VERSION_base(4,5,0) */
infixr 6 <+>
infixr 5 </>, <+/>, <//>
infixl 3 <|>

#if !MIN_VERSION_base(4,5,0)
-- | Concatenates two documents.
(<>) :: Doc -> Doc -> Doc
x <> y = x `Cat` y
#endif /* !MIN_VERSION_base(4,5,0) */

-- | Concatenates two documents with a 'space' in between, with identity
-- 'empty'.
(<+>) :: Doc -> Doc -> Doc
Doc
Empty <+> :: Doc -> Doc -> Doc
<+> Doc
y     = Doc
y
Doc
x     <+> Doc
Empty = Doc
x
Doc
x     <+> Doc
y     = Doc
x Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
space Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
y

-- | Concatenates two documents with a 'line' in between, with identity 'empty'.
(</>) :: Doc -> Doc -> Doc
Doc
Empty </> :: Doc -> Doc -> Doc
</> Doc
y     = Doc
y
Doc
x     </> Doc
Empty = Doc
x
Doc
x     </> Doc
y     = Doc
x Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
line Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
y

-- | Concatenates two documents with a 'softline' in between, with identity
-- 'empty'.
(<+/>) :: Doc -> Doc -> Doc
Doc
Empty <+/> :: Doc -> Doc -> Doc
<+/> Doc
y     = Doc
y
Doc
x     <+/> Doc
Empty = Doc
x
Doc
x     <+/> Doc
y     = Doc
x Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
softline Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
y

-- | Concatenates two documents with a 'softbreak' in between.
(<//>) :: Doc -> Doc -> Doc
Doc
x <//> :: Doc -> Doc -> Doc
<//> Doc
y = Doc
x Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
softbreak Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
y

-- | Provide alternative layouts of the same content. Invariant: both arguments
-- must flatten to the same document.
(<|>) :: Doc -> Doc -> Doc
Doc
x <|> :: Doc -> Doc -> Doc
<|> Doc
y = Doc
x Doc -> Doc -> Doc
`Alt` Doc
y

-- | The document @'group' d@ will flatten @d@ to /one/ line if there is
-- room for it, otherwise the original @d@.
group :: Doc -> Doc
group :: Doc -> Doc
group Doc
Empty = Doc
Empty
group Doc
d     = Doc -> Doc
flatten Doc
d Doc -> Doc -> Doc
`Alt` Doc
d

-- | The document @'flatten' d@ will flatten @d@ to /one/ line.
flatten :: Doc -> Doc
flatten :: Doc -> Doc
flatten Doc
Empty        = Doc
Empty
flatten (Char Char
c)     = Char -> Doc
Char Char
c
flatten (String Int
l FilePath
s) = Int -> FilePath -> Doc
String Int
l FilePath
s
flatten (Text Text
s)     = Text -> Doc
Text Text
s
flatten (LazyText Text
s) = Text -> Doc
LazyText Text
s
flatten Doc
Line         = Char -> Doc
Char Char
' '
flatten (Doc
x `Cat` Doc
y)  = Doc -> Doc
flatten Doc
x Doc -> Doc -> Doc
`Cat` Doc -> Doc
flatten Doc
y
flatten (Nest Int
i Doc
x)   = Int -> Doc -> Doc
Nest Int
i (Doc -> Doc
flatten Doc
x)
flatten (Doc
x `Alt` Doc
_)  = Doc -> Doc
flatten Doc
x
flatten (SrcLoc Loc
loc) = Loc -> Doc
SrcLoc Loc
loc
flatten (Column Int -> Doc
f)   = (Int -> Doc) -> Doc
Column (Doc -> Doc
flatten (Doc -> Doc) -> (Int -> Doc) -> Int -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc
f)
flatten (Nesting Int -> Doc
f)  = (Int -> Doc) -> Doc
Nesting (Doc -> Doc
flatten (Doc -> Doc) -> (Int -> Doc) -> Int -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc
f)

-- | The document @'enclose' l r d@ encloses the document @d@ between the
-- documents @l@ and @r@ using @<>@. It obeys the law
--
-- @'enclose' l r d = l <> d <> r@
enclose :: Doc -> Doc -> Doc -> Doc
enclose :: Doc -> Doc -> Doc -> Doc
enclose Doc
left Doc
right Doc
d = Doc
left Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
d Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
right

-- | The document @'squotes' d@ encloses the alinged document @d@ in \'...\'.
squotes :: Doc -> Doc
squotes :: Doc -> Doc
squotes = Doc -> Doc -> Doc -> Doc
enclose Doc
squote Doc
squote (Doc -> Doc) -> (Doc -> Doc) -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc
align

-- | The document @'dquotes' d@ encloses the aligned document @d@ in "...".
dquotes :: Doc -> Doc
dquotes :: Doc -> Doc
dquotes = Doc -> Doc -> Doc -> Doc
enclose Doc
dquote Doc
dquote (Doc -> Doc) -> (Doc -> Doc) -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc
align

-- | The document @'angles' d@ encloses the aligned document @d@ in \<...\>.
angles :: Doc -> Doc
angles :: Doc -> Doc
angles = Doc -> Doc -> Doc -> Doc
enclose Doc
langle Doc
rangle (Doc -> Doc) -> (Doc -> Doc) -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc
align

-- | The document @'backquotes' d@ encloses the aligned document @d@ in \`...\`.
backquotes :: Doc -> Doc
backquotes :: Doc -> Doc
backquotes = Doc -> Doc -> Doc -> Doc
enclose Doc
backquote Doc
backquote (Doc -> Doc) -> (Doc -> Doc) -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc
align

-- | The document @'braces' d@ encloses the aligned document @d@ in {...}.
braces :: Doc -> Doc
braces :: Doc -> Doc
braces = Doc -> Doc -> Doc -> Doc
enclose Doc
lbrace Doc
rbrace (Doc -> Doc) -> (Doc -> Doc) -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc
align

-- | The document @'brackets' d@ encloses the aligned document @d@ in [...].
brackets :: Doc -> Doc
brackets :: Doc -> Doc
brackets = Doc -> Doc -> Doc -> Doc
enclose Doc
lbracket Doc
rbracket (Doc -> Doc) -> (Doc -> Doc) -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc
align

-- | The document @'parens' d@ encloses the aligned document @d@ in (...).
parens :: Doc -> Doc
parens :: Doc -> Doc
parens = Doc -> Doc -> Doc -> Doc
enclose Doc
lparen Doc
rparen (Doc -> Doc) -> (Doc -> Doc) -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc
align

-- | The document @'parensIf' p d@ encloses the document @d@ in parenthesis if
-- @p@ is @True@, and otherwise yields just @d@.
parensIf :: Bool -> Doc -> Doc
parensIf :: Bool -> Doc -> Doc
parensIf Bool
True Doc
doc  = Doc -> Doc
parens Doc
doc
parensIf Bool
False Doc
doc = Doc
doc

-- | The document @'folddoc' f ds@ obeys the laws:
--
-- * @'folddoc' f [] = 'empty'@
-- * @'folddoc' f [d1, d2, ..., dnm1, dn] = d1 `f` (d2 `f` ... (dnm1 `f` dn))@
folddoc :: (Doc -> Doc -> Doc) -> [Doc] -> Doc
folddoc :: (Doc -> Doc -> Doc) -> [Doc] -> Doc
folddoc Doc -> Doc -> Doc
_ []     = Doc
empty
folddoc Doc -> Doc -> Doc
_ [Doc
x]    = Doc
x
folddoc Doc -> Doc -> Doc
f (Doc
x:[Doc]
xs) = Doc -> Doc -> Doc
f Doc
x ((Doc -> Doc -> Doc) -> [Doc] -> Doc
folddoc Doc -> Doc -> Doc
f [Doc]
xs)

-- | The document @'spread' ds@ concatenates the documents @ds@ with 'space'.
spread :: [Doc] -> Doc
spread :: [Doc] -> Doc
spread = (Doc -> Doc -> Doc) -> [Doc] -> Doc
folddoc Doc -> Doc -> Doc
(<+>)

-- | The document @'stack' ds@ concatenates the documents @ds@ with 'line'.
stack :: [Doc] -> Doc
stack :: [Doc] -> Doc
stack = (Doc -> Doc -> Doc) -> [Doc] -> Doc
folddoc Doc -> Doc -> Doc
(</>)

-- | The document @'cat' ds@ concatenates the documents @ds@ with the 'empty'
-- document as long as there is room, and uses 'line' when there isn't.
cat :: [Doc] -> Doc
cat :: [Doc] -> Doc
cat = Doc -> Doc
group (Doc -> Doc) -> ([Doc] -> Doc) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Doc -> Doc -> Doc) -> [Doc] -> Doc
folddoc Doc -> Doc -> Doc
(<//>)

-- | The document @'sep' ds@ concatenates the documents @ds@ with the 'space'
-- document as long as there is room, and uses 'line' when there isn't.
sep :: [Doc] -> Doc
sep :: [Doc] -> Doc
sep = Doc -> Doc
group (Doc -> Doc) -> ([Doc] -> Doc) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Doc -> Doc -> Doc) -> [Doc] -> Doc
folddoc Doc -> Doc -> Doc
(<+/>)

-- | The document @'punctuate' p ds@ obeys the law:
--
-- @'punctuate' p [d1, d2, ..., dn] = [d1 <> p, d2 <> p, ..., dn]@
punctuate :: Doc -> [Doc] -> [Doc]
punctuate :: Doc -> [Doc] -> [Doc]
punctuate Doc
_ []     = []
punctuate Doc
_ [Doc
d]    = [Doc
d]
punctuate Doc
p (Doc
d:[Doc]
ds) = (Doc
d Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
p) Doc -> [Doc] -> [Doc]
forall a. a -> [a] -> [a]
: Doc -> [Doc] -> [Doc]
punctuate Doc
p [Doc]
ds

-- | The document @'commasep' ds@ comma-space separates @ds@, aligning the
-- resulting document to the current nesting level.
commasep :: [Doc] -> Doc
commasep :: [Doc] -> Doc
commasep = Doc -> Doc
align (Doc -> Doc) -> ([Doc] -> Doc) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc] -> Doc
sep ([Doc] -> Doc) -> ([Doc] -> [Doc]) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> [Doc] -> [Doc]
punctuate Doc
comma

-- | The document @'semisep' ds@ semicolon-space separates @ds@, aligning the
-- resulting document to the current nesting level.
semisep :: [Doc] -> Doc
semisep :: [Doc] -> Doc
semisep = Doc -> Doc
align (Doc -> Doc) -> ([Doc] -> Doc) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc] -> Doc
sep ([Doc] -> Doc) -> ([Doc] -> [Doc]) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> [Doc] -> [Doc]
punctuate Doc
semi

-- | The document @'enclosesep' l r p ds@ separates @ds@ with the punctuation @p@
-- and encloses the result using @l@ and @r@. When wrapped, punctuation appears
-- at the end of the line. The enclosed portion of the document is aligned one
-- column to the right of the opening document.
--
-- @
-- \> ws = map text (words \"The quick brown fox jumps over the lazy dog\")
-- \> test = pretty 15 (enclosesep lparen rparen comma ws)
-- @
--
-- will be layed out as:
--
-- @
-- (The, quick,
--  brown, fox,
--  jumps, over,
--  the, lazy,
--  dog)
-- @
enclosesep :: Doc -> Doc -> Doc -> [Doc] -> Doc
enclosesep :: Doc -> Doc -> Doc -> [Doc] -> Doc
enclosesep Doc
left Doc
right Doc
p [Doc]
ds =
    case [Doc]
ds of
      [] ->  Doc
left Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
right
      [Doc
d] -> Doc
left Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
d Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
right
      [Doc]
_ ->   Doc
left Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc -> Doc
align ([Doc] -> Doc
sep (Doc -> [Doc] -> [Doc]
punctuate Doc
p [Doc]
ds)) Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
right

-- | The document @'tuple' ds@ separates @ds@ with commas and encloses them with
-- parentheses.
tuple :: [Doc] -> Doc
tuple :: [Doc] -> Doc
tuple = Doc -> Doc -> Doc -> [Doc] -> Doc
enclosesep Doc
lparen Doc
rparen Doc
comma

-- | The document @'list' ds@ separates @ds@ with commas and encloses them with
-- brackets.
list :: [Doc] -> Doc
list :: [Doc] -> Doc
list = Doc -> Doc -> Doc -> [Doc] -> Doc
enclosesep Doc
lbracket Doc
rbracket Doc
comma

-- | The document @'align' d@ renders @d@ with a nesting level set to the current
-- column.
align :: Doc -> Doc
align :: Doc -> Doc
align Doc
Empty = Doc
Empty
align Doc
d     = (Int -> Doc) -> Doc
column  ((Int -> Doc) -> Doc) -> (Int -> Doc) -> Doc
forall a b. (a -> b) -> a -> b
$ \Int
k ->
              (Int -> Doc) -> Doc
nesting ((Int -> Doc) -> Doc) -> (Int -> Doc) -> Doc
forall a b. (a -> b) -> a -> b
$ \Int
i ->
              Int -> Doc -> Doc
nest (Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
i) Doc
d

-- | The document @'hang' i d@ renders @d@ with a nesting level set to the
-- current column plus @i@, /not including/ the first line.
hang :: Int -> Doc -> Doc
hang :: Int -> Doc -> Doc
hang Int
i Doc
d = Doc -> Doc
align (Int -> Doc -> Doc
nest Int
i Doc
d)

-- | The document @'indent' i d@ renders @d@ with a nesting level set to the
-- current column plus @i@, /including/ the first line.
indent :: Int -> Doc -> Doc
indent :: Int -> Doc -> Doc
indent Int
i Doc
d = Doc -> Doc
align (Int -> Doc -> Doc
nest Int
i (Int -> Doc
spaces Int
i Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> Doc
d))

-- | The document @'nest' i d@ renders the document @d@ with the current
-- indentation level increased by @i@.
nest :: Int -> Doc -> Doc
nest :: Int -> Doc -> Doc
nest Int
i Doc
d = Int -> Doc -> Doc
Nest Int
i Doc
d

-- | The document @'column' f@ is produced by calling @f@ with the current
-- column.
column :: (Int -> Doc) -> Doc
column :: (Int -> Doc) -> Doc
column = (Int -> Doc) -> Doc
Column

-- | The document @'column' f@ is produced by calling @f@ with the
-- current nesting level.
nesting :: (Int -> Doc) -> Doc
nesting :: (Int -> Doc) -> Doc
nesting = (Int -> Doc) -> Doc
Nesting

-- | The document @'width' d f@ is produced by concatenating @d@ with the result
-- of calling @f@ with the width of the document @d@.
width :: Doc -> (Int -> Doc) -> Doc
width :: Doc -> (Int -> Doc) -> Doc
width Doc
d Int -> Doc
f = (Int -> Doc) -> Doc
column ((Int -> Doc) -> Doc) -> (Int -> Doc) -> Doc
forall a b. (a -> b) -> a -> b
$ \Int
k1 -> Doc
d Doc -> Doc -> Doc
forall a. Semigroup a => a -> a -> a
<> ((Int -> Doc) -> Doc
column ((Int -> Doc) -> Doc) -> (Int -> Doc) -> Doc
forall a b. (a -> b) -> a -> b
$ \Int
k2 -> Int -> Doc
f (Int
k2 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
k1))

-- | The document @'fill' i d@ renders document @x@, appending
-- @space@s until the width is equal to @i@. If the width of @d@ is already
-- greater than @i@, nothing is appended.
fill :: Int -> Doc -> Doc
fill :: Int -> Doc -> Doc
fill Int
f Doc
d = Doc -> (Int -> Doc) -> Doc
width Doc
d ((Int -> Doc) -> Doc) -> (Int -> Doc) -> Doc
forall a b. (a -> b) -> a -> b
$ \Int
w ->
           if Int
w Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
f
           then Doc
empty
           else Int -> Doc
spaces (Int
f Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
w)

-- | The document @'fillbreak' i d@ renders document @d@, appending @'space'@s
-- until the width is equal to @i@. If the width of @d@ is already greater than
-- @i@, the nesting level is increased by @i@ and a @line@ is appended.
fillbreak :: Int -> Doc -> Doc
fillbreak :: Int -> Doc -> Doc
fillbreak Int
f Doc
d = Doc -> (Int -> Doc) -> Doc
width Doc
d ((Int -> Doc) -> Doc) -> (Int -> Doc) -> Doc
forall a b. (a -> b) -> a -> b
$ \Int
w ->
                if (Int
w Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
f)
                then Int -> Doc -> Doc
nest Int
f Doc
line
                else Int -> Doc
spaces (Int
f Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
w)

-- | Equivalent of 'fail', but with a document instead of a string.
#if MIN_VERSION_base(4,13,0)
faildoc :: MonadFail m => Doc -> m a
#else
faildoc :: Monad m => Doc -> m a
#endif
faildoc :: forall (m :: * -> *) a. MonadFail m => Doc -> m a
faildoc = FilePath -> m a
forall a. FilePath -> m a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (FilePath -> m a) -> (Doc -> FilePath) -> Doc -> m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc -> FilePath
pretty Int
80

-- | Equivalent of 'error', but with a document instead of a string.
errordoc :: Doc -> a
errordoc :: forall a. Doc -> a
errordoc = FilePath -> a
forall a. HasCallStack => FilePath -> a
error (FilePath -> a) -> (Doc -> FilePath) -> Doc -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc -> FilePath
pretty Int
80

-- | A rendered document.
data RDoc -- | The empty document
          = REmpty
          -- | A single character
          | RChar {-# UNPACK #-} !Char RDoc
          -- | 'String' with associated length (to avoid recomputation)
          | RString {-# UNPACK #-} !Int String RDoc
          -- | 'T.Text'
          | RText T.Text RDoc
          -- | 'L.Text'
          | RLazyText L.Text RDoc
          -- | Tag output with source location
          | RPos Pos RDoc
          -- | A newline with the indentation of the subsequent line. If this is
          -- followed by a 'RPos', output an appropriate #line pragma /before/
          -- the newline.
          | RLine {-# UNPACK #-} !Int RDoc

-- | Render a document given a maximum width.
render :: Int -> Doc -> RDoc
render :: Int -> Doc -> RDoc
render Int
w Doc
x = Int -> Int -> Doc -> RDoc
best Int
w Int
0 Doc
x

type RDocS = RDoc -> RDoc

data Docs -- | No document.
          = Nil
          -- | Indentation, document and tail
          | Cons {-# UNPACK #-} !Int Doc Docs

best :: Int -> Int -> Doc -> RDoc
best :: Int -> Int -> Doc -> RDoc
best !Int
w Int
k Doc
x = Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
True Maybe Pos
forall a. Maybe a
Nothing Maybe Pos
forall a. Maybe a
Nothing Int
k RDocS
forall a. a -> a
id (Int -> Doc -> Docs -> Docs
Cons Int
0 Doc
x Docs
Nil)
  where
    be :: Bool      -- ^ Did a newline just occur?
       -> Maybe Pos -- ^ Previous source position
       -> Maybe Pos -- ^ Current source position
       -> Int       -- ^ Current column
       -> RDocS     -- ^ Our continuation
       -> Docs      -- ^ 'Docs' to layout
       -> RDoc
    be :: Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
_  Maybe Pos
_ Maybe Pos
_  !Int
_  RDocS
f Docs
Nil           = RDocS
f RDoc
REmpty
    be Bool
nl Maybe Pos
p Maybe Pos
p' !Int
k  RDocS
f (Cons Int
i Doc
d Docs
ds) =
        case Doc
d of
          Doc
Empty      -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
nl    Maybe Pos
p Maybe Pos
p' Int
k RDocS
f Docs
ds
          Char Char
c     -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
False Maybe Pos
p Maybe Pos
p' (Int
kInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) (RDocS
f RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDocS
prag RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> RDocS
RChar Char
c) Docs
ds
          String Int
l FilePath
s -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
False Maybe Pos
p Maybe Pos
p' (Int
kInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
l) (RDocS
f RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDocS
prag RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> FilePath -> RDocS
RString Int
l FilePath
s) Docs
ds
          Text Text
s     -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
False Maybe Pos
p Maybe Pos
p' (Int
kInt -> Int -> Int
forall a. Num a => a -> a -> a
+Text -> Int
T.length Text
s) (RDocS
f RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDocS
prag RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> RDocS
RText Text
s) Docs
ds
          LazyText Text
s -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
False Maybe Pos
p Maybe Pos
p' (Int
kInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Text -> Int64
L.length Text
s)) (RDocS
f RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDocS
prag RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> RDocS
RLazyText Text
s) Docs
ds
          Doc
Line       -> (RDocS
f RDocS -> RDocS -> RDocS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> RDocS
RLine Int
i) (Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
True Maybe Pos
p'' Maybe Pos
forall a. Maybe a
Nothing Int
i RDocS
forall a. a -> a
id Docs
ds)
          Doc
x `Cat` Doc
y  -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
nl Maybe Pos
p Maybe Pos
p' Int
k RDocS
f (Int -> Doc -> Docs -> Docs
Cons Int
i Doc
x (Int -> Doc -> Docs -> Docs
Cons Int
i Doc
y Docs
ds))
          Nest Int
j Doc
x   -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
nl Maybe Pos
p Maybe Pos
p' Int
k RDocS
f (Int -> Doc -> Docs -> Docs
Cons (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
j) Doc
x Docs
ds)
          Doc
x `Alt` Doc
y  -> Int -> RDocS -> RDoc -> RDocS
better Int
k RDocS
f (Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
nl Maybe Pos
p Maybe Pos
p' Int
k RDocS
forall a. a -> a
id (Int -> Doc -> Docs -> Docs
Cons Int
i Doc
x Docs
ds))
                                   (Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
nl Maybe Pos
p Maybe Pos
p' Int
k RDocS
forall a. a -> a
id (Int -> Doc -> Docs -> Docs
Cons Int
i Doc
y Docs
ds))
          SrcLoc Loc
loc -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
nl Maybe Pos
p (Maybe Pos -> Loc -> Maybe Pos
updatePos Maybe Pos
p' Loc
loc) Int
k RDocS
f Docs
ds
          Column Int -> Doc
g   -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
nl Maybe Pos
p Maybe Pos
p' Int
k RDocS
f (Int -> Doc -> Docs -> Docs
Cons Int
i (Int -> Doc
g Int
k) Docs
ds)
          Nesting Int -> Doc
g  -> Bool -> Maybe Pos -> Maybe Pos -> Int -> RDocS -> Docs -> RDoc
be Bool
nl Maybe Pos
p Maybe Pos
p' Int
k RDocS
f (Int -> Doc -> Docs -> Docs
Cons Int
i (Int -> Doc
g Int
i) Docs
ds)
      where
        p'' :: Maybe Pos
        prag :: RDocS
        (Maybe Pos
p'', RDocS
prag) = Maybe Pos -> Maybe Pos -> (Maybe Pos, RDocS)
lineLoc Maybe Pos
p Maybe Pos
p'

        -- | Given the previous and current position, figure out the actual
        -- current position and return a 'RDocS' that will add a #line pragma
        -- (in the form of an 'RPos') if necessary.
        lineLoc :: Maybe Pos          -- ^ Previous source position
                -> Maybe Pos          -- ^ Current source position
                -> (Maybe Pos, RDocS) -- ^ Current source position and position
                                      -- pragma
        lineLoc :: Maybe Pos -> Maybe Pos -> (Maybe Pos, RDocS)
lineLoc Maybe Pos
Nothing   Maybe Pos
Nothing       = (Maybe Pos
forall a. Maybe a
Nothing, RDocS
noPragma)
        lineLoc Maybe Pos
Nothing   (Just Pos
p)      = (Pos -> Maybe Pos
forall a. a -> Maybe a
Just Pos
p, Pos -> RDocS
pragma Pos
p)
        lineLoc (Just Pos
p1) (Just Pos
p2)
            | Pos -> FilePath
posFile Pos
p2 FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== Pos -> FilePath
posFile Pos
p1 Bool -> Bool -> Bool
&&
              Pos -> Int
posLine Pos
p2 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Pos -> Int
posLine Pos
p1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 = (Pos -> Maybe Pos
forall a. a -> Maybe a
Just Pos
p2, RDocS
noPragma)
            | Bool
otherwise                    = (Pos -> Maybe Pos
forall a. a -> Maybe a
Just Pos
p2, Pos -> RDocS
pragma Pos
p2)
        lineLoc (Just Pos
p1) Maybe Pos
Nothing       = (Pos -> Maybe Pos
forall a. a -> Maybe a
Just (Pos -> Pos
advance Pos
p1), RDocS
noPragma)
          where
            advance :: Pos -> Pos
            advance :: Pos -> Pos
advance (Pos FilePath
f Int
l Int
c Int
coff) = FilePath -> Int -> Int -> Int -> Pos
Pos FilePath
f (Int
lInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) Int
c Int
coff

        noPragma :: RDocS
        noPragma :: RDocS
noPragma = RDocS
forall a. a -> a
id

        -- We only insert a pragma if a newline was just output.
        pragma :: Pos -> RDocS
        pragma :: Pos -> RDocS
pragma Pos
p | Bool
nl        = Pos -> RDocS
RPos Pos
p
                 | Bool
otherwise = RDocS
forall a. a -> a
id

    better :: Int -> RDocS -> RDoc -> RDoc -> RDoc
    better :: Int -> RDocS -> RDoc -> RDocS
better !Int
k RDocS
f RDoc
x RDoc
y | Int -> RDoc -> Bool
fits (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
k) RDoc
x = RDocS
f RDoc
x
                    | Bool
otherwise      = RDocS
f RDoc
y

    fits :: Int -> RDoc -> Bool
    fits :: Int -> RDoc -> Bool
fits  !Int
w  RDoc
_        | Int
w Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = Bool
False
    fits  !Int
_  RDoc
REmpty           = Bool
True
    fits  !Int
w  (RChar Char
_ RDoc
x)      = Int -> RDoc -> Bool
fits (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) RDoc
x
    fits  !Int
w  (RString Int
l FilePath
_ RDoc
x)  = Int -> RDoc -> Bool
fits (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
l) RDoc
x
    fits  !Int
w  (RText Text
s RDoc
x)      = Int -> RDoc -> Bool
fits (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Text -> Int
T.length Text
s) RDoc
x
    fits  !Int
w  (RLazyText Text
s RDoc
x)  = Int -> RDoc -> Bool
fits (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Text -> Int64
L.length Text
s)) RDoc
x
    fits  !Int
w  (RPos Pos
_ RDoc
x)       = Int -> RDoc -> Bool
fits Int
w RDoc
x
    fits  !Int
_  (RLine Int
_ RDoc
_)      = Bool
True

    updatePos :: Maybe Pos -> Loc -> Maybe Pos
    updatePos :: Maybe Pos -> Loc -> Maybe Pos
updatePos Maybe Pos
Nothing  Loc
NoLoc     = Maybe Pos
forall a. Maybe a
Nothing
    updatePos Maybe Pos
_        (Loc Pos
p Pos
_) = Pos -> Maybe Pos
forall a. a -> Maybe a
Just Pos
p
    updatePos (Just Pos
p) Loc
NoLoc     = Pos -> Maybe Pos
forall a. a -> Maybe a
Just Pos
p

-- | Render a document without indentation on infinitely long lines. Since no
-- \'pretty\' printing is involved, this renderer is fast. The resulting output
-- contains fewer characters.
renderCompact :: Doc -> RDoc
renderCompact :: Doc -> RDoc
renderCompact Doc
doc = Int -> [Doc] -> RDoc
scan Int
0 [Doc
doc]
  where
    scan :: Int -> [Doc] -> RDoc
    scan :: Int -> [Doc] -> RDoc
scan !Int
_ []     = RDoc
REmpty
    scan !Int
k (Doc
d:[Doc]
ds) =
        case Doc
d of
          Doc
Empty       -> Int -> [Doc] -> RDoc
scan Int
k [Doc]
ds
          Char Char
c      -> Char -> RDocS
RChar Char
c (Int -> [Doc] -> RDoc
scan (Int
kInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) [Doc]
ds)
          String Int
l FilePath
s  -> Int -> FilePath -> RDocS
RString Int
l FilePath
s (Int -> [Doc] -> RDoc
scan (Int
kInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
l) [Doc]
ds)
          Text Text
s      -> Text -> RDocS
RText Text
s (Int -> [Doc] -> RDoc
scan (Int
kInt -> Int -> Int
forall a. Num a => a -> a -> a
+Text -> Int
T.length Text
s) [Doc]
ds)
          LazyText Text
s  -> Text -> RDocS
RLazyText Text
s (Int -> [Doc] -> RDoc
scan (Int
kInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Text -> Int64
L.length Text
s)) [Doc]
ds)
          Doc
Line        -> Int -> RDocS
RLine Int
0 (Int -> [Doc] -> RDoc
scan Int
0 [Doc]
ds)
          Nest Int
_ Doc
x    -> Int -> [Doc] -> RDoc
scan Int
k (Doc
xDoc -> [Doc] -> [Doc]
forall a. a -> [a] -> [a]
:[Doc]
ds)
          SrcLoc Loc
_    -> Int -> [Doc] -> RDoc
scan Int
k [Doc]
ds
          Cat Doc
x Doc
y     -> Int -> [Doc] -> RDoc
scan Int
k (Doc
xDoc -> [Doc] -> [Doc]
forall a. a -> [a] -> [a]
:Doc
yDoc -> [Doc] -> [Doc]
forall a. a -> [a] -> [a]
:[Doc]
ds)
          Alt Doc
x Doc
_     -> Int -> [Doc] -> RDoc
scan Int
k (Doc
xDoc -> [Doc] -> [Doc]
forall a. a -> [a] -> [a]
:[Doc]
ds)
          Column Int -> Doc
f    -> Int -> [Doc] -> RDoc
scan Int
k (Int -> Doc
f Int
kDoc -> [Doc] -> [Doc]
forall a. a -> [a] -> [a]
:[Doc]
ds)
          Nesting Int -> Doc
f   -> Int -> [Doc] -> RDoc
scan Int
k (Int -> Doc
f Int
0Doc -> [Doc] -> [Doc]
forall a. a -> [a] -> [a]
:[Doc]
ds)

-- | Display a rendered document.
displayS :: RDoc -> ShowS
displayS :: RDoc -> ShowS
displayS = RDoc -> ShowS
go
  where
    go :: RDoc -> ShowS
    go :: RDoc -> ShowS
go RDoc
REmpty          = ShowS
forall a. a -> a
id
    go (RChar Char
c RDoc
x)     = Char -> ShowS
showChar Char
c ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x
    go (RString Int
_ FilePath
s RDoc
x) = FilePath -> ShowS
showString FilePath
s ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x
    go (RText Text
s RDoc
x)     = FilePath -> ShowS
showString (Text -> FilePath
T.unpack Text
s) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x
    go (RLazyText Text
s RDoc
x) = FilePath -> ShowS
showString (Text -> FilePath
L.unpack Text
s) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x
    go (RPos Pos
_ RDoc
x)      = RDoc -> ShowS
go RDoc
x
    go (RLine Int
i RDoc
x)     = FilePath -> ShowS
showString (Char
'\n' Char -> ShowS
forall a. a -> [a] -> [a]
: Int -> Char -> FilePath
forall a. Int -> a -> [a]
replicate Int
i Char
' ') ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x

-- | Render and display a document.
prettyS :: Int -> Doc -> ShowS
prettyS :: Int -> Doc -> ShowS
prettyS Int
w Doc
x = RDoc -> ShowS
displayS (Int -> Doc -> RDoc
render Int
w Doc
x)

-- | Render and convert a document to a 'String'.
pretty :: Int -> Doc -> String
pretty :: Int -> Doc -> FilePath
pretty Int
w Doc
x = Int -> Doc -> ShowS
prettyS Int
w Doc
x FilePath
""

-- | Render and display a document compactly.
prettyCompactS :: Doc -> ShowS
prettyCompactS :: Doc -> ShowS
prettyCompactS Doc
x = RDoc -> ShowS
displayS (Doc -> RDoc
renderCompact Doc
x)

-- | Render and convert a document to a 'String' compactly.
prettyCompact :: Doc -> String
prettyCompact :: Doc -> FilePath
prettyCompact Doc
x = Doc -> ShowS
prettyCompactS Doc
x FilePath
""

-- | Display a rendered document with #line pragmas.
displayPragmaS :: RDoc -> ShowS
displayPragmaS :: RDoc -> ShowS
displayPragmaS = RDoc -> ShowS
go
  where
    go :: RDoc -> ShowS
    go :: RDoc -> ShowS
go RDoc
REmpty          = ShowS
forall a. a -> a
id
    go (RChar Char
c RDoc
x)     = Char -> ShowS
showChar Char
c ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x
    go (RString Int
_ FilePath
s RDoc
x) = FilePath -> ShowS
showString FilePath
s ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x
    go (RText Text
s RDoc
x)     = FilePath -> ShowS
showString (Text -> FilePath
T.unpack Text
s) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x
    go (RLazyText Text
s RDoc
x) = FilePath -> ShowS
showString (Text -> FilePath
L.unpack Text
s) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> ShowS
go RDoc
x
    go (RPos Pos
p RDoc
x)      = Pos -> ShowS
showPos Pos
p ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                         Char -> ShowS
showChar Char
'\n' ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                         RDoc -> ShowS
go RDoc
x
    go (RLine Int
i RDoc
x)     = case RDoc
x of
                           RPos Pos
p RDoc
x' -> Char -> ShowS
showChar Char
'\n' ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                                        Pos -> ShowS
showPos Pos
p ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                                        FilePath -> ShowS
showString (Char
'\n' Char -> ShowS
forall a. a -> [a] -> [a]
: Int -> Char -> FilePath
forall a. Int -> a -> [a]
replicate Int
i Char
' ') ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                                        RDoc -> ShowS
go RDoc
x'
                           RDoc
_         -> FilePath -> ShowS
showString (Char
'\n' Char -> ShowS
forall a. a -> [a] -> [a]
: Int -> Char -> FilePath
forall a. Int -> a -> [a]
replicate Int
i Char
' ') ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                                        RDoc -> ShowS
go RDoc
x

    showPos :: Pos -> ShowS
    showPos :: Pos -> ShowS
showPos Pos
p =
        FilePath -> ShowS
showString FilePath
"#line " ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
        Int -> ShowS
forall a. Show a => a -> ShowS
shows (Pos -> Int
posLine Pos
p) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
        Char -> ShowS
showChar Char
' ' ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
        Char -> ShowS
showChar Char
'"' ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
        FilePath -> ShowS
showString (Pos -> FilePath
posFile Pos
p) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
        Char -> ShowS
showChar Char
'"'

-- | Render and display a document with #line pragmas.
prettyPragmaS :: Int -> Doc -> ShowS
prettyPragmaS :: Int -> Doc -> ShowS
prettyPragmaS Int
w Doc
x = RDoc -> ShowS
displayPragmaS (Int -> Doc -> RDoc
render Int
w Doc
x)

-- | Render and convert a document to a 'String' with #line pragmas.
--
-- > > let loc = Loc (Pos "filename" 3 5 7) (Pos "filename" 5 7 9)
-- > > in  putStrLn $ prettyPragma 80 $ srcloc loc <> text "foo" </> text "bar" </> text "baz"
--
-- will be printed as
--
-- @
-- foo
-- #line 3 "filename"
-- bar
-- baz
-- @
prettyPragma :: Int -> Doc -> String
prettyPragma :: Int -> Doc -> FilePath
prettyPragma Int
w Doc
x = Int -> Doc -> ShowS
prettyPragmaS Int
w Doc
x FilePath
""

-- | Display a rendered document as 'L.Text'. Uses a builder.
displayLazyText :: RDoc -> L.Text
displayLazyText :: RDoc -> Text
displayLazyText = Builder -> Text
B.toLazyText (Builder -> Text) -> (RDoc -> Builder) -> RDoc -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> Builder
go
  where
    go :: RDoc -> B.Builder
    go :: RDoc -> Builder
go RDoc
REmpty          = Builder
forall a. Monoid a => a
mempty
    go (RChar Char
c RDoc
x)     = Char -> Builder
B.singleton Char
c Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x
    go (RString Int
_ FilePath
s RDoc
x) = FilePath -> Builder
B.fromString FilePath
s Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x
    go (RText Text
s RDoc
x)     = Text -> Builder
B.fromText Text
s Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x
    go (RLazyText Text
s RDoc
x) = Text -> Builder
B.fromLazyText Text
s Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x
    go (RPos Pos
_ RDoc
x)      = RDoc -> Builder
go RDoc
x
    go (RLine Int
i RDoc
x)     = FilePath -> Builder
B.fromString (Char
'\n'Char -> ShowS
forall a. a -> [a] -> [a]
:Int -> Char -> FilePath
forall a. Int -> a -> [a]
replicate Int
i Char
' ') Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x

-- | Render and display a document as 'L.Text'. Uses a builder.
prettyLazyText :: Int -> Doc -> L.Text
prettyLazyText :: Int -> Doc -> Text
prettyLazyText Int
w Doc
x = RDoc -> Text
displayLazyText (Int -> Doc -> RDoc
render Int
w Doc
x)

-- | Display a rendered document with #line pragmas as 'L.Text'. Uses a builder.
displayPragmaLazyText :: RDoc -> L.Text
displayPragmaLazyText :: RDoc -> Text
displayPragmaLazyText = Builder -> Text
B.toLazyText (Builder -> Text) -> (RDoc -> Builder) -> RDoc -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDoc -> Builder
go
  where
    go :: RDoc -> B.Builder
    go :: RDoc -> Builder
go RDoc
REmpty          = Builder
forall a. Monoid a => a
mempty
    go (RChar Char
c RDoc
x)     = Char -> Builder
B.singleton Char
c Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x
    go (RText Text
s RDoc
x)     = Text -> Builder
B.fromText Text
s Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x
    go (RLazyText Text
s RDoc
x) = Text -> Builder
B.fromLazyText Text
s Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x
    go (RString Int
_ FilePath
s RDoc
x) = FilePath -> Builder
B.fromString FilePath
s Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` RDoc -> Builder
go RDoc
x
    go (RPos Pos
p RDoc
x)      = Pos -> Builder
displayPos Pos
p Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
                         Char -> Builder
B.singleton Char
'\n' Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
                         RDoc -> Builder
go RDoc
x
    go (RLine Int
i RDoc
x)     = case RDoc
x of
                           RPos Pos
p RDoc
x' -> Char -> Builder
B.singleton Char
'\n' Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
                                        Pos -> Builder
displayPos Pos
p Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
                                        FilePath -> Builder
B.fromString (Char
'\n'Char -> ShowS
forall a. a -> [a] -> [a]
:Int -> Char -> FilePath
forall a. Int -> a -> [a]
replicate Int
i Char
' ') Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
                                        RDoc -> Builder
go RDoc
x'
                           RDoc
_         -> FilePath -> Builder
B.fromString (Char
'\n'Char -> ShowS
forall a. a -> [a] -> [a]
:Int -> Char -> FilePath
forall a. Int -> a -> [a]
replicate Int
i Char
' ') Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
                                        RDoc -> Builder
go RDoc
x

    displayPos :: Pos -> B.Builder
    displayPos :: Pos -> Builder
displayPos Pos
p =
        FilePath -> Builder
B.fromString FilePath
"#line " Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
        Pos -> Builder
renderPosLine Pos
p Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
        Char -> Builder
B.singleton Char
' ' Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend`
        Pos -> Builder
renderPosFile Pos
p

    renderPosLine :: Pos -> B.Builder
    renderPosLine :: Pos -> Builder
renderPosLine = RDoc -> Builder
go (RDoc -> Builder) -> (Pos -> RDoc) -> Pos -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> RDoc
renderCompact (Doc -> RDoc) -> (Pos -> Doc) -> Pos -> RDoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc
int (Int -> Doc) -> (Pos -> Int) -> Pos -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pos -> Int
posLine

    renderPosFile :: Pos -> B.Builder
    renderPosFile :: Pos -> Builder
renderPosFile = RDoc -> Builder
go (RDoc -> Builder) -> (Pos -> RDoc) -> Pos -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> RDoc
renderCompact (Doc -> RDoc) -> (Pos -> Doc) -> Pos -> RDoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc -> Doc -> Doc
enclose Doc
dquote Doc
dquote (Doc -> Doc) -> (Pos -> Doc) -> Pos -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Doc
string (FilePath -> Doc) -> (Pos -> FilePath) -> Pos -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pos -> FilePath
posFile

-- | Render and convert a document to 'L.Text' with #line pragmas. Uses a builder.
prettyPragmaLazyText :: Int -> Doc -> L.Text
prettyPragmaLazyText :: Int -> Doc -> Text
prettyPragmaLazyText Int
w Doc
x = RDoc -> Text
displayPragmaLazyText (Int -> Doc -> RDoc
render Int
w Doc
x)

-- | Render a document with a width of 80 and print it to standard output.
putDoc :: Doc -> IO ()
putDoc :: Doc -> IO ()
putDoc = Text -> IO ()
TIO.putStr (Text -> IO ()) -> (Doc -> Text) -> Doc -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc -> Text
prettyLazyText Int
80

-- | Render a document with a width of 80 and print it to standard output,
-- followed by a newline.
putDocLn :: Doc -> IO ()
putDocLn :: Doc -> IO ()
putDocLn = Text -> IO ()
TIO.putStrLn (Text -> IO ()) -> (Doc -> Text) -> Doc -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc -> Text
prettyLazyText Int
80

-- | Render a document with a width of 80 and print it to the specified handle.
hPutDoc :: Handle -> Doc -> IO ()
hPutDoc :: Handle -> Doc -> IO ()
hPutDoc Handle
h = Handle -> Text -> IO ()
TIO.hPutStr Handle
h (Text -> IO ()) -> (Doc -> Text) -> Doc -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc -> Text
prettyLazyText Int
80

-- | Render a document with a width of 80 and print it to the specified handle,
-- followed by a newline.
hPutDocLn :: Handle -> Doc -> IO ()
hPutDocLn :: Handle -> Doc -> IO ()
hPutDocLn Handle
h = Handle -> Text -> IO ()
TIO.hPutStrLn Handle
h (Text -> IO ()) -> (Doc -> Text) -> Doc -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Doc -> Text
prettyLazyText Int
80