{-# OPTIONS_GHC -fno-warn-orphans #-}

-- | A re-export of the prettyprinting library, along with some
-- convenience functions.
module Futhark.Util.Pretty
  ( -- * Rendering to texts
    prettyTuple,
    prettyTupleLines,
    prettyString,
    prettyStringOneLine,
    prettyText,
    prettyTextOneLine,
    docText,
    docTextForHandle,
    docString,

    -- * Rendering to terminal
    putDoc,
    hPutDoc,
    putDocLn,
    hPutDocLn,

    -- * Building blocks
    module Prettyprinter,
    module Prettyprinter.Symbols.Ascii,
    module Prettyprinter.Render.Terminal,
    apply,
    oneLine,
    annot,
    nestedBlock,
    textwrap,
    shorten,
    commastack,
    commasep,
    semistack,
    stack,
    parensIf,
    ppTuple',
    ppTupleLines',

    -- * Operators
    (</>),
  )
where

import Data.Text (Text)
import Data.Text qualified as T
import Numeric.Half
import Prettyprinter
import Prettyprinter.Render.Terminal (AnsiStyle, Color (..), bgColor, bgColorDull, bold, color, colorDull, italicized, underlined)
import Prettyprinter.Render.Terminal qualified
import Prettyprinter.Render.Text qualified
import Prettyprinter.Symbols.Ascii
import System.IO (Handle, hIsTerminalDevice, hPutStrLn, stdout)

-- | Print a doc with styling to the given file; stripping colors if
-- the file does not seem to support such things.
hPutDoc :: Handle -> Doc AnsiStyle -> IO ()
hPutDoc :: Handle -> Doc AnsiStyle -> IO ()
hPutDoc Handle
h Doc AnsiStyle
d = do
  Bool
colours <- Handle -> IO Bool
hIsTerminalDevice Handle
h
  if Bool
colours
    then Handle -> SimpleDocStream AnsiStyle -> IO ()
Prettyprinter.Render.Terminal.renderIO Handle
h (Doc AnsiStyle -> SimpleDocStream AnsiStyle
forall {ann}. Doc ann -> SimpleDocStream ann
layouter Doc AnsiStyle
d)
    else Handle -> Doc AnsiStyle -> IO ()
forall ann. Handle -> Doc ann -> IO ()
Prettyprinter.Render.Text.hPutDoc Handle
h Doc AnsiStyle
d
  where
    layouter :: Doc ann -> SimpleDocStream ann
layouter =
      LayoutOptions -> Doc ann -> SimpleDocStream ann
forall ann. LayoutOptions -> Doc ann -> SimpleDocStream ann
layoutSmart LayoutOptions
defaultLayoutOptions {layoutPageWidth = Unbounded}

-- | Like 'hPutDoc', but with a final newline.
hPutDocLn :: Handle -> Doc AnsiStyle -> IO ()
hPutDocLn :: Handle -> Doc AnsiStyle -> IO ()
hPutDocLn Handle
h Doc AnsiStyle
d = do
  Handle -> Doc AnsiStyle -> IO ()
hPutDoc Handle
h Doc AnsiStyle
d
  Handle -> String -> IO ()
hPutStrLn Handle
h String
""

-- | Like 'hPutDoc', but to stdout.
putDoc :: Doc AnsiStyle -> IO ()
putDoc :: Doc AnsiStyle -> IO ()
putDoc = Handle -> Doc AnsiStyle -> IO ()
hPutDoc Handle
stdout

-- | Like 'putDoc', but with a final newline.
putDocLn :: Doc AnsiStyle -> IO ()
putDocLn :: Doc AnsiStyle -> IO ()
putDocLn Doc AnsiStyle
d = do
  Doc AnsiStyle -> IO ()
putDoc Doc AnsiStyle
d
  String -> IO ()
putStrLn String
""

-- | Produce text suitable for printing on the given handle.  This
-- mostly means stripping any control characters if the handle is not
-- a terminal.
docTextForHandle :: Handle -> Doc AnsiStyle -> IO T.Text
docTextForHandle :: Handle -> Doc AnsiStyle -> IO Text
docTextForHandle Handle
h Doc AnsiStyle
d = do
  Bool
colours <- Handle -> IO Bool
hIsTerminalDevice Handle
h
  let sds :: SimpleDocStream AnsiStyle
sds = LayoutOptions -> Doc AnsiStyle -> SimpleDocStream AnsiStyle
forall ann. LayoutOptions -> Doc ann -> SimpleDocStream ann
layoutSmart LayoutOptions
defaultLayoutOptions Doc AnsiStyle
d
  Text -> IO Text
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> IO Text) -> Text -> IO Text
forall a b. (a -> b) -> a -> b
$
    if Bool
