haskeline 0.8.3.0 → 0.8.4.0
raw patch · 16 files changed
+237/−91 lines, 16 filesdep ~containersdep ~directoryPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: containers, directory
API changes (from Hackage documentation)
+ System.Console.Haskeline.ReaderT: data InputTEnv (m :: Type -> Type)
+ System.Console.Haskeline.ReaderT: fromReaderT :: forall (m :: Type -> Type) a. ReaderT (InputTEnv m) m a -> InputT m a
+ System.Console.Haskeline.ReaderT: mapInputTEnv :: (forall x. () => m x -> n x) -> InputTEnv m -> InputTEnv n
+ System.Console.Haskeline.ReaderT: toReaderT :: forall (m :: Type -> Type) a. InputT m a -> ReaderT (InputTEnv m) m a
- System.Console.Haskeline: Settings :: CompletionFunc m -> Maybe FilePath -> Bool -> Settings m
+ System.Console.Haskeline: Settings :: CompletionFunc m -> Maybe FilePath -> Bool -> Settings (m :: Type -> Type)
- System.Console.Haskeline: [autoAddHistory] :: Settings m -> Bool
+ System.Console.Haskeline: [autoAddHistory] :: Settings (m :: Type -> Type) -> Bool
- System.Console.Haskeline: [complete] :: Settings m -> CompletionFunc m
+ System.Console.Haskeline: [complete] :: Settings (m :: Type -> Type) -> CompletionFunc m
- System.Console.Haskeline: [historyFile] :: Settings m -> Maybe FilePath
+ System.Console.Haskeline: [historyFile] :: Settings (m :: Type -> Type) -> Maybe FilePath
- System.Console.Haskeline: data InputT m a
+ System.Console.Haskeline: data InputT (m :: Type -> Type) a
- System.Console.Haskeline: data Settings m
+ System.Console.Haskeline: data Settings (m :: Type -> Type)
- System.Console.Haskeline: defaultSettings :: MonadIO m => Settings m
+ System.Console.Haskeline: defaultSettings :: forall (m :: Type -> Type). MonadIO m => Settings m
- System.Console.Haskeline: getExternalPrint :: MonadIO m => InputT m (String -> IO ())
+ System.Console.Haskeline: getExternalPrint :: forall (m :: Type -> Type). MonadIO m => InputT m (String -> IO ())
- System.Console.Haskeline: getHistory :: MonadIO m => InputT m History
+ System.Console.Haskeline: getHistory :: forall (m :: Type -> Type). MonadIO m => InputT m History
- System.Console.Haskeline: getInputChar :: (MonadIO m, MonadMask m) => String -> InputT m (Maybe Char)
+ System.Console.Haskeline: getInputChar :: forall (m :: Type -> Type). (MonadIO m, MonadMask m) => String -> InputT m (Maybe Char)
- System.Console.Haskeline: getInputLine :: (MonadIO m, MonadMask m) => String -> InputT m (Maybe String)
+ System.Console.Haskeline: getInputLine :: forall (m :: Type -> Type). (MonadIO m, MonadMask m) => String -> InputT m (Maybe String)
- System.Console.Haskeline: getInputLineWithInitial :: (MonadIO m, MonadMask m) => String -> (String, String) -> InputT m (Maybe String)
+ System.Console.Haskeline: getInputLineWithInitial :: forall (m :: Type -> Type). (MonadIO m, MonadMask m) => String -> (String, String) -> InputT m (Maybe String)
- System.Console.Haskeline: getPassword :: (MonadIO m, MonadMask m) => Maybe Char -> String -> InputT m (Maybe String)
+ System.Console.Haskeline: getPassword :: forall (m :: Type -> Type). (MonadIO m, MonadMask m) => Maybe Char -> String -> InputT m (Maybe String)
- System.Console.Haskeline: haveTerminalUI :: Monad m => InputT m Bool
+ System.Console.Haskeline: haveTerminalUI :: forall (m :: Type -> Type). Monad m => InputT m Bool
- System.Console.Haskeline: mapInputT :: (forall b. m b -> m b) -> InputT m a -> InputT m a
+ System.Console.Haskeline: mapInputT :: (forall b. () => m b -> m b) -> InputT m a -> InputT m a
- System.Console.Haskeline: modifyHistory :: MonadIO m => (History -> History) -> InputT m ()
+ System.Console.Haskeline: modifyHistory :: forall (m :: Type -> Type). MonadIO m => (History -> History) -> InputT m ()
- System.Console.Haskeline: outputStr :: MonadIO m => String -> InputT m ()
+ System.Console.Haskeline: outputStr :: forall (m :: Type -> Type). MonadIO m => String -> InputT m ()
- System.Console.Haskeline: outputStrLn :: MonadIO m => String -> InputT m ()
+ System.Console.Haskeline: outputStrLn :: forall (m :: Type -> Type). MonadIO m => String -> InputT m ()
- System.Console.Haskeline: putHistory :: MonadIO m => History -> InputT m ()
+ System.Console.Haskeline: putHistory :: forall (m :: Type -> Type). MonadIO m => History -> InputT m ()
- System.Console.Haskeline: waitForAnyKey :: (MonadIO m, MonadMask m) => String -> InputT m Bool
+ System.Console.Haskeline: waitForAnyKey :: forall (m :: Type -> Type). (MonadIO m, MonadMask m) => String -> InputT m Bool
- System.Console.Haskeline: withInterrupt :: (MonadIO m, MonadMask m) => InputT m a -> InputT m a
+ System.Console.Haskeline: withInterrupt :: forall (m :: Type -> Type) a. (MonadIO m, MonadMask m) => InputT m a -> InputT m a
- System.Console.Haskeline: withRunInBase :: Monad m => ((forall a. InputT m a -> m a) -> m b) -> InputT m b
+ System.Console.Haskeline: withRunInBase :: Monad m => ((forall a. () => InputT m a -> m a) -> m b) -> InputT m b
- System.Console.Haskeline.Completion: type CompletionFunc m = (String, String) -> m (String, [Completion])
+ System.Console.Haskeline.Completion: type CompletionFunc (m :: Type -> Type) = (String, String) -> m (String, [Completion])
Files
- Changelog +11/−0
- System/Console/Haskeline.hs +9/−4
- System/Console/Haskeline/Backend/Win32.hsc +1/−3
- System/Console/Haskeline/Command.hs +9/−15
- System/Console/Haskeline/Command/Completion.hs +3/−2
- System/Console/Haskeline/Command/KillRing.hs +2/−2
- System/Console/Haskeline/Completion.hs +2/−2
- System/Console/Haskeline/Emacs.hs +2/−1
- System/Console/Haskeline/Internal.hs +3/−1
- System/Console/Haskeline/Monads.hs +1/−1
- System/Console/Haskeline/ReaderT.hs +133/−0
- System/Console/Haskeline/Vi.hs +35/−35
- haskeline.cabal +9/−6
- includes/windows_cconv.h +0/−12
- tests/RunTTY.hs +2/−2
- tests/Unit.hs +15/−5
Changelog view
@@ -1,3 +1,14 @@+Changed in version 0.8.4.0:++ * On Windows, when terminal-style and printing output, no longer skips+ `"\ESC...\STX"` sequences. (If used with legacy ConHost directly (for+ example, `conhost.exe cmd.exe`), it will require ConHost's ANSI-capability+ to be enabled separately.) (#126)++ * Added `System.Console.Haskeline.ReaderT` module for `ReaderT` interface.++ * `System.Console.Haskeline.Completion.listFiles` now returns a sorted list.+ Changed in version 0.8.3.0: * User preferences are now read from $XDG_CONFIG_HOME/haskeline/haskeline.
System/Console/Haskeline.hs view
@@ -91,6 +91,7 @@ import System.Console.Haskeline.Key import System.Console.Haskeline.RunCommand +import Control.Monad ((>=>)) import Control.Monad.Catch (MonadMask, handle) import Data.Char (isSpace, isPrint) import Data.Maybe (isJust)@@ -144,6 +145,10 @@ If @'autoAddHistory' == 'True'@ and the line input is nonblank (i.e., is not all spaces), it will be automatically added to the history.++To include ANSI escape sequences in the input prompt, terminate them by @\STX@.+See <https://github.com/haskell/haskeline/wiki/ControlSequencesInPrompt> for+more information. -} getInputLine :: (MonadIO m, MonadMask m) => String -- ^ The input prompt@@ -228,7 +233,7 @@ acceptOneChar :: Monad m => KeyCommand m InsertMode (Maybe Char) acceptOneChar = choiceCmd [useChar $ \c s -> change (insertChar c) s >> return (Just c)- , ctrlChar 'l' +> clearScreenCmd >|>+ , ctrlChar 'l' +> clearScreenCmd >=> keyCommand acceptOneChar , ctrlChar 'd' +> failCmd] @@ -282,12 +287,12 @@ where loop = choiceCmd [ simpleChar '\n' +> finish , simpleKey Backspace +> change deletePasswordChar- >|> loop'- , useChar $ \c -> change (addPasswordChar c) >|> loop'+ >=> loop'+ , useChar $ \c -> change (addPasswordChar c) >=> loop' , ctrlChar 'd' +> \p -> if null (passwordState p) then failCmd p else finish p- , ctrlChar 'l' +> clearScreenCmd >|> loop'+ , ctrlChar 'l' +> clearScreenCmd >=> loop' ] loop' = keyCommand loop
System/Console/Haskeline/Backend/Win32.hsc view
@@ -381,9 +381,7 @@ crlf = "\r\n" instance (MonadMask m, MonadIO m, MonadReader Layout m) => Term (Draw m) where- drawLineDiff (xs1,ys1) (xs2,ys2) = let- fixEsc = filter ((/= '\ESC') . baseChar)- in drawLineDiffWin (fixEsc xs1, fixEsc ys1) (fixEsc xs2, fixEsc ys2)+ drawLineDiff = drawLineDiffWin -- TODO now that we capture resize events. -- first, looks like the cursor stays on the same line but jumps -- to the beginning if cut off.
System/Console/Haskeline/Command.hs view
@@ -8,7 +8,6 @@ KeyConsumed(..), withoutConsuming, keyCommand,- (>|>), (>+>), try, effect,@@ -29,8 +28,7 @@ ) where import Data.Char(isPrint)-import Control.Applicative(Applicative(..))-import Control.Monad(ap, mplus, liftM)+import Control.Monad(ap, mplus, liftM, (>=>)) import Control.Monad.Trans.Class import System.Console.Haskeline.LineState import System.Console.Haskeline.Key@@ -45,7 +43,7 @@ data KeyMap a = KeyMap {lookupKM :: Key -> Maybe (KeyConsumed a)} -data KeyConsumed a = NotConsumed a | Consumed a+data KeyConsumed a = NotConsumed a | Consumed a deriving Show instance Functor KeyMap where fmap f km = KeyMap $ fmap (fmap f) . lookupKM km@@ -55,10 +53,10 @@ fmap f (Consumed x) = Consumed (f x) -data CmdM m a = GetKey (KeyMap (CmdM m a))- | DoEffect Effect (CmdM m a)- | CmdM (m (CmdM m a))- | Result a+data CmdM m a = GetKey (KeyMap (CmdM m a))+ | DoEffect Effect (CmdM m a)+ | CmdM (m (CmdM m a))+ | Result a type Command m s t = s -> CmdM m t @@ -112,20 +110,16 @@ keyChoiceCmdM :: [KeyMap (CmdM m a)] -> CmdM m a keyChoiceCmdM = GetKey . choiceCmd -infixr 6 >|>-(>|>) :: Monad m => Command m s t -> Command m t u -> Command m s u-f >|> g = \x -> f x >>= g- infixr 6 >+> (>+>) :: Monad m => KeyCommand m s t -> Command m t u -> KeyCommand m s u-km >+> g = fmap (>|> g) km+km >+> g = fmap (>=> g) km -- attempt to run the command (predicated on getting a valid key); but if it fails, just keep -- going. try :: Monad m => KeyCommand m s s -> Command m s s try f = keyChoiceCmd [f,withoutConsuming return] -infixr 6 +>+infixr 0 +> (+>) :: Key -> a -> KeyMap a (+>) = useKey @@ -163,4 +157,4 @@ changeFromChar f = useChar $ change . f doBefore :: Monad m => Command m s t -> KeyCommand m t u -> KeyCommand m s u-doBefore cmd = fmap (cmd >|>)+doBefore cmd = fmap (cmd >=>)
System/Console/Haskeline/Command/Completion.hs view
@@ -15,6 +15,7 @@ import System.Console.Haskeline.Completion import System.Console.Haskeline.Monads +import Control.Monad ((>=>)) import Data.List(transpose, unfoldr) useCompletion :: InsertMode -> Completion -> InsertMode@@ -35,7 +36,7 @@ -- | Create a 'Command' for word completion. completionCmd :: (MonadState Undo m, CommandMonad m) => Key -> KeyCommand m InsertMode InsertMode-completionCmd k = k +> saveForUndo >|> \oldIM -> do+completionCmd k = k +> saveForUndo >=> \oldIM -> do (rest,cs) <- askIMCompletions oldIM case cs of [] -> effect RingBell >> return oldIM@@ -59,7 +60,7 @@ menuCompletion k = loop where loop [] = setState- loop (c:cs) = change (const c) >|> try (k +> loop cs)+ loop (c:cs) = change (const c) >=> try (k +> loop cs) makePartialCompletion :: InsertMode -> [Completion] -> InsertMode makePartialCompletion im completions = insertString partial im
System/Console/Haskeline/Command/KillRing.hs view
@@ -55,14 +55,14 @@ killFromHelper :: (MonadState KillRing m, MonadState Undo m, Save s, Save t) => KillHelper -> Command m s t-killFromHelper helper = saveForUndo >|> \oldS -> do+killFromHelper helper = saveForUndo >=> \oldS -> do let (gs,newIM) = applyHelper helper (save oldS) modify (push gs) setState (restore newIM) killFromArgHelper :: (MonadState KillRing m, MonadState Undo m, Save s, Save t) => KillHelper -> Command m (ArgMode s) t-killFromArgHelper helper = saveForUndo >|> \oldS -> do+killFromArgHelper helper = saveForUndo >=> \oldS -> do let (gs,newIM) = applyArgHelper helper (fmap save oldS) modify (push gs) setState (restore newIM)
System/Console/Haskeline/Completion.hs view
@@ -18,7 +18,7 @@ import System.FilePath-import Data.List(isPrefixOf)+import Data.List(isPrefixOf, sort) import Control.Monad(forM) import System.Console.Haskeline.Directory@@ -186,7 +186,7 @@ -- get all of the files in that directory, as basenames allFiles <- if not dirExists then return []- else fmap (map completion . filterPrefix)+ else fmap (sort . map completion . filterPrefix) $ getDirectoryContents fixedDir -- The replacement text should include the directory part, and also -- have a trailing slash if it's itself a directory.
System/Console/Haskeline/Emacs.hs view
@@ -13,6 +13,7 @@ import System.Console.Haskeline.LineState import System.Console.Haskeline.InputT +import Control.Monad ((>=>)) import Control.Monad.Catch (MonadMask) import Data.Char @@ -32,7 +33,7 @@ deleteCharOrEOF s | s == emptyIM = return Nothing | otherwise = change deleteNext s >>= justDelete- justDelete = keyChoiceCmd [eotKey +> change deleteNext >|> justDelete+ justDelete = keyChoiceCmd [eotKey +> change deleteNext >=> justDelete , emacsCommands]
System/Console/Haskeline/Internal.hs view
@@ -9,6 +9,8 @@ import System.Console.Haskeline.RunCommand import System.Console.Haskeline.Term +import Control.Monad ((>=>))+ -- | This function may be used to debug Haskeline's input. -- -- It loops indefinitely; every time a key is pressed, it will@@ -33,5 +35,5 @@ effect (LineChange $ const ([],[])) effect (PrintLines [show k]) setState emptyIM)- >|> keyCommand loop+ >=> keyCommand loop prompt = stringToGraphemes "> "
System/Console/Haskeline/Monads.hs view
@@ -84,7 +84,7 @@ put s = ask >>= liftIO . flip writeIORef s evalStateT' :: Monad m => s -> StateT s m a -> m a-evalStateT' s f = liftM fst $ runStateT f s+evalStateT' = flip evalStateT orElse :: Monad m => MaybeT m a -> m a -> m a orElse (MaybeT f) g = f >>= maybe g return
+ System/Console/Haskeline/ReaderT.hs view
@@ -0,0 +1,133 @@+-- | Provides an interface for 'ReaderT' rather than 'InputT'.+--+-- @since 0.8.4.0+module System.Console.Haskeline.ReaderT+ ( -- * ReaderT+ -- $reader+ toReaderT,+ fromReaderT,+ InputTEnv,+ mapInputTEnv,+ ) where++import Control.Monad.Trans.Reader (ReaderT (ReaderT, runReaderT))+import Data.IORef (IORef)+import System.Console.Haskeline.Command.KillRing (KillRing)+import System.Console.Haskeline.History (History)+import System.Console.Haskeline.InputT(+ InputT(InputT),+ Settings(+ Settings,+ autoAddHistory,+ complete,+ historyFile+ )+ )+import System.Console.Haskeline.Prefs (Prefs)+import System.Console.Haskeline.Term (RunTerm)+++-- $reader+--+-- These functions expose the 'InputT' type in terms of ordinary 'ReaderT'.+-- This allows for easier integration with applications that do not use a+-- concrete monad transformer stack, or do not want to commit to having+-- 'InputT' in the stack.++-- | The abstract environment used by 'InputT', for 'ReaderT'+-- usage.+--+-- @since 0.8.4.0+data InputTEnv m = MkInputTEnv+ { runTerm :: RunTerm,+ history :: IORef History,+ killRing :: IORef KillRing,+ prefs :: Prefs,+ settings :: Settings m+ }++-- | Maps the environment.+--+-- @since 0.8.4.0+mapInputTEnv :: (forall x. m x -> n x) -> InputTEnv m -> InputTEnv n+mapInputTEnv f (MkInputTEnv t h k p s) =+ MkInputTEnv+ { runTerm = t,+ history = h,+ killRing = k,+ prefs = p,+ settings = settings'+ }+ where+ settings' = Settings+ { complete = f . complete s,+ historyFile = historyFile s,+ autoAddHistory = autoAddHistory s+ }++-- | Maps an 'InputT' to a 'ReaderT'. Useful for lifting 'InputT' functions+-- into some other reader-like monad.+--+-- __Examples:__+--+-- @+-- import System.Console.Haskeline qualified as H+--+-- runApp :: ReaderT (InputTEnv IO) IO ()+-- runApp = do+-- input <- toReaderT (H.getInputLine "Enter your name: ")+-- ...+-- @+--+-- @+-- -- MTL-style polymorphism+-- class MonadHaskeline m where+-- getInputLine :: String -> m (Maybe String)+--+-- -- AppT is the core type over ReaderT.+-- instance MonadHaskeline (AppT m) where+-- getInputLine = lift . toReaderT . H.getInputLine+--+-- runApp :: (MonadHaskeline m) => m ()+-- runApp = do+-- input <- getInputLine "Enter your name: "+-- ...+-- @+--+-- @since 0.8.4.0+toReaderT :: InputT m a -> ReaderT (InputTEnv m) m a+toReaderT (InputT rdr) = ReaderT $ \st ->+ usingReaderT (settings st)+ . usingReaderT (prefs st)+ . usingReaderT (killRing st)+ . usingReaderT (history st)+ . usingReaderT (runTerm st)+ $ rdr++-- | Maps a 'ReaderT' to an 'InputT'. Allows defining an application in terms+-- of 'ReaderT'.+--+-- __Examples:__+--+-- @+-- import System.Console.Haskeline qualified as H+--+-- -- Could be generalized to MonadReader, effects libraries.+-- runApp :: ReaderT (InputTEnv IO) IO ()+--+-- main :: IO ()+-- main = H.runInputT H.defaultSettings $ fromReaderT runApp+-- @+--+-- @since 0.8.4.0+fromReaderT :: ReaderT (InputTEnv m) m a -> InputT m a+fromReaderT (ReaderT onEnv) = InputT $ ReaderT+ $ \runTerm' -> ReaderT+ $ \history' -> ReaderT+ $ \killRing' -> ReaderT+ $ \prefs' -> ReaderT+ $ \settings' ->+ onEnv (MkInputTEnv runTerm' history' killRing' prefs' settings')++usingReaderT :: env -> ReaderT env m a -> m a+usingReaderT = flip runReaderT
System/Console/Haskeline/Vi.hs view
@@ -1,7 +1,7 @@ #if __GLASGOW_HASKELL__ < 802 {-# OPTIONS_GHC -Wno-redundant-constraints #-} #endif-module System.Console.Haskeline.Vi where+module System.Console.Haskeline.Vi (emptyViState, viKeyCommands) where import System.Console.Haskeline.Command import System.Console.Haskeline.Monads@@ -14,14 +14,14 @@ import System.Console.Haskeline.InputT import Data.Char-import Control.Monad(liftM)+import Control.Monad(liftM, (>=>)) import Control.Monad.Catch (MonadMask) type EitherMode = Either CommandMode InsertMode type SavedCommand m = Command (ViT m) (ArgMode CommandMode) EitherMode -data ViState m = ViState { +data ViState m = ViState { lastCommand :: SavedCommand m, lastSearch :: [Grapheme] }@@ -43,7 +43,7 @@ , ctrlChar 'd' +> eofIfEmpty , simpleInsertions >+> viCommands , simpleChar '\ESC' +> change enterCommandMode- >|> viCommandActions+ >=> viCommandActions ] viCommands :: InputCmd InsertMode (Maybe String)@@ -51,10 +51,10 @@ simpleInsertions :: InputKeyCmd InsertMode InsertMode simpleInsertions = choiceCmd- [ simpleKey LeftKey +> change goLeft + [ simpleKey LeftKey +> change goLeft , simpleKey RightKey +> change goRight- , simpleKey Backspace +> change deletePrev - , simpleKey Delete +> change deleteNext + , simpleKey Backspace +> change deletePrev+ , simpleKey Delete +> change deleteNext , simpleKey Home +> change moveToStart , simpleKey End +> change moveToEnd , insertChars@@ -72,13 +72,13 @@ insertChars :: InputKeyCmd InsertMode InsertMode insertChars = useChar $ loop [] where- loop ds d = change (insertChar d) >|> keyChoiceCmd [+ loop ds d = change (insertChar d) >=> keyChoiceCmd [ useChar $ loop (d:ds) , withoutConsuming (storeCharInsertion (reverse ds)) ]- storeCharInsertion s = storeLastCmd $ change (applyArg + storeCharInsertion s = storeLastCmd $ change (applyArg $ withCommandMode $ insertString s)- >|> return . Left+ >=> return . Left -- If we receive a ^D and the line is empty, return Nothing -- otherwise, act like '\n' (mimicking how Readline behaves)@@ -101,7 +101,7 @@ chooseEitherMode (Right im) = viCommands im exitingCommands :: InputKeyCmd CommandMode InsertMode-exitingCommands = choiceCmd [ +exitingCommands = choiceCmd [ simpleChar 'i' +> change insertFromCommandMode , simpleChar 'I' +> change (moveToStart . insertFromCommandMode) , simpleKey Home +> change (moveToStart . insertFromCommandMode)@@ -109,29 +109,29 @@ , simpleChar 'A' +> change (moveToEnd . appendFromCommandMode) , simpleKey End +> change (moveToStart . insertFromCommandMode) , simpleChar 's' +> change (insertFromCommandMode . deleteChar)- , simpleChar 'S' +> noArg >|> killAndStoreI killAll- , simpleChar 'C' +> noArg >|> killAndStoreI (SimpleMove moveToEnd)+ , simpleChar 'S' +> noArg >=> killAndStoreI killAll+ , simpleChar 'C' +> noArg >=> killAndStoreI (SimpleMove moveToEnd) ] simpleCmdActions :: InputKeyCmd CommandMode CommandMode-simpleCmdActions = choiceCmd [ +simpleCmdActions = choiceCmd [ simpleChar '\ESC' +> change id -- helps break out of loops- , simpleChar 'r' +> replaceOnce + , simpleChar 'r' +> replaceOnce , simpleChar 'R' +> replaceLoop- , simpleChar 'D' +> noArg >|> killAndStoreCmd (SimpleMove moveToEnd)+ , simpleChar 'D' +> noArg >=> killAndStoreCmd (SimpleMove moveToEnd) , ctrlChar 'l' +> clearScreenCmd , simpleChar 'u' +> commandUndo , ctrlChar 'r' +> commandRedo -- vi-mode quirk: history is put at the start of the line.- , simpleChar 'j' +> historyForward >|> change moveToStart- , simpleChar 'k' +> historyBack >|> change moveToStart- , simpleKey DownKey +> historyForward >|> change moveToStart- , simpleKey UpKey +> historyBack >|> change moveToStart+ , simpleChar 'j' +> historyForward >=> change moveToStart+ , simpleChar 'k' +> historyBack >=> change moveToStart+ , simpleKey DownKey +> historyForward >=> change moveToStart+ , simpleKey UpKey +> historyBack >=> change moveToStart , simpleChar '/' +> viEnterSearch '/' Reverse , simpleChar '?' +> viEnterSearch '?' Forward , simpleChar 'n' +> viSearchHist Reverse [] , simpleChar 'N' +> viSearchHist Forward []- , simpleKey KillLine +> noArg >|> killAndStoreCmd (SimpleMove moveToStart)+ , simpleKey KillLine +> noArg >=> killAndStoreCmd (SimpleMove moveToStart) ] replaceOnce :: InputCmd CommandMode CommandMode@@ -186,7 +186,7 @@ repeatableCommands = choiceCmd [ repeatableCmdToIMode , repeatableCmdMode >+> return . Left- , simpleChar '.' +> saveForUndo >|> runLastCommand+ , simpleChar '.' +> saveForUndo >=> runLastCommand ] where runLastCommand s = liftM lastCommand get >>= ($ s)@@ -204,7 +204,7 @@ , pureMovements ] where- repeatableChange f = storedCmdAction (saveForUndo >|> change (applyArg f))+ repeatableChange f = storedCmdAction (saveForUndo >=> change (applyArg f)) flipCase :: CommandMode -> CommandMode flipCase CEmpty = CEmpty@@ -288,14 +288,14 @@ , (simpleChar 'E', goRightUntil (atEnd isBigWordChar)) ] -{- +{- From IEEE 1003.1: A "bigword" consists of: a maximal sequence of non-blanks preceded and followed by blanks A "word" consists of either: - a maximal sequence of wordChars, delimited at both ends by non-wordchars - a maximal sequence of non-blank non-wordchars, delimited at both ends by either blanks or a wordchar.--} +-} isBigWordChar, isWordChar, isOtherChar :: Char -> Bool isBigWordChar = not . isSpace isWordChar = isAlphaNum .||. (=='_')@@ -304,7 +304,7 @@ (.||.) :: (a -> Bool) -> (a -> Bool) -> a -> Bool (f .||. g) x = f x || g x -foreachDigit :: (Monad m, LineState t) => (Int -> s -> t) -> [Char] +foreachDigit :: (Monad m, LineState t) => (Int -> s -> t) -> [Char] -> KeyCommand m s t foreachDigit f ds = choiceCmd $ map digitCmd ds where digitCmd d = simpleChar d +> change (f (toDigit d))@@ -348,7 +348,7 @@ | baseChar x == d = n-1 | otherwise = n -matchingRightBrace, matchingLeftBrace :: Char -> Maybe Char +matchingRightBrace, matchingLeftBrace :: Char -> Maybe Char matchingRightBrace = flip lookup braceList matchingLeftBrace = flip lookup (map (\(c,d) -> (d,c)) braceList) @@ -358,8 +358,8 @@ --------------- -- Replace mode replaceLoop :: InputCmd CommandMode CommandMode-replaceLoop = saveForUndo >|> change insertFromCommandMode >|> loop- >|> change enterCommandModeRight+replaceLoop = saveForUndo >=> change insertFromCommandMode >=> loop+ >=> change enterCommandModeRight where loop = try (oneReplaceCmd >+> loop) oneReplaceCmd = choiceCmd [@@ -378,15 +378,15 @@ return s storedAction :: Monad m => SavedCommand m -> SavedCommand m-storedAction act = storeLastCmd act >|> act+storedAction act = storeLastCmd act >=> act storedCmdAction :: Monad m => Command (ViT m) (ArgMode CommandMode) CommandMode -> Command (ViT m) (ArgMode CommandMode) CommandMode-storedCmdAction act = storeLastCmd (liftM Left . act) >|> act+storedCmdAction act = storeLastCmd (liftM Left . act) >=> act storedIAction :: Monad m => Command (ViT m) (ArgMode CommandMode) InsertMode -> Command (ViT m) (ArgMode CommandMode) InsertMode-storedIAction act = storeLastCmd (liftM Right . act) >|> act+storedIAction act = storeLastCmd (liftM Right . act) >=> act killAndStoreCmd :: MonadIO m => KillHelper -> Command (ViT m) (ArgMode CommandMode) CommandMode killAndStoreCmd = storedCmdAction . killFromArgHelper@@ -395,7 +395,7 @@ killAndStoreI = storedIAction . killFromArgHelper killAndStoreIE :: MonadIO m => KillHelper -> Command (ViT m) (ArgMode CommandMode) EitherMode-killAndStoreIE helper = storedAction (killFromArgHelper helper >|> return . Right)+killAndStoreIE helper = storedAction (killFromArgHelper helper >=> return . Right) noArg :: Monad m => Command m s (ArgMode s) noArg = return . startArg 1@@ -423,7 +423,7 @@ modifySE f se = se {entryState = f (entryState se)} loopEntry = keyChoiceCmd [ editEntry >+> loopEntry- , simpleChar '\n' +> \se -> + , simpleChar '\n' +> \se -> viSearchHist dir (searchText se) s , withoutConsuming (change (const s)) ]@@ -433,7 +433,7 @@ , simpleKey RightKey +> change (modifySE goRight) , simpleKey Backspace +> change (modifySE deletePrev) , simpleKey Delete +> change (modifySE deleteNext)- ] + ] viSearchHist :: forall m . Monad m => Direction -> [Grapheme] -> Command (ViT m) CommandMode CommandMode
haskeline.cabal view
@@ -1,6 +1,6 @@ Name: haskeline Cabal-Version: >=1.10-Version: 0.8.3.0+Version: 0.8.4.0 Category: User Interfaces License: BSD3 License-File: LICENSE@@ -21,6 +21,7 @@ Build-Type: Simple tested-with:+ GHC == 9.12.1 GHC == 9.10.1 GHC == 9.8.2 GHC == 9.6.5@@ -34,7 +35,7 @@ GHC == 8.2.2 GHC == 8.0.2 -extra-source-files: examples/Test.hs Changelog includes/*.h+extra-source-files: examples/Test.hs Changelog source-repository head type: git@@ -62,8 +63,8 @@ Library Build-depends:- base >= 4.9 && < 4.21- , containers >= 0.4 && < 0.8+ base >= 4.9 && < 4.23+ , containers >= 0.4 && < 0.9 , directory >= 1.1 && < 1.4 , bytestring >= 0.9 && < 0.13 , filepath >= 1.2 && < 1.6@@ -88,6 +89,7 @@ System.Console.Haskeline.History System.Console.Haskeline.IO System.Console.Haskeline.Internal+ System.Console.Haskeline.ReaderT Other-Modules: System.Console.Haskeline.Backend System.Console.Haskeline.Backend.WCWidth@@ -149,9 +151,10 @@ Main-Is: Unit.hs Build-depends: -- shared dependencies with library- base >= 4.9 && < 4.21- , containers >= 0.4 && < 0.8+ base >= 4.9 && < 4.23+ , containers >= 0.4 && < 0.9 , bytestring >= 0.9 && < 0.13+ , directory , process >= 1.0 && < 1.7 -- dependencies for test-suite , HUnit
− includes/windows_cconv.h
@@ -1,12 +0,0 @@-#ifndef WINDOWS_CCONV_H-#define WINDOWS_CCONV_H--#if defined(i386_HOST_ARCH)-# define WINDOWS_CCONV stdcall-#elif defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH)-# define WINDOWS_CCONV ccall-#else-# error Unknown mingw32 arch-#endif--#endif
tests/RunTTY.hs view
@@ -41,9 +41,9 @@ setUTF8 :: Invocation -> Invocation-setUTF8 = setLang "en_US.UTF-8"+setUTF8 = setLang "C.UTF-8" setLatin1 :: Invocation -> Invocation-setLatin1 = setLang "en_US.ISO8859-1"+setLatin1 = setLang "C.ISO8859-1" runInvocation :: Invocation
tests/Unit.hs view
@@ -16,6 +16,7 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Monoid ((<>))+import System.Directory (createDirectoryIfMissing) import System.Exit (exitFailure) import System.Process (readProcess) import Test.HUnit@@ -34,6 +35,13 @@ whenLegacy :: BC.ByteString -> BC.ByteString whenLegacy s = if legacyEncoding then s else B.empty +-- These files are used to test tab completion.+makeTestFiles :: IO ()+makeTestFiles = do+ createDirectoryIfMissing True "tests/dummy-μασ"+ writeFile "tests/dummy-μασ/bar" ""+ writeFile "tests/dummy-μασ/ςερτ" ""+ main :: IO () main = do -- forkProcess needs an absolute path to the binary.@@ -45,6 +53,7 @@ runInTTY = True, environment = [] }+ makeTestFiles result <- runTestTT $ test [interactionTests i, fileStyleTests i] when (errors result > 0 || failures result > 0) exitFailure @@ -113,7 +122,7 @@ tabCompletion i = "tab completion" ~: [ utf8Test i [ utf8 "tests/dummy-μ\t\t" ] [ prompt 0, utf8 "tests/dummy-μασ/"- <> nl <> utf8 "ςερτ bar" <> nl+ <> nl <> utf8 "bar ςερτ" <> nl <> prompt' 0 <> utf8 "tests/dummy-μασ/" ] ]@@ -259,13 +268,14 @@ ] , "line char input" ~: utf8Test (setCharInput i) [utf8 "xαβ"]- [ prompt' 0, utf8 "x" <> nl <> output 0 (utf8 "x")- <> prompt' 1 <> utf8 "α" <> nl <> output 1 (utf8 "α")- <> prompt' 2 <> utf8 "β" <> nl <> output 2 (utf8 "β")+ [ prompt' 0, utf8 "x" <> dumbnl <> output 0 (utf8 "x")+ <> prompt' 1 <> utf8 "α" <> dumbnl <> output 1 (utf8 "α")+ <> prompt' 2 <> utf8 "β" <> dumbnl <> output 2 (utf8 "β") <> prompt' 3 ] ] where+ dumbnl = raw [13,10] wideChar = T.concat $ replicate 10 $ "안기영" -------------@@ -287,7 +297,7 @@ cr = raw [13] nl :: B.ByteString-nl = raw [13,10] -- NB: see fixNL: this is really [13,13,10]+nl = raw [27, 69] output :: Int -> B.ByteString -> B.ByteString output k s = utf8 (T.pack $ "line " ++ show k ++ ":")