module System.Console.Haskeline.InputT where
import System.Console.Haskeline.History
import System.Console.Haskeline.Command.History
import System.Console.Haskeline.Command.Undo
import System.Console.Haskeline.Command.KillRing
import System.Console.Haskeline.Monads as Monads
import System.Console.Haskeline.Prefs
import System.Console.Haskeline.Completion
import System.Console.Haskeline.Backend
import System.Console.Haskeline.Term
import Control.Exception (IOException)
import Control.Monad.Catch
import Control.Monad.Fail as Fail
import Control.Monad.Fix
import Data.IORef
import System.Directory(getHomeDirectory)
import System.FilePath
import System.IO
data Settings m = Settings {complete :: CompletionFunc m,
historyFile :: Maybe FilePath,
autoAddHistory :: Bool
}
setComplete :: CompletionFunc m -> Settings m -> Settings m
setComplete f s = s {complete = f}
newtype InputT m a = InputT {unInputT ::
ReaderT RunTerm
(ReaderT (IORef History)
(ReaderT (IORef KillRing)
(ReaderT Prefs
(ReaderT (Settings m) m)))) a}
deriving (Functor, Applicative, Monad, MonadIO,
MonadThrow, MonadCatch, MonadMask)
instance MonadTrans InputT where
lift = InputT . lift . lift . lift . lift . lift
instance ( Fail.MonadFail m ) => Fail.MonadFail (InputT m) where
fail = lift . Fail.fail
instance ( MonadFix m ) => MonadFix (InputT m) where
mfix f = InputT (mfix (unInputT . f))
withRunInBase :: Monad m =>
((forall a . InputT m a -> m a) -> m b) -> InputT m b
withRunInBase inner = InputT $ do
runTerm <- ask
history <- ask
killRing <- ask
prefs <- ask
settings <- ask
lift $ lift $ lift $ lift $ lift $ inner $
flip runReaderT settings .
flip runReaderT prefs .
flip runReaderT killRing .
flip runReaderT history .
flip runReaderT runTerm .
unInputT
getHistory :: MonadIO m => InputT m History
getHistory = InputT get
putHistory :: MonadIO m => History -> InputT m ()
putHistory = InputT . put
modifyHistory :: MonadIO m => (History -> History) -> InputT m ()
modifyHistory = InputT . modify
type InputCmdT m = StateT Layout (UndoT (StateT HistLog (ReaderT (IORef KillRing)
(ReaderT Prefs (ReaderT (Settings m) m)))))
runInputCmdT :: MonadIO m => TermOps -> InputCmdT m a -> InputT m a
runInputCmdT tops f = InputT $ do
layout <- liftIO $ getLayout tops
history <- get
lift $ lift $ evalStateT' (histLog history) $ runUndoT $ evalStateT' layout f
instance (MonadIO m, MonadMask m) => CommandMonad (InputCmdT m) where
runCompletion lcs = do
settings <- ask
lift $ lift $ lift $ lift $ lift $ lift $ complete settings lcs
runInputTWithPrefs :: (MonadIO m, MonadMask m) => Prefs -> Settings m -> InputT m a -> m a
runInputTWithPrefs = runInputTBehaviorWithPrefs defaultBehavior
runInputT :: (MonadIO m, MonadMask m) => Settings m -> InputT m a -> m a
runInputT = runInputTBehavior defaultBehavior
haveTerminalUI :: Monad m => InputT m Bool
haveTerminalUI = InputT $ asks isTerminalStyle
data Behavior = Behavior (IO RunTerm)
withBehavior :: (MonadIO m, MonadMask m) => Behavior -> (RunTerm -> m a) -> m a
withBehavior (Behavior run) f = bracket (liftIO run) (liftIO . closeTerm) f
runInputTBehavior :: (MonadIO m, MonadMask m) => Behavior -> Settings m -> InputT m a -> m a
runInputTBehavior behavior settings f = withBehavior behavior $ \run -> do
prefs <- if isTerminalStyle run
then liftIO readPrefsFromHome
else return defaultPrefs
execInputT prefs settings run f
runInputTBehaviorWithPrefs :: (MonadIO m, MonadMask m)
=> Behavior -> Prefs -> Settings m -> InputT m a -> m a
runInputTBehaviorWithPrefs behavior prefs settings f
= withBehavior behavior $ flip (execInputT prefs settings) f
execInputT :: (MonadIO m, MonadMask m) => Prefs -> Settings m -> RunTerm
-> InputT m a -> m a
execInputT prefs settings run (InputT f)
= runReaderT' settings $ runReaderT' prefs
$ runKillRing
$ runHistoryFromFile (historyFile settings) (maxHistorySize prefs)
$ runReaderT f run
mapInputT :: (forall b . m b -> m b) -> InputT m a -> InputT m a
mapInputT f = InputT . mapReaderT (mapReaderT (mapReaderT
(mapReaderT (mapReaderT f))))
. unInputT
defaultBehavior :: Behavior
defaultBehavior = Behavior defaultRunTerm
useFileHandle :: Handle -> Behavior
useFileHandle = Behavior . fileHandleRunTerm
useFile :: FilePath -> Behavior
useFile file = Behavior $ do
h <- openBinaryFile file ReadMode
rt <- fileHandleRunTerm h
return rt { closeTerm = closeTerm rt >> hClose h}
preferTerm :: Behavior
preferTerm = Behavior terminalRunTerm
readPrefsFromHome :: IO Prefs
readPrefsFromHome = handle (\(_::IOException) -> return defaultPrefs) $ do
home <- getHomeDirectory
readPrefs (home </> ".haskeline")