{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -- | Example application for the @rhine-terminal@ library. module Main where -- base import System.Exit (exitSuccess) import System.IO hiding (putChar) import Prelude hiding (putChar) -- text import Data.Text (Text) import qualified Data.Text as T -- terminal import System.Terminal import System.Terminal.Internal -- rhine import FRP.Rhine -- rhine-terminal import FRP.Rhine.Terminal type App = TerminalT LocalTerminal IO -- Clocks data Input = Char Char Modifiers | Space | Backspace | Enter | Exit type InputClock = SelectClock TerminalEventClock Input inputClock :: InputClock inputClock = SelectClock { mainClock = TerminalEventClock , select = \case Right (KeyEvent (CharKey k) m) -- Don't display Ctrl-J https://github.com/lpeterse/haskell-terminal/issues/17 | k /= 'J' || m /= ctrlKey -> Just (Char k m) Right (KeyEvent SpaceKey _) -> Just Space Right (KeyEvent BackspaceKey _) -> Just Backspace Right (KeyEvent EnterKey _) -> Just Enter Left _ -> Just Exit _ -> Nothing } type PromptClock = LiftClock IO (TerminalT LocalTerminal) (Millisecond 1000) type AppClock = ParallelClock InputClock PromptClock -- ClSFs inputSource :: ClSF App InputClock () Input inputSource = tagS promptSource :: ClSF App PromptClock () Text promptSource = flip T.cons " > " . (cycle " ." !!) <$> count inputSink :: ClSF App cl Input () inputSink = arrMCl $ \case Char c _ -> putChar c >> flush Space -> putChar ' ' >> flush Backspace -> moveCursorBackward 1 >> deleteChars 1 >> flush Enter -> putLn >> changePrompt " > " >> flush Exit -> do putLn putStringLn "Exiting program." flush liftIO exitSuccess changePrompt :: (MonadScreen m) => Text -> m () changePrompt prmpt = do Position _ column <- getCursorPosition if column /= 0 then do moveCursorBackward column putText prmpt setCursorColumn column else putText prmpt flush promptSink :: ClSF App cl Text () promptSink = arrMCl changePrompt -- Rhines mainRhine :: Rhine App AppClock () () mainRhine = inputRhine |@| promptRhine where inputRhine :: Rhine App InputClock () () inputRhine = inputSource >-> inputSink @@ inputClock promptRhine :: Rhine App PromptClock () () promptRhine = promptSource >-> promptSink @@ liftClock waitClock -- Main main :: IO () main = do hSetBuffering stdin NoBuffering hSetBuffering stdout NoBuffering withTerminal $ \term -> flowTerminal term mainRhine