doclayout-0.5: src/Text/DocLayout/ANSIFont.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Text.DocLayout.ANSIFont
( Font(..)
, baseFont
, StyleReq(..)
, Weight(..)
, Shape(..)
, Color8(..)
, Underline(..)
, Strikeout(..)
, Foreground(..)
, Background(..)
, (~>)
, renderFont
, renderOSC8
) where
import Data.Data (Data)
import Data.String
import Data.Text (Text)
data Font = Font
{ ftWeight :: Weight,
ftShape :: Shape,
ftUnderline :: Underline,
ftStrikeout :: Strikeout,
ftForeground :: Foreground,
ftBackground :: Background,
ftLink :: Maybe Text
}
deriving (Show, Eq, Read, Data, Ord)
baseFont :: Font
baseFont = Font Normal Roman ULNone Unstruck FGDefault BGDefault Nothing
data Weight = Normal | Bold deriving (Show, Eq, Read, Data, Ord)
data Shape = Roman | Italic deriving (Show, Eq, Read, Data, Ord)
data Color8 = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White deriving (Show, Eq, Enum, Read, Data, Ord)
data Underline = ULNone | ULSingle | ULDouble | ULCurly deriving (Show, Eq, Read, Data, Ord)
data Strikeout = Unstruck | Struck deriving (Show, Eq, Read, Data, Ord)
data Foreground = FGDefault | FG Color8 deriving (Show, Eq, Read, Data, Ord)
data Background = BGDefault | BG Color8 deriving (Show, Eq, Read, Data, Ord)
data StyleReq
= RWeight Weight
| RShape Shape
| RForeground Foreground
| RBackground Background
| RUnderline Underline
| RStrikeout Strikeout
deriving (Show, Eq, Read, Data, Ord)
(~>) :: Font -> StyleReq -> Font
(~>) f (RWeight w) = f{ftWeight = w}
(~>) f (RShape s) = f{ftShape = s}
(~>) f (RForeground c) = f{ftForeground = c}
(~>) f (RBackground c) = f{ftBackground = c}
(~>) f (RUnderline u) = f{ftUnderline = u}
(~>) f (RStrikeout u) = f{ftStrikeout = u}
rawSGR :: (Semigroup a, IsString a) => a -> a
rawSGR n = "\ESC[" <> n <> "m"
class SGR b where
renderSGR :: (Semigroup a, IsString a) => b -> a
instance SGR Weight where
renderSGR Normal = rawSGR "22"
renderSGR Bold = rawSGR "1"
instance SGR Shape where
renderSGR Roman = rawSGR "23"
renderSGR Italic = rawSGR "3"
instance SGR Foreground where
renderSGR FGDefault = rawSGR "39"
renderSGR (FG a) = (rawSGR . fromString . show . (+) 30 . fromEnum) a
instance SGR Background where
renderSGR BGDefault = rawSGR "49"
renderSGR (BG a) = (rawSGR . fromString . show . (+) 40 . fromEnum) a
instance SGR Underline where
renderSGR ULNone = rawSGR "24"
renderSGR ULSingle = rawSGR "4"
renderSGR ULDouble = rawSGR "21"
renderSGR ULCurly = rawSGR "4:3"
instance SGR Strikeout where
renderSGR Unstruck = rawSGR "29"
renderSGR Struck = rawSGR "9"
renderFont :: (Semigroup a, IsString a) => Font -> a
renderFont f
| f == baseFont = rawSGR "0"
| otherwise =
renderSGR (ftWeight f)
<> renderSGR (ftShape f)
<> renderSGR (ftForeground f)
<> renderSGR (ftBackground f)
<> renderSGR (ftUnderline f)
<> renderSGR (ftStrikeout f)
renderOSC8 :: (Semigroup a, IsString a) => Maybe a -> a
renderOSC8 Nothing = "\ESC]8;;\ESC\\"
renderOSC8 (Just t) = "\ESC]8;;" <> t <> "\ESC\\"