module System.Console.Repline (
HaskelineT,
runHaskelineT,
Cmd,
Options,
WordCompleter,
LineCompleter,
CompleterStyle(..),
Command,
CompletionFunc,
wordCompleter,
listCompleter,
fileCompleter,
listWordCompleter,
runMatcher,
evalRepl,
abort,
tryAction,
trimComplete,
) where
import System.Console.Haskeline.Completion
import System.Console.Haskeline.MonadException
import qualified System.Console.Haskeline as H
import Data.List (isPrefixOf)
import Control.Applicative
import Control.Monad.State.Strict
newtype HaskelineT (m :: * -> *) a = HaskelineT { unHaskeline :: H.InputT m a }
deriving (Monad, Functor, Applicative, MonadIO, MonadException, MonadTrans, MonadHaskeline)
runHaskelineT :: MonadException m => H.Settings m -> HaskelineT m a -> m a
runHaskelineT s m = H.runInputT s (H.withInterrupt (unHaskeline m))
class MonadException m => MonadHaskeline m where
getInputLine :: String -> m (Maybe String)
getInputChar :: String -> m (Maybe Char)
outputStr :: String -> m ()
outputStrLn :: String -> m ()
instance MonadException m => MonadHaskeline (H.InputT m) where
getInputLine = H.getInputLine
getInputChar = H.getInputChar
outputStr = H.outputStr
outputStrLn = H.outputStrLn
instance MonadState s m => MonadState s (HaskelineT m) where
get = lift get
put = lift . put
instance (MonadHaskeline m) => MonadHaskeline (StateT s m) where
getInputLine = lift . getInputLine
getInputChar = lift . getInputChar
outputStr = lift . outputStr
outputStrLn = lift . outputStrLn
type Cmd m = [String] -> m ()
type Options m = [(String, Cmd m)]
type Command m = String -> m ()
type WordCompleter m = (String -> m [String])
type LineCompleter m = (String -> String -> m [Completion])
tryAction :: MonadException m => HaskelineT m a -> HaskelineT m a
tryAction (HaskelineT f) = HaskelineT (H.withInterrupt loop)
where loop = handle (\H.Interrupt -> loop) f
abort :: MonadIO m => HaskelineT m a
abort = throwIO H.Interrupt
replLoop :: MonadException m
=> String
-> Command (HaskelineT m)
-> Options (HaskelineT m)
-> HaskelineT m ()
replLoop banner cmdM opts = loop
where
loop = do
minput <- H.handleInterrupt (return (Just "")) $ getInputLine banner
case minput of
Nothing -> outputStrLn "Goodbye."
Just "" -> loop
Just ":" -> loop
Just (':' : cmds) -> do
let (cmd:args) = words cmds
optMatcher cmd opts args
loop
Just input -> do
H.handleInterrupt (return ()) $ cmdM input
loop
optMatcher :: MonadHaskeline m => String -> Options m -> [String] -> m ()
optMatcher s [] _ = outputStrLn $ "No such command :" ++ s
optMatcher s ((x, m):xs) args
| s `isPrefixOf` x = m args
| otherwise = optMatcher s xs args
evalRepl :: MonadException m
=> String
-> Command (HaskelineT m)
-> Options (HaskelineT m)
-> CompleterStyle m
-> HaskelineT m a
-> m ()
evalRepl banner cmd opts comp initz = runHaskelineT _readline (initz >> monad)
where
monad = replLoop banner cmd opts
_readline = H.Settings
{ H.complete = mkCompleter comp
, H.historyFile = Just ".history"
, H.autoAddHistory = True
}
data CompleterStyle m
= Word (WordCompleter m)
| Word0 (WordCompleter m)
| Cursor (LineCompleter m)
| File
| Prefix
(CompletionFunc m)
[(String, CompletionFunc m)]
mkCompleter :: MonadIO m => CompleterStyle m -> CompletionFunc m
mkCompleter (Word f) = completeWord (Just '\\') " \t()[]" (_simpleComplete f)
mkCompleter (Word0 f) = completeWord (Just '\\') " \t()[]" (_simpleCompleteNoSpace f)
mkCompleter (Cursor f) = completeWordWithPrev (Just '\\') " \t()[]" (unRev0 f)
mkCompleter File = completeFilename
mkCompleter (Prefix def opts) = runMatcher opts def
unRev0 :: LineCompleter m -> LineCompleter m
unRev0 f x y = f (reverse x) y
trimComplete :: String -> Completion -> Completion
trimComplete prefix (Completion a b c) = Completion (drop (length prefix) a) b c
_simpleComplete :: (Monad m) => (String -> m [String]) -> String -> m [Completion]
_simpleComplete f word = f word >>= return . map simpleCompletion
_simpleCompleteNoSpace :: (Monad m) => (String -> m [String]) -> String -> m [Completion]
_simpleCompleteNoSpace f word = f word >>= return . map completionNoSpace
completionNoSpace :: String -> Completion
completionNoSpace str = Completion str str False
wordCompleter :: Monad m => WordCompleter m -> CompletionFunc m
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)
listWordCompleter :: Monad m => [String] -> WordCompleter m
listWordCompleter = complete_aux
fileCompleter :: MonadIO m => CompletionFunc m
fileCompleter = completeFilename
complete_aux :: Monad m => [String] -> WordCompleter m
complete_aux names n = return $ filter (isPrefixOf n) names
completeMatcher :: (Monad m) => CompletionFunc m -> String
-> [(String, CompletionFunc m)]
-> CompletionFunc m
completeMatcher def _ [] args = def args
completeMatcher def [] _ args = def args
completeMatcher def s ((x, f):xs) args
| x `isPrefixOf` s = f args
| otherwise = completeMatcher def s xs args
runMatcher :: Monad m => [(String, CompletionFunc m)]
-> CompletionFunc m
-> CompletionFunc m
runMatcher opts def (start, n) = do
(completeMatcher def (n ++ reverse start) opts) (start, n)