Merge branch 'master' into script-fsm
authorLuke Lau <luke_lau@icloud.com>
Mon, 9 Jul 2018 00:27:48 +0000 (01:27 +0100)
committerLuke Lau <luke_lau@icloud.com>
Mon, 9 Jul 2018 00:27:48 +0000 (01:27 +0100)
1  2 
haskell-lsp-test.cabal
lib/Language/Haskell/LSP/Test.hs
lib/Language/Haskell/LSP/Test/Replay.hs
src/Language/Haskell/LSP/Test/Parsing.hs

diff --combined haskell-lsp-test.cabal
index 7deb60eb507226b149236d15644ae1df2225c186,4a3c5ed9c1d957b9546382f254c6b86c0a738815..c5d1391eca38a99d3546c5ef407c06eaf5f564a0
@@@ -10,46 -10,15 +10,46 @@@ maintainer:          luke_lau@icloud.co
  copyright:           2018 Luke Lau
  category:            Testing
  build-type:          Simple
 -cabal-version:       >=1.10
 +cabal-version:       >=2.0
  extra-source-files:  README.md
  
  library
 -  hs-source-dirs:      src
 +  hs-source-dirs:      lib
    exposed-modules:     Language.Haskell.LSP.Test
                       , Language.Haskell.LSP.Test.Replay
 +                     , Language.Haskell.LSP.Test.Machine
    default-language:    Haskell2010
    build-depends:       base >= 4.7 && < 5
 +                     , haskell-lsp-types
 +                     , haskell-lsp >= 0.3
 +                     , haskell-lsp-test-internal
 +                     , aeson
 +                     , bytestring
 +                     , containers
 +                     , data-default
 +                     , directory
 +                     , filepath
 +                     , lens
 +                     , parser-combinators
 +                     , text
 +                     , unordered-containers
 +                     , yi-rope
 +
 +  ghc-options:         -W
 +
 +library haskell-lsp-test-internal
 +  hs-source-dirs:      src
 +  default-language:    Haskell2010
 +  exposed-modules:     Language.Haskell.LSP.Test.Compat
 +                       Language.Haskell.LSP.Test.Decoding
 +                       Language.Haskell.LSP.Test.Exceptions
 +                       Language.Haskell.LSP.Test.Files
 +                       Language.Haskell.LSP.Test.Messages
 +                       Language.Haskell.LSP.Test.Parsing
 +                       Language.Haskell.LSP.Test.Script
 +                       Language.Haskell.LSP.Test.Server
 +                       Language.Haskell.LSP.Test.Session
 +  build-depends:       base
                       , haskell-lsp-types
                       , haskell-lsp >= 0.3
                       , aeson
@@@ -64,7 -33,6 +64,7 @@@
                       , filepath
                       , lens
                       , mtl
 +                     , scientific
                       , parser-combinators
                       , process
                       , text
      build-depends:     Win32
    else
      build-depends:     unix
 -  other-modules:       Language.Haskell.LSP.Test.Compat
 -                       Language.Haskell.LSP.Test.Decoding
 -                       Language.Haskell.LSP.Test.Exceptions
 -                       Language.Haskell.LSP.Test.Files
 -                       Language.Haskell.LSP.Test.Messages
 -                       Language.Haskell.LSP.Test.Parsing
 -                       Language.Haskell.LSP.Test.Server
 -                       Language.Haskell.LSP.Test.Session
    ghc-options:         -W
  
 +
 +executable lsp-test
 +  hs-source-dirs:     lsp-test
 +  main-is:            Main.hs
 +  default-language:   Haskell2010
 +  build-depends:      base >= 4.7 && < 5
 +                    , haskell-lsp-types
 +                    , haskell-lsp >= 0.3
 +                    , haskell-lsp-test-internal
 +                    , haskell-lsp-test
 +                    , aeson
 +                    , bytestring
 +                    , directory
 +                    , filepath
 +                    , text
 +                    , unordered-containers
 +                    , scientific
 +
  test-suite tests
    type:                exitcode-stdio-1.0
    main-is:             Test.hs
                       , data-default
                       , directory
                       , haskell-lsp-test
 +                     , haskell-lsp-test-internal
                       , haskell-lsp
                       , haskell-lsp-types
                       , conduit
                       , aeson
                       , unordered-containers
                       , text
