@@ -14,6 +14,7 @@
15{-# LANGUAGE OverloadedStrings #-}
15{-# LANGUAGE OverloadedStrings #-}
16{-# LANGUAGE ScopedTypeVariables #-}
16{-# LANGUAGE ScopedTypeVariables #-}
17+{-# LANGUAGE NoImplicitPrelude #-}
17module Language.PureScript.Ide.Integration
18module Language.PureScript.Ide.Integration
19 -- managing the server process
20 -- managing the server process
@@ -44,19 +45,15 @@ module Language.PureScript.Ide.Integration
44 , parseTextResult
45 , parseTextResult
47−import Control.Concurrent (threadDelay)
48+import Protolude
48−import Control.Exception
49+import Unsafe (fromJust)
49−import Control.Monad (join, when)
50+
50import Data.Aeson
51import Data.Aeson
51import Data.Aeson.Types
52import Data.Aeson.Types
52−import qualified Data.ByteString.Lazy.UTF8 as BSL
53−import Data.Either (isRight)
54−import Data.Maybe (fromJust, isNothing, fromMaybe)
55import qualified Data.Text as T
53import qualified Data.Text as T
56import qualified Data.Vector as V
54import qualified Data.Vector as V
57import Language.PureScript.Ide.Util
55import Language.PureScript.Ide.Util
58import System.Directory
56import System.Directory
60import System.FilePath
57import System.FilePath
61import System.IO.Error (mkIOError, userErrorType)
58import System.IO.Error (mkIOError, userErrorType)
62import System.Process
59import System.Process
@@ -81,26 +78,23 @@ stopServer = terminateProcess
81withServer :: IO a -> IO a
78withServer :: IO a -> IO a
82withServer s = do
79withServer s = do
83 _ <- startServer
80 _ <- startServer
84− started <- tryNTimes 5 (shush <$> (try getCwd :: IO (Either SomeException String)))
81+ started <- tryNTimes 5 (rightToMaybe <$> (try getCwd :: IO (Either SomeException Text)))
85 when (isNothing started) $
82 when (isNothing started) $
86 throwIO (mkIOError userErrorType "psc-ide-server didn't start in time" Nothing Nothing)
83 throwIO (mkIOError userErrorType "psc-ide-server didn't start in time" Nothing Nothing)
88 quitServer
85 quitServer
91−shush :: Either a b -> Maybe b
92−shush = either (const Nothing) Just
94-- project management utils
88-- project management utils
96compileTestProject :: IO Bool
90compileTestProject :: IO Bool
97compileTestProject = do
91compileTestProject = do
98 pdir <- projectDirectory
92 pdir <- projectDirectory
99 (_, _, _, procHandle) <- createProcess $
93 (_, _, _, procHandle) <- createProcess $
100− (shell $ "psc " ++ fileGlob) { cwd = Just pdir
94+ (shell . toS $ "psc " <> fileGlob) { cwd = Just pdir
101− , std_out = CreatePipe
95+ , std_out = CreatePipe
102− , std_err = CreatePipe
96+ , std_err = CreatePipe
104 r <- tryNTimes 5 (getProcessExitCode procHandle)
98 r <- tryNTimes 5 (getProcessExitCode procHandle)
105 pure (fromMaybe False (isSuccess <$> r))
99 pure (fromMaybe False (isSuccess <$> r))
@@ -122,24 +116,17 @@ deleteOutputFolder = do
122deleteFileIfExists :: FilePath -> IO ()
116deleteFileIfExists :: FilePath -> IO ()
123deleteFileIfExists fp = whenM (doesFileExist fp) (removeFile fp)
117deleteFileIfExists fp = whenM (doesFileExist fp) (removeFile fp)
125−whenM :: Monad m => m Bool -> m () -> m ()
130isSuccess :: ExitCode -> Bool
119isSuccess :: ExitCode -> Bool
131isSuccess ExitSuccess = True
120isSuccess ExitSuccess = True
132isSuccess (ExitFailure _) = False
121isSuccess (ExitFailure _) = False
134−fileGlob :: String
123+fileGlob :: Text
136− [ "\"src/**/*.purs\""
124+fileGlob = "\"src/**/*.purs\""
139-- Integration Testing API
126-- Integration Testing API
141−sendCommand :: Value -> IO String
128+sendCommand :: Value -> IO Text
142−sendCommand v = readCreateProcess
129+sendCommand v = toS <$> readCreateProcess
143 ((shell "psc-ide-client") { std_out=CreatePipe
130 ((shell "psc-ide-client") { std_out=CreatePipe
144 , std_err=CreatePipe
131 , std_err=CreatePipe
@@ -147,68 +134,68 @@ sendCommand v = readCreateProcess
148quitServer :: IO ()
135quitServer :: IO ()
149quitServer = do
136quitServer = do
150− let quitCommand = object ["command" .= ("quit" :: String)]
137+ let quitCommand = object ["command" .= ("quit" :: Text)]
151− _ <- try $ sendCommand quitCommand :: IO (Either SomeException String)
138+ _ <- try $ sendCommand quitCommand :: IO (Either SomeException Text)
152 return ()
139 return ()
154reset :: IO ()
141reset :: IO ()
155reset = do
142reset = do
156− let resetCommand = object ["command" .= ("reset" :: String)]
143+ let resetCommand = object ["command" .= ("reset" :: Text)]
157− _ <- try $ sendCommand resetCommand :: IO (Either SomeException String)
144+ _ <- try $ sendCommand resetCommand :: IO (Either SomeException Text)
158 return ()
145 return ()
160−getCwd :: IO String
147+getCwd :: IO Text
161getCwd = do
148getCwd = do
162− let cwdCommand = object ["command" .= ("cwd" :: String)]
149+ let cwdCommand = object ["command" .= ("cwd" :: Text)]
163 sendCommand cwdCommand
150 sendCommand cwdCommand
165−loadModule :: String -> IO String
152+loadModule :: Text -> IO Text
166loadModule m = loadModules [m]
153loadModule m = loadModules [m]
168−loadModules :: [String] -> IO String
155+loadModules :: [Text] -> IO Text
169loadModules = sendCommand . load
156loadModules = sendCommand . load
171−loadAll :: IO String
158+loadAll :: IO Text
172loadAll = sendCommand (load [])
159loadAll = sendCommand (load [])
174−getFlexCompletions :: String -> IO [(String, String, String)]
161+getFlexCompletions :: Text -> IO [(Text, Text, Text)]
175getFlexCompletions q = parseCompletions <$> sendCommand (completion [] (Just (flexMatcher q)) Nothing)
162getFlexCompletions q = parseCompletions <$> sendCommand (completion [] (Just (flexMatcher q)) Nothing)
177−getFlexCompletionsInModule :: String -> String -> IO [(String, String, String)]
164+getFlexCompletionsInModule :: Text -> Text -> IO [(Text, Text, Text)]
178getFlexCompletionsInModule q m = parseCompletions <$> sendCommand (completion [] (Just (flexMatcher q)) (Just m))
165getFlexCompletionsInModule q m = parseCompletions <$> sendCommand (completion [] (Just (flexMatcher q)) (Just m))
180−getType :: String -> IO [(String, String, String)]
167+getType :: Text -> IO [(Text, Text, Text)]
181getType q = parseCompletions <$> sendCommand (typeC q [])
168getType q = parseCompletions <$> sendCommand (typeC q [])
183−addImport :: String -> FilePath -> FilePath -> IO String
170+addImport :: Text -> FilePath -> FilePath -> IO Text
184addImport identifier fp outfp = sendCommand (addImportC identifier fp outfp)
171addImport identifier fp outfp = sendCommand (addImportC identifier fp outfp)
186−addImplicitImport :: String -> FilePath -> FilePath -> IO String
173+addImplicitImport :: Text -> FilePath -> FilePath -> IO Text
187addImplicitImport mn fp outfp = sendCommand (addImplicitImportC mn fp outfp)
174addImplicitImport mn fp outfp = sendCommand (addImplicitImportC mn fp outfp)
189−rebuildModule :: FilePath -> IO String
176+rebuildModule :: FilePath -> IO Text
190rebuildModule m = sendCommand (rebuildC m Nothing)
177rebuildModule m = sendCommand (rebuildC m Nothing)
192-- Command Encoding
179-- Command Encoding
194−commandWrapper :: String -> Value -> Value
181+commandWrapper :: Text -> Value -> Value
195commandWrapper c p = object ["command" .= c, "params" .= p]
182commandWrapper c p = object ["command" .= c, "params" .= p]
197−load :: [String] -> Value
184+load :: [Text] -> Value
198load ms = commandWrapper "load" (object ["modules" .= ms])
185load ms = commandWrapper "load" (object ["modules" .= ms])
200−typeC :: String -> [Value] -> Value
187+typeC :: Text -> [Value] -> Value
201typeC q filters = commandWrapper "type" (object ["search" .= q, "filters" .= filters])
188typeC q filters = commandWrapper "type" (object ["search" .= q, "filters" .= filters])
203−addImportC :: String -> FilePath -> FilePath -> Value
190+addImportC :: Text -> FilePath -> FilePath -> Value
204addImportC identifier = addImportW $
191addImportC identifier = addImportW $
205− object [ "importCommand" .= ("addImport" :: String)
192+ object [ "importCommand" .= ("addImport" :: Text)
206 , "identifier" .= identifier
193 , "identifier" .= identifier
209−addImplicitImportC :: String -> FilePath -> FilePath -> Value
196+addImplicitImportC :: Text -> FilePath -> FilePath -> Value
210addImplicitImportC mn = addImportW $
197addImplicitImportC mn = addImportW $
211− object [ "importCommand" .= ("addImplicitImport" :: String)
198+ object [ "importCommand" .= ("addImplicitImport" :: Text)
212 , "module" .= mn
199 , "module" .= mn
@@ -226,7 +213,7 @@ addImportW importCommand fp outfp =
229−completion :: [Value] -> Maybe Value -> Maybe String -> Value
216+completion :: [Value] -> Maybe Value -> Maybe Text -> Value
230completion filters matcher currentModule =
217completion filters matcher currentModule =
232 matcher' = case matcher of
219 matcher' = case matcher of
@@ -238,16 +225,16 @@ completion filters matcher currentModule =
239 commandWrapper "complete" (object $ "filters" .= filters : matcher' ++ currentModule' )
226 commandWrapper "complete" (object $ "filters" .= filters : matcher' ++ currentModule' )
241−flexMatcher :: String -> Value
228+flexMatcher :: Text -> Value
242−flexMatcher q = object [ "matcher" .= ("flex" :: String)
229+flexMatcher q = object [ "matcher" .= ("flex" :: Text)
243 , "params" .= object ["search" .= q]
230 , "params" .= object ["search" .= q]
246-- Result parsing
233-- Result parsing
248−unwrapResult :: Value -> Parser (Either String Value)
235+unwrapResult :: Value -> Parser (Either Text Value)
249unwrapResult = withObject "result" $ \o -> do
236unwrapResult = withObject "result" $ \o -> do
250− (rt :: String) <- o .: "resultType"
237+ (rt :: Text) <- o .: "resultType"
251 case rt of
238 case rt of
252 "error" -> do
239 "error" -> do
253 res <- o .: "result"
240 res <- o .: "result"
@@ -255,16 +242,16 @@ unwrapResult = withObject "result" $ \o -> do
255 "success" -> do
242 "success" -> do
256 res <- o .: "result"
243 res <- o .: "result"
257 pure (Right res)
244 pure (Right res)
258− _ -> fail "lol"
245+ _ -> mzero
260−withResult :: (Value -> Parser a) -> Value -> Parser (Either String a)
247+withResult :: (Value -> Parser a) -> Value -> Parser (Either Text a)
261withResult p v = do
248withResult p v = do
262 r <- unwrapResult v
249 r <- unwrapResult v
263 case r of
250 case r of
264 Left err -> pure (Left err)
251 Left err -> pure (Left err)
265 Right res -> Right <$> p res
252 Right res -> Right <$> p res
267−completionParser :: Value -> Parser [(String, String, String)]
254+completionParser :: Value -> Parser [(Text, Text, Text)]
268completionParser = withArray "res" $ \cs ->
255completionParser = withArray "res" $ \cs ->
269 mapM (withObject "completion" $ \o -> do
256 mapM (withObject "completion" $ \o -> do
270 ident <- o .: "identifier"
257 ident <- o .: "identifier"
@@ -272,22 +259,16 @@ completionParser = withArray "res" $ \cs ->
272 ty <- o .: "type"
259 ty <- o .: "type"
273 pure (module', ident, ty)) (V.toList cs)
260 pure (module', ident, ty)) (V.toList cs)
275−valueFromString :: String -> Value
262+valueFromText :: Text -> Value
276−valueFromString = fromJust . decode . BSL.fromString
263+valueFromText = fromJust . decode . toS
278−resultIsSuccess :: String -> Bool
265+resultIsSuccess :: Text -> Bool
279−resultIsSuccess = isRight . join . parseEither unwrapResult . valueFromString
266+resultIsSuccess = isRight . join . first toS . parseEither unwrapResult . valueFromText
281−parseCompletions :: String -> [(String, String, String)]
268+parseCompletions :: Text -> [(Text, Text, Text)]
282−parseCompletions s = fromJust $ do
269+parseCompletions s =
283− cs <- parseMaybe (withResult completionParser) (valueFromString s)
270+ fromJust $ join (rightToMaybe <$> parseMaybe (withResult completionParser) (valueFromText s))
285− Left _ -> error "Failed to parse completions"
286− Right cs' -> pure cs'
288−parseTextResult :: String -> String
272+parseTextResult :: Text -> Text
289−parseTextResult s = fromJust $ do
273+parseTextResult s =
290− r <- parseMaybe (withResult (withText "tr" pure)) (valueFromString s)
274+ fromJust $ join (rightToMaybe <$> parseMaybe (withResult (withText "tr" pure)) (valueFromText s))
292− Left _ -> error "Failed to parse textResult"
293− Right r' -> pure (T.unpack r')