fused-effects-readline-0.0.0.0: src/Control/Carrier/Readline/Haskeline.hs
{-# LANGUAGE GeneralizedNewtypeDeriving, LambdaCase, MultiParamTypeClasses, ScopedTypeVariables, TypeApplications #-}
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
import Control.Monad.Fix (MonadFix)
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad.Trans.Class (MonadTrans(..))
import Data.Coerce (coerce)
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)
runReadline :: MonadException m => Prefs -> Settings m -> ReadlineC m a -> m a
runReadline prefs settings (ReadlineC m) = runInputTWithPrefs prefs (coerce settings) (runM (runReader (Line 0) m))
runReadlineWithHistory :: MonadException m => ReadlineC m a -> m a
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 { runReadlineC :: ReaderC Line (LiftC (InputT m)) a }
deriving (Applicative, Functor, Monad, MonadFix, MonadIO)
instance MonadTrans ReadlineC where
lift = ReadlineC . lift . lift . lift
instance MonadException m => Algebra Readline (ReadlineC m) where
alg = \case
Prompt prompt k -> ReadlineC $ do
str <- sendM (getInputLine @m (cyan <> prompt <> plain))
Line line <- ask
local increment (runReadlineC (k line str))
where cyan = "\ESC[1;36m\STX"
plain = "\ESC[0m\STX"
Print doc k -> do
s <- maybe 80 Size.width <$> liftIO size
liftIO (renderIO stdout (layoutSmart defaultLayoutOptions { layoutPageWidth = AvailablePerLine s 0.8 } (doc <> line)))
k
newtype Line = Line Int
increment :: Line -> Line
increment (Line n) = Line (n + 1)