{-# 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.Concurrent   (threadDelay)
import           Lens.Micro.Platform           (use, (%~), (.=))
import           Data.Monoid          ((<>))
import qualified Data.Text            as T (Text)
import           Yi.Buffer
import           Yi.Core              (sendToProcess, startSubprocess, withSyntax)
import           Yi.Editor
import           Yi.History           (historyFinishGen, historyMoveGen, historyStartGen)
import           Yi.Keymap            (YiM, topKeymapA)
import           Yi.Keymap.Keys       (Key (KEnter, KHome), char, choice, meta, spec, (<||), (?>>!))
import           Yi.Lexer.Alex        (Tok)
import           Yi.Lexer.Compilation (Token)
import qualified Yi.Mode.Compilation  as Compilation (mode)
import           Yi.Mode.Common       (lookupMode)
import           Yi.Monad             (gets)
import qualified Yi.Rope              as R (YiString, fromText, toString, toText)
import qualified Yi.Syntax.OnlineTree as OnlineTree (Tree)
import           Yi.Utils             (io)


mode :: Mode (OnlineTree.Tree (Tok Token))
mode :: Mode (Tree (Tok Token))
mode = Mode (Tree (Tok Token))
Compilation.mode
  { modeApplies :: FilePath -> YiString -> Bool
modeApplies = FilePath -> YiString -> Bool
forall a b. a -> b -> Bool
modeNeverApplies,
    modeName :: Text
modeName = Text
"interactive",
    modeKeymap :: KeymapSet -> KeymapSet
modeKeymap = (Keymap -> Identity Keymap) -> KeymapSet -> Identity KeymapSet
Lens' KeymapSet Keymap
topKeymapA ((Keymap -> Identity Keymap) -> KeymapSet -> Identity KeymapSet)
-> (Keymap -> Keymap) -> KeymapSet -> KeymapSet
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ Keymap -> Keymap -> Keymap
forall (f :: * -> *) w e a.
MonadInteract f w e =>
f a -> f a -> f a
(<||)
     ([Keymap] -> Keymap
forall (m :: * -> *) w e a.
(MonadInteract m w e, MonadFail m) =>
[m a] -> m a
choice
      [Key -> Event
spec Key
KHome Event -> BufferM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! BufferM ()
moveToSol,
       Key -> Event
spec Key
KEnter Event -> YiM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! do
          Bool
eof <- BufferM Bool -> YiM Bool
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM Bool
atLastLine
          if Bool
eof
            then YiM ()
feedCommand
            else (forall syntax. Mode syntax -> syntax -> Action) -> YiM ()
forall x a.
(Show x, YiAction a x) =>
(forall syntax. Mode syntax -> syntax -> a) -> YiM ()
withSyntax forall syntax. Mode syntax -> syntax -> Action
modeFollow,
       Event -> Event
meta (Char -> Event
char Char
'p') Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! Int -> EditorM ()
interactHistoryMove Int
1,
       Event -> Event
meta (Char -> Event
char Char
'n') Event -> EditorM () -> Keymap
forall (m :: * -> *) a x.
(MonadInteract m Action Event, YiAction a x, Show x) =>
Event -> a -> m ()
?>>! Int -> EditorM ()
interactHistoryMove (-Int
1)
      ])
  }

interactId :: T.Text
interactId :: Text
interactId = Text
"Interact"

-- | TODO: we're just converting back and forth here, 'historyMoveGen'
-- and friends need to migrate to YiString it seems.
interactHistoryMove :: Int -> EditorM ()
interactHistoryMove :: Int -> EditorM ()
interactHistoryMove Int
delta =
  Text -> Int -> EditorM Text -> EditorM Text
historyMoveGen Text
interactId Int
delta (YiString -> Text
R.toText (YiString -> Text) -> EditorM YiString -> EditorM Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BufferM YiString -> EditorM YiString
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM YiString
getInput) EditorM Text -> (Text -> EditorM ()) -> EditorM ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> EditorM ()
inp
  where
    inp :: Text -> EditorM ()
inp = BufferM () -> EditorM ()
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer (BufferM () -> EditorM ())
-> (Text -> BufferM ()) -> Text -> EditorM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. YiString -> BufferM ()
setInput (YiString -> BufferM ())
-> (Text -> YiString) -> Text -> BufferM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> YiString
R.fromText

interactHistoryFinish :: EditorM ()
interactHistoryFinish :: EditorM ()
interactHistoryFinish =
  Text -> EditorM Text -> EditorM ()
historyFinishGen Text
interactId (YiString -> Text
R.toText (YiString -> Text) -> EditorM YiString -> EditorM Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BufferM YiString -> EditorM YiString
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM YiString
getInput)

interactHistoryStart :: EditorM ()
interactHistoryStart :: EditorM ()
interactHistoryStart = Text -> EditorM ()
historyStartGen Text
interactId

getInputRegion :: BufferM Region
getInputRegion :: BufferM Region
getInputRegion = do Mark
mo <- Maybe FilePath -> BufferM Mark
getMarkB (FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
"StdOUT")
                    Point
p <- BufferM () -> BufferM Point
forall a. BufferM a -> BufferM Point
pointAt BufferM ()
botB
                    Point
q <- Getting Point FBuffer Point -> BufferM Point
forall s (m :: * -> *) a. MonadState s m => Getting a s a -> m a
use (Getting Point FBuffer Point -> BufferM Point)
-> Getting Point FBuffer Point -> BufferM Point
forall a b. (a -> b) -> a -> b
$ Mark -> Lens' FBuffer Point
markPointA Mark
mo
                    Region -> BufferM Region
forall (m :: * -> *) a. Monad m => a -> m a
return (Region -> BufferM Region) -> Region -> BufferM Region
forall a b. (a -> b) -> a -> b
$ Point -> Point -> Region
mkRegion Point
p Point
q

getInput :: BufferM R.YiString
getInput :: BufferM YiString
getInput = Region -> BufferM YiString
readRegionB (Region -> BufferM YiString) -> BufferM Region -> BufferM YiString
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< BufferM Region
getInputRegion

setInput :: R.YiString -> BufferM ()
setInput :: YiString -> BufferM ()
setInput YiString
val = (Region -> YiString -> BufferM ())
-> YiString -> Region -> BufferM ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip Region -> YiString -> BufferM ()
replaceRegionB YiString
val (Region -> BufferM ()) -> BufferM Region -> BufferM ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< BufferM Region
getInputRegion

-- | Open a new buffer for interaction with a process.
spawnProcess :: String -> [String] -> YiM BufferRef
spawnProcess :: FilePath -> [FilePath] -> YiM BufferRef
spawnProcess = Mode (Tree (Tok Token)) -> FilePath -> [FilePath] -> YiM BufferRef
forall syntax.
Mode syntax -> FilePath -> [FilePath] -> YiM BufferRef
spawnProcessMode Mode (Tree (Tok Token))
mode

-- | open a new buffer for interaction with a process, using any
-- interactive-derived mode
spawnProcessMode :: Mode syntax -> FilePath -> [String] -> YiM BufferRef
spawnProcessMode :: Mode syntax -> FilePath -> [FilePath] -> YiM BufferRef
spawnProcessMode Mode syntax
interMode FilePath
cmd [FilePath]
args = do
    BufferRef
b <- FilePath
-> [FilePath]
-> (Either SomeException ExitCode -> YiM ())
-> YiM BufferRef
forall x.
FilePath
-> [FilePath]
-> (Either SomeException ExitCode -> YiM x)
-> YiM BufferRef
startSubprocess FilePath
cmd [FilePath]
args (YiM () -> Either SomeException ExitCode -> YiM ()
forall a b. a -> b -> a
const (YiM () -> Either SomeException ExitCode -> YiM ())
-> YiM () -> Either SomeException ExitCode -> YiM ()
forall a b. (a -> b) -> a -> b
$ () -> YiM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ())
    EditorM () -> YiM ()
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM ()
interactHistoryStart
    AnyMode
mode' <- AnyMode -> YiM AnyMode
lookupMode (AnyMode -> YiM AnyMode) -> AnyMode -> YiM AnyMode
forall a b. (a -> b) -> a -> b
$ Mode syntax -> AnyMode
forall syntax. Mode syntax -> AnyMode
AnyMode Mode syntax
interMode
    BufferM () -> YiM ()
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer (BufferM () -> YiM ()) -> BufferM () -> YiM ()
forall a b. (a -> b) -> a -> b
$ do
        Mark
m1 <- Maybe FilePath -> BufferM Mark
getMarkB (FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
"StdERR")
        Mark
m2 <- Maybe FilePath -> BufferM Mark
getMarkB (FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
"StdOUT")
        Mark -> (MarkValue -> MarkValue) -> BufferM ()
modifyMarkB Mark
m1 (\MarkValue
v -> MarkValue
v {markGravity :: Direction
markGravity = Direction
Backward})
        Mark -> (MarkValue -> MarkValue) -> BufferM ()
modifyMarkB Mark
m2 (\MarkValue
v -> MarkValue
v {markGravity :: Direction
markGravity = Direction
Backward})
        AnyMode -> BufferM ()
setAnyMode AnyMode
mode'
    BufferRef -> YiM BufferRef
forall (m :: * -> *) a. Monad m => a -> m a
return BufferRef
b



-- | Send the type command to the process
feedCommand :: YiM ()
feedCommand :: YiM ()
feedCommand = do
  BufferRef
b <- (Editor -> BufferRef) -> YiM BufferRef
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets Editor -> BufferRef
currentBuffer
  EditorM () -> YiM ()
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM ()
interactHistoryFinish
  FilePath
cmd <- BufferM FilePath -> YiM FilePath
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer (BufferM FilePath -> YiM FilePath)
-> BufferM FilePath -> YiM FilePath
forall a b. (a -> b) -> a -> b
$ do
      BufferM ()
botB
      BufferM ()
newlineB
      Mark
me <- Maybe FilePath -> BufferM Mark
getMarkB (FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
"StdERR")
      Mark
mo <- Maybe FilePath -> BufferM Mark
getMarkB (FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
"StdOUT")
      Point
p <- BufferM Point
pointB
      Point
q <- Getting Point FBuffer Point -> BufferM Point
forall s (m :: * -> *) a. MonadState s m => Getting a s a -> m a
use (Getting Point FBuffer Point -> BufferM Point)
-> Getting Point FBuffer Point -> BufferM Point
forall a b. (a -> b) -> a -> b
$ Mark -> Lens' FBuffer Point
markPointA Mark
mo
      YiString
cmd <- Region -> BufferM YiString
readRegionB (Region -> BufferM YiString) -> Region -> BufferM YiString
forall a b. (a -> b) -> a -> b
$ Point -> Point -> Region
mkRegion Point
p Point
q
      Mark -> Lens' FBuffer Point
markPointA Mark
me ((Point -> Identity Point) -> FBuffer -> Identity FBuffer)
-> Point -> BufferM ()
forall s (m :: * -> *) a b.
MonadState s m =>
ASetter s s a b -> b -> m ()
.= Point
p
      Mark -> Lens' FBuffer Point
markPointA Mark
mo ((Point -> Identity Point) -> FBuffer -> Identity FBuffer)
-> Point -> BufferM ()
forall s (m :: * -> *) a b.
MonadState s m =>
ASetter s s a b -> b -> m ()
.= Point
p
      FilePath -> BufferM FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return (FilePath -> BufferM FilePath) -> FilePath -> BufferM FilePath
forall a b. (a -> b) -> a -> b
$ YiString -> FilePath
R.toString YiString
cmd
  EditorM () -> YiM ()
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM ()
interactHistoryStart
  BufferRef -> FilePath -> YiM ()
sendToProcess BufferRef
b FilePath
cmd

-- | Send command, recieve reply
queryReply :: BufferRef -> String -> YiM R.YiString
queryReply :: BufferRef -> FilePath -> YiM YiString
queryReply BufferRef
buf FilePath
cmd = do
    Point
start <- BufferRef -> BufferM Point -> YiM Point
forall (m :: * -> *) a.
MonadEditor m =>
BufferRef -> BufferM a -> m a
withGivenBuffer BufferRef
buf (BufferM ()
botB BufferM () -> BufferM Point -> BufferM Point
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> BufferM Point
pointB)
    BufferRef -> FilePath -> YiM ()
sendToProcess BufferRef
buf (FilePath
cmd FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
"\n")
    IO () -> YiM ()
forall (m :: * -> *) a. MonadBase IO m => IO a -> m a
io (IO () -> YiM ()) -> IO () -> YiM ()
forall a b. (a -> b) -> a -> b
$ Int -> IO ()
threadDelay Int
50000  -- Hack to let ghci finish writing its output.
    BufferRef -> BufferM YiString -> YiM YiString
forall (m :: * -> *) a.
MonadEditor m =>
BufferRef -> BufferM a -> m a
withGivenBuffer BufferRef
buf (BufferM YiString -> YiM YiString)
-> BufferM YiString -> YiM YiString
forall a b. (a -> b) -> a -> b
$ do
      BufferM ()
botB
      BufferM ()
moveToSol
      BufferM ()
leftB -- There is probably a much better way to do this moving around, but it works
      Point
end <- BufferM Point
pointB
      YiString
result <- Region -> BufferM YiString
readRegionB (Point -> Point -> Region
mkRegion Point
start Point
end)
      BufferM ()
botB
      YiString -> BufferM YiString
forall (m :: * -> *) a. Monad m => a -> m a
return YiString
result