+
+-- | Waits for diagnostics to be published and returns them.
+waitForDiagnostics :: Session [Diagnostic]
+waitForDiagnostics = do
+ diagsNot <- skipManyTill anyMessage (message STextDocumentPublishDiagnostics)
+ 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
+ let res = filter matches diags
+ if null res
+ then waitForDiagnosticsSource src
+ else return res
+ where
+ matches :: Diagnostic -> Bool
+ matches d = d ^. source == Just (T.pack src)
+
+-- | Expects a 'PublishDiagnosticsNotification' and throws an
+-- 'UnexpectedDiagnostics' exception if there are any diagnostics
+-- returned.
+noDiagnostics :: Session ()
+noDiagnostics = do
+ diagsNot <- message STextDocumentPublishDiagnostics
+ when (diagsNot ^. params . LSP.diagnostics /= List []) $ liftIO $ throw UnexpectedDiagnostics
+
+-- | Returns the symbols in a document.
+getDocumentSymbols :: TextDocumentIdentifier -> Session (Either [DocumentSymbol] [SymbolInformation])
+getDocumentSymbols doc = do
+ ResponseMessage _ rspLid res <- request STextDocumentDocumentSymbol (DocumentSymbolParams Nothing Nothing doc) :: Session DocumentSymbolsResponse
+ case res of
+ Right (L (List xs)) -> return (Left xs)
+ Right (R (List xs)) -> return (Right xs)
+ Left err -> throw (UnexpectedResponseError (SomeLspId $ fromJust rspLid) err)
+
+-- | Returns the code actions in the specified range.
+getCodeActions :: TextDocumentIdentifier -> Range -> Session [Command |? CodeAction]
+getCodeActions doc range = do
+ ctx <- getCodeActionContext doc
+ rsp <- request STextDocumentCodeAction (CodeActionParams Nothing Nothing doc range ctx)
+
+ case rsp ^. result of
+ Right (List xs) -> return xs
+ Left error -> throw (UnexpectedResponseError (SomeLspId $ fromJust $ rsp ^. LSP.id) error)
+
+-- | Returns all the code actions in a document by
+-- querying the code actions at each of the current
+-- diagnostics' positions.
+getAllCodeActions :: TextDocumentIdentifier -> Session [Command |? CodeAction]
+getAllCodeActions doc = do
+ ctx <- getCodeActionContext doc
+
+ foldM (go ctx) [] =<< getCurrentDiagnostics doc
+
+ where
+ go :: CodeActionContext -> [Command |? CodeAction] -> Diagnostic -> Session [Command |? CodeAction]
+ go ctx acc diag = do
+ ResponseMessage _ rspLid res <- request STextDocumentCodeAction (CodeActionParams Nothing Nothing doc (diag ^. range) ctx)
+
+ case res of
+ Left e -> throw (UnexpectedResponseError (SomeLspId $ fromJust rspLid) e)
+ Right (List cmdOrCAs) -> pure (acc ++ cmdOrCAs)
+
+getCodeActionContext :: TextDocumentIdentifier -> Session CodeActionContext
+getCodeActionContext doc = do
+ curDiags <- getCurrentDiagnostics doc
+ return $ CodeActionContext (List curDiags) Nothing
+
+-- | Returns the current diagnostics that have been sent to the client.
+-- Note that this does not wait for more to come in.
+getCurrentDiagnostics :: TextDocumentIdentifier -> Session [Diagnostic]
+getCurrentDiagnostics doc = fromMaybe [] . Map.lookup (toNormalizedUri $ doc ^. uri) . curDiagnostics <$> get
+
+-- | Executes a command.
+executeCommand :: Command -> Session ()
+executeCommand cmd = do
+ let args = decode $ encode $ fromJust $ cmd ^. arguments
+ execParams = ExecuteCommandParams Nothing (cmd ^. command) args
+ request_ SWorkspaceExecuteCommand execParams
+
+-- | Executes a code action.
+-- Matching with the specification, if a code action
+-- contains both an edit and a command, the edit will
+-- be applied first.
+executeCodeAction :: CodeAction -> Session ()
+executeCodeAction action = do
+ maybe (return ()) handleEdit $ action ^. edit
+ maybe (return ()) executeCommand $ action ^. command
+
+ where handleEdit :: WorkspaceEdit -> Session ()
+ handleEdit e =
+ -- Its ok to pass in dummy parameters here as they aren't used
+ let req = RequestMessage "" (IdInt 0) SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing e)
+ in updateState (FromServerMess SWorkspaceApplyEdit req)
+
+-- | Adds the current version to the document, as tracked by the session.
+getVersionedDoc :: TextDocumentIdentifier -> Session VersionedTextDocumentIdentifier
+getVersionedDoc (TextDocumentIdentifier uri) = do
+ fs <- vfsMap . vfs <$> get
+ let ver =
+ case fs Map.!? toNormalizedUri uri of
+ Just vf -> Just (virtualFileVersion vf)
+ _ -> Nothing
+ return (VersionedTextDocumentIdentifier uri ver)
+
+-- | Applys an edit to the document and returns the updated document version.
+applyEdit :: TextDocumentIdentifier -> TextEdit -> Session VersionedTextDocumentIdentifier
+applyEdit doc edit = do
+
+ verDoc <- getVersionedDoc doc
+
+ caps <- asks sessionCapabilities
+
+ let supportsDocChanges = fromMaybe False $ do
+ let mWorkspace = caps ^. LSP.workspace
+ C.WorkspaceClientCapabilities _ mEdit _ _ _ _ _ _ <- mWorkspace
+ C.WorkspaceEditClientCapabilities mDocChanges _ _ <- mEdit
+ mDocChanges
+
+ let wEdit = if supportsDocChanges
+ then
+ let docEdit = TextDocumentEdit verDoc (List [edit])
+ in WorkspaceEdit Nothing (Just (List [docEdit]))
+ else
+ let changes = HashMap.singleton (doc ^. uri) (List [edit])
+ in WorkspaceEdit (Just changes) Nothing
+
+ let req = RequestMessage "" (IdInt 0) SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wEdit)
+ updateState (FromServerMess SWorkspaceApplyEdit req)
+
+ -- version may have changed
+ getVersionedDoc doc
+
+-- | Returns the completions for the position in the document.
+getCompletions :: TextDocumentIdentifier -> Position -> Session [CompletionItem]
+getCompletions doc pos = do
+ rsp <- request STextDocumentCompletion (CompletionParams doc pos Nothing Nothing Nothing)
+
+ case getResponseResult rsp of
+ L (List items) -> return items
+ R (CompletionList _ (List items)) -> return items
+
+-- | Returns the references for the position in the document.
+getReferences :: TextDocumentIdentifier -- ^ The document to lookup in.
+ -> Position -- ^ The position to lookup.
+ -> Bool -- ^ Whether to include declarations as references.
+ -> Session (List Location) -- ^ The locations of the references.
+getReferences doc pos inclDecl =
+ let ctx = ReferenceContext inclDecl
+ params = ReferenceParams doc pos Nothing Nothing ctx
+ in getResponseResult <$> request STextDocumentReferences params
+
+-- | Returns the declarations(s) for the term at the specified position.
+getDeclarations :: TextDocumentIdentifier -- ^ The document the term is in.
+ -> Position -- ^ The position the term is at.
+ -> Session ([Location] |? [LocationLink])
+getDeclarations = getDeclarationyRequest STextDocumentDeclaration DeclarationParams
+
+-- | Returns the definition(s) for the term at the specified position.
+getDefinitions :: TextDocumentIdentifier -- ^ The document the term is in.
+ -> Position -- ^ The position the term is at.
+ -> Session ([Location] |? [LocationLink])
+getDefinitions = getDeclarationyRequest STextDocumentDefinition DefinitionParams
+
+-- | Returns the type definition(s) for the term at the specified position.
+getTypeDefinitions :: TextDocumentIdentifier -- ^ The document the term is in.
+ -> Position -- ^ The position the term is at.
+ -> Session ([Location] |? [LocationLink])
+getTypeDefinitions = getDeclarationyRequest STextDocumentTypeDefinition TypeDefinitionParams
+
+-- | Returns the type definition(s) for the term at the specified position.
+getImplementations :: TextDocumentIdentifier -- ^ The document the term is in.
+ -> Position -- ^ The position the term is at.
+ -> Session ([Location] |? [LocationLink])
+getImplementations = getDeclarationyRequest STextDocumentImplementation ImplementationParams
+
+
+getDeclarationyRequest :: (ResponseParams m ~ (Location |? (List Location |? List LocationLink)))
+ => SClientMethod m
+ -> (TextDocumentIdentifier
+ -> Position
+ -> Maybe ProgressToken
+ -> Maybe ProgressToken
+ -> MessageParams m)
+ -> TextDocumentIdentifier
+ -> Position
+ -> Session ([Location] |? [LocationLink])
+getDeclarationyRequest method paramCons doc pos = do
+ let params = paramCons doc pos Nothing Nothing
+ rsp <- request method params
+ case getResponseResult rsp of
+ L loc -> pure (L [loc])
+ R (L (List locs)) -> pure (L locs)
+ R (R (List locLinks)) -> pure (R locLinks)
+
+-- | Renames the term at the specified position.
+rename :: TextDocumentIdentifier -> Position -> String -> Session ()
+rename doc pos newName = do
+ let params = RenameParams doc pos Nothing (T.pack newName)
+ rsp <- request STextDocumentRename params :: Session RenameResponse
+ let wEdit = getResponseResult rsp
+ req = RequestMessage "" (IdInt 0) SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wEdit)
+ updateState (FromServerMess SWorkspaceApplyEdit req)
+
+-- | Returns the hover information at the specified position.
+getHover :: TextDocumentIdentifier -> Position -> Session Hover
+getHover doc pos =
+ let params = HoverParams doc pos Nothing
+ in getResponseResult <$> request STextDocumentHover params
+
+-- | Returns the highlighted occurences of the term at the specified position
+getHighlights :: TextDocumentIdentifier -> Position -> Session (List DocumentHighlight)
+getHighlights doc pos =
+ let params = DocumentHighlightParams doc pos Nothing Nothing
+ in getResponseResult <$> request STextDocumentDocumentHighlight params
+
+-- | Checks the response for errors and throws an exception if needed.
+-- Returns the result if successful.
+getResponseResult :: ResponseMessage m -> ResponseParams m
+getResponseResult rsp =
+ case rsp ^. result of
+ Right x -> x
+ Left err -> throw $ UnexpectedResponseError (SomeLspId $ fromJust $ rsp ^. LSP.id) err
+
+-- | Applies formatting to the specified document.
+formatDoc :: TextDocumentIdentifier -> FormattingOptions -> Session ()
+formatDoc doc opts = do
+ let params = DocumentFormattingParams Nothing doc opts
+ edits <- getResponseResult <$> request STextDocumentFormatting params
+ applyTextEdits doc edits
+
+-- | Applies formatting to the specified range in a document.
+formatRange :: TextDocumentIdentifier -> FormattingOptions -> Range -> Session ()
+formatRange doc opts range = do
+ let params = DocumentRangeFormattingParams Nothing doc range opts
+ edits <- getResponseResult <$> request STextDocumentRangeFormatting params
+ applyTextEdits doc edits
+
+applyTextEdits :: TextDocumentIdentifier -> List TextEdit -> Session ()
+applyTextEdits doc edits =
+ let wEdit = WorkspaceEdit (Just (HashMap.singleton (doc ^. uri) edits)) Nothing
+ -- Send a dummy message to updateState so it can do bookkeeping
+ req = RequestMessage "" (IdInt 0) SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wEdit)
+ in updateState (FromServerMess SWorkspaceApplyEdit req)
+
+-- | Returns the code lenses for the specified document.
+getCodeLenses :: TextDocumentIdentifier -> Session [CodeLens]
+getCodeLenses tId = do
+ rsp <- request STextDocumentCodeLens (CodeLensParams Nothing Nothing tId) :: Session CodeLensResponse
+ case getResponseResult rsp of
+ List res -> pure res
+
+-- | Returns a list of capabilities that the server has requested to /dynamically/
+-- register during the 'Session'.
+--
+-- @since 0.11.0.0
+getRegisteredCapabilities :: Session [SomeRegistration]
+getRegisteredCapabilities = (Map.elems . curDynCaps) <$> get