{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RecordWildCards #-}
module Patat.PrettyPrint
( Doc
, toString
, dimensions
, null
, hPutDoc
, putDoc
, string
, text
, space
, spaces
, softline
, hardline
, wrapAt
, Trimmable (..)
, indent
, ansi
, (<+>)
, (<$$>)
, vcat
, intersperse
, Alignment (..)
, align
, paste
, removeControls
, clearScreen
, goToLine
) where
import Control.Monad.Reader (asks, local)
import Control.Monad.RWS (RWS, runRWS)
import Control.Monad.State (get, gets, modify)
import Control.Monad.Writer (tell)
import Data.Char.WCWidth.Extended (wcstrwidth)
import qualified Data.List as L
import Data.String (IsString (..))
import qualified Data.Text as T
import Prelude hiding (null)
import qualified System.Console.ANSI as Ansi
import qualified System.IO as IO
data Control
= ClearScreenControl
| GoToLineControl Int
deriving (Control -> Control -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Control -> Control -> Bool
$c/= :: Control -> Control -> Bool
== :: Control -> Control -> Bool
$c== :: Control -> Control -> Bool
Eq)
data Chunk
= StringChunk [Ansi.SGR] String
| NewlineChunk
| ControlChunk Control
deriving (Chunk -> Chunk -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Chunk -> Chunk -> Bool
$c/= :: Chunk -> Chunk -> Bool
== :: Chunk -> Chunk -> Bool
$c== :: Chunk -> Chunk -> Bool
Eq)
type Chunks = [Chunk]
hPutChunk :: IO.Handle -> Chunk -> IO ()
hPutChunk :: Handle -> Chunk -> IO ()
hPutChunk Handle
h Chunk
NewlineChunk = Handle -> String -> IO ()
IO.hPutStrLn Handle
h String
""
hPutChunk Handle
h (StringChunk [SGR]
codes String
str) = do
Handle -> [SGR] -> IO ()
Ansi.hSetSGR Handle
h (forall a. [a] -> [a]
reverse [SGR]
codes)
Handle -> String -> IO ()
IO.hPutStr Handle
h String
str
Handle -> [SGR] -> IO ()
Ansi.hSetSGR Handle
h [SGR
Ansi.Reset]
hPutChunk Handle
h (ControlChunk Control
ctrl) = case Control
ctrl of
Control
ClearScreenControl -> Handle -> IO ()
Ansi.hClearScreen Handle
h
GoToLineControl Int
l -> Handle -> Int -> Int -> IO ()
Ansi.hSetCursorPosition Handle
h Int
l Int
0
chunkToString :: Chunk -> String
chunkToString :: Chunk -> String
chunkToString Chunk
NewlineChunk = String
"\n"
chunkToString (StringChunk [SGR]
_ String
str) = String
str
chunkToString (ControlChunk Control
_) = String
""
optimizeChunks :: Chunks -> Chunks
optimizeChunks :: Chunks -> Chunks
optimizeChunks (StringChunk [SGR]
c1 String
s1 : StringChunk [SGR]
c2 String
s2 : Chunks
chunks)
| [SGR]
c1 forall a. Eq a => a -> a -> Bool
== [SGR]
c2 = Chunks -> Chunks
optimizeChunks ([SGR] -> String -> Chunk
StringChunk [SGR]
c1 (String
s1 forall a. Semigroup a => a -> a -> a
<> String
s2) forall a. a -> [a] -> [a]
: Chunks
chunks)
| Bool
otherwise =
[SGR] -> String -> Chunk
StringChunk [SGR]
c1 String
s1 forall a. a -> [a] -> [a]
: Chunks -> Chunks
optimizeChunks ([SGR] -> String -> Chunk
StringChunk [SGR]
c2 String
s2 forall a. a -> [a] -> [a]
: Chunks
chunks)
optimizeChunks (Chunk
x : Chunks
chunks) = Chunk
x forall a. a -> [a] -> [a]
: Chunks -> Chunks
optimizeChunks Chunks
chunks
optimizeChunks [] = []
chunkLines :: Chunks -> [Chunks]
chunkLines :: Chunks -> [Chunks]
chunkLines Chunks
chunks = case forall a. (a -> Bool) -> [a] -> ([a], [a])
break (forall a. Eq a => a -> a -> Bool
== Chunk
NewlineChunk) Chunks
chunks of
(Chunks
xs, Chunk
_newline : Chunks
ys) -> Chunks
xs forall a. a -> [a] -> [a]
: Chunks -> [Chunks]
chunkLines Chunks
ys
(Chunks
xs, []) -> [Chunks
xs]
data DocE d
= String String
| Softspace
| Hardspace
| Softline
| Hardline
| WrapAt
{ forall d. DocE d -> Maybe Int
wrapAtCol :: Maybe Int
, forall d. DocE d -> d
wrapDoc :: d
}
| Ansi
{ forall d. DocE d -> [SGR] -> [SGR]
ansiCode :: [Ansi.SGR] -> [Ansi.SGR]
, forall d. DocE d -> d
ansiDoc :: d
}
| Indent
{ forall d. DocE d -> LineBuffer
indentFirstLine :: LineBuffer
, forall d. DocE d -> LineBuffer
indentOtherLines :: LineBuffer
, forall d. DocE d -> d
indentDoc :: d
}
| Control Control
deriving (forall a b. a -> DocE b -> DocE a
forall a b. (a -> b) -> DocE a -> DocE b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> DocE b -> DocE a
$c<$ :: forall a b. a -> DocE b -> DocE a
fmap :: forall a b. (a -> b) -> DocE a -> DocE b
$cfmap :: forall a b. (a -> b) -> DocE a -> DocE b
Functor)
chunkToDocE :: Chunk -> DocE Doc
chunkToDocE :: Chunk -> DocE Doc
chunkToDocE Chunk
NewlineChunk = forall d. DocE d
Hardline
chunkToDocE (StringChunk [SGR]
c1 String
str) = forall d. ([SGR] -> [SGR]) -> d -> DocE d
Ansi (\[SGR]
c0 -> [SGR]
c1 forall a. [a] -> [a] -> [a]
++ [SGR]
c0) ([DocE Doc] -> Doc
Doc [forall d. String -> DocE d
String String
str])
chunkToDocE (ControlChunk Control
ctrl) = forall d. Control -> DocE d
Control Control
ctrl
newtype Doc = Doc {Doc -> [DocE Doc]
unDoc :: [DocE Doc]}
deriving (Semigroup Doc
Doc
[Doc] -> Doc
Doc -> Doc -> Doc
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
mconcat :: [Doc] -> Doc
$cmconcat :: [Doc] -> Doc
mappend :: Doc -> Doc -> Doc
$cmappend :: Doc -> Doc -> Doc
mempty :: Doc
$cmempty :: Doc
Monoid, NonEmpty Doc -> Doc
Doc -> Doc -> Doc
forall b. Integral b => b -> Doc -> Doc
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
stimes :: forall b. Integral b => b -> Doc -> Doc
$cstimes :: forall b. Integral b => b -> Doc -> Doc
sconcat :: NonEmpty Doc -> Doc
$csconcat :: NonEmpty Doc -> Doc
<> :: Doc -> Doc -> Doc
$c<> :: Doc -> Doc -> Doc
Semigroup)
instance IsString Doc where
fromString :: String -> Doc
fromString = String -> Doc
string
instance Show Doc where
show :: Doc -> String
show = Doc -> String
toString
data DocEnv = DocEnv
{ DocEnv -> [SGR]
deCodes :: [Ansi.SGR]
, DocEnv -> LineBuffer
deIndent :: LineBuffer
, DocEnv -> Maybe Int
deWrap :: Maybe Int
}
type DocM = RWS DocEnv Chunks LineBuffer
data Trimmable a
= NotTrimmable !a
| Trimmable !a
deriving (forall a. Eq a => a -> Trimmable a -> Bool
forall a. Num a => Trimmable a -> a
forall a. Ord a => Trimmable a -> a
forall m. Monoid m => Trimmable m -> m
forall a. Trimmable a -> Bool
forall a. Trimmable a -> Int
forall a. Trimmable a -> [a]
forall a. (a -> a -> a) -> Trimmable a -> a
forall m a. Monoid m => (a -> m) -> Trimmable a -> m
forall b a. (b -> a -> b) -> b -> Trimmable a -> b
forall a b. (a -> b -> b) -> b -> Trimmable a -> b
forall (t :: * -> *).
(forall m. Monoid m => t m -> m)
-> (forall m a. Monoid m => (a -> m) -> t a -> m)
-> (forall m a. Monoid m => (a -> m) -> t a -> m)
-> (forall a b. (a -> b -> b) -> b -> t a -> b)
-> (forall a b. (a -> b -> b) -> b -> t a -> b)
-> (forall b a. (b -> a -> b) -> b -> t a -> b)
-> (forall b a. (b -> a -> b) -> b -> t a -> b)
-> (forall a. (a -> a -> a) -> t a -> a)
-> (forall a. (a -> a -> a) -> t a -> a)
-> (forall a. t a -> [a])
-> (forall a. t a -> Bool)
-> (forall a. t a -> Int)
-> (forall a. Eq a => a -> t a -> Bool)
-> (forall a. Ord a => t a -> a)
-> (forall a. Ord a => t a -> a)
-> (forall a. Num a => t a -> a)
-> (forall a. Num a => t a -> a)
-> Foldable t
product :: forall a. Num a => Trimmable a -> a
$cproduct :: forall a. Num a => Trimmable a -> a
sum :: forall a. Num a => Trimmable a -> a
$csum :: forall a. Num a => Trimmable a -> a
minimum :: forall a. Ord a => Trimmable a -> a
$cminimum :: forall a. Ord a => Trimmable a -> a
maximum :: forall a. Ord a => Trimmable a -> a
$cmaximum :: forall a. Ord a => Trimmable a -> a
elem :: forall a. Eq a => a -> Trimmable a -> Bool
$celem :: forall a. Eq a => a -> Trimmable a -> Bool
length :: forall a. Trimmable a -> Int
$clength :: forall a. Trimmable a -> Int
null :: forall a. Trimmable a -> Bool
$cnull :: forall a. Trimmable a -> Bool
toList :: forall a. Trimmable a -> [a]
$ctoList :: forall a. Trimmable a -> [a]
foldl1 :: forall a. (a -> a -> a) -> Trimmable a -> a
$cfoldl1 :: forall a. (a -> a -> a) -> Trimmable a -> a
foldr1 :: forall a. (a -> a -> a) -> Trimmable a -> a
$cfoldr1 :: forall a. (a -> a -> a) -> Trimmable a -> a
foldl' :: forall b a. (b -> a -> b) -> b -> Trimmable a -> b
$cfoldl' :: forall b a. (b -> a -> b) -> b -> Trimmable a -> b
foldl :: forall b a. (b -> a -> b) -> b -> Trimmable a -> b
$cfoldl :: forall b a. (b -> a -> b) -> b -> Trimmable a -> b
foldr' :: forall a b. (a -> b -> b) -> b -> Trimmable a -> b
$cfoldr' :: forall a b. (a -> b -> b) -> b -> Trimmable a -> b
foldr :: forall a b. (a -> b -> b) -> b -> Trimmable a -> b
$cfoldr :: forall a b. (a -> b -> b) -> b -> Trimmable a -> b
foldMap' :: forall m a. Monoid m => (a -> m) -> Trimmable a -> m
$cfoldMap' :: forall m a. Monoid m => (a -> m) -> Trimmable a -> m
foldMap :: forall m a. Monoid m => (a -> m) -> Trimmable a -> m
$cfoldMap :: forall m a. Monoid m => (a -> m) -> Trimmable a -> m
fold :: forall m. Monoid m => Trimmable m -> m
$cfold :: forall m. Monoid m => Trimmable m -> m
Foldable, forall a b. a -> Trimmable b -> Trimmable a
forall a b. (a -> b) -> Trimmable a -> Trimmable b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Trimmable b -> Trimmable a
$c<$ :: forall a b. a -> Trimmable b -> Trimmable a
fmap :: forall a b. (a -> b) -> Trimmable a -> Trimmable b
$cfmap :: forall a b. (a -> b) -> Trimmable a -> Trimmable b
Functor, Functor Trimmable
Foldable Trimmable
forall (t :: * -> *).
Functor t
-> Foldable t
-> (forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> t a -> f (t b))
-> (forall (f :: * -> *) a. Applicative f => t (f a) -> f (t a))
-> (forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> t a -> m (t b))
-> (forall (m :: * -> *) a. Monad m => t (m a) -> m (t a))
-> Traversable t
forall (m :: * -> *) a.
Monad m =>
Trimmable (m a) -> m (Trimmable a)
forall (f :: * -> *) a.
Applicative f =>
Trimmable (f a) -> f (Trimmable a)
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Trimmable a -> m (Trimmable b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Trimmable a -> f (Trimmable b)
sequence :: forall (m :: * -> *) a.
Monad m =>
Trimmable (m a) -> m (Trimmable a)
$csequence :: forall (m :: * -> *) a.
Monad m =>
Trimmable (m a) -> m (Trimmable a)
mapM :: forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Trimmable a -> m (Trimmable b)
$cmapM :: forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Trimmable a -> m (Trimmable b)
sequenceA :: forall (f :: * -> *) a.
Applicative f =>
Trimmable (f a) -> f (Trimmable a)
$csequenceA :: forall (f :: * -> *) a.
Applicative f =>
Trimmable (f a) -> f (Trimmable a)
traverse :: forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Trimmable a -> f (Trimmable b)
$ctraverse :: forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Trimmable a -> f (Trimmable b)
Traversable)
type LineBuffer = [Trimmable Chunk]
bufferToChunks :: LineBuffer -> Chunks
bufferToChunks :: LineBuffer -> Chunks
bufferToChunks = forall a b. (a -> b) -> [a] -> [b]
map forall {a}. Trimmable a -> a
trimmableToChunk forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
dropWhile forall a. Trimmable a -> Bool
isTrimmable
where
isTrimmable :: Trimmable a -> Bool
isTrimmable (NotTrimmable a
_) = Bool
False
isTrimmable (Trimmable a
_) = Bool
True
trimmableToChunk :: Trimmable a -> a
trimmableToChunk (NotTrimmable a
c) = a
c
trimmableToChunk (Trimmable a
c) = a
c
docToChunks :: Doc -> Chunks
docToChunks :: Doc -> Chunks
docToChunks Doc
doc0 =
let env0 :: DocEnv
env0 = [SGR] -> LineBuffer -> Maybe Int -> DocEnv
DocEnv [] [] forall a. Maybe a
Nothing
((), LineBuffer
b, Chunks
cs) = forall r w s a. RWS r w s a -> r -> s -> (a, s, w)
runRWS ([DocE Doc] -> DocM ()
go forall a b. (a -> b) -> a -> b
$ Doc -> [DocE Doc]
unDoc Doc
doc0) DocEnv
env0 forall a. Monoid a => a
mempty in
Chunks -> Chunks
optimizeChunks (Chunks
cs forall a. Semigroup a => a -> a -> a
<> LineBuffer -> Chunks
bufferToChunks LineBuffer
b)
where
go :: [DocE Doc] -> DocM ()
go :: [DocE Doc] -> DocM ()
go [] = forall (m :: * -> *) a. Monad m => a -> m a
return ()
go (String String
str : [DocE Doc]
docs) = do
Chunk
chunk <- String -> DocM Chunk
makeChunk String
str
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (forall a. a -> Trimmable a
NotTrimmable Chunk
chunk forall a. a -> [a] -> [a]
:)
[DocE Doc] -> DocM ()
go [DocE Doc]
docs
go (DocE Doc
Softspace : [DocE Doc]
docs) = do
DocE Doc
hard <- DocE Doc -> [DocE Doc] -> DocM (DocE Doc)
softConversion forall d. DocE d
Softspace [DocE Doc]
docs
[DocE Doc] -> DocM ()
go (DocE Doc
hard forall a. a -> [a] -> [a]
: [DocE Doc]
docs)
go (DocE Doc
Hardspace : [DocE Doc]
docs) = do
Chunk
chunk <- String -> DocM Chunk
makeChunk String
" "
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (forall a. a -> Trimmable a
NotTrimmable Chunk
chunk forall a. a -> [a] -> [a]
:)
[DocE Doc] -> DocM ()
go [DocE Doc]
docs
go (DocE Doc
Softline : [DocE Doc]
docs) = do
DocE Doc
hard <- DocE Doc -> [DocE Doc] -> DocM (DocE Doc)
softConversion forall d. DocE d
Softline [DocE Doc]
docs
[DocE Doc] -> DocM ()
go (DocE Doc
hard forall a. a -> [a] -> [a]
: [DocE Doc]
docs)
go (DocE Doc
Hardline : [DocE Doc]
docs) = do
LineBuffer
buffer <- forall s (m :: * -> *). MonadState s m => m s
get
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell forall a b. (a -> b) -> a -> b
$ LineBuffer -> Chunks
bufferToChunks LineBuffer
buffer forall a. Semigroup a => a -> a -> a
<> [Chunk
NewlineChunk]
LineBuffer
indentation <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks DocEnv -> LineBuffer
deIndent
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify forall a b. (a -> b) -> a -> b
$ \LineBuffer
_ -> if forall (t :: * -> *) a. Foldable t => t a -> Bool
L.null [DocE Doc]
docs then [] else LineBuffer
indentation
[DocE Doc] -> DocM ()
go [DocE Doc]
docs
go (WrapAt {Maybe Int
Doc
wrapDoc :: Doc
wrapAtCol :: Maybe Int
wrapDoc :: forall d. DocE d -> d
wrapAtCol :: forall d. DocE d -> Maybe Int
..} : [DocE Doc]
docs) = do
forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local (\DocEnv
env -> DocEnv
env {deWrap :: Maybe Int
deWrap = Maybe Int
wrapAtCol}) forall a b. (a -> b) -> a -> b
$ [DocE Doc] -> DocM ()
go (Doc -> [DocE Doc]
unDoc Doc
wrapDoc)
[DocE Doc] -> DocM ()
go [DocE Doc]
docs
go (Ansi {Doc
[SGR] -> [SGR]
ansiDoc :: Doc
ansiCode :: [SGR] -> [SGR]
ansiDoc :: forall d. DocE d -> d
ansiCode :: forall d. DocE d -> [SGR] -> [SGR]
..} : [DocE Doc]
docs) = do
forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local (\DocEnv
env -> DocEnv
env {deCodes :: [SGR]
deCodes = [SGR] -> [SGR]
ansiCode (DocEnv -> [SGR]
deCodes DocEnv
env)}) forall a b. (a -> b) -> a -> b
$
[DocE Doc] -> DocM ()
go (Doc -> [DocE Doc]
unDoc Doc
ansiDoc)
[DocE Doc] -> DocM ()
go [DocE Doc]
docs
go (Indent {LineBuffer
Doc
indentDoc :: Doc
indentOtherLines :: LineBuffer
indentFirstLine :: LineBuffer
indentDoc :: forall d. DocE d -> d
indentOtherLines :: forall d. DocE d -> LineBuffer
indentFirstLine :: forall d. DocE d -> LineBuffer
..} : [DocE Doc]
docs) = do
forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local (\DocEnv
env -> DocEnv
env {deIndent :: LineBuffer
deIndent = LineBuffer
indentOtherLines forall a. [a] -> [a] -> [a]
++ DocEnv -> LineBuffer
deIndent DocEnv
env}) forall a b. (a -> b) -> a -> b
$ do
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (LineBuffer
indentFirstLine forall a. [a] -> [a] -> [a]
++)
[DocE Doc] -> DocM ()
go (Doc -> [DocE Doc]
unDoc Doc
indentDoc)
[DocE Doc] -> DocM ()
go [DocE Doc]
docs
go (Control Control
ctrl : [DocE Doc]
docs) = do
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [Control -> Chunk
ControlChunk Control
ctrl]
[DocE Doc] -> DocM ()
go [DocE Doc]
docs
makeChunk :: String -> DocM Chunk
makeChunk :: String -> DocM Chunk
makeChunk String
str = do
[SGR]
codes <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks DocEnv -> [SGR]
deCodes
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [SGR] -> String -> Chunk
StringChunk [SGR]
codes String
str
softConversion :: DocE Doc -> [DocE Doc] -> DocM (DocE Doc)
softConversion :: DocE Doc -> [DocE Doc] -> DocM (DocE Doc)
softConversion DocE Doc
soft [DocE Doc]
docs = do
Maybe Int
mbWrapCol <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks DocEnv -> Maybe Int
deWrap
case Maybe Int
mbWrapCol of
Maybe Int
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return DocE Doc
hard
Just Int
maxCol -> do
String
currentLine <- forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Chunk -> String
chunkToString forall b c a. (b -> c) -> (a -> b) -> a -> c
. LineBuffer -> Chunks
bufferToChunks)
let currentCol :: Int
currentCol = String -> Int
wcstrwidth String
currentLine
case [DocE Doc] -> Maybe Int
nextWordLength [DocE Doc]
docs of
Maybe Int
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return DocE Doc
hard
Just Int
l
| Int
currentCol forall a. Num a => a -> a -> a
+ Int
1 forall a. Num a => a -> a -> a
+ Int
l forall a. Ord a => a -> a -> Bool
<= Int
maxCol -> forall (m :: * -> *) a. Monad m => a -> m a
return forall d. DocE d
Hardspace
| Bool
otherwise -> forall (m :: * -> *) a. Monad m => a -> m a
return forall d. DocE d
Hardline
where
hard :: DocE Doc
hard = case DocE Doc
soft of
DocE Doc
Softspace -> forall d. DocE d
Hardspace
DocE Doc
Softline -> forall d. DocE d
Hardline
DocE Doc
_ -> DocE Doc
soft
nextWordLength :: [DocE Doc] -> Maybe Int
nextWordLength :: [DocE Doc] -> Maybe Int
nextWordLength [] = forall a. Maybe a
Nothing
nextWordLength (String String
x : [DocE Doc]
xs)
| forall (t :: * -> *) a. Foldable t => t a -> Bool
L.null String
x = [DocE Doc] -> Maybe Int
nextWordLength [DocE Doc]
xs
| Bool
otherwise = forall a. a -> Maybe a
Just (String -> Int
wcstrwidth String
x)
nextWordLength (DocE Doc
Softspace : [DocE Doc]
xs) = [DocE Doc] -> Maybe Int
nextWordLength [DocE Doc]
xs
nextWordLength (DocE Doc
Hardspace : [DocE Doc]
xs) = [DocE Doc] -> Maybe Int
nextWordLength [DocE Doc]
xs
nextWordLength (DocE Doc
Softline : [DocE Doc]
xs) = [DocE Doc] -> Maybe Int
nextWordLength [DocE Doc]
xs
nextWordLength (DocE Doc
Hardline : [DocE Doc]
_) = forall a. Maybe a
Nothing
nextWordLength (WrapAt {Maybe Int
Doc
wrapDoc :: Doc
wrapAtCol :: Maybe Int
wrapDoc :: forall d. DocE d -> d
wrapAtCol :: forall d. DocE d -> Maybe Int
..} : [DocE Doc]
xs) = [DocE Doc] -> Maybe Int
nextWordLength (Doc -> [DocE Doc]
unDoc Doc
wrapDoc forall a. [a] -> [a] -> [a]
++ [DocE Doc]
xs)
nextWordLength (Ansi {Doc
[SGR] -> [SGR]
ansiDoc :: Doc
ansiCode :: [SGR] -> [SGR]
ansiDoc :: forall d. DocE d -> d
ansiCode :: forall d. DocE d -> [SGR] -> [SGR]
..} : [DocE Doc]
xs) = [DocE Doc] -> Maybe Int
nextWordLength (Doc -> [DocE Doc]
unDoc Doc
ansiDoc forall a. [a] -> [a] -> [a]
++ [DocE Doc]
xs)
nextWordLength (Indent {LineBuffer
Doc
indentDoc :: Doc
indentOtherLines :: LineBuffer
indentFirstLine :: LineBuffer
indentDoc :: forall d. DocE d -> d
indentOtherLines :: forall d. DocE d -> LineBuffer
indentFirstLine :: forall d. DocE d -> LineBuffer
..} : [DocE Doc]
xs) = [DocE Doc] -> Maybe Int
nextWordLength (Doc -> [DocE Doc]
unDoc Doc
indentDoc forall a. [a] -> [a] -> [a]
++ [DocE Doc]
xs)
nextWordLength (Control Control
_ : [DocE Doc]
_) = forall a. Maybe a
Nothing
toString :: Doc -> String
toString :: Doc -> String
toString = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Chunk -> String
chunkToString forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Chunks
docToChunks
dimensions :: Doc -> (Int, Int)
dimensions :: Doc -> (Int, Int)
dimensions Doc
doc =
let ls :: [String]
ls = String -> [String]
lines (Doc -> String
toString Doc
doc) in
(forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
ls, forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr forall a. Ord a => a -> a -> a
max Int
0 (forall a b. (a -> b) -> [a] -> [b]
map String -> Int
wcstrwidth [String]
ls))
null :: Doc -> Bool
null :: Doc -> Bool
null Doc
doc = case Doc -> [DocE Doc]
unDoc Doc
doc of [] -> Bool
True; [DocE Doc]
_ -> Bool
False
hPutDoc :: IO.Handle -> Doc -> IO ()
hPutDoc :: Handle -> Doc -> IO ()
hPutDoc Handle
h = forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Handle -> Chunk -> IO ()
hPutChunk Handle
h) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Chunks
docToChunks
putDoc :: Doc -> IO ()
putDoc :: Doc -> IO ()
putDoc = Handle -> Doc -> IO ()
hPutDoc Handle
IO.stdout
mkDoc :: DocE Doc -> Doc
mkDoc :: DocE Doc -> Doc
mkDoc DocE Doc
e = [DocE Doc] -> Doc
Doc [DocE Doc
e]
string :: String -> Doc
string :: String -> Doc
string = DocE Doc -> Doc
mkDoc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall d. String -> DocE d
String
text :: T.Text -> Doc
text :: Text -> Doc
text = String -> Doc
string forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack
space :: Doc
space :: Doc
space = DocE Doc -> Doc
mkDoc forall d. DocE d
Softspace
spaces :: Int -> Doc
spaces :: Int -> Doc
spaces Int
n = forall a. Monoid a => [a] -> a
mconcat forall a b. (a -> b) -> a -> b
$ forall a. Int -> a -> [a]
replicate Int
n Doc
space
softline :: Doc
softline :: Doc
softline = DocE Doc -> Doc
mkDoc forall d. DocE d
Softline
hardline :: Doc
hardline :: Doc
hardline = DocE Doc -> Doc
mkDoc forall d. DocE d
Hardline
wrapAt :: Maybe Int -> Doc -> Doc
wrapAt :: Maybe Int -> Doc -> Doc
wrapAt Maybe Int
wrapAtCol Doc
wrapDoc = DocE Doc -> Doc
mkDoc WrapAt {Maybe Int
Doc
wrapDoc :: Doc
wrapAtCol :: Maybe Int
wrapDoc :: Doc
wrapAtCol :: Maybe Int
..}
indent :: Trimmable Doc -> Trimmable Doc -> Doc -> Doc
indent :: Trimmable Doc -> Trimmable Doc -> Doc -> Doc
indent Trimmable Doc
firstLineDoc Trimmable Doc
otherLinesDoc Doc
doc = DocE Doc -> Doc
mkDoc forall a b. (a -> b) -> a -> b
$ Indent
{ indentFirstLine :: LineBuffer
indentFirstLine = forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse Doc -> Chunks
docToChunks Trimmable Doc
firstLineDoc
, indentOtherLines :: LineBuffer
indentOtherLines = forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse Doc -> Chunks
docToChunks Trimmable Doc
otherLinesDoc
, indentDoc :: Doc
indentDoc = Doc
doc
}
ansi :: [Ansi.SGR] -> Doc -> Doc
ansi :: [SGR] -> Doc -> Doc
ansi [SGR]
codes = DocE Doc -> Doc
mkDoc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall d. ([SGR] -> [SGR]) -> d -> DocE d
Ansi ([SGR]
codes forall a. [a] -> [a] -> [a]
++)
(<+>) :: Doc -> Doc -> Doc
Doc
x <+> :: Doc -> Doc -> Doc
<+> Doc
y = Doc
x forall a. Semigroup a => a -> a -> a
<> Doc
space forall a. Semigroup a => a -> a -> a
<> Doc
y
infixr 6 <+>
(<$$>) :: Doc -> Doc -> Doc
Doc
x <$$> :: Doc -> Doc -> Doc
<$$> Doc
y = Doc
x forall a. Semigroup a => a -> a -> a
<> Doc
hardline forall a. Semigroup a => a -> a -> a
<> Doc
y
infixr 5 <$$>
vcat :: [Doc] -> Doc
vcat :: [Doc] -> Doc
vcat = Doc -> [Doc] -> Doc
intersperse Doc
hardline
intersperse :: Doc -> [Doc] -> Doc
intersperse :: Doc -> [Doc] -> Doc
intersperse Doc
sep = forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> [a] -> [a]
L.intersperse Doc
sep
data Alignment = AlignLeft | AlignCenter | AlignRight deriving (Alignment -> Alignment -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Alignment -> Alignment -> Bool
$c/= :: Alignment -> Alignment -> Bool
== :: Alignment -> Alignment -> Bool
$c== :: Alignment -> Alignment -> Bool
Eq, Eq Alignment
Alignment -> Alignment -> Bool
Alignment -> Alignment -> Ordering
Alignment -> Alignment -> Alignment
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Alignment -> Alignment -> Alignment
$cmin :: Alignment -> Alignment -> Alignment
max :: Alignment -> Alignment -> Alignment
$cmax :: Alignment -> Alignment -> Alignment
>= :: Alignment -> Alignment -> Bool
$c>= :: Alignment -> Alignment -> Bool
> :: Alignment -> Alignment -> Bool
$c> :: Alignment -> Alignment -> Bool
<= :: Alignment -> Alignment -> Bool
$c<= :: Alignment -> Alignment -> Bool
< :: Alignment -> Alignment -> Bool
$c< :: Alignment -> Alignment -> Bool
compare :: Alignment -> Alignment -> Ordering
$ccompare :: Alignment -> Alignment -> Ordering
Ord, Int -> Alignment -> ShowS
[Alignment] -> ShowS
Alignment -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Alignment] -> ShowS
$cshowList :: [Alignment] -> ShowS
show :: Alignment -> String
$cshow :: Alignment -> String
showsPrec :: Int -> Alignment -> ShowS
$cshowsPrec :: Int -> Alignment -> ShowS
Show)
align :: Int -> Alignment -> Doc -> Doc
align :: Int -> Alignment -> Doc -> Doc
align Int
width Alignment
alignment Doc
doc0 =
let chunks0 :: Chunks
chunks0 = Doc -> Chunks
docToChunks forall a b. (a -> b) -> a -> b
$ Doc -> Doc
removeControls Doc
doc0
lines_ :: [Chunks]
lines_ = Chunks -> [Chunks]
chunkLines Chunks
chunks0 in
[Doc] -> Doc
vcat
[ [DocE Doc] -> Doc
Doc (forall a b. (a -> b) -> [a] -> [b]
map Chunk -> DocE Doc
chunkToDocE (Chunks -> Chunks
alignLine Chunks
line))
| Chunks
line <- [Chunks]
lines_
]
where
lineWidth :: [Chunk] -> Int
lineWidth :: Chunks -> Int
lineWidth = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (String -> Int
wcstrwidth forall b c a. (b -> c) -> (a -> b) -> a -> c
. Chunk -> String
chunkToString)
alignLine :: [Chunk] -> [Chunk]
alignLine :: Chunks -> Chunks
alignLine Chunks
line =
let actual :: Int
actual = Chunks -> Int
lineWidth Chunks
line
chunkSpaces :: Int -> Chunks
chunkSpaces Int
n = [[SGR] -> String -> Chunk
StringChunk [] (forall a. Int -> a -> [a]
replicate Int
n Char
' ')] in
case Alignment
alignment of
Alignment
AlignLeft -> Chunks
line forall a. Semigroup a => a -> a -> a
<> Int -> Chunks
chunkSpaces (Int
width forall a. Num a => a -> a -> a
- Int
actual)
Alignment
AlignRight -> Int -> Chunks
chunkSpaces (Int
width forall a. Num a => a -> a -> a
- Int
actual) forall a. Semigroup a => a -> a -> a
<> Chunks
line
Alignment
AlignCenter ->
let r :: Int
r = (Int
width forall a. Num a => a -> a -> a
- Int
actual) forall a. Integral a => a -> a -> a
`div` Int
2
l :: Int
l = (Int
width forall a. Num a => a -> a -> a
- Int
actual) forall a. Num a => a -> a -> a
- Int
r in
Int -> Chunks
chunkSpaces Int
l forall a. Semigroup a => a -> a -> a
<> Chunks
line forall a. Semigroup a => a -> a -> a
<> Int -> Chunks
chunkSpaces Int
r
paste :: [Doc] -> Doc
paste :: [Doc] -> Doc
paste [Doc]
docs0 =
let chunkss :: [Chunks]
chunkss = forall a b. (a -> b) -> [a] -> [b]
map (Doc -> Chunks
docToChunks forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> Doc
removeControls) [Doc]
docs0 :: [Chunks]
cols :: [[Chunks]]
cols = forall a b. (a -> b) -> [a] -> [b]
map Chunks -> [Chunks]
chunkLines [Chunks]
chunkss :: [[Chunks]]
rows0 :: [[Chunks]]
rows0 = forall a. [[a]] -> [[a]]
L.transpose [[Chunks]]
cols :: [[Chunks]]
rows1 :: [[Doc]]
rows1 = forall a b. (a -> b) -> [a] -> [b]
map (forall a b. (a -> b) -> [a] -> [b]
map ([DocE Doc] -> Doc
Doc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Chunk -> DocE Doc
chunkToDocE)) [[Chunks]]
rows0 :: [[Doc]] in
[Doc] -> Doc
vcat forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall a. Monoid a => [a] -> a
mconcat [[Doc]]
rows1
removeControls :: Doc -> Doc
removeControls :: Doc -> Doc
removeControls = [DocE Doc] -> Doc
Doc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
filter forall {d}. DocE d -> Bool
isNotControl forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Doc -> Doc
removeControls) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> [DocE Doc]
unDoc
where
isNotControl :: DocE d -> Bool
isNotControl (Control Control
_) = Bool
False
isNotControl DocE d
_ = Bool
True
clearScreen :: Doc
clearScreen :: Doc
clearScreen = DocE Doc -> Doc
mkDoc forall a b. (a -> b) -> a -> b
$ forall d. Control -> DocE d
Control Control
ClearScreenControl
goToLine :: Int -> Doc
goToLine :: Int -> Doc
goToLine = DocE Doc -> Doc
mkDoc forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall d. Control -> DocE d
Control forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Control
GoToLineControl