pretty-terminal-0.1.0.0: src/System/Console/Pretty.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-| Useful helpers to style and color text with ANSI escape sequences.
-}
module System.Console.Pretty
( Color(..) , Pretty(..) , Section(..) , Style(..)
, supportsPretty)
where
import qualified Data.Char as C
import Data.Monoid ((<>))
import qualified Data.Text as T
import GHC.IO.Handle (Handle)
import System.Environment (lookupEnv)
import System.IO (hIsTerminalDevice, stdout)
---------------------------------------------------------------------------------
-- TYPES
-- | A section to be colored, either foreground or background.
data Section = Foreground | Background
-- | Colors for an ANSI terminal
data Color = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White | Default
deriving (Enum)
-- | SGR paramaters, aka text styles for an ANSI terminal
data Style
= Normal | Bold | Faint | Italic
| Underline | SlowBlink | ColoredNormal | Reverse
deriving (Enum)
---------------------------------------------------------------------------------
-- CLASS
-- | A class to color and style
class Pretty a where
-- | Helper to set foreground color
color :: Color -> a -> a
color = colorize Foreground
-- | Helper to set background color
bgColor :: Color -> a -> a
bgColor = colorize Background
-- | Set the color of the (fg/bg) with the color
colorize :: Section -> Color -> a -> a
-- | Set the style
style :: Style -> a -> a
---------------------------------------------------------------------------------
-- TEXT
-- | Instance of `Pretty` for `T.Text`
instance Pretty T.Text where
colorize section col str =
"\x1b[" <> -- escape code
sectionNum <> -- bg/foreground
(T.singleton $ C.intToDigit $ fromEnum col) -- color code
<> "m" <> -- delim
str <> -- inner string
"\x1b[0m" -- reset
where
sectionNum :: T.Text
sectionNum = case section of
Foreground -> "9"
Background -> "4"
style sty str =
"\x1b[" <> -- escape code
(T.singleton $ C.intToDigit $ fromEnum sty) -- style
<> "m" <> -- delim
str <> -- inner string
"\x1b[0m" -- reset
---------------------------------------------------------------------------------
-- STRING
-- | Instance of `Pretty` for `String`
instance Pretty String where
colorize section col str =
"\x1b[" <> -- escape code
sectionNum <> -- bg/foreground
show (fromEnum col) -- color code
<> "m" <> -- delim
str <> -- inner string
"\x1b[0m" -- reset
where
sectionNum :: String
sectionNum = case section of
Foreground -> "9"
Background -> "4"
style sty str =
"\x1b[" <> -- escape code
show (fromEnum sty) -- style
<> "m" <> -- delim
str <> -- string
"\x1b[0m" -- reset
---------------------------------------------------------------------------------
-- SUPPORTED CHECK
-- | Whether or not the current terminal supports pretty-terminal
supportsPretty :: IO Bool
supportsPretty =
hSupportsANSI stdout
where
-- | Use heuristics to determine whether the functions defined in this
-- package will work with a given handle.
--
-- The current implementation checks that the handle is a terminal, and
-- that the @TERM@ environment variable doesn't say @dumb@ (whcih is what
-- Emacs sets for its own terminal).
hSupportsANSI :: Handle -> IO Bool
-- Borrowed from an HSpec patch by Simon Hengel
-- (https://github.com/hspec/hspec/commit/d932f03317e0e2bd08c85b23903fb8616ae642bd)
hSupportsANSI h = (&&) <$> hIsTerminalDevice h <*> (not <$> isDumb)
where
isDumb = (== Just "dumb") <$> lookupEnv "TERM"