{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_HADDOCK show-extensions #-} -- | -- Module : Yi.Mode.Interactive -- License : GPL-2 -- Maintainer : yi-devel@googlegroups.com -- Stability : experimental -- Portability : portable -- -- Collection of 'Mode's for working with Haskell. module Yi.Mode.Interactive where import Control.Applicative import Control.Concurrent (threadDelay) import Control.Lens import Data.Monoid import qualified Data.Text as T import Yi.Buffer import Yi.Core (sendToProcess, startSubprocess, withSyntax) import Yi.Editor import Yi.History import Yi.Keymap import Yi.Keymap.Keys import Yi.Lexer.Alex (Tok) import Yi.Lexer.Compilation (Token) import qualified Yi.Mode.Compilation as Compilation import Yi.Modes import Yi.Monad import qualified Yi.Rope as R import qualified Yi.Syntax.OnlineTree as OnlineTree import Yi.Utils mode :: Mode (OnlineTree.Tree (Tok Token)) mode = Compilation.mode { modeApplies = modeNeverApplies, modeName = "interactive", modeKeymap = topKeymapA %~ (<||) (choice [spec KHome ?>>! moveToSol, spec KEnter ?>>! do eof <- withCurrentBuffer atLastLine if eof then feedCommand else withSyntax modeFollow, meta (char 'p') ?>>! interactHistoryMove 1, meta (char 'n') ?>>! interactHistoryMove (-1) ]) } interactId :: T.Text interactId = "Interact" -- | TODO: we're just converting back and forth here, 'historyMoveGen' -- and friends need to migrate to YiString it seems. interactHistoryMove :: Int -> EditorM () interactHistoryMove delta = historyMoveGen interactId delta (R.toText <$> withCurrentBuffer getInput) >>= inp where inp = withCurrentBuffer . setInput . R.fromText interactHistoryFinish :: EditorM () interactHistoryFinish = historyFinishGen interactId (R.toText <$> withCurrentBuffer getInput) interactHistoryStart :: EditorM () interactHistoryStart = historyStartGen interactId getInputRegion :: BufferM Region getInputRegion = do mo <- getMarkB (Just "StdOUT") p <- pointAt botB q <- use $ markPointA mo return $ mkRegion p q getInput :: BufferM R.YiString getInput = readRegionB =<< getInputRegion setInput :: R.YiString -> BufferM () setInput val = flip replaceRegionB val =<< getInputRegion -- | Open a new buffer for interaction with a process. spawnProcess :: String -> [String] -> YiM BufferRef spawnProcess = spawnProcessMode mode -- | open a new buffer for interaction with a process, using any -- interactive-derived mode spawnProcessMode :: Mode syntax -> FilePath -> [String] -> YiM BufferRef spawnProcessMode interMode cmd args = do b <- startSubprocess cmd args (const $ return ()) withEditor interactHistoryStart mode' <- lookupMode $ AnyMode interMode withCurrentBuffer $ do m1 <- getMarkB (Just "StdERR") m2 <- getMarkB (Just "StdOUT") modifyMarkB m1 (\v -> v {markGravity = Backward}) modifyMarkB m2 (\v -> v {markGravity = Backward}) setAnyMode mode' return b -- | Send the type command to the process feedCommand :: YiM () feedCommand = do b <- gets currentBuffer withEditor interactHistoryFinish cmd <- withCurrentBuffer $ do botB newlineB me <- getMarkB (Just "StdERR") mo <- getMarkB (Just "StdOUT") p <- pointB q <- use $ markPointA mo cmd <- readRegionB $ mkRegion p q markPointA me .= p markPointA mo .= p return $ R.toString cmd withEditor interactHistoryStart sendToProcess b cmd -- | Send command, recieve reply queryReply :: BufferRef -> String -> YiM R.YiString queryReply buf cmd = do start <- withGivenBuffer buf (botB >> pointB) sendToProcess buf (cmd <> "\n") io $ threadDelay 50000 -- Hack to let ghci finish writing its output. withGivenBuffer buf $ do botB moveToSol leftB -- There is probably a much better way to do this moving around, but it works end <- pointB result <- readRegionB (mkRegion start end) botB return result