Complete documentation and update .cabal
authorLuke Lau <luke_lau@icloud.com>
Wed, 1 Aug 2018 13:10:38 +0000 (14:10 +0100)
committerLuke Lau <luke_lau@icloud.com>
Wed, 1 Aug 2018 13:10:38 +0000 (14:10 +0100)
lsp-test.cabal
src/Language/Haskell/LSP/Test.hs
src/Language/Haskell/LSP/Test/Parsing.hs
src/Language/Haskell/LSP/Test/Session.hs

index 07169a59474970de94b05ea6fabb616691e4f08d..c293534f1f2be532976d2b963b213540d3809369 100644 (file)
@@ -1,7 +1,18 @@
 name:                lsp-test
 version:             0.1.0.0
 synopsis:            Functional test framework for LSP servers.
--- description:
+description:
+  A test framework for writing tests against 
+  <https://microsoft.github.io/language-server-protocol/ Language Server Protocol servers>.
+  @lsp-test@ launches your server as a subprocess and allows you to simulate a session
+  down to the wire.
+  Used for testing in <https://github.com/haskell/haskell-ide-engine haskell-ide-engine>.
+  > runSession "hie" fullCaps "path/to/root/dir" $ do
+  > doc <- openDoc "Desktop/simple.hs" "haskell"
+  > diags <- waitForDiagnostics
+  > let pos = Position 12 5
+  >     params = TextDocumentPositionParams doc
+  > hover <- request TextDocumentHover params
 homepage:            https://github.com/Bubba/haskell-lsp-test#readme
 license:             BSD3
 license-file:        LICENSE
@@ -12,8 +23,13 @@ bug-reports:         https://github.com/Bubba/haskell-lsp-test/issues
 copyright:           2018 Luke Lau
 category:            Testing
 build-type:          Simple
-cabal-version:       >=1.10
+cabal-version:       2.0
 extra-source-files:  README.md
+tested-with:         GHC == 8.2.2 , GHC == 8.4.2 , GHC == 8.4.3
+
+source-repository head
+  type:     git
+  location: https://github.com/Bubba/haskell-lsp-test/
 
 library
   hs-source-dirs:      src
index b7057c2d5a4127f00a519c5ed01aa3e2a9ecf6fc..ed60e585822a9c51da7b4a63e35252b2d2179f3b 100644 (file)
@@ -10,9 +10,9 @@ Maintainer  : luke_lau@icloud.com
 Stability   : experimental
 Portability : POSIX
 
