{-# LANGUAGE FlexibleInstances #-}
module Rainbow.Translate where
import Control.Exception (try)
import Data.ByteString (ByteString)
import Data.List (intersperse)
import Data.Text (Text)
import Data.Word (Word8)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import qualified Data.Text.Encoding as X
import qualified Rainbow.Types as T
import qualified System.Console.Terminfo as Terminfo
import qualified System.IO as IO
single :: Char -> [ByteString] -> [ByteString]
single c = ((BS8.singleton c):)
escape :: [ByteString] -> [ByteString]
escape = single '\x1B'
csi :: [ByteString] -> [ByteString]
csi = escape . single '['
sgr :: ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
sgr sq = csi . sq . single 'm'
params :: Show a => [a] -> [ByteString] -> [ByteString]
params cs = ((intersperse semi . map (BS8.pack . show) $ cs) ++)
where
semi = BS8.singleton ';'
sgrSingle :: Word -> [ByteString] -> [ByteString]
sgrSingle w = sgr $ params [w]
sgrDouble :: Word -> Word -> [ByteString] -> [ByteString]
sgrDouble x y = sgr $ params [x, y]
normalDefault :: [ByteString] -> [ByteString]
normalDefault = sgrSingle 0
bold :: [ByteString] -> [ByteString]
bold = sgrSingle 1
faint :: [ByteString] -> [ByteString]
faint = sgrSingle 2
italic :: [ByteString] -> [ByteString]
italic = sgrSingle 3
underline :: [ByteString] -> [ByteString]
underline = sgrSingle 4
blink :: [ByteString] -> [ByteString]
blink = sgrSingle 5
inverse :: [ByteString] -> [ByteString]
inverse = sgrSingle 7
invisible :: [ByteString] -> [ByteString]
invisible = sgrSingle 8
strikeout :: [ByteString] -> [ByteString]
strikeout = sgrSingle 9
foreBlack :: [ByteString] -> [ByteString]
foreBlack = sgrSingle 30
foreRed :: [ByteString] -> [ByteString]
foreRed = sgrSingle 31
foreGreen :: [ByteString] -> [ByteString]
foreGreen = sgrSingle 32
foreYellow :: [ByteString] -> [ByteString]
foreYellow = sgrSingle 33
foreBlue :: [ByteString] -> [ByteString]
foreBlue = sgrSingle 34
foreMagenta :: [ByteString] -> [ByteString]
foreMagenta = sgrSingle 35
foreCyan :: [ByteString] -> [ByteString]
foreCyan = sgrSingle 36
foreWhite :: [ByteString] -> [ByteString]
foreWhite = sgrSingle 37
foreDefault :: [ByteString] -> [ByteString]
foreDefault = sgrSingle 39
backBlack :: [ByteString] -> [ByteString]
backBlack = sgrSingle 40
backRed :: [ByteString] -> [ByteString]
backRed = sgrSingle 41
backGreen :: [ByteString] -> [ByteString]
backGreen = sgrSingle 42
backYellow :: [ByteString] -> [ByteString]
backYellow = sgrSingle 43
backBlue :: [ByteString] -> [ByteString]
backBlue = sgrSingle 44
backMagenta :: [ByteString] -> [ByteString]
backMagenta = sgrSingle 45
backCyan :: [ByteString] -> [ByteString]
backCyan = sgrSingle 46
backWhite :: [ByteString] -> [ByteString]
backWhite = sgrSingle 47
backDefault :: [ByteString] -> [ByteString]
backDefault = sgrSingle 49
fore256 :: Word8 -> [ByteString] -> [ByteString]
fore256 c = sgr $ params [38,5,c]
back256 :: Word8 -> [ByteString] -> [ByteString]
back256 c = sgr $ params [48,5,c]
foreColor8 :: T.Enum8 -> [ByteString] -> [ByteString]
foreColor8 e8 = case e8 of
T.E0 -> foreBlack
T.E1 -> foreRed
T.E2 -> foreGreen
T.E3 -> foreYellow
T.E4 -> foreBlue
T.E5 -> foreMagenta
T.E6 -> foreCyan
T.E7 -> foreWhite
backColor8 :: T.Enum8 -> [ByteString] -> [ByteString]
backColor8 e8 = case e8 of
T.E0 -> backBlack
T.E1 -> backRed
T.E2 -> backGreen
T.E3 -> backYellow
T.E4 -> backBlue
T.E5 -> backMagenta
T.E6 -> backCyan
T.E7 -> backWhite
renderFormat :: T.Format -> [ByteString] -> [ByteString]
renderFormat (T.Format bld fnt ita und bli ivr isb stk)
= effect bold bld
. effect faint fnt
. effect italic ita
. effect underline und
. effect blink bli
. effect inverse ivr
. effect invisible isb
. effect strikeout stk
where
effect on x = if x then on else id
renderStyle8 :: T.Style T.Enum8 -> [ByteString] -> [ByteString]
renderStyle8 (T.Style fore back format)
= effect foreColor8 fore
. effect backColor8 back
. renderFormat format
where
effect on (T.Color may) = maybe id on may
renderStyle256 :: T.Style Word8 -> [ByteString] -> [ByteString]
renderStyle256 (T.Style fore back format)
= effect fore256 fore
. effect back256 back
. renderFormat format
where
effect on (T.Color may) = maybe id on may
render :: Text -> [ByteString] -> [ByteString]
render x = (X.encodeUtf8 x :)
toByteStringsColors0
:: T.Chunk
-> [ByteString]
-> [ByteString]
toByteStringsColors0 (T.Chunk _ yn) = render yn
toByteStringsColors8
:: T.Chunk
-> [ByteString]
-> [ByteString]
toByteStringsColors8 (T.Chunk (T.Scheme s8 _) yn)
= normalDefault
. renderStyle8 s8
. render yn
. normalDefault
toByteStringsColors256
:: T.Chunk
-> [ByteString]
-> [ByteString]
toByteStringsColors256 (T.Chunk (T.Scheme _ s256) yn)
= normalDefault
. renderStyle256 s256
. render yn
. normalDefault
byteStringMakerFromEnvironment
:: IO (T.Chunk -> [ByteString] -> [ByteString])
byteStringMakerFromEnvironment = fmap g (try Terminfo.setupTermFromEnv)
where
g (Left e) = toByteStringsColors0
where
_types = e :: Terminfo.SetupTermError
g (Right terminal) =
case Terminfo.getCapability terminal (Terminfo.tiGetNum "colors") of
Nothing -> toByteStringsColors0
Just c
| c >= 256 -> toByteStringsColors256
| c >= 8 -> toByteStringsColors8
| otherwise -> toByteStringsColors0
byteStringMakerFromHandle
:: IO.Handle
-> IO (T.Chunk -> [ByteString] -> [ByteString])
byteStringMakerFromHandle h = IO.hIsTerminalDevice h >>= f
where
f isTerm | isTerm = byteStringMakerFromEnvironment
| otherwise = return toByteStringsColors0
chunksToByteStrings
:: (T.Chunk -> [ByteString] -> [ByteString])
-> [T.Chunk]
-> [ByteString]
chunksToByteStrings mk = ($ []) . foldr (.) id . map mk
hPutChunks :: IO.Handle -> [T.Chunk] -> IO ()
hPutChunks h cks = do
maker <- byteStringMakerFromEnvironment
let bsList = chunksToByteStrings maker cks
mapM_ (BS.hPut h) bsList
hPutChunksLn :: IO.Handle -> [T.Chunk] -> IO ()
hPutChunksLn h cks = do
hPutChunks h cks
IO.hPutStr h "\n"
putChunks :: [T.Chunk] -> IO ()
putChunks = hPutChunks IO.stdout
putChunksLn :: [T.Chunk] -> IO ()
putChunksLn cks = do
putChunks cks
IO.putStr "\n"
putChunk :: T.Chunk -> IO ()
putChunk ck = do
mkr <- byteStringMakerFromEnvironment
mapM_ BS.putStr . chunksToByteStrings mkr $ [ck]
putChunkLn :: T.Chunk -> IO ()
putChunkLn ck = putChunk ck >> putStrLn ""