module System.Console.Haskeline.Command( -- * Commands Effect(..), KeyMap(), lookupKM, KeyAction(..), CmdAction(..), (>=>), Command(), runCommand, continue, (>|>), (>+>), acceptKey, acceptKeyM, acceptKeyOrFail, loopUntil, try, finish, failCmd, simpleCommand, charCommand, change, changeFromChar, changeWithoutKey, clearScreenCmd, (+>), choiceCmd, withState ) where import Data.Char(isPrint) import Control.Monad(mplus) import System.Console.Haskeline.LineState import System.Console.Haskeline.Key data Effect s = Change {effectState :: s} | PrintLines {linesToPrint :: [String], effectState :: s} | Redraw {shouldClearScreen :: Bool, effectState :: s} | RingBell {effectState :: s} newtype KeyMap m s = KeyMap {lookupKM :: Key -> Maybe (s -> Either (Maybe String) (m (KeyAction m)))} useKey :: Key -> (s -> Either (Maybe String) (m (KeyAction m))) -> KeyMap m s useKey k f = KeyMap $ \k' -> if k==k' then Just f else Nothing data KeyAction m = forall t . LineState t => KeyAction (Effect t) (KeyMap m t) nullKM :: KeyMap m s nullKM = KeyMap $ const Nothing orKM :: KeyMap m s -> KeyMap m s -> KeyMap m s orKM (KeyMap m) (KeyMap n) = KeyMap $ \k -> m k `mplus` n k choiceKM :: [KeyMap m s] -> KeyMap m s choiceKM = foldl orKM nullKM newtype Command m s t = Command (KeyMap m t -> KeyMap m s) runCommand :: Command m s s -> KeyMap m s runCommand (Command f) = let m = f m in m continue :: Command m s s continue = Command id infixl 6 >|> (>|>) :: Command m s t -> Command m t u -> Command m s u Command f >|> Command g = Command (f . g) infixl 6 >+> (>+>) :: (Monad m, LineState s) => Key -> Command m s t -> Command m s t k >+> f = k +> change id >|> f data CmdAction m s = forall t . LineState t => CmdAction (Effect t) (Command m t s) (>=>) :: LineState t => Effect t -> Command m t s -> CmdAction m s (>=>) = CmdAction acceptKey :: (Monad m) => (s -> CmdAction m t) -> Key -> Command m s t acceptKey f = acceptKeyFull (Just . return . f) acceptKeyM :: Monad m => (s -> m (CmdAction m t)) -> Key -> Command m s t acceptKeyM f = acceptKeyFull (Just . f) acceptKeyFull :: Monad m => (s -> Maybe (m (CmdAction m t))) -> Key -> Command m s t acceptKeyFull f k = Command $ \next -> useKey k $ \s -> case f s of Nothing -> Left Nothing Just act -> Right $ do CmdAction effect (Command g) <- act return (KeyAction effect (g next)) acceptKeyOrFail :: Monad m => (s -> Maybe (CmdAction m t)) -> Key -> Command m s t acceptKeyOrFail f = acceptKeyFull (fmap return . f) loopUntil :: Command m s s -> Command m s t -> Command m s t loopUntil f g = choiceCmd [g, f >|> loopUntil f g] try :: Command m s s -> Command m s s try (Command f) = Command $ \next -> (f next) `orKM` next finish :: forall s m t . (Result s, Monad m) => Key -> Command m s t finish k = Command $ \_-> useKey k (Left . Just . toResult) failCmd :: forall s m t . (LineState s, Monad m) => Key -> Command m s t failCmd k = Command $ \_-> useKey k (const $ Left Nothing) simpleCommand :: (LineState t, Monad m) => (s -> m (Effect t)) -> Key -> Command m s t simpleCommand f = acceptKeyM $ \s -> do act <- f s return (act >=> continue) charCommand :: (LineState t, Monad m) => (Char -> s -> m (Effect t)) -> Command m s t charCommand f = Command $ \next -> KeyMap $ \k -> case k of Key m (KeyChar c) | isPrint c && m==noModifier-> Just $ \s -> Right $ do effect <- f c s return (KeyAction effect next) _ -> Nothing change :: (LineState t, Monad m) => (s -> t) -> Key -> Command m s t change f = simpleCommand (return . Change . f) changeFromChar :: (Monad m, LineState t) => (Char -> s -> t) -> Command m s t changeFromChar f = charCommand (\c s -> return $ Change (f c s)) changeWithoutKey :: (s -> t) -> Command m s t changeWithoutKey f = Command $ \(KeyMap next) -> KeyMap $ fmap (. f) . next clearScreenCmd :: (LineState s, Monad m) => Key -> Command m s s clearScreenCmd k = k +> simpleCommand (\s -> return (Redraw True s)) infixl 7 +> (+>) :: Key -> (Key -> a) -> a k +> f = f k choiceCmd :: [Command m s t] -> Command m s t choiceCmd cmds = Command $ \next -> choiceKM $ map (\(Command f) -> f next) cmds withState :: Monad m => (s -> m a) -> Command m s t -> Command m s t withState act (Command thisCmd) = Command $ \next -> KeyMap $ \k -> case lookupKM (thisCmd next) k of Nothing -> Nothing Just f -> Just $ \s -> case f s of Left r -> Left r Right effect -> Right $ act s >> effect