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
(
, runSessionWithConfig
, SessionConfig(..)
, defaultConfig
- , module Language.Haskell.LSP.Test.Capabilities
+ , module Language.Haskell.LSP.Types.Capabilities
-- ** Exceptions
- , SessionException(..)
- , anySessionException
+ , module Language.Haskell.LSP.Test.Exceptions
, withTimeout
-- * Sending
, request
, request_
, sendRequest
, sendNotification
- , sendRequestMessage
- , 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
import Data.Maybe
import Language.Haskell.LSP.Types hiding (id, capabilities, message)
import qualified Language.Haskell.LSP.Types as LSP
-import qualified Language.Haskell.LSP.Types.Capabilities as LSP
+import Language.Haskell.LSP.Types.Capabilities
import Language.Haskell.LSP.Messages
import Language.Haskell.LSP.VFS
-import Language.Haskell.LSP.Test.Capabilities
import Language.Haskell.LSP.Test.Compat
import Language.Haskell.LSP.Test.Decoding
import Language.Haskell.LSP.Test.Exceptions
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.
+ -> ClientCapabilities -- ^ The capabilities that the client should declare.
-> FilePath -- ^ The filepath to the root directory for the session.
-> Session a -- ^ The session to run.
-> 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.
+ -> ClientCapabilities -- ^ The capabilities that the client should declare.
-> FilePath -- ^ The filepath to the root directory for the session.
-> Session a -- ^ The session to run.
-> IO a
object ["jsonrpc" .= rpc, "id" .= id, "method" .= method, "params" .= params]
-sendRequestMessage :: (ToJSON a, ToJSON b) => RequestMessage ClientMethod a b -> Session ()
-sendRequestMessage req = do
- -- Update the request map
- reqMap <- requestMap <$> ask
- liftIO $ modifyMVar_ reqMap $
- \r -> return $ updateRequestMap r (req ^. LSP.id) (req ^. method)
-
- sendMessage req
-
-- | Sends a notification to the server.
sendNotification :: ToJSON a
=> ClientMethod -- ^ The notification method.
-> 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
oldVFS <- vfs <$> get
newVFS <- liftIO $ openVFS oldVFS n
modify (\s -> s { vfs = newVFS })
- sendNotification' n
+ 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
oldVFS <- vfs <$> get
newVFS <- liftIO $ closeVFS oldVFS n
modify (\s -> s { vfs = newVFS })
- sendNotification' n
-
-sendNotification method params = sendNotification' (NotificationMessage "2.0" method params)
+ sendMessage n
-sendNotification' :: (ToJSON a, ToJSON b) => NotificationMessage a b -> Session ()
-sendNotification' = sendMessage
+sendNotification method params = sendMessage (NotificationMessage "2.0" method params)
+-- | Sends a response to the server.
sendResponse :: ToJSON a => ResponseMessage a -> Session ()
sendResponse = sendMessage
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
when (diagsNot ^. params . LSP.diagnostics /= List []) $ liftIO $ throw UnexpectedDiagnostics
-- | Returns the symbols in a document.
-getDocumentSymbols :: TextDocumentIdentifier -> Session [SymbolInformation]
+getDocumentSymbols :: TextDocumentIdentifier -> Session (Either [DocumentSymbol] [SymbolInformation])
getDocumentSymbols doc = do
- ResponseMessage _ rspLid mRes mErr <- request TextDocumentDocumentSymbol (DocumentSymbolParams doc)
+ ResponseMessage _ rspLid mRes mErr <- request TextDocumentDocumentSymbol (DocumentSymbolParams doc) :: Session DocumentSymbolsResponse
maybe (return ()) (throw . UnexpectedResponseError rspLid) mErr
- let (Just (List symbols)) = mRes
- return symbols
+ case mRes of
+ Just (DSDocumentSymbols (List xs)) -> return (Left xs)
+ Just (DSSymbolInformation (List xs)) -> return (Right xs)
+ Nothing -> Prelude.error "No result and no error in DocumentSymbolsResponse"
-- | Returns all the code actions in a document by
-- querying the code actions at each of the current
-- diagnostics' positions.
-getAllCodeActions :: TextDocumentIdentifier -> Session [CommandOrCodeAction]
+getAllCodeActions :: TextDocumentIdentifier -> Session [CAResult]
getAllCodeActions doc = do
curDiags <- fromMaybe [] . Map.lookup (doc ^. uri) . curDiagnostics <$> get
let ctx = CodeActionContext (List curDiags) Nothing
foldM (go ctx) [] curDiags
where
- go :: CodeActionContext -> [CommandOrCodeAction] -> Diagnostic -> Session [CommandOrCodeAction]
+ go :: CodeActionContext -> [CAResult] -> Diagnostic -> Session [CAResult]
go ctx acc diag = do
ResponseMessage _ rspLid mRes mErr <- request TextDocumentCodeAction (CodeActionParams doc (diag ^. range) ctx)
caps <- asks sessionCapabilities
let supportsDocChanges = fromMaybe False $ do
- let LSP.ClientCapabilities mWorkspace _ _ = caps
- LSP.WorkspaceClientCapabilities _ mEdit _ _ _ _ <- mWorkspace
- LSP.WorkspaceEditClientCapabilities mDocChanges <- mEdit
+ let ClientCapabilities mWorkspace _ _ = caps
+ WorkspaceClientCapabilities _ mEdit _ _ _ _ _ _ <- mWorkspace
+ WorkspaceEditClientCapabilities mDocChanges <- mEdit
mDocChanges
let wEdit = if supportsDocChanges
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)