//===- 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 "../lsp-server-support/Logging.h" #include "../lsp-server-support/Protocol.h" #include "../lsp-server-support/Transport.h" #include "MLIRServer.h" #include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/StringMap.h" #define DEBUG_TYPE "mlir-lsp-server" using namespace mlir; using namespace mlir::lsp; //===----------------------------------------------------------------------===// // LSPServer::Impl //===----------------------------------------------------------------------===// struct LSPServer::Impl { Impl(MLIRServer &server, JSONTransport &transport) : server(server), transport(transport) {} //===--------------------------------------------------------------------===// // 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); //===--------------------------------------------------------------------===// // Fields //===--------------------------------------------------------------------===// MLIRServer &server; JSONTransport &transport; /// 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; }; //===----------------------------------------------------------------------===// // Initialization void LSPServer::Impl::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}, }}, {"definitionProvider", true}, {"referencesProvider", true}, {"hoverProvider", true}, // For now we only support documenting symbols when the client supports // hierarchical symbols. {"documentSymbolProvider", params.capabilities.hierarchicalDocumentSymbol}, }; 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::Impl::onInitialized(const InitializedParams &) {} void LSPServer::Impl::onShutdown(const NoParams &, Callback reply) { shutdownRequestReceived = true; reply(nullptr); } //===----------------------------------------------------------------------===// // Document Change void LSPServer::Impl::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::Impl::onDocumentDidClose( const DidCloseTextDocumentParams ¶ms) { 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::Impl::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::Impl::onGoToDefinition(const TextDocumentPositionParams ¶ms, Callback> reply) { std::vector locations; server.getLocationsOf(params.textDocument.uri, params.position, locations); reply(std::move(locations)); } void LSPServer::Impl::onReference(const ReferenceParams ¶ms, Callback> reply) { std::vector locations; server.findReferencesOf(params.textDocument.uri, params.position, locations); reply(std::move(locations)); } //===----------------------------------------------------------------------===// // Hover void LSPServer::Impl::onHover(const TextDocumentPositionParams ¶ms, Callback> reply) { reply(server.findHover(params.textDocument.uri, params.position)); } //===----------------------------------------------------------------------===// // Document Symbols void LSPServer::Impl::onDocumentSymbol( const DocumentSymbolParams ¶ms, Callback> reply) { std::vector symbols; server.findDocumentSymbols(params.textDocument.uri, symbols); reply(std::move(symbols)); } //===----------------------------------------------------------------------===// // LSPServer //===----------------------------------------------------------------------===// LSPServer::LSPServer(MLIRServer &server, JSONTransport &transport) : impl(std::make_unique(server, transport)) {} LSPServer::~LSPServer() = default; LogicalResult LSPServer::run() { MessageHandler messageHandler(impl->transport); // Initialization messageHandler.method("initialize", impl.get(), &Impl::onInitialize); messageHandler.notification("initialized", impl.get(), &Impl::onInitialized); messageHandler.method("shutdown", impl.get(), &Impl::onShutdown); // Document Changes messageHandler.notification("textDocument/didOpen", impl.get(), &Impl::onDocumentDidOpen); messageHandler.notification("textDocument/didClose", impl.get(), &Impl::onDocumentDidClose); messageHandler.notification("textDocument/didChange", impl.get(), &Impl::onDocumentDidChange); // Definitions and References messageHandler.method("textDocument/definition", impl.get(), &Impl::onGoToDefinition); messageHandler.method("textDocument/references", impl.get(), &Impl::onReference); // Hover messageHandler.method("textDocument/hover", impl.get(), &Impl::onHover); // Document Symbols messageHandler.method("textDocument/documentSymbol", impl.get(), &Impl::onDocumentSymbol); // Diagnostics impl->publishDiagnostics = messageHandler.outgoingNotification( "textDocument/publishDiagnostics"); // Run the main loop of the transport. LogicalResult result = success(); if (llvm::Error error = impl->transport.run(messageHandler)) { Logger::error("Transport error: {0}", error); llvm::consumeError(std::move(error)); result = failure(); } else { result = success(impl->shutdownRequestReceived); } return result; }