packages feed

grapesy-1.0.0: interop/Interop/Util/ANSI.hs

{-# LANGUAGE OverloadedStrings #-}

module Interop.Util.ANSI (
    Doc(Color, Show)
  , putDoc
  , putDocLn
    -- * Re-exports
  , ColorIntensity(..)
  , Color(..)
  , IsString(..)
  ) where

import Data.String
import System.Console.ANSI
import System.IO

data Doc =
    Color Color Doc
  | String String
  | forall a. Show a => Show a
  | Empty
  | Append Doc Doc

instance Semigroup Doc where (<>)       = Append
instance Monoid    Doc where mempty     = Empty
instance IsString  Doc where fromString = String

putDoc :: Doc -> IO ()
putDoc = \doc -> do
    stdoutSupportsANSI <- hSupportsANSIColor stdout
    if stdoutSupportsANSI
      then go doc
      else putStr $ flattenDoc doc
  where
    -- By rights we should keep track of the state of the console here, so that
    -- we can properly deal with nesting, rather than just resetting everything.
    -- For now this is good enough.
    go :: Doc -> IO ()
    go (Color color doc) = do
      setSGR [SetColor Foreground Vivid color]
      go doc
      setSGR [Reset]
    go (String str) =
      putStr str
    go (Show x) =
      putStr $ show x
    go Empty =
      return ()
    go (Append doc1 doc2) = do
      go doc1
      go doc2

putDocLn :: Doc -> IO ()
putDocLn doc = putDoc (doc <> "\n")

flattenDoc :: Doc -> String
flattenDoc (Color _ doc)      = flattenDoc doc
flattenDoc (String str)       = str
flattenDoc (Show x)           = show x
flattenDoc Empty              = mempty
flattenDoc (Append doc1 doc2) = mappend (flattenDoc doc1) (flattenDoc doc2)