-   other-modules:       ParsingTests
    default-language:    Haskell2010
  
 -executable example
 +executable lsp-test-example
    hs-source-dirs:      example
    main-is:             Main.hs
    default-language:    Haskell2010
index 6c2c052570bfca437d7f568f56643e62893cc451,eda3cd2f2925bd34fdef4014482d4c8eef2a6133..eda3cd2f2925bd34fdef4014482d4c8eef2a6133
@@@ -19,9 -19,9 +19,9 @@@ module Language.Haskell.LSP.Tes
    , runSessionWithConfig
    , Session
    , SessionConfig(..)
-   , MonadSessionConfig(..)
    , SessionException(..)
    , anySessionException
+   , withTimeout
    -- * Sending
    , sendRequest
    , sendRequest_
    , sendNotification'
    , sendResponse
    -- * Receving
+   , message
    , anyRequest
-   , request
    , anyResponse
-   , response
    , anyNotification
-   , notification
    , anyMessage
    , loggingNotification
    , publishDiagnosticsNotification
@@@ -94,7 -92,7 +92,7 @@@ import Data.Defaul
  import qualified Data.HashMap.Strict as HashMap
  import qualified Data.Map as Map
  import Data.Maybe
- import Language.Haskell.LSP.Types hiding (id, capabilities)
+ import Language.Haskell.LSP.Types hiding (id, capabilities, message)
  import qualified Language.Haskell.LSP.Types as LSP
  import Language.Haskell.LSP.Messages
  import Language.Haskell.LSP.VFS
@@@ -151,20 -149,19 +149,19 @@@ runSessionWithConfig config serverExe r
        sendNotification Exit ExitParams
  
        return result
+   where
    -- | Listens to the server output, makes sure it matches the record and
    -- signals any semaphores
listenServer :: Handle -> Session ()
listenServer serverOut = do
-   msgBytes <- liftIO $ getNextMessage serverOut
  listenServer :: Handle -> SessionContext -> IO ()
  listenServer serverOut context = do
+     msgBytes <- getNextMessage serverOut
  
-   context <- ask
-   reqMap <- liftIO $ readMVar $ requestMap context
+     reqMap <- readMVar $ requestMap context
  
      let msg = decodeFromServerMsg reqMap msgBytes
-   liftIO $ writeChan (messageChan context) msg
+     writeChan (messageChan context) (ServerMessage msg)
  
-   listenServer serverOut
+     listenServer serverOut context
  
  -- | The current text contents of a document.
  documentContents :: TextDocumentIdentifier -> Session T.Text
@@@ -177,7 -174,7 +174,7 @@@ documentContents doc = d
  -- and returns the new content
  getDocumentEdit :: TextDocumentIdentifier -> Session T.Text
  getDocumentEdit doc = do
-   req <- request :: Session ApplyWorkspaceEditRequest
+   req <- message :: Session ApplyWorkspaceEditRequest
  
    unless (checkDocumentChanges req || checkChanges req) $
      liftIO $ throw (IncorrectApplyEditRequestException (show req))
@@@ -318,7 -315,7 +315,7 @@@ getDocUri file = d
  
  waitForDiagnostics :: Session [Diagnostic]
  waitForDiagnostics = do
-   diagsNot <- skipManyTill anyMessage notification :: Session PublishDiagnosticsNotification
+   diagsNot <- skipManyTill anyMessage message :: Session PublishDiagnosticsNotification
    let (List diags) = diagsNot ^. params . LSP.diagnostics
    return diags
  
  -- returned.
  noDiagnostics :: Session ()
  noDiagnostics = do
