From f619613a17fb554a88e11b4f089c54a023e059bc Mon Sep 17 00:00:00 2001 From: "judah.jacobson" <judah.jacobson@gmail.com> Date: Fri, 12 Dec 2008 06:49:22 +0000 Subject: [PATCH] Add module System.Console.Haskeline.History. --- System/Console/Haskeline.hs | 4 +- System/Console/Haskeline/Command/History.hs | 31 ++------- System/Console/Haskeline/History.hs | 72 +++++++++++++++++++++ System/Console/Haskeline/InputT.hs | 11 +++- haskeline.cabal | 1 + 5 files changed, 88 insertions(+), 31 deletions(-) create mode 100644 System/Console/Haskeline/History.hs diff --git a/System/Console/Haskeline.hs b/System/Console/Haskeline.hs index 393395c..7e66b31 100644 --- a/System/Console/Haskeline.hs +++ b/System/Console/Haskeline.hs @@ -59,10 +59,10 @@ module System.Console.Haskeline( import System.Console.Haskeline.LineState import System.Console.Haskeline.Command -import System.Console.Haskeline.Command.History import System.Console.Haskeline.Vi import System.Console.Haskeline.Emacs import System.Console.Haskeline.Prefs +import System.Console.Haskeline.History import System.Console.Haskeline.Monads import System.Console.Haskeline.MonadException import System.Console.Haskeline.InputT @@ -154,7 +154,7 @@ getInputCmdLine tops prefix = do repeatTillFinish tops getEvent prefix ls emode -- Add the line to the history if it's nonempty. case result of - Just line | not (all isSpace line) -> addHistory line + Just line | not (all isSpace line) -> modify (addHistory line) _ -> return () return result diff --git a/System/Console/Haskeline/Command/History.hs b/System/Console/Haskeline/Command/History.hs index 7fc9f10..9442485 100644 --- a/System/Console/Haskeline/Command/History.hs +++ b/System/Console/Haskeline/Command/History.hs @@ -7,13 +7,7 @@ import Control.Monad(liftM,mplus) import System.Console.Haskeline.Monads import Data.List import Data.Maybe(fromMaybe) -import Control.Exception.Extensible(evaluate) -import qualified Data.ByteString as B -import qualified Data.ByteString.UTF8 as UTF8 - -import System.Directory(doesFileExist) - -data History = History {historyLines :: [String]} -- stored in reverse +import System.Console.Haskeline.History data HistLog = HistLog {pastHistory, futureHistory :: [String]} deriving Show @@ -32,28 +26,13 @@ histLog :: History -> HistLog histLog hist = HistLog {pastHistory = historyLines hist, futureHistory = []} runHistoryFromFile :: MonadIO m => Maybe FilePath -> Maybe Int -> StateT History m a -> m a -runHistoryFromFile Nothing _ f = evalStateT' (History []) f +runHistoryFromFile Nothing _ f = evalStateT' emptyHistory f runHistoryFromFile (Just file) stifleAmt f = do - contents <- liftIO $ do - exists <- doesFileExist file - if exists - -- use binary file I/O to avoid Windows CRLF line endings - -- which cause confusion when switching between systems. - then fmap UTF8.toString (B.readFile file) - else return "" - liftIO $ evaluate (length contents) -- force file closed - let oldHistory = History (lines contents) - (x,newHistory) <- runStateT f oldHistory - let stifle = case stifleAmt of - Nothing -> id - Just m -> take m - liftIO $ B.writeFile file $ UTF8.fromString - $ unlines $ stifle $ historyLines newHistory + oldHistory <- liftIO $ readHistory file + (x,newHistory) <- runStateT f (stifleHistory stifleAmt oldHistory) + liftIO $ writeHistory file newHistory return x -addHistory :: MonadState History m => String -> m () -addHistory l = modify $ \(History ls) -> History (l:ls) - runHistLog :: Monad m => StateT HistLog m a -> StateT History m a runHistLog f = do history <- get diff --git a/System/Console/Haskeline/History.hs b/System/Console/Haskeline/History.hs new file mode 100644 index 0000000..0214df6 --- /dev/null +++ b/System/Console/Haskeline/History.hs @@ -0,0 +1,72 @@ +module System.Console.Haskeline.History( + History(), + emptyHistory, + addHistory, + historyLines, + readHistory, + writeHistory, + stifleHistory + ) where + +import qualified Data.Sequence as Seq +import Data.Foldable + +import qualified Data.ByteString as B +import qualified Data.ByteString.UTF8 as UTF8 +import Control.Exception.Extensible(evaluate) + +import System.Directory(doesFileExist) + +data History = History {histLines :: Seq.Seq String, + stifleAmount :: Maybe Int} + -- stored in reverse + +instance Show History where + show = show . histLines + +emptyHistory :: History +emptyHistory = History Seq.empty Nothing + +historyLines :: History -> [String] +historyLines = toList . histLines + +-- TODO: am I doing the right thing, error-handling-wise? +-- should probably just silently catch all errors. + +-- | Reads the line input history from the given file. Returns +-- 'emptyHistory' if the file does not exist. +readHistory :: FilePath -> IO History +readHistory file = do + exists <- doesFileExist file + contents <- if exists + -- use binary file I/O to avoid Windows CRLF line endings + -- which cause confusion when switching between systems. + then fmap UTF8.toString (B.readFile file) + else return "" + evaluate (length contents) -- force file closed + return $ History {histLines = Seq.fromList $ lines contents, + stifleAmount = Nothing} + +-- | Writes the line history to the given file. If there is an +-- error when writing the file, ignores it. +writeHistory :: FilePath -> History -> IO () +writeHistory file = B.writeFile file . UTF8.fromString + . unlines . historyLines + +stifleHistory :: Maybe Int -> History -> History +stifleHistory Nothing hist = hist {stifleAmount = Nothing} +stifleHistory a@(Just n) hist = History {histLines = stifleFnc (histLines hist), + stifleAmount = a} + where + stifleFnc = if n > Seq.length (histLines hist) + then id + else Seq.fromList . take n . toList + +addHistory :: String -> History -> History +addHistory s h = h {histLines = s Seq.<| stifledLines} + where + stifledLines = if maybe True (> Seq.length (histLines h)) (stifleAmount h) + then histLines h + else case Seq.viewr (histLines h) of + Seq.EmptyR -> histLines h -- shouldn't ever happen + ls Seq.:> _ -> ls diff --git a/System/Console/Haskeline/InputT.hs b/System/Console/Haskeline/InputT.hs index f516d4c..bdaa121 100644 --- a/System/Console/Haskeline/InputT.hs +++ b/System/Console/Haskeline/InputT.hs @@ -1,6 +1,7 @@ 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 @@ -12,7 +13,7 @@ import System.Console.Haskeline.Term import System.Directory(getHomeDirectory) import System.FilePath import Control.Applicative -import Control.Monad(liftM, ap) +import qualified Control.Monad.State as State -- | Application-specific customizations to the user interface. data Settings m = Settings {complete :: CompletionFunc m, -- ^ Custom tab completion @@ -38,11 +39,11 @@ newtype InputT m a = InputT {unInputT :: ReaderT RunTerm MonadReader RunTerm) instance Monad m => Functor (InputT m) where - fmap = liftM + fmap = State.liftM instance Monad m => Applicative (InputT m) where pure = return - (<*>) = ap + (<*>) = State.ap instance MonadTrans InputT where lift = InputT . lift . lift . lift . lift @@ -52,6 +53,10 @@ instance MonadException m => MonadException (InputT m) where unblock = InputT . unblock . unInputT catch f h = InputT $ Monads.catch (unInputT f) (unInputT . h) +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)))) diff --git a/haskeline.cabal b/haskeline.cabal index 8f2d3cb..a0c2b29 100644 --- a/haskeline.cabal +++ b/haskeline.cabal @@ -44,6 +44,7 @@ Library System.Console.Haskeline System.Console.Haskeline.Completion System.Console.Haskeline.MonadException + System.Console.Haskeline.History System.Console.Haskeline.IO Other-Modules: System.Console.Haskeline.Backend -- GitLab