packages feed

freestyle-0.1.0.0: test/Main.hs

module Main (main) where

import Control.Concurrent
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad
import Data.Semigroup
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as T
import Freestyle
import Prettyprinter
import Prettyprinter.Render.Util.SimpleDocTree
import Prettyprinter.Render.Terminal
import System.IO

main :: IO ()
main = join $ atomically $ do
  s <- newTVar initMain
  f <- newFreestyle
  return $ mapConcurrently_ id
    [ runFreestyle f $ mainCfg s
    , runWheel s
    , runKeyReader s
    , runWaterfall s
    ]

data Main = Main
  { colorWheel :: [Color]
  , offset :: Double
  , switch :: Bool
  , waterfall :: [[Color]]
  }
  deriving Eq

initMain :: Main
initMain = Main
  { colorWheel = [Red, Green, Yellow, Blue]
  , offset = 0
  , switch = False
  , waterfall =
    [ rotate i r
    | (i, r) <- zip [1..] $ replicate 25 $ stimes (5 :: Int)
      [Blue, Green, Blue, Blue, Yellow, Red, Cyan, Green, Blue]
    ]
  }

mainCfg :: TVar Main -> FreestyleCfg Main Style
mainCfg s = FreestyleCfg
  { readState = readTVar s
  , layoutDoc = return . layoutPretty defaultLayoutOptions
  , renderDoc = return . renderDisplay
  , drawState = mainDraw
  }

mainDraw :: Main -> STM (Doc Style)
mainDraw (Main cs rads en wf) =
  return $ vsep
    [ hcat $ zipWith renderColorChar (cycle cs) "~~~~~~~~~~ Freestyle!"
    , renderSin rads
    , pretty $ if en then "ON" else "OFF"
    , drawWaterfall wf
    ]

runWheel :: TVar Main -> IO a
runWheel s = forever $ do
  atomically $ modifyTVar' s updateWheel
  threadDelay 100000
  where
    updateWheel m@(Main cs o _ _) = m{colorWheel=cs', offset=o'}
      where
        cs' = drop 1 cs <> take 1 cs
        o'  = o + 0.1

runKeyReader :: TVar Main -> IO a
runKeyReader s = do
  hSetBuffering stdin NoBuffering
  forever $ do
    ch <- getChar
    when (ch == 'a') $
      atomically $ modifyTVar' s $ \m -> m{switch=not $ switch m}

runWaterfall :: TVar Main -> IO a
runWaterfall s = forever $ do
  atomically $ modifyTVar' s $ \m ->
    m{waterfall=cycleWaterfall $ waterfall m}
  threadDelay 50000

rotate :: Int -> [a] -> [a]
rotate _ [] = []
rotate n xs = zipWith const (drop n (cycle xs)) xs

drawWaterfall :: [[Color]] -> Doc Style
drawWaterfall rs = vsep [renderRow r | r <- rs]
  where
    renderRow r = hcat [renderCell c | c <- r]
      where
        renderCell c = annotate (Ansi $ bgColor c) space

cycleWaterfall :: [[Color]] -> [[Color]]
cycleWaterfall w = drop 1 w <> take 1 w

renderSin :: Double -> Doc ann
renderSin o = vsep [renderRow r | r <- [-5..5]]
  where
    renderRow r = hcat [renderCell c | c <- [0..31]]
      where
        renderCell c
          | floor (y * 5) == (r :: Integer) = pretty "*"
          | otherwise = pretty " "
          where
            y = sin (c / 10 + o)

renderColorChar :: Color -> Char -> Doc Style
renderColorChar clr ch = annotate (Ansi $ color clr) $ pretty ch

renderDisplay :: SimpleDocStream Style -> Text
renderDisplay = renderSdt . treeForm

renderSdt :: SimpleDocTree Style -> Text
renderSdt sdt = case sdt of
  STEmpty -> mempty
  STChar c -> T.singleton c
  STText _ t -> T.fromStrict t
  STLine i -> T.singleton '\n' <> T.replicate (fromIntegral i) (T.singleton ' ')
  STAnn ann rest -> renderStyle ann rest $ renderSdt rest
  STConcat xs -> foldMap renderSdt xs

renderStyle :: Style -> SimpleDocTree Style -> Text -> Text
renderStyle d _ s = case d of
  Ansi a -> go $ annotate a $ pretty s
  Title  -> go $ annotate (bold <> underlined <> color Blue) $ pretty s
  where
    go = renderLazy . layoutPretty defaultLayoutOptions

data Style
  = Ansi AnsiStyle
  | Title
  deriving (Eq, Show)