-   diagsNot <- notification :: Session PublishDiagnosticsNotification
+   diagsNot <- message :: Session PublishDiagnosticsNotification
    when (diagsNot ^. params . LSP.diagnostics /= List []) $ liftIO $ throw UnexpectedDiagnosticsException
  
  -- | Returns the symbols in a document.
@@@ -370,4 -367,4 +367,4 @@@ executeCodeAction action = d
    where handleEdit :: WorkspaceEdit -> Session ()
          handleEdit e =
            let req = RequestMessage "" (IdInt 0) WorkspaceApplyEdit (ApplyWorkspaceEditParams e)
-             in processMessage (ReqApplyWorkspaceEdit req)
+             in updateState (ReqApplyWorkspaceEdit req)
index 250fb2acb7537c783e01e4782853e059089bec51,b224be6cbf0132a6e50b6bc0380edd453ac94e2d..b224be6cbf0132a6e50b6bc0380edd453ac94e2d
@@@ -19,7 -19,6 +19,6 @@@ import           Data.Lis
  import           Data.Maybe
  import           Control.Lens hiding (List)
  import           Control.Monad
- import           System.IO
  import           System.FilePath
  import           Language.Haskell.LSP.Test
  import           Language.Haskell.LSP.Test.Files
@@@ -123,44 -122,51 +122,51 @@@ isNotification (NotShowMessag
  isNotification (NotCancelRequestFromServer _) = True
  isNotification _                              = False
  
- listenServer :: [FromServerMessage] -> RequestMap -> MVar LspId -> MVar LspIdRsp -> MVar () -> ThreadId -> Handle -> Session ()
- listenServer [] _ _ _ passSema _ _ = liftIO $ putMVar passSema ()
- listenServer expectedMsgs reqMap reqSema rspSema passSema mainThreadId serverOut  = do
-   msgBytes <- liftIO $ getNextMessage serverOut
+ -- listenServer :: [FromServerMessage]
+ --              -> RequestMap
+ --              -> MVar LspId
+ --              -> MVar LspIdRsp
+ --              -> MVar ()
+ --              -> ThreadId
+ --              -> Handle
+ --              -> SessionContext
+ --              -> IO ()
+ listenServer [] _ _ _ passSema _ _ _ = putMVar passSema ()
+ listenServer expectedMsgs reqMap reqSema rspSema passSema mainThreadId serverOut ctx = do
+   msgBytes <- getNextMessage serverOut
    let msg = decodeFromServerMsg reqMap msgBytes
  
    handleServerMessage request response notification msg
  
    if shouldSkip msg
-     then listenServer expectedMsgs reqMap reqSema rspSema passSema mainThreadId serverOut
+     then listenServer expectedMsgs reqMap reqSema rspSema passSema mainThreadId serverOut ctx
      else if inRightOrder msg expectedMsgs
-       then listenServer (delete msg expectedMsgs) reqMap reqSema rspSema passSema mainThreadId serverOut
+       then listenServer (delete msg expectedMsgs) reqMap reqSema rspSema passSema mainThreadId serverOut ctx
        else let remainingMsgs = takeWhile (not . isNotification) expectedMsgs
                  ++ [head $ dropWhile isNotification expectedMsgs]
                 exc = ReplayOutOfOrderException msg remainingMsgs
              in liftIO $ throwTo mainThreadId exc
  
    where
-   response :: ResponseMessage a -> Session ()
+   response :: ResponseMessage a -> IO ()
    response res = do
-     liftIO $ putStrLn $ "Got response for id " ++ show (res ^. id)
+     putStrLn $ "Got response for id " ++ show (res ^. id)
  
-     liftIO $ putMVar rspSema (res ^. id) -- unblock the handler waiting to send a request
+     putMVar rspSema (res ^. id) -- unblock the handler waiting to send a request
  
-   request :: RequestMessage ServerMethod a b -> Session ()
+   request :: RequestMessage ServerMethod a b -> IO ()
    request req = do
-     liftIO
-       $  putStrLn
+     putStrLn
        $  "Got request for id "
        ++ show (req ^. id)
        ++ " "
        ++ show (req ^. method)
  
-     liftIO $ putMVar reqSema (req ^. id) -- unblock the handler waiting for a response
+     putMVar reqSema (req ^. id) -- unblock the handler waiting for a response
  
-   notification :: NotificationMessage ServerMethod a -> Session ()
-   notification n = liftIO $ putStrLn $ "Got notification " ++ show (n ^. method)
+   notification :: NotificationMessage ServerMethod a -> IO ()
+   notification n = putStrLn $ "Got notification " ++ show (n ^. method)
  
  
  
index 06776cb7b8ca663dda5a001bbc8bdd97b75cd2d0,3ecc53888b31f5090b64344a866d512b5dd1460b..88109a5155801e2ed3015861c019ee4fd93217d6
@@@ -8,27 -8,37 +8,37 @@@ import Control.Applicativ
  import Control.Concurrent
  import Control.Lens
  import Control.Monad.IO.Class
- import Control.Monad.Trans.Class
+ import Control.Monad
  import Data.Aeson
  import qualified Data.ByteString.Lazy.Char8 as B
  import Data.Conduit.Parser
  import Data.Maybe
+ import qualified Data.Text as T
+ import Data.Typeable
  import Language.Haskell.LSP.Messages
  import Language.Haskell.LSP.Types as LSP hiding (error)
- import Language.Haskell.LSP.Test.Exceptions
  import Language.Haskell.LSP.Test.Messages
  import Language.Haskell.LSP.Test.Session
  import System.Console.ANSI
  
- satisfy :: (MonadIO m, MonadSessionConfig m) => (FromServerMessage -> Bool) -> ConduitParser FromServerMessage m FromServerMessage
+ satisfy :: (FromServerMessage -> Bool) -> Session FromServerMessage
  satisfy pred = do
-   timeout <- timeout <$> lift sessionConfig
-   tId <- liftIO myThreadId
-   timeoutThread <- liftIO $ forkIO $ do
+   
+   skipTimeout <- overridingTimeout <$> get
+   timeoutId <- curTimeoutId <$> get
+   unless skipTimeout $ do
+     chan <- asks messageChan
+     timeout <- asks (messageTimeout . config)
+     void $ liftIO $ forkIO $ do
        threadDelay (timeout * 1000000)
-     throwTo tId TimeoutException
+       writeChan chan (TimeoutMessage timeoutId)
    x <- await
-   liftIO $ killThread timeoutThread
+   unless skipTimeout $
+     modify $ \s -> s { curTimeoutId = timeoutId + 1 }
+   modify $ \s -> s { lastReceivedMessage = Just x }
  
    if pred x
      then do
        return x
      else empty
  
- -- | Matches if the message is a notification.
- anyNotification :: (MonadIO m, MonadSessionConfig m) => ConduitParser FromServerMessage m FromServerMessage
- anyNotification = named "Any notification" $ satisfy isServerNotification
- notification :: forall m a. (MonadIO m, MonadSessionConfig m, FromJSON a) => ConduitParser FromServerMessage m (NotificationMessage ServerMethod a)
- notification = named "Notification" $ do
-   let parser = decode . encodeMsg :: FromServerMessage -> Maybe (NotificationMessage ServerMethod a)
+ -- | Matches a message of type 'a'.
+ message :: forall a. (Typeable a, FromJSON a) => Session a
+ message =
+   let parser = decode . encodeMsg :: FromServerMessage -> Maybe a
+   in named (T.pack $ show $ head $ snd $ splitTyConApp $ last $ typeRepArgs $ typeOf parser) $ do
      x <- satisfy (isJust . parser)
      return $ castMsg x
  
+ -- | Matches if the message is a notification.
+ anyNotification :: Session FromServerMessage
+ anyNotification = named "Any notification" $ satisfy isServerNotification
  -- | Matches if the message is a request.
- anyRequest :: (MonadIO m, MonadSessionConfig m) => ConduitParser FromServerMessage m FromServerMessage
+ anyRequest :: Session FromServerMessage
  anyRequest = named "Any request" $ satisfy isServerRequest
  
- request :: forall m a b. (MonadIO m, MonadSessionConfig m, FromJSON a, FromJSON b) => ConduitParser FromServerMessage m (RequestMessage ServerMethod a b)
- request = named "Request" $ do
-   let parser = decode . encodeMsg :: FromServerMessage -> Maybe (RequestMessage ServerMethod a b)
-   x <- satisfy (isJust . parser)
-   return $ castMsg x
  -- | Matches if the message is a response.
- anyResponse :: (MonadIO m, MonadSessionConfig m) => ConduitParser FromServerMessage m FromServerMessage
+ anyResponse :: Session FromServerMessage
  anyResponse = named "Any response" $ satisfy isServerResponse
  
- response :: forall m a. (MonadIO m, MonadSessionConfig m, FromJSON a) => ConduitParser FromServerMessage m (ResponseMessage a)
- response = named "Response" $ do
-   let parser = decode . encodeMsg :: FromServerMessage -> Maybe (ResponseMessage a)
-   x <- satisfy (isJust . parser)
-   return $ castMsg x
- responseForId :: forall m a. (MonadIO m, MonadSessionConfig m, FromJSON a) => LspId -> ConduitParser FromServerMessage m (ResponseMessage a)
- responseForId lid = named "Response for id" $ do
+ responseForId :: forall a. FromJSON a => LspId -> Session (ResponseMessage a)
+ responseForId lid = named (T.pack $ "Response for id: " ++ show lid) $ do
    let parser = decode . encodeMsg :: FromServerMessage -> Maybe (ResponseMessage a)
    x <- satisfy (maybe False (\z -> z ^. LSP.id == responseId lid) . parser)
    return $ castMsg x
  
- anyMessage :: (MonadIO m, MonadSessionConfig m) => ConduitParser FromServerMessage m FromServerMessage
+ anyMessage :: Session FromServerMessage
  anyMessage = satisfy (const True)
  
  -- | A stupid method for getting out the inner message.
@@@ -85,13 -85,10 +85,13 @@@ castMsg = fromMaybe (error "Failed cast
  -- | A version of encode that encodes FromServerMessages as if they
  -- weren't wrapped.
  encodeMsg :: FromServerMessage -> B.ByteString
 -encodeMsg = encode . genericToJSON (defaultOptions { sumEncoding = UntaggedValue })
 +encodeMsg = encode . toJSONMsg
 +
 +toJSONMsg :: FromServerMessage -> Value
 +toJSONMsg = genericToJSON (defaultOptions { sumEncoding = UntaggedValue })
  
  -- | Matches if the message is a log message notification or a show message notification/request.
- loggingNotification :: (MonadIO m, MonadSessionConfig m) => ConduitParser FromServerMessage m FromServerMessage
+ loggingNotification :: Session FromServerMessage
  loggingNotification = named "Logging notification" $ satisfy shouldSkip
    where
      shouldSkip (NotLogMessage _) = True
@@@ -99,7 -96,7 +99,7 @@@
      shouldSkip (ReqShowMessage _) = True
      shouldSkip _ = False
  
- publishDiagnosticsNotification :: (MonadIO m, MonadSessionConfig m) => ConduitParser FromServerMessage m PublishDiagnosticsNotification
+ publishDiagnosticsNotification :: Session PublishDiagnosticsNotification
  publishDiagnosticsNotification = named "Publish diagnostics notification" $ do
    NotPublishDiagnostics diags <- satisfy test
    return diags