{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}


module Fmt.Internal.Formatters where


-- Generic useful things
import Data.List (intersperse)
import Lens.Micro
#if __GLASGOW_HASKELL__ < 804
import Data.Monoid ((<>))
#endif
-- Text
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import Data.Text (Text)
-- 'Buildable' and text-format stuff
import Formatting.Buildable
import qualified Formatting.Internal.Raw as F
-- Text 'Builder'
import Data.Text.Lazy.Builder hiding (fromString)
-- 'Foldable' and 'IsList' for list/map formatters
import Data.Foldable (toList)
import GHC.Exts (IsList, Item)
import qualified GHC.Exts as IsList (toList)

import Fmt.Internal.Core


----------------------------------------------------------------------------
-- Doctest setup
----------------------------------------------------------------------------

-- $setup
-- >>> import Fmt

----------------------------------------------------------------------------
-- Text formatters
----------------------------------------------------------------------------

{- |
Indent a block of text.

>>> fmt $ "This is a list:\n" <> indentF 4 (blockListF [1,2,3])
This is a list:
    - 1
    - 2
    - 3

The output will always end with a newline, even when the input doesn't.
-}
indentF :: Int -> Builder -> Builder
indentF :: Int -> Builder -> Builder
indentF Int
n Builder
a = case Text -> [Text]
TL.lines (Builder -> Text
toLazyText Builder
a) of
    [] -> Text -> Builder
fromLazyText (Text
spaces forall a. Semigroup a => a -> a -> a
<> Text
"\n")
    [Text]
xs -> Text -> Builder
fromLazyText forall a b. (a -> b) -> a -> b
$ [Text] -> Text
TL.unlines (forall a b. (a -> b) -> [a] -> [b]
map (Text
spaces forall a. Semigroup a => a -> a -> a
<>) [Text]
xs)
  where
    spaces :: Text
spaces = Int64 -> Text -> Text
TL.replicate (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n) (Char -> Text
TL.singleton Char
' ')

{- | Add a prefix to the first line, and indent all lines but the first one.

The output will always end with a newline, even when the input doesn't.
-}
indentF' :: Int -> T.Text -> Builder -> Builder
indentF' :: Int -> Text -> Builder -> Builder
indentF' Int
n Text
pref Builder
a = case Text -> [Text]
TL.lines (Builder -> Text
toLazyText Builder
a) of
  []     -> Text -> Builder
fromText Text
pref forall a. Semigroup a => a -> a -> a
<> Builder
"\n"
  (Text
x:[Text]
xs) -> Text -> Builder
fromLazyText forall a b. (a -> b) -> a -> b
$
            [Text] -> Text
TL.unlines forall a b. (a -> b) -> a -> b
$ (Text -> Text
TL.fromStrict Text
pref forall a. Semigroup a => a -> a -> a
<> Text
x) forall a. a -> [a] -> [a]
: forall a b. (a -> b) -> [a] -> [b]
map (Text
spaces forall a. Semigroup a => a -> a -> a
<>) [Text]
xs
  where
    spaces :: Text
spaces = Int64 -> Text -> Text
TL.replicate (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n) (Char -> Text
TL.singleton Char
' ')

{- | Attach a name to anything:

>>> fmt $ nameF "clients" $ blockListF ["Alice", "Bob", "Zalgo"]
clients:
  - Alice
  - Bob
  - Zalgo
-}
nameF :: Builder -> Builder -> Builder
nameF :: Builder -> Builder -> Builder
nameF Builder
k Builder
v = case Text -> [Text]
TL.lines (Builder -> Text
toLazyText Builder
v) of
    []  -> Builder
k forall a. Semigroup a => a -> a -> a
<> Builder
":\n"
    [Text
l] -> Builder
k forall a. Semigroup a => a -> a -> a
<> Builder
": " forall a. Semigroup a => a -> a -> a
<> Text -> Builder
fromLazyText Text
l forall a. Semigroup a => a -> a -> a
<> Builder
"\n"
    [Text]
ls  -> Builder
k forall a. Semigroup a => a -> a -> a
<> Builder
":\n" forall a. Semigroup a => a -> a -> a
<>
           forall a. Monoid a => [a] -> a
mconcat [Builder
"  " forall a. Semigroup a => a -> a -> a
<> Text -> Builder
fromLazyText Text
s forall a. Semigroup a => a -> a -> a
<> Builder
"\n" | Text
s <- [Text]
ls]