colours
      then SimpleDocStream AnsiStyle -> Text
Prettyprinter.Render.Terminal.renderStrict SimpleDocStream AnsiStyle
sds
      else SimpleDocStream AnsiStyle -> Text
forall ann. SimpleDocStream ann -> Text
Prettyprinter.Render.Text.renderStrict SimpleDocStream AnsiStyle
sds

-- | Prettyprint a value to a 'String', appropriately wrapped.
prettyString :: (Pretty a) => a -> String
prettyString :: forall a. Pretty a => a -> String
prettyString = Text -> String
T.unpack (Text -> String) -> (a -> Text) -> a -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Text
forall a. Pretty a => a -> Text
prettyText

-- | Prettyprint a value to a 'String' on a single line.
prettyStringOneLine :: (Pretty a) => a -> String
prettyStringOneLine :: forall a. Pretty a => a -> String
prettyStringOneLine = Text -> String
T.unpack (Text -> String) -> (a -> Text) -> a -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Text
forall a. Pretty a => a -> Text
prettyTextOneLine

-- | Prettyprint a value to a 'Text', appropriately wrapped.
prettyText :: (Pretty a) => a -> Text
prettyText :: forall a. Pretty a => a -> Text
prettyText = Doc Any -> Text
forall a. Doc a -> Text
docText (Doc Any -> Text) -> (a -> Doc Any) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Doc Any
forall ann. a -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty

-- | Convert a 'Doc' to text.  This ignores any annotations (i.e. it
-- will be non-coloured output).
docText :: Doc a -> T.Text
docText :: forall a. Doc a -> Text
docText = SimpleDocStream a -> Text
forall ann. SimpleDocStream ann -> Text
Prettyprinter.Render.Text.renderStrict (SimpleDocStream a -> Text)
-> (Doc a -> SimpleDocStream a) -> Doc a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc a -> SimpleDocStream a
forall {ann}. Doc ann -> SimpleDocStream ann
layouter
  where
    layouter :: Doc ann -> SimpleDocStream ann
layouter =
      LayoutOptions -> Doc ann -> SimpleDocStream ann
forall ann. LayoutOptions -> Doc ann -> SimpleDocStream ann
layoutSmart LayoutOptions
defaultLayoutOptions {layoutPageWidth = Unbounded}

-- | Convert a 'Doc' to a 'String', through 'docText'. Intended for
-- debugging.
docString :: Doc a -> String
docString :: forall a. Doc a -> String
docString = Text -> String
T.unpack (Text -> String) -> (Doc a -> Text) -> Doc a -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc a -> Text
forall a. Doc a -> Text
docText

-- | Prettyprint a value to a 'Text' on a single line.
prettyTextOneLine :: (Pretty a) => a -> Text
prettyTextOneLine :: forall a. Pretty a => a -> Text
prettyTextOneLine = SimpleDocStream Any -> Text
forall ann. SimpleDocStream ann -> Text
Prettyprinter.Render.Text.renderStrict (SimpleDocStream Any -> Text)
-> (a -> SimpleDocStream Any) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LayoutOptions -> Doc Any -> SimpleDocStream Any
forall ann. LayoutOptions -> Doc ann -> SimpleDocStream ann
layoutSmart LayoutOptions
oneLineLayout (Doc Any -> SimpleDocStream Any)
-> (a -> Doc Any) -> a -> SimpleDocStream Any
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc Any -> Doc Any
forall ann. Doc ann -> Doc ann
group (Doc Any -> Doc Any) -> (a -> Doc Any) -> a -> Doc Any
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Doc Any
forall ann. a -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty
  where
    oneLineLayout :: LayoutOptions
oneLineLayout = LayoutOptions
defaultLayoutOptions {layoutPageWidth = Unbounded}

