fused-effects-readline-0.1.0.0: src/Control/Carrier/Readline/Haskeline.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
module Control.Carrier.Readline.Haskeline
( -- * Readline carrier
runReadline
, runReadlineWithHistory
, ReadlineC(ReadlineC)
-- * Readline effect
, module Control.Effect.Readline
) where
import Control.Algebra
import Control.Carrier.Lift
import Control.Carrier.Reader
import Control.Effect.Readline
#if MIN_VERSION_haskeline(0, 8, 0)
import Control.Monad.Catch (MonadMask(..))
#endif
import Control.Monad.Fix (MonadFix)
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad.Trans.Class (MonadTrans(..))
import Data.Text.Prettyprint.Doc
import Data.Text.Prettyprint.Doc.Render.Terminal
import System.Console.Haskeline
import System.Console.Terminal.Size as Size
import System.Directory
import System.Environment
import System.FilePath
import System.IO (stdout)
#if MIN_VERSION_haskeline(0, 8, 0)
runReadline :: (MonadIO m, MonadMask m) => Prefs -> Settings m -> ReadlineC m a -> m a
#else
runReadline :: MonadException m => Prefs -> Settings m -> ReadlineC m a -> m a
#endif
runReadline prefs settings (ReadlineC m) = runInputTWithPrefs prefs settings (runM (runReader (Line 0) m))
#if MIN_VERSION_haskeline(0, 8, 0)
runReadlineWithHistory :: (MonadIO m, MonadMask m) => ReadlineC m a -> m a
#else
runReadlineWithHistory :: MonadException m => ReadlineC m a -> m a
#endif
runReadlineWithHistory block = do
homeDir <- liftIO getHomeDirectory
prefs <- liftIO $ readPrefs (homeDir </> ".haskeline")
prog <- liftIO getExecutablePath
let settingsDir = homeDir </> ".local" </> dropExtension (takeFileName prog)
settings = Settings
{ complete = noCompletion
, historyFile = Just (settingsDir </> "repl_history")
, autoAddHistory = True
}
liftIO $ createDirectoryIfMissing True settingsDir
runReadline prefs settings block
newtype ReadlineC m a = ReadlineC (ReaderC Line (LiftC (InputT m)) a)
deriving (Applicative, Functor, Monad, MonadFix, MonadIO)
instance MonadTrans ReadlineC where
lift = ReadlineC . lift . lift . lift
#if MIN_VERSION_haskeline(0, 8, 0)
instance (MonadIO m, MonadMask m) => Algebra Readline (ReadlineC m) where
#else
instance MonadException m => Algebra Readline (ReadlineC m) where
#endif
alg _ sig ctx = case sig of
Prompt prompt -> ReadlineC $ do
str <- sendM (getInputLine @m (cyan <> prompt <> plain))
Line line <- ask
local increment $ pure ((line, str) <$ ctx)
where cyan = "\ESC[1;36m\STX"
plain = "\ESC[0m\STX"
Print doc -> do
s <- maybe 80 Size.width <$> liftIO size
let docstream = layoutSmart (layoutOptions s) (doc <> line)
(<$ ctx) <$> (liftIO . renderIO stdout $ docstream)
where layoutOptions s = defaultLayoutOptions { layoutPageWidth = AvailablePerLine s 0.8 }
newtype Line = Line Int
increment :: Line -> Line
increment (Line n) = Line (n + 1)