{- | Put spaces between elements.

>>> fmt $ unwordsF ["hello", "world"]
hello world

Of course, it works on anything 'Buildable':

>>> fmt $ unwordsF [1, 2]
1 2
-}
unwordsF :: (Foldable f, Buildable a) => f a -> Builder
unwordsF :: forall (f :: * -> *) a. (Foldable f, Buildable a) => f a -> Builder
unwordsF = forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> [a] -> [a]
intersperse Builder
" " forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall p. Buildable p => p -> Builder
build forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> [a]
toList

{-# SPECIALIZE unwordsF :: Buildable a => [a] -> Builder #-}

{- | Arrange elements on separate lines.

>>> fmt $ unlinesF ["hello", "world"]
hello
world
-}
unlinesF :: (Foldable f, Buildable a) => f a -> Builder
unlinesF :: forall (f :: * -> *) a. (Foldable f, Buildable a) => f a -> Builder
unlinesF = forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (Builder -> Builder
nl forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall p. Buildable p => p -> Builder
build) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> [a]
toList
  where
    nl :: Builder -> Builder
nl Builder
x | Text
"\n" Text -> Text -> Bool
`TL.isSuffixOf` Builder -> Text
toLazyText Builder
x = Builder
x
         | Bool
otherwise = Builder
x forall a. Semigroup a => a -> a -> a
<> Builder
"\n"

{-# SPECIALIZE unlinesF :: Buildable a => [a] -> Builder #-}

----------------------------------------------------------------------------
-- List formatters
----------------------------------------------------------------------------

{- | A simple comma-separated list formatter.

>>> listF ["hello", "world"]
"[hello, world]"

For multiline output, use 'jsonListF'.
-}
listF :: (Foldable f, Buildable a) => f a -> Builder
listF :: forall (f :: * -> *) a. (Foldable f, Buildable a) => f a -> Builder
listF = forall (f :: * -> *) a.
Foldable f =>
(a -> Builder) -> f a -> Builder
listF' forall p. Buildable p => p -> Builder
build
{-# INLINE listF #-}

{- | A version of 'listF' that lets you supply your own building function for
list elements.

For instance, to format a list of numbers as hex:

>>> listF' hexF [1234, 5678]
"[4d2, 162e]"
-}
listF' :: (Foldable f) => (a -> Builder) -> f a -> Builder
listF' :: forall (f :: * -> *) a.
Foldable f =>
(a -> Builder) -> f a -> Builder
listF' a -> Builder
fbuild f a
xs = forall a. Monoid a => [a] -> a
mconcat forall a b. (a -> b) -> a -> b
$
  Builder
"[" forall a. a -> [a] -> [a]
:
  forall a. a -> [a] -> [a]
intersperse Builder
", " (forall a b. (a -> b) -> [a] -> [b]
map a -> Builder
fbuild (forall (t :: * -> *) a. Foldable t => t a -> [a]
toList f a
xs)) forall a. [a] -> [a] -> [a]
++
  [Builder
"]"]

{-# SPECIALIZE listF' :: (a -> Builder) -> [a] -> Builder #-}

{- Note [Builder appending]
~~~~~~~~~~~~~~~~~~~~~~~~~~~

The documentation for 'Builder' says that it's preferrable to associate
'Builder' appends to the right (i.e. @a <> (b <> c)@). The maximum possible
association-to-the-right is achieved when we avoid appending builders until
the last second (i.e. in the latter scenario):

    -- (a1 <> x) <> (a2 <> x) <> ...
    mconcat [a <> x | a <- as]

    -- a1 <> x <> a2 <> x <> ...
    mconcat $ concat [[a, x] | a <- as]

However, benchmarks have shown that the former way is actually faster.
-}

{- | A multiline formatter for lists.

>>> fmt $ blockListF [1,2,3]
- 1
- 2
- 3

Multi-line elements are indented correctly:

>>> fmt $ blockListF ["hello\nworld", "foo\nbar\nquix"]
- hello
  world
- foo
  bar
  quix

-}
blockListF :: forall f a. (Foldable f, Buildable a) => f a -> Builder
blockListF :: forall (f :: * -> *) a. (Foldable f, Buildable a) => f a -> Builder
blockListF = forall (f :: * -> *) a.
Foldable f =>
Text -> (a -> Builder) -> f a -> Builder
blockListF' Text
"-" forall p. Buildable p => p -> Builder
build
{-# INLINE blockListF #-}

{- | A version of 'blockListF' that lets you supply your own building function
for list elements (instead of 'build') and choose the bullet character
(instead of @"-"@).
-}
blockListF'
  :: forall f a. Foldable f
  => Text                       -- ^ Bullet
  -> (a -> Builder)             -- ^ Builder for elements
  -> f a                        -- ^ Structure with elements
  -> Builder
blockListF' :: forall (f :: * -> *) a.
Foldable f =>
Text -> (a -> Builder) -> f a -> Builder
blockListF' Text
bullet a -> Builder
fbuild f a
xs = if forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Builder]
items then Builder
"[]\n" else forall a. Monoid a => [a] -> a
mconcat [Builder]
items
  where
    items :: [Builder]
items = forall a b. (a -> b) -> [a] -> [b]
map a -> Builder
buildItem (forall (t :: * -> *) a. Foldable t => t a -> [a]
toList f a
xs)
    spaces :: Builder
spaces = forall a. Monoid a => [a] -> a
mconcat forall a b. (a -> b) -> a -> b
$ forall a. Int -> a -> [a]
replicate (Text -> Int
T.length Text
bullet forall a. Num a => a -> a -> a
+ Int
1) (Char -> Builder
singleton Char
' ')
    buildItem :: a -> Builder
buildItem a
x = case Text -> [Text]
TL.lines (Builder -> Text
toLazyText (a -> Builder
fbuild a
x)) of
      []     -> Text
bullet forall a b. (Buildable a, FromBuilder b) => a -> Builder -> b
|+ Builder
"\n"
      (Text
l:[Text]
ls) -> Text
bullet forall a b. (Buildable a, FromBuilder b) => a -> Builder -> b
|+ Builder
" " forall b. FromBuilder b => Builder -> Builder -> b
+| Text
l forall a b. (Buildable a, FromBuilder b) => a -> Builder -> b
|+ Builder
"\n" forall a. Semigroup a => a -> a -> a
<>
                forall a. Monoid a => [a] -> a
mconcat [Builder
spaces forall a. Semigroup a => a -> a -> a
<> Text -> Builder
fromLazyText Text
s forall a. Semigroup a => a -> a -> a
<> Builder
"\n" | Text
s <- [Text]
ls]

{-# SPECIALIZE blockListF' :: Text -> (a -> Builder) -> [a] -> Builder #-}

{- | A JSON-style formatter for lists.

>>> fmt $ jsonListF [1,2,3]
[
  1
, 2
, 3
]

Like 'blockListF', it handles multiline elements well:

>>> fmt $ jsonListF ["hello\nworld", "foo\nbar\nquix"]
[
  hello
  world
, foo
  bar
  quix
]
-}
jsonListF :: forall f a. (Foldable f, Buildable a) => f a -> Builder
jsonListF :: forall (f :: * -> *) a. (Foldable f, Buildable a) => f a -> Builder
jsonListF = forall (f :: * -> *) a.
Foldable f =>
(a -> Builder) -> f a -> Builder
jsonListF' forall p. Buildable p => p -> Builder
build
{-# INLINE jsonListF #-}

{- | A version of 'jsonListF' that lets you supply your own building function
for list elements.
-}
jsonListF' :: forall f a. (Foldable f) => (a -> Builder) -> f a -> Builder
jsonListF' :: forall (f :: * -> *) a.
Foldable f =>
(a -> Builder) -> f a -> Builder
jsonListF' a -> Builder
fbuild f a
xs
  | forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Builder]
items = Builder
"[]\n"
  | Bool
otherwise  = Builder
"[\n" forall a. Semigroup a => a -> a -> a
<> forall a. Monoid a => [a] -> a
mconcat [Builder]
items forall a. Semigroup a => a -> a -> a
<> Builder
"]\n"
  where
    items :: [Builder]
items = forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Bool -> a -> Builder
buildItem (Bool
True forall a. a -> [a] -> [a]
: forall a. a -> [a]
repeat Bool
False) (forall (t :: * -> *) a. Foldable t => t a -> [a]
toList f a
xs)
    -- Item builder
    buildItem :: Bool -> a -> Builder
    buildItem :: Bool -> a -> Builder
buildItem Bool
isFirst a
x =
      case forall a b. (a -> b) -> [a] -> [b]
map Text -> Builder
fromLazyText (Text -> [Text]
TL.lines (Builder -> Text
toLazyText (a -> Builder
fbuild a
x))) of
        [] | Bool
isFirst   -> Builder
"\n"
           | Bool
otherwise -> Builder
",\n"
        [Builder]
ls ->
            forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (forall a. Semigroup a => a -> a -> a
<> Builder
"\n") forall a b. (a -> b) -> a -> b
$
              [Builder]
ls forall a b. a -> (a -> b) -> b
& forall s a. Cons s s a a => Traversal' s a
_head forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ (if Bool
isFirst then (Builder
"  " forall a. Semigroup a => a -> a -> a
<>) else (Builder
", " forall a. Semigroup a => a -> a -> a
<>))
                 forall a b. a -> (a -> b) -> b
& forall s a. Cons s s a a => Traversal' s s
_tailforall b c a. (b -> c) -> (a -> b) -> a -> c
.forall s t a b. Each s t a b => Traversal s t a b
each forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ (Builder
"  " forall a. Semigroup a => a -> a -> a
<>)

{-# SPECIALIZE jsonListF' :: (a -> Builder) -> [a] -> Builder #-}

----------------------------------------------------------------------------
-- Map formatters
----------------------------------------------------------------------------

{- | A simple JSON-like map formatter; works for Map, HashMap, etc, as well as
ordinary lists of pairs.

>>> mapF [("a", 1), ("b", 4)]
"{a: 1, b: 4}"

For multiline output, use 'jsonMapF'.
-}
mapF :: (IsList t, Item t ~ (k, v), Buildable k, Buildable v) => t -> Builder
mapF :: forall t k v.
(IsList t, Item t ~ (k, v), Buildable k, Buildable v) =>
t -> Builder
mapF = forall t k v.
(IsList t, Item t ~ (k, v)) =>
(k -> Builder) -> (v -> Builder) -> t -> Builder
mapF' forall p. Buildable p => p -> Builder
build forall p. Buildable p => p -> Builder
build
{-# INLINE mapF #-}

{- | A version of 'mapF' that lets you supply your own building function for
keys and values.
-}
mapF'
  :: (IsList t, Item t ~ (k, v))
  => (k -> Builder) -> (v -> Builder) -> t -> Builder
mapF' :: forall t k v.
(IsList t, Item t ~ (k, v)) =>
(k -> Builder) -> (v -> Builder) -> t -> Builder
mapF' k -> Builder
fbuild_k v -> Builder
fbuild_v t
xs =
  Builder
"{" forall a. Semigroup a => a -> a -> a
<> forall a. Monoid a => [a] -> a
mconcat (forall a. a -> [a] -> [a]
intersperse Builder
", " (forall a b. (a -> b) -> [a] -> [b]
map (k, v) -> Builder
buildPair (forall l. IsList l => l -> [Item l]
IsList.toList t
xs))) forall a. Semigroup a => a -> a -> a
<> Builder
"}"
  where
    buildPair :: (k, v) -> Builder
buildPair (k
k, v
v) = k -> Builder
fbuild_k k
k forall a. Semigroup a => a -> a -> a
<> Builder
": " forall a. Semigroup a => a -> a -> a
<> v -> Builder
fbuild_v v
v

{- | A YAML-like map formatter:

>>> fmt $ blockMapF [("Odds", blockListF [1,3]), ("Evens", blockListF [2,4])]
Odds:
  - 1
  - 3
Evens:
  - 2
  - 4
-}
blockMapF :: (IsList t, Item t ~ (k, v), Buildable k, Buildable v) => t -> Builder
blockMapF :: forall t k v.
(IsList t, Item t ~ (k, v), Buildable k, Buildable v) =>
t -> Builder
blockMapF = forall t k v.
(IsList t, Item t ~ (k, v)) =>
(k -> Builder) -> (v -> Builder) -> t -> Builder
blockMapF' forall p. Buildable p => p -> Builder
build forall p. Buildable p => p -> Builder
build
{-# INLINE blockMapF #-}

{- | A version of 'blockMapF' that lets you supply your own building function
for keys and values.
-}
blockMapF'
  :: (IsList t, Item t ~ (k, v))
  => (k -> Builder) -> (v -> Builder) -> t -> Builder
blockMapF' :: forall t k v.
(IsList t, Item t ~ (k, v)) =>
(k -> Builder) -> (v -> Builder) -> t -> Builder
blockMapF' k -> Builder
fbuild_k v -> Builder
fbuild_v t
xs
  | forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Builder]
items = Builder
"{}\n"
  | Bool
otherwise  = forall a. Monoid a => [a] -> a
mconcat [Builder]
items
  where
    items :: [Builder]
items = forall a b. (a -> b) -> [a] -> [b]
map (\(k
k, v
v) -> Builder -> Builder -> Builder
nameF (k -> Builder
fbuild_k k
k) (v -> Builder
fbuild_v v
v)) (forall l. IsList l => l -> [Item l]
IsList.toList t
xs)

{- | A JSON-like map formatter (unlike 'mapF', always multiline):

>>> fmt $ jsonMapF [("Odds", jsonListF [1,3]), ("Evens", jsonListF [2,4])]
{
  Odds:
    [
      1
    , 3
    ]
, Evens:
    [
      2
    , 4
    ]
}
-}
jsonMapF :: (IsList t, Item t ~ (k, v), Buildable k, Buildable v) => t -> Builder
jsonMapF :: forall t k v.
(IsList t, Item t ~ (k, v), Buildable k, Buildable v) =>
t -> Builder
jsonMapF = forall t k v.
(IsList t, Item t ~ (k, v)) =>
(k -> Builder) -> (v -> Builder) -> t -> Builder
jsonMapF' forall p. Buildable p => p -> Builder
build forall p. Buildable p => p -> Builder
build
{-# INLINE jsonMapF #-}

{- | A version of 'jsonMapF' that lets you supply your own building function
for keys and values.
-}
jsonMapF'
  :: forall t k v.
     (IsList t, Item t ~ (k, v))
  => (k -> Builder) -> (v -> Builder) -> t -> Builder
jsonMapF' :: forall t k v.
(IsList t, Item t ~ (k, v)) =>
(k -> Builder) -> (v -> Builder) -> t -> Builder
jsonMapF' k -> Builder
fbuild_k v -> Builder
fbuild_v t
xs
  | forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Builder]
items = Builder
"{}\n"
  | Bool
otherwise  = Builder
"{\n" forall a. Semigroup a => a -> a -> a
<> forall a. Monoid a => [a] -> a
mconcat [Builder]
items forall a. Semigroup a => a -> a -> a
<> Builder
"}\n"
  where
    items :: [Builder]
items = forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Bool -> (k, v) -> Builder
buildItem (Bool
True forall a. a -> [a] -> [a]
: forall a. a -> [a]
repeat Bool
False) (forall l. IsList l => l -> [Item l]
IsList.toList t
xs)
    -- Item builder
    buildItem :: Bool -> (k, v) -> Builder
    buildItem :: Bool -> (k, v) -> Builder
buildItem Bool
isFirst (k
k, v
v) = do
      let kb :: Builder
kb = (if Bool
isFirst then Builder
"  " else Builder
", ") forall a. Semigroup a => a -> a -> a
<> k -> Builder
fbuild_k k
k
      case forall a b. (a -> b) -> [a] -> [b]
map Text -> Builder
fromLazyText (Text -> [Text]
TL.lines (Builder -> Text
toLazyText (v -> Builder
fbuild_v v
v))) of
        []  -> Builder
kb forall a. Semigroup a => a -> a -> a
<> Builder
":\n"
        [Builder
l] -> Builder
kb forall a. Semigroup a => a -> a -> a
<> Builder
": " forall a. Semigroup a => a -> a -> a
<> Builder
l forall a. Semigroup a => a -> a -> a
<> Builder
"\n"
        [Builder]
ls  -> Builder
kb forall a. Semigroup a => a -> a -> a
<> Builder
":\n" forall a. Semigroup a => a -> a -> a
<>
               forall a. Monoid a => [a] -> a
mconcat [Builder
"    " forall a. Semigroup a => a -> a -> a
<> Builder
s forall a. Semigroup a => a -> a -> a
<> Builder
"\n" | Builder
s <- [Builder]
ls]

----------------------------------------------------------------------------
-- ADT formatters
----------------------------------------------------------------------------

{- | Like 'build' for 'Maybe', but displays 'Nothing' as @\<Nothing\>@ instead
of an empty string.

'build':

>>> build (Nothing :: Maybe Int)
""
>>> build (Just 1 :: Maybe Int)
"1"

'maybeF':

>>> maybeF (Nothing :: Maybe Int)
"<Nothing>"
>>> maybeF (Just 1 :: Maybe Int)
"1"
-}
maybeF :: Buildable a => Maybe a -> Builder
maybeF :: forall a. Buildable a => Maybe a -> Builder
maybeF = forall b a. b -> (a -> b) -> Maybe a -> b
maybe Builder
"<Nothing>" forall p. Buildable p => p -> Builder
build

{- |
Format an 'Either':

>>> eitherF (Right 1 :: Either Bool Int)
"<Right: 1>"
-}
eitherF :: (Buildable a, Buildable b) => Either a b -> Builder
eitherF :: forall a b. (Buildable a, Buildable b) => Either a b -> Builder
eitherF = forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (\a
x -> Builder
"<Left: " forall a. Semigroup a => a -> a -> a
<> forall p. Buildable p => p -> Builder
build a
x forall a. Semigroup a => a -> a -> a
<> Builder
">")
                 (\b
x -> Builder
"<Right: " forall a. Semigroup a => a -> a -> a
<> forall p. Buildable p => p -> Builder
build b
x forall a. Semigroup a => a -> a -> a
<> Builder
">")

----------------------------------------------------------------------------
-- Other formatters
----------------------------------------------------------------------------

{- |
Take the first N characters:

>>> prefixF 3 "hello"
"hel"
-}
prefixF :: Buildable a => Int -> a -> Builder
prefixF :: forall a. Buildable a => Int -> a -> Builder
prefixF Int
size =
  Text -> Builder
fromLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Text -> Text
TL.take (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall p. Buildable p => p -> Builder
build

{- |
Take the last N characters:

>>> suffixF 3 "hello"
"llo"
-}
suffixF :: Buildable a => Int -> a -> Builder
suffixF :: forall a. Buildable a => Int -> a -> Builder
suffixF Int
size =
  Text -> Builder
fromLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  (\Text
t -> Int64 -> Text -> Text
TL.drop (Text -> Int64
TL.length Text
t forall a. Num a => a -> a -> a
- forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size) Text
t) forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  Builder -> Text
toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall p. Buildable p => p -> Builder
build

{- |
@padLeftF n c@ pads the string with character @c@ from the left side until it
becomes @n@ characters wide (and does nothing if the string is already that
long, or longer):

>>> padLeftF 5 '0' 12
"00012"
>>> padLeftF 5 '0' 123456
"123456"
-}
padLeftF :: Buildable a => Int -> Char -> a -> Builder
padLeftF :: forall a. Buildable a => Int -> Char -> a -> Builder
padLeftF = forall a. Buildable a => Int -> Char -> a -> Builder
F.left

{- |
@padRightF n c@ pads the string with character @c@ from the right side until
it becomes @n@ characters wide (and does nothing if the string is already
that long, or longer):

>>> padRightF 5 ' ' "foo"
"foo  "
>>> padRightF 5 ' ' "foobar"
"foobar"
-}
padRightF :: Buildable a => Int -> Char -> a -> Builder
padRightF :: forall a. Buildable a => Int -> Char -> a -> Builder
padRightF = forall a. Buildable a => Int -> Char -> a -> Builder
F.right

{- |
@padBothF n c@ pads the string with character @c@ from both sides until
it becomes @n@ characters wide (and does nothing if the string is already
that long, or longer):

>>> padBothF 5 '=' "foo"
"=foo="
>>> padBothF 5 '=' "foobar"
"foobar"

When padding can't be distributed equally, the left side is preferred:

>>> padBothF 8 '=' "foo"
"===foo=="
-}
padBothF :: Buildable a => Int -> Char -> a -> Builder
padBothF :: forall a. Buildable a => Int -> Char -> a -> Builder
padBothF Int
i Char
c =
  Text -> Builder
fromLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Char -> Text -> Text
TL.center (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
i) Char
c forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall p. Buildable p => p -> Builder
build

----------------------------------------------------------------------------
-- Conditional formatters
----------------------------------------------------------------------------

{- | Display something only if the condition is 'True' (empty string
otherwise).

Note that it can only take a 'Builder' (because otherwise it would be
unusable with ('+|')-formatted strings which can resolve to any
'FromBuilder'). You can use 'build' to convert any value to a 'Builder'.
-}
whenF :: Bool -> Builder -> Builder
whenF :: Bool -> Builder -> Builder
whenF Bool
True  Builder
x = Builder
x
whenF Bool
False Builder
_ = forall a. Monoid a => a
mempty
{-# INLINE whenF #-}

{- | Display something only if the condition is 'False' (empty string
otherwise).
-}
unlessF :: Bool -> Builder -> Builder
unlessF :: Bool -> Builder -> Builder
unlessF Bool
False Builder
x = Builder
x
unlessF Bool
True  Builder
_ = forall a. Monoid a => a
mempty
{-# INLINE unlessF #-}