{-# LANGUAGE MagicHash #-}

module Text.Builder.ANSI
  ( -- $intro

    -- * Foreground color
    black,
    red,
    green,
    yellow,
    blue,
    magenta,
    cyan,
    white,
    brightBlack,
    brightRed,
    brightGreen,
    brightYellow,
    brightBlue,
    brightMagenta,
    brightCyan,
    brightWhite,
    rgb,

    -- * Background color
    blackBg,
    redBg,
    greenBg,
    yellowBg,
    blueBg,
    magentaBg,
    cyanBg,
    whiteBg,
    brightBlackBg,
    brightRedBg,
    brightGreenBg,
    brightYellowBg,
    brightBlueBg,
    brightMagentaBg,
    brightCyanBg,
    brightWhiteBg,
    rgbBg,

    -- * Style
    bold,
    faint,
    italic,
    underline,
    doubleUnderline,
    strikethrough,
    frame,
    encircle,
    overline,
  )
where

import Data.Text.Builder.Linear (Builder)
import qualified Data.Text.Builder.Linear as Builder
import Data.Word (Word8)
import System.IO.Unsafe (unsafePerformIO)
import System.Posix.Internals (c_isatty)

-- $intro
--
-- Text styling for ANSI terminals using SGR codes, as defined by the
-- <https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf ECMA-48>
-- standard.
--
-- Supports foreground\/background color, bold\/faint intensity, italic,
-- single\/double underline, strikethrough, frame, encircle, and overline escape
-- sequences. Some styles may not work on your terminal.
--
-- Also features terminal detection, so redirecting styled output to a file will
-- automatically strip the ANSI escape sequences.

-- | Black foreground.
black :: Builder -> Builder
black :: Builder -> Builder
black =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"30"#)
{-# INLINE black #-}

-- | Red foreground.
red :: Builder -> Builder
red :: Builder -> Builder
red =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"31"#)
{-# INLINE red #-}

-- | Green foreground.
green :: Builder -> Builder
green :: Builder -> Builder
green =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"32"#)
{-# INLINE green #-}

-- | Yellow foreground.
yellow :: Builder -> Builder
yellow :: Builder -> Builder
yellow =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"33"#)
{-# INLINE yellow #-}

-- | Blue foreground.
blue :: Builder -> Builder
blue :: Builder -> Builder
blue =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"34"#)
{-# INLINE blue #-}

-- | Magenta foreground.
magenta :: Builder -> Builder
magenta :: Builder -> Builder
magenta =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"35"#)
{-# INLINE magenta #-}

-- | Cyan foreground.
cyan :: Builder -> Builder
cyan :: Builder -> Builder
cyan =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"36"#)
{-# INLINE cyan #-}

-- | White foreground.
white :: Builder -> Builder
white :: Builder -> Builder
white =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"37"#)
{-# INLINE white #-}

-- | Bright black foreground.
brightBlack :: Builder -> Builder
brightBlack :: Builder -> Builder
brightBlack =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"90"#)
{-# INLINE brightBlack #-}

-- | Bright red foreground.
brightRed :: Builder -> Builder
brightRed :: Builder -> Builder
brightRed =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"91"#)
{-# INLINE brightRed #-}

-- | Bright green foreground.
brightGreen :: Builder -> Builder
brightGreen :: Builder -> Builder
brightGreen =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"92"#)
{-# INLINE brightGreen #-}

-- | Bright yellow foreground.
brightYellow :: Builder -> Builder
brightYellow :: Builder -> Builder
brightYellow =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"93"#)
{-# INLINE brightYellow #-}

-- | Bright blue foreground.
brightBlue :: Builder -> Builder
brightBlue :: Builder -> Builder
brightBlue =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"94"#)
{-# INLINE brightBlue #-}

-- | Bright magenta foreground.
brightMagenta :: Builder -> Builder
brightMagenta :: Builder -> Builder
brightMagenta =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"95"#)
{-# INLINE brightMagenta #-}

-- | Bright cyan foreground.
brightCyan :: Builder -> Builder
brightCyan :: Builder -> Builder
brightCyan =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"96"#)
{-# INLINE brightCyan #-}

-- | Bright white foreground.
brightWhite :: Builder -> Builder
brightWhite :: Builder -> Builder
brightWhite =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"97"#)
{-# INLINE brightWhite #-}

-- | RGB foreground.
rgb :: Word8 -> Word8 -> Word8 -> Builder -> Builder
rgb :: Word8 -> Word8 -> Word8 -> Builder -> Builder
rgb Word8
r Word8
g Word8
b =
  Builder -> Builder -> Builder
foreground (Addr# -> Builder
Builder.fromAddr Addr#
"38;2;"# forall a. Semigroup a => a -> a -> a
<> forall a. (Integral a, FiniteBits a) => a -> Builder
Builder.fromDec Word8
r forall a. Semigroup a => a -> a -> a
<> Builder
semi forall a. Semigroup a => a -> a -> a
<> forall a. (Integral a, FiniteBits a) => a -> Builder
Builder.fromDec Word8
g forall a. Semigroup a => a -> a -> a
<> Builder
semi forall a. Semigroup a => a -> a -> a
<> forall a. (Integral a, FiniteBits a) => a -> Builder
Builder.fromDec Word8
b)
{-# INLINE rgb #-}

foreground :: Builder -> Builder -> Builder
foreground :: Builder -> Builder -> Builder
foreground Builder
s =
  Builder -> Builder -> Builder -> Builder
surround Builder
s (Addr# -> Builder
Builder.fromAddr Addr#
"39"#)
{-# INLINE foreground #-}

-- | Black background.
blackBg :: Builder -> Builder
blackBg :: Builder -> Builder
blackBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"40"#)
{-# INLINE blackBg #-}

-- | Red background.
redBg :: Builder -> Builder
redBg :: Builder -> Builder
redBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"41"#)
{-# INLINE redBg #-}

-- | Green background.
greenBg :: Builder -> Builder
greenBg :: Builder -> Builder
greenBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"42"#)
{-# INLINE greenBg #-}

-- | Yellow background.
yellowBg :: Builder -> Builder
yellowBg :: Builder -> Builder
yellowBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"43"#)
{-# INLINE yellowBg #-}

-- | Blue background.
blueBg :: Builder -> Builder
blueBg :: Builder -> Builder
blueBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"44"#)
{-# INLINE blueBg #-}

-- | Magenta background.
magentaBg :: Builder -> Builder
magentaBg :: Builder -> Builder
magentaBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"45"#)
{-# INLINE magentaBg #-}

-- | Cyan background.
cyanBg :: Builder -> Builder
cyanBg :: Builder -> Builder
cyanBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"46"#)
{-# INLINE cyanBg #-}

-- | White background.
whiteBg :: Builder -> Builder
whiteBg :: Builder -> Builder
whiteBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"47"#)
{-# INLINE whiteBg #-}

-- | Bright black background.
brightBlackBg :: Builder -> Builder
brightBlackBg :: Builder -> Builder
brightBlackBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"100"#)
{-# INLINE brightBlackBg #-}

-- | Bright red background.
brightRedBg :: Builder -> Builder
brightRedBg :: Builder -> Builder
brightRedBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"101"#)
{-# INLINE brightRedBg #-}

-- | Bright green background.
brightGreenBg :: Builder -> Builder
brightGreenBg :: Builder -> Builder
brightGreenBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"102"#)
{-# INLINE brightGreenBg #-}

-- | Bright yellow background.
brightYellowBg :: Builder -> Builder
brightYellowBg :: Builder -> Builder
brightYellowBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"103"#)
{-# INLINE brightYellowBg #-}

-- | Bright blue background.
brightBlueBg :: Builder -> Builder
brightBlueBg :: Builder -> Builder
brightBlueBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"104"#)
{-# INLINE brightBlueBg #-}

-- | Bright magenta background.
brightMagentaBg :: Builder -> Builder
brightMagentaBg :: Builder -> Builder
brightMagentaBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"105"#)
{-# INLINE brightMagentaBg #-}

-- | Bright cyan background.
brightCyanBg :: Builder -> Builder
brightCyanBg :: Builder -> Builder
brightCyanBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"106"#)
{-# INLINE brightCyanBg #-}

-- | Bright white background.
brightWhiteBg :: Builder -> Builder
brightWhiteBg :: Builder -> Builder
brightWhiteBg =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"107"#)
{-# INLINE brightWhiteBg #-}

background :: Builder -> Builder -> Builder
background :: Builder -> Builder -> Builder
background Builder
s =
  Builder -> Builder -> Builder -> Builder
surround Builder
s (Addr# -> Builder
Builder.fromAddr Addr#
"49"#)
{-# INLINE background #-}

-- | RGB background.
rgbBg :: Word8 -> Word8 -> Word8 -> Builder -> Builder
rgbBg :: Word8 -> Word8 -> Word8 -> Builder -> Builder
rgbBg Word8
r Word8
g Word8
b =
  Builder -> Builder -> Builder
background (Addr# -> Builder
Builder.fromAddr Addr#
"48;2;"# forall a. Semigroup a => a -> a -> a
<> forall a. (Integral a, FiniteBits a) => a -> Builder
Builder.fromDec Word8
r forall a. Semigroup a => a -> a -> a
<> Builder
semi forall a. Semigroup a => a -> a -> a
<> forall a. (Integral a, FiniteBits a) => a -> Builder
Builder.fromDec Word8
g forall a. Semigroup a => a -> a -> a
<> Builder
semi forall a. Semigroup a => a -> a -> a
<> forall a. (Integral a, FiniteBits a) => a -> Builder
Builder.fromDec Word8
b)
{-# INLINE rgbBg #-}

-- | __Bold__ style (high intensity).
bold :: Builder -> Builder
bold :: Builder -> Builder
bold =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"1"#) (Addr# -> Builder
Builder.fromAddr Addr#
"22"#)
{-# INLINE bold #-}

-- | Faint style (low intensity).
faint :: Builder -> Builder
faint :: Builder -> Builder
faint =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"2"#) (Addr# -> Builder
Builder.fromAddr Addr#
"22"#)
{-# INLINE faint #-}

-- | /Italic/ style.
italic :: Builder -> Builder
italic :: Builder -> Builder
italic =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"3"#) (Addr# -> Builder
Builder.fromAddr Addr#
"23"#)
{-# INLINE italic #-}

-- | U̲n̲d̲e̲r̲l̲i̲n̲e̲ style.
underline :: Builder -> Builder
underline :: Builder -> Builder
underline =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"4"#) (Addr# -> Builder
Builder.fromAddr Addr#
"24"#)
{-# INLINE underline #-}

-- | D̳o̳u̳b̳l̳e̳ ̳u̳n̳d̳e̳r̳l̳i̳n̳e̳ style.
doubleUnderline :: Builder -> Builder
doubleUnderline :: Builder -> Builder
doubleUnderline =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"21"#) (Addr# -> Builder
Builder.fromAddr Addr#
"24"#)
{-# INLINE doubleUnderline #-}

-- | S̶t̶r̶i̶k̶e̶t̶h̶r̶o̶u̶g̶h̶ style.
strikethrough :: Builder -> Builder
strikethrough :: Builder -> Builder
strikethrough =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"9"#) (Addr# -> Builder
Builder.fromAddr Addr#
"29"#)
{-# INLINE strikethrough #-}

-- | Frame style.
frame :: Builder -> Builder
frame :: Builder -> Builder
frame =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"51"#) (Addr# -> Builder
Builder.fromAddr Addr#
"54"#)
{-# INLINE frame #-}

-- | Encircle style.
encircle :: Builder -> Builder
encircle :: Builder -> Builder
encircle =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"52"#) (Addr# -> Builder
Builder.fromAddr Addr#
"54"#)
{-# INLINE encircle #-}

-- | O̅v̅e̅r̅l̅i̅n̅e̅ style.
overline :: Builder -> Builder
overline :: Builder -> Builder
overline =
  Builder -> Builder -> Builder -> Builder
surround (Addr# -> Builder
Builder.fromAddr Addr#
"53"#) (Addr# -> Builder
Builder.fromAddr Addr#
"55"#)
{-# INLINE overline #-}

--------------------------------------------------------------------------------

surround :: Builder -> Builder -> Builder -> Builder
surround :: Builder -> Builder -> Builder -> Builder
surround Builder
open Builder
close Builder
text
  | Bool
isatty = Builder
esc forall a. Semigroup a => a -> a -> a
<> Builder
open forall a. Semigroup a => a -> a -> a
<> Builder
m forall a. Semigroup a => a -> a -> a
<> Builder
text forall a. Semigroup a => a -> a -> a
<> Builder
esc forall a. Semigroup a => a -> a -> a
<> Builder
close forall a. Semigroup a => a -> a -> a
<> Builder
m
  | Bool
otherwise = Builder
text
-- Don't inline before phase 1
{-# NOINLINE [1] surround #-}

esc :: Builder
esc :: Builder
esc =
  Addr# -> Builder
Builder.fromAddr Addr#
"\ESC["#

m :: Builder
m :: Builder
m =
  Char -> Builder
Builder.fromChar Char
'm'

semi :: Builder
semi :: Builder
semi =
  Char -> Builder
Builder.fromChar Char
';'

isatty :: Bool
isatty :: Bool
isatty =
  forall a. IO a -> a
unsafePerformIO (CInt -> IO CInt
c_isatty CInt
1) forall a. Eq a => a -> a -> Bool
== CInt
1
{-# NOINLINE isatty #-}

-- Collapse surround/surround to a single surround before phase 1
{-# RULES
"surround/surround" [~1] forall a b c d s.
  surround a b (surround c d s) =
    surround (a <> semi <> c) (b <> semi <> d) s
  #-}