diff options
| author | tv <tv@shackspace.de> | 2014-07-27 13:38:22 +0200 | 
|---|---|---|
| committer | tv <tv@shackspace.de> | 2014-07-27 13:38:22 +0200 | 
| commit | 7d4433f98c0156a374cef2de5b0bddf744925038 (patch) | |
| tree | cb60114b5290d08dc9632f4a09323ca16f3dec8a | |
| parent | 3e9b581112b9ec12f0ec97f369f0d545ede4805b (diff) | |
use ExecM monad to execCommand
| -rw-r--r-- | Main.hs | 237 | 
1 files changed, 133 insertions, 104 deletions
| @@ -1,6 +1,7 @@ -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-}  module Main where +import Control.Applicative  import Control.Concurrent  import Control.Concurrent.MVar  import Control.Monad @@ -14,6 +15,10 @@ import System.IO  import System.Locale (defaultTimeLocale, rfc822DateFormat)  --import System.Posix.Signals +import Control.Monad.Error +import Control.Monad.Writer +import Control.Monad.State +  data VTState = VTState      { buffer :: Buffer @@ -50,40 +55,36 @@ main = do  dateThread delay lock = forever $ do      t <- getCurrentTime      withMVar lock $ \ buf -> do -      putLine $ formatTime defaultTimeLocale rfc822DateFormat t  +      clearLine +      putStrLn $ formatTime defaultTimeLocale rfc822DateFormat t        renderInputLine buf        hFlush stdout      threadDelay delay -  +  uiThread mod lock = do      c <- getCommand mod -    --mbMode <- modifyMVar lock (execCommand c) -    --case mbMode of -    --    Nothing -> -    --        uiThread mode lock -    --    Just mode' ->  -    --        uiThread mode' lock      mod' <- modifyMVar lock $ \ buf -> do          let st = VTState                  { mode = mod                  , buffer = buf                  } -        mbst' <- execCommand c st - -        case mbst' of -          Nothing -> do -            ringBell -            hFlush stdout -            return (buf, mod) -          Just st' -> do -            clearLine -            when (show (mode st) /= show (mode st')) $ do -                putStrLn $ "change mode: " ++ (show $ mode st') -            renderInputLine (buffer st') -            hFlush stdout - -            return (buffer st', mode st') + +        ((eSt, lines), st') <- runExecCommand st (execCommand c) + +        clearLine +        forM_ lines putStrLn + +        whenLeft eSt $ \err -> +          ringBell >> +          putStrLn (prettyError err) + +        when (show (mode st) /= show (mode st')) $ do +            putStrLn $ "change mode: " ++ (show $ mode st') + +        renderInputLine (buffer st') +        hFlush stdout +        return (buffer st', mode st')      uiThread mod' lock @@ -103,34 +104,45 @@ data Command    | GotoBOL    | GotoEOL +data ExecError +  = UnboundSequenceError String String +  | UnhandledInputError String +  | OtherError String + +instance Error ExecError where +  noMsg = OtherError "something went wrong" + +prettyError :: ExecError -> String +prettyError e = rec e +  where +    color cc s = "\x1b[" ++ cc ++ "m" ++ s ++ "\x1b[m" +    rec (UnboundSequenceError s n) = +      color "31" $ "unbound sequence: <" ++ (pp "31;1" s) ++ "\x1b[;31m> " +                  ++ (pp "31;1" n) + +    rec (UnhandledInputError s) = +      color "31" $ "unhandled input: <" ++ (pp "31;1" s) ++ "\x1b[;31m>" ---finishCommand :: Buffer -> IO (Buffer, Maybe Mode) ---finishCommand buf = do ---    clearLine ---    renderInputLine buf ---    hFlush stdout ---    return (buf, Nothing) --- ---finishCommandChangeMode :: Buffer -> Mode -> IO (Buffer, Maybe Mode) ---finishCommandChangeMode buf mode = do ---    clearLine ---    putStrLn $ "change mode: " ++ (show mode) ---    renderInputLine buf ---    hFlush stdout ---    return (buf, Just mode) +    rec (OtherError s) = +      color "31" $ "error: " ++ s --- TODO execCommand :: Command -> VTState -> VTState +    -- TODO cc is ColorCode +    pp cc = concat . map (pp1 cc) +    pp1 cc c +      | isPrint c = [c] +      | otherwise = specialChar cc $ +        case c of +          '\x1b' -> "^[" +          _ -> charToCode c +    specialChar cc s = "\x1b[1;35m" ++ s ++ "\x1b[;" ++ cc ++ "m" ---execCommand :: Command -> Buffer -> IO (Buffer, Maybe Mode) --- TODO instead of propagating Maybe to caller, use ---      something like Writer monad to generate bell -modifyBuffer :: (Buffer -> Maybe Buffer) -> VTState -> Maybe VTState -modifyBuffer f st = -  case f (buffer st) of -    Nothing -> Nothing -    Just b' -> Just st { buffer = b' } + +modifyBuffer :: (Buffer -> Buffer) -> ExecM () +modifyBuffer f = +    modify $ \st -> st { buffer = f (buffer st) } +  -- TODO instance Show Buffer (w/newtype Buffer) @@ -138,75 +150,87 @@ showBuffer :: Buffer -> String  showBuffer (lhs, rhs) = lhs ++ rhs -execCommand :: Command -> VTState -> IO (Maybe VTState) -execCommand GotoBOL q = -    return . modifyBuffer (\(lhs, rhs) -> Just ("", lhs ++ rhs)) $ q +newtype ExecM a = ExecM +    ( ErrorT ExecError (WriterT [String] (StateT VTState IO)) a +    ) +  deriving +    ( Applicative +    , Functor +    , Monad +    , MonadError ExecError +    , MonadIO +    , MonadState VTState +    , MonadWriter [String] +    ) + +runExecCommand :: +    VTState -> ExecM a -> IO ((Either ExecError a, [String]), VTState) + +runExecCommand st (ExecM ex) = +    runStateT (runWriterT (runErrorT ex)) st + -execCommand GotoEOL q = -    return . modifyBuffer (\(lhs, rhs) -> Just (lhs ++ rhs, "")) $ q -execCommand MoveCursorLeft q = -    return . modifyBuffer (\(lhs, rhs) -> -      if null lhs then Nothing else Just (init lhs, last lhs : rhs) -                          ) $ q +execCommand :: Command -> ExecM () -execCommand MoveCursorRight q = -    return . modifyBuffer (\(lhs, rhs) -> -      if null lhs then Nothing else Just (lhs ++ [head rhs], tail rhs) -                          ) $ q +execCommand GotoBOL = +    modifyBuffer $ \(lhs, rhs) -> ("", lhs ++ rhs) -execCommand (InsertChar c) q = -    return . modifyBuffer (\(lhs, rhs) -> Just (lhs ++ [c], rhs)) $ q +execCommand GotoEOL = +    modifyBuffer $ \(lhs, rhs) -> (lhs ++ rhs, "") -execCommand (InsertCharThenChangeMode c m) q = -    execCommand (InsertChar c) q { mode = m } +execCommand MoveCursorLeft = do +    get >>= flip (when . null . fst . buffer) +                 (throwError $ OtherError "no char to move left") +    modifyBuffer $ \(lhs, rhs) -> (init lhs, last lhs : rhs) -execCommand InsertNextCharVerbatim q = -    return . modifyBuffer Just $ q { mode = VerbatimMode } +execCommand MoveCursorRight = do +    get >>= flip (when . null . snd . buffer) +                 (throwError $ OtherError "no char to move right") +    modifyBuffer $ \(lhs, rhs) -> (lhs ++ [head rhs], tail rhs) -execCommand ExecuteInputBuffer q = do -    -- TODO Writer monad? -    putLine $ concat -      [ "input: <", concat $ map (reform 32) $ showBuffer . buffer $ q, ">" -      ] -    return . modifyBuffer (const $ Just emptyBuffer) $ q +execCommand (InsertChar c) = +    modifyBuffer $ \(lhs, rhs) -> (lhs ++ [c], rhs) -execCommand KillNextChar q = -    return . modifyBuffer (\(lhs, _:rhs') -> Just (lhs, rhs')) $ q +execCommand (InsertCharThenChangeMode c m) = +    modify $ \ q -> q +      { buffer = (\(lhs, rhs) -> (lhs ++ [c], rhs)) $ buffer q +      , mode = m +      } -execCommand KillLastChar q = -    return . modifyBuffer (\(lhs, rhs) -> -      if null lhs then Nothing else Just (init lhs, rhs) -                          ) $ q +execCommand InsertNextCharVerbatim = +    modify $ \ q -> q { mode = VerbatimMode } -execCommand KillLastWord q = -    return . modifyBuffer (\(lhs, rhs) -> -      if null lhs then Nothing -          else Just (foldr dropWhileEnd lhs [not . isSpace, isSpace], rhs) -                          ) $ q +execCommand ExecuteInputBuffer = do +    b <- gets buffer +    tell [ "input: <" ++ (concat $ map (reform 32) $ showBuffer b) ++ ">" ] +    modifyBuffer (const emptyBuffer) -execCommand (AlertBadInput s) q = do -    putLine $ "unhandled input: <" ++ (concat $ map (reform 31) s) ++ ">" -    return Nothing -    --return . Just $ q +execCommand KillNextChar = do +    get >>= flip (when . null . snd . buffer) +                 (throwError $ OtherError "nothing to kill right") +    modifyBuffer $ \(lhs, _:rhs') -> (lhs, rhs') -execCommand (UnboundSequence s n) q = do -    putLine $ "unbound sequence: <" ++ (concat $ map (reform 31) s) ++ "> " -              ++ (special 31 n) -    --return . Just $ q -    return Nothing +execCommand KillLastChar = do +    get >>= flip (when . null . fst . buffer) +                 (throwError $ OtherError "nothing to kill left") +    modifyBuffer $ \(lhs, rhs) -> (init lhs, rhs) ---execCommand _ q = do ---    ringBell ---    hFlush stdout ---    return q +execCommand KillLastWord = do +    get >>= flip (when . null . fst . buffer) +                 (throwError $ OtherError "nothing to kill left") +    modifyBuffer $ +      \(lhs, rhs) -> (foldr dropWhileEnd lhs [not . isSpace, isSpace], rhs) + +execCommand (AlertBadInput s) = +    throwError (UnhandledInputError s) + +execCommand (UnboundSequence s n) = +    throwError (UnboundSequenceError s n) -putLine s = do -    clearLine -- TODO this renders finishCommand's clearLine redundant -    putStrLn s @@ -224,13 +248,11 @@ special colorCode s = "\x1b[1;" ++ show colorCode ++ "m" ++ s ++ "\x1b[m" --- XXX assumes that the cursor is already at the input line +-- XXX assumes that the cursor is already at the (cleared) input line  renderInputLine :: Buffer -> IO ()  renderInputLine (lhs, rhs) = do -    --clearLine      putStr $ "> " ++ pp lhs ++ pp rhs      moveCursorLeft (length $ ppVis rhs) -    --hFlush stdout    where      pp = concat . map reform      reform c = @@ -240,7 +262,7 @@ renderInputLine (lhs, rhs) = do            "\x1b[35m" ++ (              case ord c of                27 -> "^[" -              _ -> "\\" ++ show (ord c) +              _ -> charToCode c            ) ++ "\x1b[m"      ppVis = concat . map reformVis @@ -250,7 +272,7 @@ renderInputLine (lhs, rhs) = do          else              case ord c of                27 -> "^[" -              _ -> "\\" ++ show (ord c) +              _ -> charToCode c @@ -398,3 +420,10 @@ verbatimKeymap = do    --return $ InsertCharThenChangeMode c defaultGetCommand    return $ InsertCharThenChangeMode c (NormalMode nmap) + +-- TODO Control.Monad.whenLeft +whenLeft :: Monad m => Either a b -> (a -> m ()) -> m () +whenLeft (Left x) f = f x +whenLeft _ _ = return () + + | 