ppTuple' :: [Doc a] -> Doc a
ppTuple' :: forall a. [Doc a] -> Doc a
ppTuple' [Doc a]
ets = Doc a -> Doc a
forall ann. Doc ann -> Doc ann
braces (Doc a -> Doc a) -> Doc a -> Doc a
forall a b. (a -> b) -> a -> b
$ [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
commasep ([Doc a] -> Doc a) -> [Doc a] -> Doc a
forall a b. (a -> b) -> a -> b
$ (Doc a -> Doc a) -> [Doc a] -> [Doc a]
forall a b. (a -> b) -> [a] -> [b]
map Doc a -> Doc a
forall ann. Doc ann -> Doc ann
align [Doc a]
ets

ppTupleLines' :: [Doc a] -> Doc a
ppTupleLines' :: forall a. [Doc a] -> Doc a
ppTupleLines' [Doc a]
ets = Doc a -> Doc a
forall ann. Doc ann -> Doc ann
braces (Doc a -> Doc a) -> Doc a -> Doc a
forall a b. (a -> b) -> a -> b
$ [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
commastack ([Doc a] -> Doc a) -> [Doc a] -> Doc a
forall a b. (a -> b) -> a -> b
$ (Doc a -> Doc a) -> [Doc a] -> [Doc a]
forall a b. (a -> b) -> [a] -> [b]
map Doc a -> Doc a
forall ann. Doc ann -> Doc ann
align [Doc a]
ets

-- | Prettyprint a list enclosed in curly braces.
prettyTuple :: (Pretty a) => [a] -> Text
prettyTuple :: forall a. Pretty a => [a] -> Text
prettyTuple = Doc Any -> Text
forall a. Doc a -> Text
docText (Doc Any -> Text) -> ([a] -> Doc Any) -> [a] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc Any] -> Doc Any
forall a. [Doc a] -> Doc a
ppTuple' ([Doc Any] -> Doc Any) -> ([a] -> [Doc Any]) -> [a] -> Doc Any
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Doc Any) -> [a] -> [Doc Any]
forall a b. (a -> b) -> [a] -> [b]
map a -> Doc Any
forall ann. a -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty

-- | Like 'prettyTuple', but put a linebreak after every element.
prettyTupleLines :: (Pretty a) => [a] -> Text
prettyTupleLines :: forall a. Pretty a => [a] -> Text
prettyTupleLines = Doc Any -> Text
forall a. Doc a -> Text
docText (Doc Any -> Text) -> ([a] -> Doc Any) -> [a] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc Any] -> Doc Any
forall a. [Doc a] -> Doc a
ppTupleLines' ([Doc Any] -> Doc Any) -> ([a] -> [Doc Any]) -> [a] -> Doc Any
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Doc Any) -> [a] -> [Doc Any]
forall a b. (a -> b) -> [a] -> [b]
map a -> Doc Any
forall ann. a -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty

-- | The document @'apply' ds@ separates @ds@ with commas and encloses them with
-- parentheses.
apply :: [Doc a] -> Doc a
apply :: forall a. [Doc a] -> Doc a
apply = Doc a -> Doc a
forall ann. Doc ann -> Doc ann
parens (Doc a -> Doc a) -> ([Doc a] -> Doc a) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc a -> Doc a
forall ann. Doc ann -> Doc ann
align (Doc a -> Doc a) -> ([Doc a] -> Doc a) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
commasep ([Doc a] -> Doc a) -> ([Doc a] -> [Doc a]) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Doc a -> Doc a) -> [Doc a] -> [Doc a]
forall a b. (a -> b) -> [a] -> [b]
map Doc a -> Doc a
forall ann. Doc ann -> Doc ann
align

-- | Make sure that the given document is printed on just a single line.
oneLine :: Doc a -> Doc a
oneLine :: forall ann. Doc ann -> Doc ann
oneLine = Doc a -> Doc a
forall ann. Doc ann -> Doc ann
group

