From 4ea72562a11e860c1ebc14f58714491ea81bfdb0 Mon Sep 17 00:00:00 2001 From: Nikita Kartashov Date: Tue, 2 Feb 2016 13:09:29 +0300 Subject: [PATCH 1/3] Support flushing command history Previously command history was only flushed after the end of session, this commit adds flushHistory function which performs the flushing and a flag to enforce flushing after every command. --- System/Console/Haskeline.hs | 10 ++++++++-- System/Console/Haskeline/InputT.hs | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/System/Console/Haskeline.hs b/System/Console/Haskeline.hs index f42277bc..7febf8ab 100644 --- a/System/Console/Haskeline.hs +++ b/System/Console/Haskeline.hs @@ -70,6 +70,7 @@ module System.Console.Haskeline( getHistory, putHistory, modifyHistory, + flushHistory, -- * Ctrl-C handling withInterrupt, Interrupt(..), @@ -94,6 +95,7 @@ import System.Console.Haskeline.RunCommand import Control.Monad.Catch (MonadMask, handle) import Data.Char (isSpace, isPrint) import Data.Maybe (isJust) +import Control.Monad (when) import System.IO @@ -109,7 +111,8 @@ import System.IO defaultSettings :: MonadIO m => Settings m defaultSettings = Settings {complete = completeFilename, historyFile = Nothing, - autoAddHistory = True} + autoAddHistory = True, + flushEveryCommand = False} {- $outputfncs The following functions enable cross-platform output of text that may contain @@ -194,7 +197,10 @@ maybeAddHistory result = do AlwaysAdd -> addHistory IgnoreConsecutive -> addHistoryUnlessConsecutiveDupe IgnoreAll -> addHistoryRemovingAllDupes - in modifyHistory (adder line) + in do + modifyHistory (adder line) + when (flushEveryCommand settings) + flushHistory _ -> return () ---------- diff --git a/System/Console/Haskeline/InputT.hs b/System/Console/Haskeline/InputT.hs index d8849b96..4da8bedc 100644 --- a/System/Console/Haskeline/InputT.hs +++ b/System/Console/Haskeline/InputT.hs @@ -25,8 +25,10 @@ data Settings m = Settings {complete :: CompletionFunc m, -- ^ Custom tab comple 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 + autoAddHistory :: Bool, -- ^ If 'True', each nonblank line returned by -- @getInputLine@ will be automatically added to the history. + flushEveryCommand :: Bool -- ^ If 'True' and @historyFile@ not 'Nothing' + -- flushed command history after every command } @@ -94,6 +96,17 @@ getHistory = InputT get putHistory :: MonadIO m => History -> InputT m () putHistory = InputT . put +-- | Flush history if @historyFile@ is not 'Nothing' +flushHistory :: forall m . MonadIO m => InputT m () +flushHistory = do + settings :: Settings m <- InputT ask + getHistory >>= maybeFlushHistory (historyFile settings) + +-- | Flushes history if given filepath is not 'Nothing' +maybeFlushHistory :: MonadIO m => Maybe FilePath -> History -> InputT m () +maybeFlushHistory Nothing _ = return () +maybeFlushHistory (Just f) hist = liftIO $ writeHistory f hist + -- | Change the current line input history. modifyHistory :: MonadIO m => (History -> History) -> InputT m () modifyHistory = InputT . modify From b76d726749f2674a3b78c820af906c092afb08cf Mon Sep 17 00:00:00 2001 From: Nikita Kartashov Date: Tue, 2 Feb 2016 13:09:29 +0300 Subject: [PATCH 2/3] Support flushing command history Previously command history was only flushed after the end of session, this commit adds flushHistory function which performs the flushing and a flag to Prefs to enforce flushing after every command. --- System/Console/Haskeline.hs | 7 +++---- System/Console/Haskeline/InputT.hs | 9 +++------ System/Console/Haskeline/Prefs.hs | 10 +++++++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/System/Console/Haskeline.hs b/System/Console/Haskeline.hs index 7febf8ab..2bb9f684 100644 --- a/System/Console/Haskeline.hs +++ b/System/Console/Haskeline.hs @@ -111,8 +111,7 @@ import System.IO defaultSettings :: MonadIO m => Settings m defaultSettings = Settings {complete = completeFilename, historyFile = Nothing, - autoAddHistory = True, - flushEveryCommand = False} + autoAddHistory = True} {- $outputfncs The following functions enable cross-platform output of text that may contain @@ -191,6 +190,7 @@ maybeAddHistory :: forall m . MonadIO m => Maybe String -> InputT m () maybeAddHistory result = do settings :: Settings m <- InputT ask histDupes <- InputT $ asks historyDuplicates + doFlush <- InputT $ asks flushEveryCommand case result of Just line | autoAddHistory settings && not (all isSpace line) -> let adder = case histDupes of @@ -199,8 +199,7 @@ maybeAddHistory result = do IgnoreAll -> addHistoryRemovingAllDupes in do modifyHistory (adder line) - when (flushEveryCommand settings) - flushHistory + when doFlush flushHistory _ -> return () ---------- diff --git a/System/Console/Haskeline/InputT.hs b/System/Console/Haskeline/InputT.hs index 4da8bedc..5ec57568 100644 --- a/System/Console/Haskeline/InputT.hs +++ b/System/Console/Haskeline/InputT.hs @@ -25,12 +25,9 @@ data Settings m = Settings {complete :: CompletionFunc m, -- ^ Custom tab comple 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 + autoAddHistory :: Bool -- ^ If 'True', each nonblank line returned by -- @getInputLine@ will be automatically added to the history. - flushEveryCommand :: Bool -- ^ If 'True' and @historyFile@ not 'Nothing' - -- flushed command history after every command - - } + } -- | Because 'complete' is the only field of 'Settings' depending on @m@, -- the expression @defaultSettings {completionFunc = f}@ leads to a type error @@ -96,7 +93,7 @@ getHistory = InputT get putHistory :: MonadIO m => History -> InputT m () putHistory = InputT . put --- | Flush history if @historyFile@ is not 'Nothing' +-- | Writes command history to file if 'historyFile' is not 'Nothing' flushHistory :: forall m . MonadIO m => InputT m () flushHistory = do settings :: Settings m <- InputT ask diff --git a/System/Console/Haskeline/Prefs.hs b/System/Console/Haskeline/Prefs.hs index 8033a8f8..bc0b8d52 100644 --- a/System/Console/Haskeline/Prefs.hs +++ b/System/Console/Haskeline/Prefs.hs @@ -48,7 +48,10 @@ data Prefs = Prefs { bellStyle :: !BellStyle, -- presses @TAB@ again. customBindings :: Map.Map Key [Key], -- (termName, keysequence, key) - customKeySequences :: [(Maybe String, String,Key)] + customKeySequences :: [(Maybe String, String,Key)], + flushEveryCommand :: Bool + -- ^ If 'True' and @historyFile@ not 'Nothing' + -- flushes command history after every command } deriving Show @@ -77,8 +80,9 @@ defaultPrefs = Prefs {bellStyle = AudibleBell, listCompletionsImmediately = True, historyDuplicates = AlwaysAdd, customBindings = Map.empty, - customKeySequences = [] - } + customKeySequences = [], + flushEveryCommand = False + } mkSettor :: Read a => (a -> Prefs -> Prefs) -> String -> Prefs -> Prefs mkSettor f str = maybe id f (readMaybe str) From 2497daa972390c23efed8d026b1c6884414fa594 Mon Sep 17 00:00:00 2001 From: Owen Shepherd Date: Tue, 24 Nov 2020 23:19:14 +0000 Subject: [PATCH 3/3] Append to history when using flushHistoryAfterEveryCommand --- System/Console/Haskeline.hs | 9 +++++---- System/Console/Haskeline/Command/History.hs | 17 ++++++++++++----- System/Console/Haskeline/History.hs | 4 ++-- System/Console/Haskeline/InputT.hs | 17 ++++------------- System/Console/Haskeline/Prefs.hs | 7 ++++--- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/System/Console/Haskeline.hs b/System/Console/Haskeline.hs index 2bb9f684..a1d80f3e 100644 --- a/System/Console/Haskeline.hs +++ b/System/Console/Haskeline.hs @@ -70,7 +70,6 @@ module System.Console.Haskeline( getHistory, putHistory, modifyHistory, - flushHistory, -- * Ctrl-C handling withInterrupt, Interrupt(..), @@ -190,7 +189,7 @@ maybeAddHistory :: forall m . MonadIO m => Maybe String -> InputT m () maybeAddHistory result = do settings :: Settings m <- InputT ask histDupes <- InputT $ asks historyDuplicates - doFlush <- InputT $ asks flushEveryCommand + doFlush <- InputT $ asks incAppendHistory case result of Just line | autoAddHistory settings && not (all isSpace line) -> let adder = case histDupes of @@ -198,8 +197,10 @@ maybeAddHistory result = do IgnoreConsecutive -> addHistoryUnlessConsecutiveDupe IgnoreAll -> addHistoryRemovingAllDupes in do - modifyHistory (adder line) - when doFlush flushHistory + modifyHistory (adder line) + when doFlush $ case historyFile settings of + Nothing -> pure () + Just f -> liftIO $ appendFile f (line ++ "\n") _ -> return () ---------- diff --git a/System/Console/Haskeline/Command/History.hs b/System/Console/Haskeline/Command/History.hs index f1891f2d..c3e3b40a 100644 --- a/System/Console/Haskeline/Command/History.hs +++ b/System/Console/Haskeline/Command/History.hs @@ -9,6 +9,7 @@ import Data.List import Data.Maybe(fromMaybe) import System.Console.Haskeline.History import Data.IORef +import Control.Monad (when) import Control.Monad.Catch data HistLog = HistLog {pastHistory, futureHistory :: [[Grapheme]]} @@ -28,12 +29,17 @@ histLog :: History -> HistLog histLog hist = HistLog {pastHistory = map stringToGraphemes $ historyLines hist, futureHistory = []} -runHistoryFromFile :: (MonadIO m, MonadMask m) => Maybe FilePath -> Maybe Int - -> ReaderT (IORef History) m a -> m a -runHistoryFromFile Nothing _ f = do +runHistoryFromFile + :: (MonadIO m, MonadMask m) + => Maybe FilePath + -> Maybe Int + -> Bool + -> ReaderT (IORef History) m a + -> m a +runHistoryFromFile Nothing _ _ f = do historyRef <- liftIO $ newIORef emptyHistory runReaderT f historyRef -runHistoryFromFile (Just file) stifleAmt f = do +runHistoryFromFile (Just file) stifleAmt writeHistoryOnExit f = do oldHistory <- liftIO $ readHistory file historyRef <- liftIO $ newIORef $ stifleHistory stifleAmt oldHistory -- Run the action and then write the new history, even on an exception. @@ -41,7 +47,8 @@ runHistoryFromFile (Just file) stifleAmt f = do -- the user's previously-entered commands. -- (Note that this requires using ReaderT (IORef History) instead of StateT. x <- runReaderT f historyRef - `finally` (liftIO $ readIORef historyRef >>= writeHistory file) + `finally` when writeHistoryOnExit + (liftIO $ readIORef historyRef >>= writeHistory file) return x prevHistory, firstHistory :: Save s => s -> HistLog -> (s, HistLog) diff --git a/System/Console/Haskeline/History.hs b/System/Console/Haskeline/History.hs index 3328b033..cc84412e 100644 --- a/System/Console/Haskeline/History.hs +++ b/System/Console/Haskeline/History.hs @@ -62,7 +62,7 @@ readHistory file = handle (\(_::IOException) -> return emptyHistory) $ do then readUTF8File file else return "" _ <- evaluate (length contents) -- force file closed - return History {histLines = Seq.fromList $ lines contents, + return History {histLines = Seq.fromList $ reverse $ lines contents, stifleAmt = Nothing} -- | Writes the line history to the given file. If there is an @@ -70,7 +70,7 @@ readHistory file = handle (\(_::IOException) -> return emptyHistory) $ do writeHistory :: FilePath -> History -> IO () writeHistory file = handle (\(_::IOException) -> return ()) . writeUTF8File file - . unlines . historyLines + . unlines . reverse . historyLines -- | Limit the number of lines stored in the history. stifleHistory :: Maybe Int -> History -> History diff --git a/System/Console/Haskeline/InputT.hs b/System/Console/Haskeline/InputT.hs index 5ec57568..3ae2b840 100644 --- a/System/Console/Haskeline/InputT.hs +++ b/System/Console/Haskeline/InputT.hs @@ -25,9 +25,10 @@ data Settings m = Settings {complete :: CompletionFunc m, -- ^ Custom tab comple 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 + 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 @@ -93,17 +94,6 @@ getHistory = InputT get putHistory :: MonadIO m => History -> InputT m () putHistory = InputT . put --- | Writes command history to file if 'historyFile' is not 'Nothing' -flushHistory :: forall m . MonadIO m => InputT m () -flushHistory = do - settings :: Settings m <- InputT ask - getHistory >>= maybeFlushHistory (historyFile settings) - --- | Flushes history if given filepath is not 'Nothing' -maybeFlushHistory :: MonadIO m => Maybe FilePath -> History -> InputT m () -maybeFlushHistory Nothing _ = return () -maybeFlushHistory (Just f) hist = liftIO $ writeHistory f hist - -- | Change the current line input history. modifyHistory :: MonadIO m => (History -> History) -> InputT m () modifyHistory = InputT . modify @@ -191,6 +181,7 @@ execInputT prefs settings run (InputT f) = runReaderT' settings $ runReaderT' prefs $ runKillRing $ runHistoryFromFile (historyFile settings) (maxHistorySize prefs) + (incAppendHistory prefs) $ runReaderT f run -- | Map a user interaction by modifying the base monad computation. diff --git a/System/Console/Haskeline/Prefs.hs b/System/Console/Haskeline/Prefs.hs index bc0b8d52..4b717a52 100644 --- a/System/Console/Haskeline/Prefs.hs +++ b/System/Console/Haskeline/Prefs.hs @@ -49,7 +49,7 @@ data Prefs = Prefs { bellStyle :: !BellStyle, customBindings :: Map.Map Key [Key], -- (termName, keysequence, key) customKeySequences :: [(Maybe String, String,Key)], - flushEveryCommand :: Bool + incAppendHistory :: Bool -- ^ If 'True' and @historyFile@ not 'Nothing' -- flushes command history after every command } @@ -81,8 +81,8 @@ defaultPrefs = Prefs {bellStyle = AudibleBell, historyDuplicates = AlwaysAdd, customBindings = Map.empty, customKeySequences = [], - flushEveryCommand = False - } + incAppendHistory = False + } mkSettor :: Read a => (a -> Prefs -> Prefs) -> String -> Prefs -> Prefs mkSettor f str = maybe id f (readMaybe str) @@ -104,6 +104,7 @@ settors = [("bellstyle", mkSettor $ \x p -> p {bellStyle = x}) ,("historyduplicates", mkSettor $ \x p -> p {historyDuplicates = x}) ,("bind", addCustomBinding) ,("keyseq", addCustomKeySequence) + ,("incappendhistory", mkSettor $ \x p -> p {incAppendHistory = x}) ] addCustomBinding :: String -> Prefs -> Prefs