Blame Test/QuickCheck/Text.hs

Packit 1d8052
-- | Terminal control. Internal QuickCheck module.
Packit 1d8052
module Test.QuickCheck.Text
Packit 1d8052
  ( Str(..)
Packit 1d8052
  , ranges
Packit 1d8052
Packit 1d8052
  , number
Packit 1d8052
  , short
Packit 1d8052
  , showErr
Packit 1d8052
  , oneLine
Packit 1d8052
  , isOneLine
Packit 1d8052
  , bold
Packit 1d8052
Packit 1d8052
  , newTerminal
Packit 1d8052
  , withStdioTerminal
Packit 1d8052
  , withNullTerminal
Packit 1d8052
  , terminalOutput
Packit 1d8052
  , handle
Packit 1d8052
  , Terminal
Packit 1d8052
  , putTemp
Packit 1d8052
  , putPart
Packit 1d8052
  , putLine
Packit 1d8052
  )
Packit 1d8052
 where
Packit 1d8052
Packit 1d8052
--------------------------------------------------------------------------
Packit 1d8052
-- imports
Packit 1d8052
Packit 1d8052
import System.IO
Packit 1d8052
  ( hFlush
Packit 1d8052
  , hPutStr
Packit 1d8052
  , stdout
Packit 1d8052
  , stderr
Packit 1d8052
  , Handle
Packit 1d8052
  , BufferMode (..)
Packit 1d8052
  , hGetBuffering
Packit 1d8052
  , hSetBuffering
Packit 1d8052
  , hIsTerminalDevice
Packit 1d8052
  )
Packit 1d8052
Packit 1d8052
import Data.IORef
Packit 1d8052
import Test.QuickCheck.Exception
Packit 1d8052
Packit 1d8052
--------------------------------------------------------------------------
Packit 1d8052
-- literal string
Packit 1d8052
Packit 1d8052
newtype Str = MkStr String
Packit 1d8052
Packit 1d8052
instance Show Str where
Packit 1d8052
  show (MkStr s) = s
Packit 1d8052
Packit 1d8052
ranges :: (Show a, Integral a) => a -> a -> Str
Packit 1d8052
ranges k n = MkStr (show n' ++ " -- " ++ show (n'+k-1))
Packit 1d8052
 where
Packit 1d8052
  n' = k * (n `div` k)
Packit 1d8052
Packit 1d8052
--------------------------------------------------------------------------
Packit 1d8052
-- formatting
Packit 1d8052
Packit 1d8052
number :: Int -> String -> String
Packit 1d8052
number n s = show n ++ " " ++ s ++ if n == 1 then "" else "s"
Packit 1d8052
Packit 1d8052
short :: Int -> String -> String
Packit 1d8052
short n s
Packit 1d8052
  | n < k     = take (n-2-i) s ++ ".." ++ drop (k-i) s
Packit 1d8052
  | otherwise = s
Packit 1d8052
 where
Packit 1d8052
  k = length s
Packit 1d8052
  i = if n >= 5 then 3 else 0
Packit 1d8052
Packit 1d8052
showErr :: Show a => a -> String
Packit 1d8052
showErr = unwords . words . show
Packit 1d8052
Packit 1d8052
oneLine :: String -> String
Packit 1d8052
oneLine = unwords . words
Packit 1d8052
Packit 1d8052
isOneLine :: String -> Bool
Packit 1d8052
isOneLine xs = '\n' `notElem` xs
Packit 1d8052
Packit 1d8052
bold :: String -> String
Packit 1d8052
-- not portable:
Packit 1d8052
--bold s = "\ESC[1m" ++ s ++ "\ESC[0m"
Packit 1d8052
bold s = s -- for now
Packit 1d8052
Packit 1d8052
--------------------------------------------------------------------------
Packit 1d8052
-- putting strings
Packit 1d8052
Packit 1d8052
data Terminal
Packit 1d8052
  = MkTerminal (IORef ShowS) (IORef Int) (String -> IO ()) (String -> IO ())
Packit 1d8052
Packit 1d8052
newTerminal :: (String -> IO ()) -> (String -> IO ()) -> IO Terminal
Packit 1d8052
newTerminal out err =
Packit 1d8052
  do res <- newIORef (showString "")
Packit 1d8052
     tmp <- newIORef 0
Packit 1d8052
     return (MkTerminal res tmp out err)
Packit 1d8052
Packit 1d8052
withBuffering :: IO a -> IO a
Packit 1d8052
withBuffering action = do
Packit 1d8052
  mode <- hGetBuffering stderr
Packit 1d8052
  -- By default stderr is unbuffered.  This is very slow, hence we explicitly
Packit 1d8052
  -- enable line buffering.
Packit 1d8052
  hSetBuffering stderr LineBuffering
Packit 1d8052
  action `finally` hSetBuffering stderr mode
Packit 1d8052
Packit 1d8052
withStdioTerminal :: (Terminal -> IO a) -> IO a
Packit 1d8052
withStdioTerminal action = do
Packit 1d8052
  isatty <- hIsTerminalDevice stderr
Packit 1d8052
  let err = if isatty then handle stderr else const (return ())
Packit 1d8052
  withBuffering (newTerminal (handle stdout) err >>= action)
Packit 1d8052
Packit 1d8052
withNullTerminal :: (Terminal -> IO a) -> IO a
Packit 1d8052
withNullTerminal action =
Packit 1d8052
  newTerminal (const (return ())) (const (return ())) >>= action
Packit 1d8052
Packit 1d8052
terminalOutput :: Terminal -> IO String
Packit 1d8052
terminalOutput (MkTerminal res _ _ _) = fmap ($ "") (readIORef res)
Packit 1d8052
Packit 1d8052
handle :: Handle -> String -> IO ()
Packit 1d8052
handle h s = do
Packit 1d8052
  hPutStr h s
Packit 1d8052
  hFlush h
Packit 1d8052
Packit 1d8052
flush :: Terminal -> IO ()
Packit 1d8052
flush (MkTerminal _ tmp _ err) =
Packit 1d8052
  do n <- readIORef tmp
Packit 1d8052
     writeIORef tmp 0
Packit 1d8052
     err (replicate n ' ' ++ replicate n '\b')
Packit 1d8052
Packit 1d8052
putPart, putTemp, putLine :: Terminal -> String -> IO ()
Packit 1d8052
putPart tm@(MkTerminal res _ out _) s =
Packit 1d8052
  do flush tm
Packit 1d8052
     force s
Packit 1d8052
     out s
Packit 1d8052
     modifyIORef res (. showString s)
Packit 1d8052
  where
Packit 1d8052
    force :: [a] -> IO ()
Packit 1d8052
    force = evaluate . seqList
Packit 1d8052
Packit 1d8052
    seqList :: [a] -> ()
Packit 1d8052
    seqList [] = ()
Packit 1d8052
    seqList (x:xs) = x `seq` seqList xs
Packit 1d8052
Packit 1d8052
putLine tm s = putPart tm (s ++ "\n")
Packit 1d8052
Packit 1d8052
putTemp tm@(MkTerminal _ tmp _ err) s =
Packit 1d8052
  do flush tm
Packit 1d8052
     err (s ++ [ '\b' | _ <- s ])
Packit 1d8052
     modifyIORef tmp (+ length s)
Packit 1d8052
Packit 1d8052
--------------------------------------------------------------------------
Packit 1d8052
-- the end.