haskeline-0.4: System/Console/Haskeline/InputT.hs
module System.Console.Haskeline.InputT where
import System.Console.Haskeline.Command.History
import System.Console.Haskeline.Command.Undo
import System.Console.Haskeline.Monads as Monads
import System.Console.Haskeline.Prefs
import System.Console.Haskeline.Command(Layout)
import System.Console.Haskeline.Completion
import System.Console.Haskeline.Backend
import System.Console.Haskeline.Term
import System.Directory(getHomeDirectory)
import System.FilePath
import Control.Applicative
import Control.Monad(liftM, ap)
-- | Application-specific customizations to the user interface.
data Settings m = Settings {complete :: CompletionFunc m, -- ^ Custom tab completion
historyFile :: Maybe FilePath
}
-- | Because 'complete' is the only field of 'Settings' depending on @m@,
-- the expression @defaultSettings {completionFunc = f}@ leads to a type error
-- from being too general. This function works around that issue, and may become unnecessary if another field
-- depending on @m@ is added.
setComplete :: CompletionFunc m -> Settings m -> Settings m
setComplete f s = s {complete = f}
-- | A monad transformer which carries all of the state and settings
-- relevant to a line-reading application.
newtype InputT m a = InputT {unInputT :: ReaderT RunTerm
(StateT History (ReaderT Prefs
(ReaderT (Settings m) m))) a}
deriving (Monad,MonadIO, MonadState History,
MonadReader Prefs, MonadReader (Settings m),
MonadReader RunTerm)
instance Monad m => Functor (InputT m) where
fmap = liftM
instance Monad m => Applicative (InputT m) where
pure = return
(<*>) = ap
instance MonadTrans InputT where
lift = InputT . lift . lift . lift . lift
instance MonadException m => MonadException (InputT m) where
block = InputT . block . unInputT
unblock = InputT . unblock . unInputT
catch f h = InputT $ Monads.catch (unInputT f) (unInputT . h)
-- for internal use only
type InputCmdT m = ReaderT Layout (UndoT (StateT HistLog
(ReaderT Prefs (ReaderT (Settings m) m))))
instance MonadIO m => MonadLayout (InputCmdT m) where
runInputCmdT :: MonadIO m => TermOps -> InputCmdT m a -> InputT m a
runInputCmdT tops f = InputT $ do
layout <- liftIO $ getLayout tops
lift $ runHistLog $ runUndoT $ runReaderT' layout f
liftCmdT :: Monad m => m a -> InputCmdT m a
liftCmdT = lift . lift . lift . lift . lift
runInputTWithPrefs :: MonadException m => Prefs -> Settings m -> InputT m a -> m a
runInputTWithPrefs prefs settings (InputT f) = bracket (liftIO myRunTerm)
(liftIO . closeTerm)
$ \run -> runReaderT' settings $ runReaderT' prefs
$ runHistoryFromFile (historyFile settings) (maxHistorySize prefs)
$ runReaderT f run
-- | Run a line-reading application, reading user 'Prefs' from
-- @~/.haskeline@
runInputT :: MonadException m => Settings m -> InputT m a -> m a
runInputT settings f = do
prefs <- liftIO readPrefsFromHome
runInputTWithPrefs prefs settings f
-- | Read 'Prefs' from @~/.haskeline.@ If there is an error reading the file,
-- the 'defaultPrefs' will be returned.
readPrefsFromHome :: IO Prefs
readPrefsFromHome = handle (\(_::IOException) -> return defaultPrefs) $ do
home <- getHomeDirectory
readPrefs (home </> ".haskeline")