repline 0.2.1.0 → 0.2.2.0
raw patch · 4 files changed
+88/−23 lines, 4 filesdep +exceptions
Dependencies added: exceptions
Files
- ChangeLog.md +7/−0
- README.md +15/−5
- repline.cabal +5/−3
- src/System/Console/Repline.hs +61/−15
ChangeLog.md view
@@ -1,9 +1,16 @@ HEAD ==== +0.2.2.0+=======++- `ReplOpts` configuration type and `evalReplOpts` function.+- Only use `fail` for GHC<8.0+ 0.2.1.0 ======= +- Add `exceptions` dependency. - Add a `MonadFail` instance to `HaskelineT`. 0.2.0.0
README.md view
@@ -8,6 +8,13 @@ transformers. Mostly exists because I got tired of implementing the same interface for simple shells over and over and decided to canonize the giant pile of hacks that I use to make Haskeline work. +Examples+--------++* [Simple](examples/Simple.hs)+* [Prefix](examples/Prefix.hs)+* [Stateful](examples/Stateful.hs)+ Usage ----- @@ -49,8 +56,9 @@ Trying it out: ```haskell-$ runhaskell Simple.hs-# Or if in a sandbox: cabal exec runhaskell Simple.hs+$ stack repl Simple.hs+Prelude> main+ Welcome! >>> <TAB> kirk spock mccoy@@ -113,7 +121,7 @@ -- Tab completion inside of StateT repl :: IO () repl = flip evalStateT Set.empty- $ evalRepl (pure ">>> ") cmd opts Nothing (Word comp) init+ $ evalRepl (pure ">>> ") cmd opts Nothing (Word comp) ini ``` @@ -170,7 +178,9 @@ Trying it out: ```haskell-$ runhaskell Main.hs+$ stack repl examples/Prefix.hs+Prelude> main+ >>> :file <TAB> sample1.txt sample2.txt @@ -183,5 +193,5 @@ License ------- -Copyright (c) 2014-2019, Stephen Diehl+Copyright (c) 2014-2020, Stephen Diehl Released under the MIT License
repline.cabal view
@@ -1,5 +1,5 @@ name: repline-version: 0.2.1.0+version: 0.2.2.0 synopsis: Haskeline wrapper for GHCi-like REPL interfaces. license: MIT license-file: LICENSE@@ -31,8 +31,10 @@ build-depends: base >= 4.6 && <5.0, containers >= 0.5 && <0.7,- fail >= 4.9 && <4.10,+ exceptions >= 0.10 && < 0.11, mtl >= 2.2 && <2.3, process >= 1.2 && <2.0,- haskeline >= 0.7 && <0.8+ haskeline >= 0.7 && <0.9+ if !impl(ghc >= 8.0)+ Build-Depends: fail >= 4.9 && <4.10 default-language: Haskell2010
src/System/Console/Repline.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeSynonymInstances #-}@@ -104,9 +105,16 @@ -} module System.Console.Repline (+ -- * Repline Monad HaskelineT, runHaskelineT, + -- * Toplevel+ evalRepl,+ ReplOpts(..),+ evalReplOpts,++ -- * Repline Types Cmd, Options, WordCompleter,@@ -114,20 +122,20 @@ CompleterStyle(..), Command, + -- * Completers CompletionFunc, -- re-export wordCompleter, listCompleter, fileCompleter, listWordCompleter,- runMatcher,- evalRepl,+ trimComplete,++ -- * Utilities abort, tryAction, dontCrash,-- trimComplete, ) where import System.Console.Haskeline.Completion@@ -147,6 +155,7 @@ newtype HaskelineT (m :: * -> *) a = HaskelineT { unHaskeline :: H.InputT m a } deriving (Monad, Functor, Applicative, MonadIO, MonadException, MonadTrans, MonadHaskeline) +-- | Run HaskelineT monad runHaskelineT :: MonadException m => H.Settings m -> HaskelineT m a -> m a runHaskelineT s m = H.runInputT s (H.withInterrupt (unHaskeline m)) @@ -183,11 +192,19 @@ -- Repl ------------------------------------------------------------------------------- +-- | Command function synonym type Cmd m = [String] -> m ()++-- | Options function synonym type Options m = [(String, Cmd m)]++-- | Command function synonym type Command m = String -> m () +-- | Word completer type WordCompleter m = (String -> m [String])++-- | Line completer type LineCompleter m = (String -> String -> m [Completion]) -- | Wrap a HasklineT action so that if an interrupt is thrown the shell continues as normal.@@ -205,10 +222,10 @@ -- | Completion loop. replLoop :: (Functor m, MonadException m)- => HaskelineT m String- -> Command (HaskelineT m)- -> Options (HaskelineT m)- -> Maybe Char+ => HaskelineT m String -- ^ banner function+ -> Command (HaskelineT m) -- ^ command function+ -> Options (HaskelineT m) -- ^ options function+ -> Maybe Char -- ^ options prefix -> HaskelineT m () replLoop banner cmdM opts optsPrefix = loop where@@ -241,6 +258,31 @@ | s `isPrefixOf` x = m args | otherwise = optMatcher s xs args +-------------------------------------------------------------------------------+-- Toplevel+-------------------------------------------------------------------------------++-- | REPL Options datatype+data ReplOpts m = ReplOpts {+ banner :: HaskelineT m String -- ^ Banner+ , command :: Command (HaskelineT m) -- ^ Command function+ , options :: Options (HaskelineT m) -- ^ Options list and commands+ , prefix :: Maybe Char -- ^ Optional command prefix ( passing Nothing ignores the Options argument )+ , tabComplete :: CompleterStyle m -- ^ Tab completion function+ , initialiser :: HaskelineT m () -- ^ Initialiser+ }++-- | Evaluate the REPL logic into a MonadException context from the ReplOpts+-- configuration.+evalReplOpts :: (Functor m, MonadException m) => ReplOpts m -> m ()+evalReplOpts (ReplOpts {..}) = evalRepl+ banner+ command+ options+ prefix+ tabComplete+ initialiser+ -- | Evaluate the REPL logic into a MonadException context. evalRepl :: (Functor m, MonadException m) -- Terminal monad ( often IO ). => HaskelineT m String -- ^ Banner@@ -248,7 +290,7 @@ -> 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+ -> HaskelineT m a -- ^ Initialiser -> m () evalRepl banner cmd opts optsPrefix comp initz = runHaskelineT _readline (initz >> monad) where@@ -263,8 +305,6 @@ -- Completions ------------------------------------------------------------------------------- ---type CompletionFunc m = (String, String) -> m (String, [Completion])- data CompleterStyle m = Word (WordCompleter m) -- ^ Completion function takes single word. | Word0 (WordCompleter m) -- ^ Completion function takes single word ( no space ).@@ -274,6 +314,7 @@ (CompletionFunc m) [(String, CompletionFunc m)] -- ^ Conditional tab completion based on prefix. +-- | Make a completer function from a completion type mkCompleter :: MonadIO m => CompleterStyle m -> CompletionFunc m mkCompleter (Word f) = completeWord (Just '\\') " \t()[]" (_simpleComplete f) mkCompleter (Word0 f) = completeWord (Just '\\') " \t()[]" (_simpleCompleteNoSpace f)@@ -283,7 +324,7 @@ -- haskeline takes the first argument as the reversed string, don't know why unRev0 :: LineCompleter m -> LineCompleter m-unRev0 f x y = f (reverse x) y+unRev0 f x = f (reverse x) trimComplete :: String -> Completion -> Completion trimComplete prefix (Completion a b c) = Completion (drop (length prefix) a) b c@@ -297,15 +338,18 @@ completionNoSpace :: String -> Completion completionNoSpace str = Completion str str False +-- | Word completer function wordCompleter :: Monad m => WordCompleter m -> CompletionFunc m wordCompleter f (start, n) = completeWord (Just '\\') " \t()[]" (_simpleComplete f) (start, n) +-- | List completer function listCompleter :: Monad m => [String] -> CompletionFunc m listCompleter names (start, n) = completeWord (Just '\\') " \t()[]" (_simpleComplete (complete_aux names)) (start, n) listWordCompleter :: Monad m => [String] -> WordCompleter m listWordCompleter = complete_aux +-- | File completer function fileCompleter :: MonadIO m => CompletionFunc m fileCompleter = completeFilename @@ -321,8 +365,10 @@ | x `isPrefixOf` s = f args | otherwise = completeMatcher def s xs args -runMatcher :: Monad m => [(String, CompletionFunc m)]- -> CompletionFunc m- -> CompletionFunc m+-- | Return a completion function a line fragment+runMatcher+ :: Monad m => [(String, CompletionFunc m)]+ -> CompletionFunc m+ -> CompletionFunc m runMatcher opts def (start, n) = completeMatcher def (n ++ reverse start) opts (start, n)