-A framework for testing
-<https://github.com/Microsoft/language-server-protocol Language Server Protocol servers>
-functionally.
+Provides the framework to start functionally testing
+<https://github.com/Microsoft/language-server-protocol Language Server Protocol servers>.
+You should import "Language.Haskell.LSP.Types" alongside this.
 -}
 module Language.Haskell.LSP.Test
   (
@@ -25,8 +25,7 @@ module Language.Haskell.LSP.Test
   , defaultConfig
   , module Language.Haskell.LSP.Test.Capabilities
   -- ** Exceptions
-  , SessionException(..)
-  , anySessionException
+  , module Language.Haskell.LSP.Test.Exceptions
   , withTimeout
   -- * Sending
   , request
@@ -35,16 +34,10 @@ module Language.Haskell.LSP.Test
   , sendNotification
   , sendResponse
   -- * Receving
-  , message
-  , anyRequest
-  , anyResponse
-  , anyNotification
-  , anyMessage
-  , loggingNotification
-  , publishDiagnosticsNotification
-  -- * Combinators
-  , satisfy
+  , module Language.Haskell.LSP.Test.Parsing
   -- * Utilities
+  -- | Quick helper functions for common tasks.
+  -- ** Initialization
   , initializeResponse
   -- ** Documents
   , openDoc
@@ -114,6 +107,13 @@ import System.FilePath
 import qualified Yi.Rope as Rope
 
 -- | Starts a new session.
+-- 
+-- > runSession "hie" fullCaps "path/to/root/dir" $ do
+-- >   doc <- openDoc "Desktop/simple.hs" "haskell"
+-- >   diags <- waitForDiagnostics
+-- >   let pos = Position 12 5
+-- >       params = TextDocumentPositionParams doc
+-- >   hover <- request TextDocumentHover params
 runSession :: String -- ^ The command to run the server.
            -> LSP.ClientCapabilities -- ^ The capabilities that the client should declare.
            -> FilePath -- ^ The filepath to the root directory for the session.
@@ -121,7 +121,7 @@ runSession :: String -- ^ The command to run the server.
            -> IO a
 runSession = runSessionWithConfig def
 
--- | Starts a new sesion with a client with the specified capabilities.
+-- | Starts a new sesion with a custom configuration.
 runSessionWithConfig :: SessionConfig -- ^ Configuration options for the session.
                      -> String -- ^ The command to run the server.
                      -> LSP.ClientCapabilities -- ^ The capabilities that the client should declare.
@@ -254,7 +254,7 @@ sendNotification :: ToJSON a
                  -> a -- ^ The notification parameters.
                  -> Session ()
 
--- Open a virtual file if we send a did open text document notification
+-- Open a virtual file if we send a did open text document notification
 sendNotification TextDocumentDidOpen params = do
   let params' = fromJust $ decode $ encode params
       n :: DidOpenTextDocumentNotification
@@ -264,7 +264,7 @@ sendNotification TextDocumentDidOpen params = do
   modify (\s -> s { vfs = newVFS })
   sendMessage n
 
--- Close a virtual file if we send a close text document notification
+-- Close a virtual file if we send a close text document notification
 sendNotification TextDocumentDidClose params = do
   let params' = fromJust $ decode $ encode params
       n :: DidCloseTextDocumentNotification
@@ -276,6 +276,7 @@ sendNotification TextDocumentDidClose params = do
 
 sendNotification method params = sendMessage (NotificationMessage "2.0" method params)
 
+-- | Sends a response to the server.
 sendResponse :: ToJSON a => ResponseMessage a -> Session ()
 sendResponse = sendMessage
 
@@ -327,6 +328,8 @@ waitForDiagnostics = do
   let (List diags) = diagsNot ^. params . LSP.diagnostics
   return diags
 
+-- | The same as 'waitForDiagnostics', but will only match a specific
+-- 'Language.Haskell.LSP.Types._source'.
 waitForDiagnosticsSource :: String -> Session [Diagnostic]
 waitForDiagnosticsSource src = do
   diags <- waitForDiagnostics
@@ -462,7 +465,7 @@ getDefinitions doc pos =
   let params = TextDocumentPositionParams doc pos
   in getResponseResult <$> request TextDocumentDefinition params
 
--- ^ Renames the term at the specified position.
+-- | Renames the term at the specified position.
 rename :: TextDocumentIdentifier -> Position -> String -> Session ()
 rename doc pos newName = do
   let params = RenameParams doc pos (T.pack newName)
index b6830357db5b32144f9629d900d5f42b91f64196..128cfb4018a4e9ce40bbabe86a86ac66f4edae22 100644 (file)
@@ -2,7 +2,18 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE OverloadedStrings #-}
-module Language.Haskell.LSP.Test.Parsing where
+
+module Language.Haskell.LSP.Test.Parsing
+  ( -- $receiving
+    message
+  , anyRequest
+  , anyResponse
+  , anyNotification
+  , anyMessage
+  , loggingNotification
+  , publishDiagnosticsNotification
+  , responseForId
+  ) where
 
 import Control.Applicative
 import Control.Concurrent
@@ -16,10 +27,38 @@ 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.Types as LSP hiding (error, message)
 import Language.Haskell.LSP.Test.Messages
 import Language.Haskell.LSP.Test.Session
 
+-- $receiving
+-- To receive a message, just specify the type that expect:
+-- 
+-- @
+-- msg1 <- message :: Session ApplyWorkspaceEditRequest
+-- msg2 <- message :: Session HoverResponse
+-- @
+--
+-- 'Language.Haskell.LSP.Test.Session' is actually just a parser
+-- that operates on messages under the hood. This means that you
+-- can create and combine parsers to match speicifc sequences of
+-- messages that you expect.
+--
+-- For example, if you wanted to match either a definition or
+-- references request:
+-- 
+-- > defOrImpl = (message :: Session DefinitionRequest)
+-- >          <|> (message :: Session ReferencesRequest)
+--
+-- If you wanted to match any number of telemetry
+-- notifications immediately followed by a response:
+-- 
+-- @
+-- logThenDiags =
+--  skipManyTill (message :: Session TelemetryNotification)
+--               anyResponse 
+-- @
+
 satisfy :: (FromServerMessage -> Bool) -> Session FromServerMessage
 satisfy pred = do
   
@@ -64,12 +103,14 @@ anyRequest = named "Any request" $ satisfy isServerRequest
 anyResponse :: Session FromServerMessage
 anyResponse = named "Any response" $ satisfy isServerResponse
 
+-- | Matches a response for a specific id.
 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
 
+-- | Matches any type of message.
 anyMessage :: Session FromServerMessage
 anyMessage = satisfy (const True)
 
@@ -91,6 +132,8 @@ loggingNotification = named "Logging notification" $ satisfy shouldSkip
     shouldSkip (ReqShowMessage _) = True
     shouldSkip _ = False
 
+-- | Matches a 'Language.Haskell.LSP.Test.PublishDiagnosticsNotification'
+-- (textDocument/publishDiagnostics) notification.
 publishDiagnosticsNotification :: Session PublishDiagnosticsNotification
 publishDiagnosticsNotification = named "Publish diagnostics notification" $ do
   NotPublishDiagnostics diags <- satisfy test
index 1fee2be798f4ebdf504667556d5aa0523bd3e280..8e4754f391fe606decc98280b66c2364ef598d92 100644 (file)
@@ -65,12 +65,7 @@ import System.IO
 -- You can send and receive messages to the server within 'Session' via 'getMessage',
 -- 'sendRequest' and 'sendNotification'.
 --
--- @
--- runSession \"path\/to\/root\/dir\" $ do
---   docItem <- getDocItem "Desktop/simple.hs" "haskell"
---   sendNotification TextDocumentDidOpen (DidOpenTextDocumentParams docItem)
---   diagnostics <- getMessage :: Session PublishDiagnosticsNotification
--- @
+
 type Session = ParserStateReader FromServerMessage SessionState SessionContext IO
 
 -- | Stuff you can configure for a 'Session'.