-
Notifications
You must be signed in to change notification settings - Fork 12
Prototype for evaluation on the server. #30
Changes from all commits
08bf944
3093e54
f26ffd0
9d74ebf
c72ea41
849ef87
361d2d7
d928022
c38b39b
f3242f1
8180950
799eb42
2fe4d13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| module ReplSession ( | ||
| ReplSession, | ||
| evalInSession, | ||
| startSession, | ||
| endSession | ||
| ) where | ||
|
|
||
| import System.IO | ||
| import System.Process | ||
| import System.Directory (getDirectoryContents) | ||
| import Data.List (isSuffixOf) | ||
| import Control.Monad (liftM) | ||
|
|
||
| data ReplSession = ReplSession { | ||
| replIn :: Handle, | ||
| replOut :: Handle, | ||
| replError :: Handle, | ||
| replProcess :: ProcessHandle | ||
| } | ||
|
|
||
| evalInSession :: String -> ReplSession -> IO (Either String String) | ||
| evalInSession cmd session@(ReplSession input out err _) = do | ||
| clearHandle out 0 | ||
| clearHandle err 0 | ||
| sendCommand (cmd ++ "\n") session | ||
| readEvalOutput session | ||
|
|
||
| readEvalOutput :: ReplSession -> IO (Either String String) | ||
| readEvalOutput (ReplSession _ out err _) = do | ||
| output <- readUntil out ("--EvalFinished\n" `isSuffixOf`) | ||
| let onlyOutput = take (length output - length "--EvalFinished\n") output | ||
| hasErrorOutput <- hReady err | ||
| if hasErrorOutput | ||
| then readAll err >>= \errorOutput -> return . Left $ errorOutput | ||
| else return . Right $ onlyOutput | ||
|
|
||
| readUntil :: Handle -> (String -> Bool) -> IO String | ||
| readUntil handle predicate = readUntil' handle "" predicate | ||
|
|
||
| readUntil' :: Handle -> String -> (String -> Bool) -> IO String | ||
| readUntil' handle output predicate = do | ||
| char <- hGetChar handle | ||
| let newOutput = output ++ [char] | ||
| if predicate $ newOutput | ||
| then return newOutput | ||
| else readUntil' handle newOutput predicate | ||
|
|
||
| readAll :: Handle -> IO String | ||
| readAll handle = untilM' (liftM not $ hReady handle) (hGetChar handle) | ||
|
|
||
| startSession :: FilePath -> IO ReplSession | ||
| startSession path = do | ||
| cabalProject <- isCabalProject path | ||
| let (cmd, args) = if cabalProject then ("cabal", ["repl"]) else ("ghci", []) | ||
| (input, out, err, process) <- runInteractiveProcess cmd args (Just path) Nothing | ||
| let session = ReplSession input out err process | ||
| prepareSession session | ||
| return session | ||
|
|
||
| isCabalProject :: FilePath -> IO Bool | ||
| isCabalProject dir = do | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, this was just a temporary solution. I thought I might just keep the logic in the ClojureScript part. Do we (or Light Table itself) pass the name of the file around when we start the server?
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It makes more sense on the clojure side. Tell the haskell client to boot in the project's dir; that should be easier to tell from the LT side. All I can see from other plugins is they boot with the |
||
| files <- getDirectoryContents dir | ||
| return $ any (".cabal" `isSuffixOf`) files | ||
|
|
||
| prepareSession :: ReplSession -> IO () | ||
| prepareSession session@(ReplSession _ out _ _) = do | ||
| sendCommand ":set prompt \"--EvalFinished\\n\"\n" session | ||
| clearHandle out 1000 | ||
|
|
||
| sendCommand :: String -> ReplSession -> IO () | ||
| sendCommand cmd (ReplSession input _ _ _) = do | ||
| hPutStrLn input cmd | ||
| hFlush input | ||
|
|
||
| clearHandle :: Handle -> Int -> IO () | ||
| clearHandle handle wait = | ||
| untilM (liftM not $ hWaitForInput handle wait) $ do | ||
| hGetChar handle | ||
|
|
||
| untilM :: (Monad m) => m Bool -> m a -> m () | ||
| untilM predicate action = untilM' predicate action >> return () | ||
|
|
||
| untilM' :: (Monad m) => m Bool -> m a -> m [a] | ||
| untilM' predicate action = do | ||
| isFinished <- predicate | ||
| if isFinished | ||
| then return [] | ||
| else do | ||
| res <- action | ||
| others <- untilM' predicate action | ||
| return $ res : others | ||
|
|
||
| endSession :: ReplSession -> IO () | ||
| endSession session = do | ||
| sendCommand ":quit\n" session | ||
| waitForProcess $ replProcess session | ||
| return () | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to handle closing the repl session gracefully here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you already wrote
endSession. Does that do the job here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it should. I'll add it.