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.Monads as Monads import System.Console.Haskeline.Prefs 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 qualified Control.Monad.State as State -- | Application-specific customizations to the user interface. data Settings m = Settings {complete :: CompletionFunc m, -- ^ Custom tab completion. historyFile :: Maybe FilePath, -- ^ Where to read/write the history at the -- start and end of each -- line input session. autoAddHistory :: Bool -- ^ If 'True', each nonblank line returned by -- @getInputLine@ will be automatically added to the history. } -- | 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, MonadException, MonadState History, MonadReader Prefs, MonadReader (Settings m), MonadReader RunTerm) instance Monad m => Functor (InputT m) where fmap = State.liftM instance Monad m => Applicative (InputT m) where pure = return (<*>) = State.ap instance MonadTrans InputT where lift = InputT . lift . lift . lift . lift instance Monad m => State.MonadState History (InputT m) where get = get put = put -- 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")