//===- LSPServer.cpp - MLIR Language Server -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "LSPServer.h" #include "MLIRServer.h" #include "Protocol.h" #include "mlir/Tools/lsp-server-support/Logging.h" #include "mlir/Tools/lsp-server-support/Transport.h" #include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/StringMap.h" #include #define DEBUG_TYPE "mlir-lsp-server" using namespace mlir; using namespace mlir::lsp; //===----------------------------------------------------------------------===// // LSPServer //===----------------------------------------------------------------------===// namespace { struct LSPServer { LSPServer(MLIRServer &server) : server(server) {} //===--------------------------------------------------------------------===// // Initialization void onInitialize(const InitializeParams ¶ms, Callback reply); void onInitialized(const InitializedParams ¶ms); void onShutdown(const NoParams ¶ms, Callback reply); //===--------------------------------------------------------------------===// // Document Change void onDocumentDidOpen(const DidOpenTextDocumentParams ¶ms); void onDocumentDidClose(const DidCloseTextDocumentParams ¶ms); void onDocumentDidChange(const DidChangeTextDocumentParams ¶ms); //===--------------------------------------------------------------------===// // Definitions and References void onGoToDefinition(const TextDocumentPositionParams ¶ms, Callback> reply); void onReference(const ReferenceParams ¶ms, Callback> reply); //===--------------------------------------------------------------------===// // Hover void onHover(const TextDocumentPositionParams ¶ms, Callback> reply); //===--------------------------------------------------------------------===// // Document Symbols void onDocumentSymbol(const DocumentSymbolParams ¶ms, Callback> reply); //===--------------------------------------------------------------------===// // Code Completion void onCompletion(const CompletionParams ¶ms, Callback reply); //===--------------------------------------------------------------------===// // Code Action void onCodeAction(const CodeActionParams ¶ms, Callback reply); //===--------------------------------------------------------------------===// // Bytecode void onConvertFromBytecode(const MLIRConvertBytecodeParams ¶ms, Callback reply); void onConvertToBytecode(const MLIRConvertBytecodeParams ¶ms, Callback reply); //===--------------------------------------------------------------------===// // Fields //===--------------------------------------------------------------------===// MLIRServer &server; /// An outgoing notification used to send diagnostics to the client when they /// are ready to be processed. OutgoingNotification publishDiagnostics; /// Used to indicate that the 'shutdown' request was received from the /// Language Server client. bool shutdownRequestReceived = false; }; } // namespace //===----------------------------------------------------------------------===// // Initialization void LSPServer::onInitialize(const InitializeParams ¶ms, Callback reply) { // Send a response with the capabilities of this server. llvm::json::Object serverCaps{ {"textDocumentSync", llvm::json::Object{ {"openClose", true}, {"change", (int)TextDocumentSyncKind::Full}, {"save", true}, }}, {"completionProvider", llvm::json::Object{ {"allCommitCharacters", { "\t", ";", ",", ".", "=", }}, {"resolveProvider", false}, {"triggerCharacters", {".", "%", "^", "!", "#", "(", ",", "<", ":", "[", " ", "\"", "/"}}, }}, {"definitionProvider", true}, {"referencesProvider", true}, {"hoverProvider", true}, // For now we only support documenting symbols when the client supports // hierarchical symbols. {"documentSymbolProvider", params.capabilities.hierarchicalDocumentSymbol}, }; // Per LSP, codeActionProvider can be either boolean or CodeActionOptions. // CodeActionOptions is only valid if the client supports action literal // via textDocument.codeAction.codeActionLiteralSupport. serverCaps["codeActionProvider"] = params.capabilities.codeActionStructure ? llvm::json::Object{{"codeActionKinds", {CodeAction::kQuickFix, CodeAction::kRefactor, CodeAction::kInfo}}} : llvm::json::Value(true); llvm::json::Object result{ {{"serverInfo", llvm::json::Object{{"name", "mlir-lsp-server"}, {"version", "0.0.0"}}}, {"capabilities", std::move(serverCaps)}}}; reply(std::move(result)); } void LSPServer::onInitialized(const InitializedParams &) {} void LSPServer::onShutdown(const NoParams &, Callback reply) { shutdownRequestReceived = true; reply(nullptr); } //===----------------------------------------------------------------------===// // Document Change void LSPServer::onDocumentDidOpen(const DidOpenTextDocumentParams ¶ms) { PublishDiagnosticsParams diagParams(params.textDocument.uri, params.textDocument.version); server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text, params.textDocument.version, diagParams.diagnostics); // Publish any recorded diagnostics. publishDiagnostics(diagParams); } void LSPServer::onDocumentDidClose(const DidCloseTextDocumentParams ¶ms) { std::optional version = server.removeDocument(params.textDocument.uri); if (!version) return; // Empty out the diagnostics shown for this document. This will clear out // anything currently displayed by the client for this document (e.g. in the // "Problems" pane of VSCode). publishDiagnostics( PublishDiagnosticsParams(params.textDocument.uri, *version)); } void LSPServer::onDocumentDidChange(const DidChangeTextDocumentParams ¶ms) { // TODO: We currently only support full document updates, we should refactor // to avoid this. if (params.contentChanges.size() != 1) return; PublishDiagnosticsParams diagParams(params.textDocument.uri, params.textDocument.version); server.addOrUpdateDocument( params.textDocument.uri, params.contentChanges.front().text, params.textDocument.version, diagParams.diagnostics); // Publish any recorded diagnostics. publishDiagnostics(diagParams); } //===----------------------------------------------------------------------===// // Definitions and References void LSPServer::onGoToDefinition(const TextDocumentPositionParams ¶ms, Callback> reply) { std::vector locations; server.getLocationsOf(params.textDocument.uri, params.position, locations); reply(std::move(locations)); } void LSPServer::onReference(const ReferenceParams ¶ms, Callback> reply) { std::vector locations; server.findReferencesOf(params.textDocument.uri, params.position, locations); reply(std::move(locations)); } //===----------------------------------------------------------------------===// // Hover void LSPServer::onHover(const TextDocumentPositionParams ¶ms, Callback> reply) { reply(server.findHover(params.textDocument.uri, params.position)); } //===----------------------------------------------------------------------===// // Document Symbols void LSPServer::onDocumentSymbol(const DocumentSymbolParams ¶ms, Callback> reply) { std::vector symbols; server.findDocumentSymbols(params.textDocument.uri, symbols); reply(std::move(symbols)); } //===----------------------------------------------------------------------===// // Code Completion void LSPServer::onCompletion(const CompletionParams ¶ms, Callback reply) { reply(server.getCodeCompletion(params.textDocument.uri, params.position)); } //===----------------------------------------------------------------------===// // Code Action void LSPServer::onCodeAction(const CodeActionParams ¶ms, Callback reply) { URIForFile uri = params.textDocument.uri; // Check whether a particular CodeActionKind is included in the response. auto isKindAllowed = [only(params.context.only)](StringRef kind) { if (only.empty()) return true; return llvm::any_of(only, [&](StringRef base) { return kind.consume_front(base) && (kind.empty() || kind.starts_with(".")); }); }; // We provide a code action for fixes on the specified diagnostics. std::vector actions; if (isKindAllowed(CodeAction::kQuickFix)) server.getCodeActions(uri, params.range.start, params.context, actions); reply(std::move(actions)); } //===----------------------------------------------------------------------===// // Bytecode void LSPServer::onConvertFromBytecode( const MLIRConvertBytecodeParams ¶ms, Callback reply) { reply(server.convertFromBytecode(params.uri)); } void LSPServer::onConvertToBytecode(const MLIRConvertBytecodeParams ¶ms, Callback reply) { reply(server.convertToBytecode(params.uri)); } //===----------------------------------------------------------------------===// // Entry point //===----------------------------------------------------------------------===// LogicalResult lsp::runMlirLSPServer(MLIRServer &server, JSONTransport &transport) { LSPServer lspServer(server); MessageHandler messageHandler(transport); // Initialization messageHandler.method("initialize", &lspServer, &LSPServer::onInitialize); messageHandler.notification("initialized", &lspServer, &LSPServer::onInitialized); messageHandler.method("shutdown", &lspServer, &LSPServer::onShutdown); // Document Changes messageHandler.notification("textDocument/didOpen", &lspServer, &LSPServer::onDocumentDidOpen); messageHandler.notification("textDocument/didClose", &lspServer, &LSPServer::onDocumentDidClose); messageHandler.notification("textDocument/didChange", &lspServer, &LSPServer::onDocumentDidChange); // Definitions and References messageHandler.method("textDocument/definition", &lspServer, &LSPServer::onGoToDefinition); messageHandler.method("textDocument/references", &lspServer, &LSPServer::onReference); // Hover messageHandler.method("textDocument/hover", &lspServer, &LSPServer::onHover); // Document Symbols messageHandler.method("textDocument/documentSymbol", &lspServer, &LSPServer::onDocumentSymbol); // Code Completion messageHandler.method("textDocument/completion", &lspServer, &LSPServer::onCompletion); // Code Action messageHandler.method("textDocument/codeAction", &lspServer, &LSPServer::onCodeAction); // Bytecode messageHandler.method("mlir/convertFromBytecode", &lspServer, &LSPServer::onConvertFromBytecode); messageHandler.method("mlir/convertToBytecode", &lspServer, &LSPServer::onConvertToBytecode); // Diagnostics lspServer.publishDiagnostics = messageHandler.outgoingNotification( "textDocument/publishDiagnostics"); // Run the main loop of the transport. LogicalResult result = success(); if (llvm::Error error = transport.run(messageHandler)) { Logger::error("Transport error: {0}", error); llvm::consumeError(std::move(error)); result = failure(); } else { result = success(lspServer.shutdownRequestReceived); } return result; }