-- | Splits the string into words and permits line breaks between all
-- of them.
textwrap :: T.Text -> Doc a
textwrap :: forall a. Text -> Doc a
textwrap = [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
fillSep ([Doc a] -> Doc a) -> (Text -> [Doc a]) -> Text -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Doc a) -> [Text] -> [Doc a]
forall a b. (a -> b) -> [a] -> [b]
map Text -> Doc a
forall a. Text -> Doc a
forall a ann. Pretty a => a -> Doc ann
pretty ([Text] -> [Doc a]) -> (Text -> [Text]) -> Text -> [Doc a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.words

-- | Stack and prepend a list of 'Doc's to another 'Doc', separated by
-- a linebreak.  If the list is empty, the second 'Doc' will be
-- returned without a preceding linebreak.
annot :: [Doc a] -> Doc a -> Doc a
annot :: forall a. [Doc a] -> Doc a -> Doc a
annot [] Doc a
s = Doc a
s
annot [Doc a]
l Doc a
s = [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
vsep ([Doc a]
l [Doc a] -> [Doc a] -> [Doc a]
forall a. [a] -> [a] -> [a]
++ [Doc a
s])

-- | Surround the given document with enclosers and add linebreaks and
-- indents.
nestedBlock :: Doc a -> Doc a -> Doc a -> Doc a
nestedBlock :: forall a. Doc a -> Doc a -> Doc a -> Doc a
nestedBlock Doc a
pre Doc a
post Doc a
body = [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
vsep [Doc a
pre, Int -> Doc a -> Doc a
forall ann. Int -> Doc ann -> Doc ann
indent Int
2 Doc a
body, Doc a
post]

-- | Prettyprint on a single line up to at most some appropriate
-- number of characters, with trailing ... if necessary.  Used for
-- error messages.
shorten :: Doc a -> Doc b
shorten :: forall a b. Doc a -> Doc b
shorten Doc a
a
  | Text -> Int
T.length Text
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
70 = Text -> Doc b
forall a. Text -> Doc a
forall a ann. Pretty a => a -> Doc ann
pretty (Int -> Text -> Text
T.take Int
70 Text
s) Doc b -> Doc b -> Doc b
forall a. Semigroup a => a -> a -> a
<> Doc b
"..."
  | Bool
otherwise = Text -> Doc b
forall a. Text -> Doc a
forall a ann. Pretty a => a -> Doc ann
pretty Text
s
  where
    s :: Text
s = SimpleDocStream Any -> Text
forall ann. SimpleDocStream ann -> Text
Prettyprinter.Render.Text.renderStrict (SimpleDocStream Any -> Text) -> SimpleDocStream Any -> Text
forall a b. (a -> b) -> a -> b
$ Doc a -> SimpleDocStream Any
forall ann1 ann2. Doc ann1 -> SimpleDocStream ann2
layoutCompact Doc a
a

-- | Like 'commasep', but a newline after every comma.
commastack :: [Doc a] -> Doc a
commastack :: forall a. [Doc a] -> Doc a
commastack = Doc a -> Doc a
forall ann. Doc ann -> Doc ann
align (Doc a -> Doc a) -> ([Doc a] -> Doc a) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
vsep ([Doc a] -> Doc a) -> ([Doc a] -> [Doc a]) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc a -> [Doc a] -> [Doc a]
forall ann. Doc ann -> [Doc ann] -> [Doc ann]
punctuate Doc a
forall ann. Doc ann
comma

-- | Separate with semicolons and newlines.
semistack :: [Doc a] -> Doc a
semistack :: forall a. [Doc a] -> Doc a
semistack = Doc a -> Doc a
forall ann. Doc ann -> Doc ann
align (Doc a -> Doc a) -> ([Doc a] -> Doc a) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
vsep ([Doc a] -> Doc a) -> ([Doc a] -> [Doc a]) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc a -> [Doc a] -> [Doc a]
forall ann. Doc ann -> [Doc ann] -> [Doc ann]
punctuate Doc a
forall ann. Doc ann
semi

-- | Separate with commas.
commasep :: [Doc a] -> Doc a
commasep :: forall a. [Doc a] -> Doc a
commasep = [Doc a] -> Doc a
forall a. [Doc a] -> Doc a
hsep ([Doc a] -> Doc a) -> ([Doc a] -> [Doc a]) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc a -> [Doc a] -> [Doc a]
forall ann. Doc ann -> [Doc ann] -> [Doc ann]
punctuate Doc a
forall ann. Doc ann
comma

-- | Separate with linebreaks.
stack :: [Doc a] -> Doc a
stack :: forall a. [Doc a] -> Doc a
stack = Doc a -> Doc a
forall ann. Doc ann -> Doc ann
align (Doc a -> Doc a) -> ([Doc a] -> Doc a) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc a] -> Doc a
forall a. Monoid a => [a] -> a
mconcat ([Doc a] -> Doc a) -> ([Doc a] -> [Doc a]) -> [Doc a] -> Doc a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc a -> [Doc a] -> [Doc a]
forall ann. Doc ann -> [Doc ann] -> [Doc ann]
punctuate Doc a
forall ann. Doc ann
line

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

instance Pretty Half where
  pretty :: forall ann. Half -> Doc ann
pretty = Half -> Doc ann
forall a ann. Show a => a -> Doc ann
viaShow

(</>) :: Doc a -> Doc a -> Doc a
Doc a
a </> :: forall a. Doc a -> Doc a -> Doc a
</> Doc a
b = Doc a
a Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
forall ann. Doc ann
line Doc a -> Doc a -> Doc a
forall a. Semigroup a => a -> a -> a
<> Doc a
b