repline 0.1.7.0 → 0.2.0.0
raw patch · 3 files changed
+50/−33 lines, 3 filesdep ~containersPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: containers
API changes (from Hackage documentation)
+ System.Console.Repline: dontCrash :: (MonadIO m, MonadException m) => m () -> m ()
+ System.Console.Repline: instance Control.Monad.Reader.Class.MonadReader r m => Control.Monad.Reader.Class.MonadReader r (System.Console.Repline.HaskelineT m)
- System.Console.Repline: evalRepl :: MonadException m => String -> Command (HaskelineT m) -> Options (HaskelineT m) -> CompleterStyle m -> HaskelineT m a -> m ()
+ System.Console.Repline: evalRepl :: (Functor m, MonadException m) => HaskelineT m String -> Command (HaskelineT m) -> Options (HaskelineT m) -> Maybe Char -> CompleterStyle m -> HaskelineT m a -> m ()
- System.Console.Repline: type LineCompleter m = String -> String -> m [Completion]
+ System.Console.Repline: type LineCompleter m = (String -> String -> m [Completion])
- System.Console.Repline: type WordCompleter m = String -> m [String]
+ System.Console.Repline: type WordCompleter m = (String -> m [String])
Files
- README.md +6/−6
- repline.cabal +2/−2
- src/System/Console/Repline.hs +42/−25
README.md view
@@ -43,7 +43,7 @@ ini = liftIO $ putStrLn "Welcome!" repl :: IO ()-repl = evalRepl ">>> " cmd options (Word completer) ini+repl = evalRepl (pure ">>> ") cmd options (Word completer) ini ``` Trying it out:@@ -62,9 +62,9 @@ "spam" >>> :say Hello Haskell- _______________ + _______________ < Hello Haskell >- --------------- + --------------- \ ^__^ \ (oo)\_______ (__)\ )\/\@@ -113,12 +113,12 @@ -- Tab completion inside of StateT repl :: IO () repl = flip evalStateT Set.empty- $ evalRepl ">>> " cmd opts (Word comp) init+ $ evalRepl (pure ">>> ") cmd opts (Word comp) init ``` Prefix Completion-----------------+----------------- Just as GHCi will provide different tab completion for kind-level vs type-level symbols based on which prefix the user has entered, we can also set up a provide this as a first-level construct using a ``Prefix`` tab@@ -164,7 +164,7 @@ init = return () repl :: IO ()-repl = evalRepl ">> " cmd opts (Prefix (wordCompleter byWord) defaultMatcher) init+repl = evalRepl (pure ">>> ") cmd opts (Prefix (wordCompleter byWord) defaultMatcher) init ``` Trying it out:
repline.cabal view
@@ -1,5 +1,5 @@ name: repline-version: 0.1.7.0+version: 0.2.0.0 synopsis: Haskeline wrapper for GHCi-like REPL interfaces. license: MIT license-file: LICENSE@@ -26,7 +26,7 @@ exposed-modules: System.Console.Repline build-depends: base >= 4.6 && <5.0,- containers >= 0.5 && <0.6,+ containers >= 0.5 && <0.7, mtl >= 2.2 && <2.3, process >= 1.2 && <2.0, haskeline >= 0.7 && <0.8
src/System/Console/Repline.hs view
@@ -72,7 +72,7 @@ Putting it all together we have a little shell. > main :: IO ()-> main = evalRepl ">>> " cmd options (Word completer) ini+> main = evalRepl (pure ">>> ") cmd options (Word completer) ini Putting this in a file we can test out our cow-trek shell. @@ -123,6 +123,7 @@ evalRepl, abort, tryAction,+ dontCrash, trimComplete, ) where@@ -134,6 +135,7 @@ import Data.List (isPrefixOf) import Control.Applicative import Control.Monad.State.Strict+import Control.Monad.Reader ------------------------------------------------------------------------------- -- Haskeline Transformer@@ -161,6 +163,10 @@ get = lift get put = lift . put +instance MonadReader r m => MonadReader r (HaskelineT m) where+ ask = lift ask+ local f (HaskelineT m) = HaskelineT $ H.mapInputT (local f) m+ instance (MonadHaskeline m) => MonadHaskeline (StateT s m) where getInputLine = lift . getInputLine getInputChar = lift . getInputChar@@ -183,35 +189,45 @@ tryAction (HaskelineT f) = HaskelineT (H.withInterrupt loop) where loop = handle (\H.Interrupt -> loop) f +-- | Catch all toplevel failures.+dontCrash :: (MonadIO m, H.MonadException m) => m () -> m ()+dontCrash m = H.catch m ( \ e@SomeException{} -> liftIO ( putStrLn ( show e ) ) )+ -- | Abort the current REPL loop, and continue. abort :: MonadIO m => HaskelineT m a abort = throwIO H.Interrupt -- | Completion loop.-replLoop :: MonadException m- => String+replLoop :: (Functor m, MonadException m)+ => HaskelineT m String -> Command (HaskelineT m) -> Options (HaskelineT m)+ -> Maybe Char -> HaskelineT m ()-replLoop banner cmdM opts = loop+replLoop banner cmdM opts optsPrefix = loop where loop = do- minput <- H.handleInterrupt (return (Just "")) $ getInputLine banner+ prefix <- banner+ minput <- H.handleInterrupt (return (Just "")) $ getInputLine prefix case minput of Nothing -> outputStrLn "Goodbye."- Just "" -> loop- Just ":" -> loop-- Just (':' : cmds) -> do- let (cmd:args) = words cmds- optMatcher cmd opts args- loop-+ Just (prefix: cmds)+ | null cmds -> handleInput [prefix] >> loop+ | Just prefix == optsPrefix ->+ case words cmds of+ [] -> loop+ (cmd:args) -> do+ let optAction = optMatcher cmd opts args+ result <- H.handleInterrupt (return Nothing) $ Just <$> optAction+ maybe exit (const loop) result Just input -> do- H.handleInterrupt (return ()) $ cmdM input+ handleInput input loop + handleInput input = H.handleInterrupt exit $ cmdM input+ exit = return ()+ -- | Match the options. optMatcher :: MonadHaskeline m => String -> Options m -> [String] -> m () optMatcher s [] _ = outputStrLn $ "No such command :" ++ s@@ -220,16 +236,17 @@ | otherwise = optMatcher s xs args -- | Evaluate the REPL logic into a MonadException context.-evalRepl :: MonadException m -- Terminal monad ( often IO ).- => String -- ^ Banner- -> Command (HaskelineT m) -- ^ Command function- -> Options (HaskelineT m) -- ^ Options list and commands- -> CompleterStyle m -- ^ Tab completion function- -> HaskelineT m a -- ^ Initializer+evalRepl :: (Functor m, MonadException m) -- Terminal monad ( often IO ).+ => HaskelineT m String -- ^ Banner+ -> Command (HaskelineT m) -- ^ Command function+ -> Options (HaskelineT m) -- ^ Options list and commands+ -> Maybe Char -- ^ Optional command prefix ( passing Nothing ignores the Options argument )+ -> CompleterStyle m -- ^ Tab completion function+ -> HaskelineT m a -- ^ Initializer -> m ()-evalRepl banner cmd opts comp initz = runHaskelineT _readline (initz >> monad)+evalRepl banner cmd opts optsPrefix comp initz = runHaskelineT _readline (initz >> monad) where- monad = replLoop banner cmd opts+ monad = replLoop banner cmd opts optsPrefix _readline = H.Settings { H.complete = mkCompleter comp , H.historyFile = Just ".history"@@ -275,7 +292,7 @@ completionNoSpace str = Completion str str False wordCompleter :: Monad m => WordCompleter m -> CompletionFunc m-wordCompleter f (start, n) = (completeWord (Just '\\') " \t()[]" (_simpleComplete f)) (start, n)+wordCompleter f (start, n) = completeWord (Just '\\') " \t()[]" (_simpleComplete f) (start, n) listCompleter :: Monad m => [String] -> CompletionFunc m listCompleter names (start, n) = completeWord (Just '\\') " \t()[]" (_simpleComplete (complete_aux names)) (start, n)@@ -301,5 +318,5 @@ runMatcher :: Monad m => [(String, CompletionFunc m)] -> CompletionFunc m -> CompletionFunc m-runMatcher opts def (start, n) = do- (completeMatcher def (n ++ reverse start) opts) (start, n)+runMatcher opts def (start, n) =+ completeMatcher def (n ++ reverse start) opts (start, n)