MLIR 23.0.0git
LSPServer.cpp
Go to the documentation of this file.
1//===- LSPServer.cpp - MLIR Language Server -------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "LSPServer.h"
10#include "MLIRServer.h"
11#include "Protocol.h"
12#include "llvm/Support/LSP/Logging.h"
13#include "llvm/Support/LSP/Transport.h"
14#include <optional>
15
16#define DEBUG_TYPE "mlir-lsp-server"
17
18using namespace mlir;
19using namespace mlir::lsp;
20
21using llvm::lsp::Callback;
22using llvm::lsp::CodeAction;
23using llvm::lsp::CodeActionParams;
24using llvm::lsp::CompletionList;
25using llvm::lsp::CompletionParams;
26using llvm::lsp::DidChangeTextDocumentParams;
27using llvm::lsp::DidCloseTextDocumentParams;
28using llvm::lsp::DidOpenTextDocumentParams;
29using llvm::lsp::DocumentSymbol;
30using llvm::lsp::DocumentSymbolParams;
31using llvm::lsp::Hover;
32using llvm::lsp::InitializedParams;
33using llvm::lsp::InitializeParams;
34using llvm::lsp::JSONTransport;
35using llvm::lsp::Location;
36using llvm::lsp::Logger;
37using llvm::lsp::MessageHandler;
40using llvm::lsp::NoParams;
41using llvm::lsp::OutgoingNotification;
42using llvm::lsp::PublishDiagnosticsParams;
43using llvm::lsp::ReferenceParams;
44using llvm::lsp::TextDocumentPositionParams;
45using llvm::lsp::TextDocumentSyncKind;
46using llvm::lsp::URIForFile;
47
48//===----------------------------------------------------------------------===//
49// LSPServer
50//===----------------------------------------------------------------------===//
51
52namespace {
53struct LSPServer {
54 LSPServer(MLIRServer &server) : server(server) {}
55
56 //===--------------------------------------------------------------------===//
57 // Initialization
58
59 void onInitialize(const InitializeParams &params,
60 Callback<llvm::json::Value> reply);
61 void onInitialized(const InitializedParams &params);
62 void onShutdown(const NoParams &params, Callback<std::nullptr_t> reply);
63
64 //===--------------------------------------------------------------------===//
65 // Document Change
66
67 void onDocumentDidOpen(const DidOpenTextDocumentParams &params);
68 void onDocumentDidClose(const DidCloseTextDocumentParams &params);
69 void onDocumentDidChange(const DidChangeTextDocumentParams &params);
70
71 //===--------------------------------------------------------------------===//
72 // Definitions and References
73
74 void onGoToDefinition(const TextDocumentPositionParams &params,
75 Callback<std::vector<Location>> reply);
76 void onReference(const ReferenceParams &params,
77 Callback<std::vector<Location>> reply);
78
79 //===--------------------------------------------------------------------===//
80 // Hover
81
82 void onHover(const TextDocumentPositionParams &params,
83 Callback<std::optional<Hover>> reply);
84
85 //===--------------------------------------------------------------------===//
86 // Document Symbols
87
88 void onDocumentSymbol(const DocumentSymbolParams &params,
89 Callback<std::vector<DocumentSymbol>> reply);
90
91 //===--------------------------------------------------------------------===//
92 // Code Completion
93
94 void onCompletion(const CompletionParams &params,
95 Callback<CompletionList> reply);
96
97 //===--------------------------------------------------------------------===//
98 // Code Action
99
100 void onCodeAction(const CodeActionParams &params,
101 Callback<llvm::json::Value> reply);
102
103 //===--------------------------------------------------------------------===//
104 // Bytecode
105
106 void onConvertFromBytecode(const MLIRConvertBytecodeParams &params,
107 Callback<MLIRConvertBytecodeResult> reply);
108 void onConvertToBytecode(const MLIRConvertBytecodeParams &params,
109 Callback<MLIRConvertBytecodeResult> reply);
110
111 //===--------------------------------------------------------------------===//
112 // Fields
113 //===--------------------------------------------------------------------===//
114
115 MLIRServer &server;
116
117 /// An outgoing notification used to send diagnostics to the client when they
118 /// are ready to be processed.
119 OutgoingNotification<PublishDiagnosticsParams> publishDiagnostics;
120
121 /// Used to indicate that the 'shutdown' request was received from the
122 /// Language Server client.
123 bool shutdownRequestReceived = false;
124};
125} // namespace
126
127//===----------------------------------------------------------------------===//
128// Initialization
129//===----------------------------------------------------------------------===//
130
131void LSPServer::onInitialize(const InitializeParams &params,
132 Callback<llvm::json::Value> reply) {
133 // Configure the workspace root if it was provided.
134 if (params.rootUri) {
135 llvm::Expected<URIForFile> rootURI = URIForFile::fromURI(*params.rootUri);
136 if (rootURI)
137 server.setWorkspaceRoot(rootURI->file());
138 else
139 consumeError(rootURI.takeError());
140 } else if (params.rootPath) {
141 server.setWorkspaceRoot(*params.rootPath);
142 }
143
144 // Send a response with the capabilities of this server.
145 llvm::json::Object serverCaps{
146 {"textDocumentSync",
147 llvm::json::Object{
148 {"openClose", true},
149 {"change", (int)TextDocumentSyncKind::Full},
150 {"save", true},
151 }},
152 {"completionProvider",
153 llvm::json::Object{
154 {"allCommitCharacters",
155 {
156 "\t",
157 ";",
158 ",",
159 ".",
160 "=",
161 }},
162 {"resolveProvider", false},
163 {"triggerCharacters",
164 {".", "%", "^", "!", "#", "(", ",", "<", ":", "[", " ", "\"", "/"}},
165 }},
166 {"definitionProvider", true},
167 {"referencesProvider", true},
168 {"hoverProvider", true},
169
170 // For now we only support documenting symbols when the client supports
171 // hierarchical symbols.
172 {"documentSymbolProvider",
173 params.capabilities.hierarchicalDocumentSymbol},
174 };
175
176 // Per LSP, codeActionProvider can be either boolean or CodeActionOptions.
177 // CodeActionOptions is only valid if the client supports action literal
178 // via textDocument.codeAction.codeActionLiteralSupport.
179 serverCaps["codeActionProvider"] =
180 params.capabilities.codeActionStructure
181 ? llvm::json::Object{{"codeActionKinds",
182 {CodeAction::kQuickFix, CodeAction::kRefactor,
183 CodeAction::kInfo}}}
184 : llvm::json::Value(true);
185
186 llvm::json::Object result{
187 {{"serverInfo",
188 llvm::json::Object{{"name", "mlir-lsp-server"}, {"version", "0.0.0"}}},
189 {"capabilities", std::move(serverCaps)}}};
190 reply(std::move(result));
191}
192void LSPServer::onInitialized(const InitializedParams &) {}
193void LSPServer::onShutdown(const NoParams &, Callback<std::nullptr_t> reply) {
194 shutdownRequestReceived = true;
195 reply(nullptr);
196}
197
198//===----------------------------------------------------------------------===//
199// Document Change
200//===----------------------------------------------------------------------===//
201
202void LSPServer::onDocumentDidOpen(const DidOpenTextDocumentParams &params) {
203 PublishDiagnosticsParams diagParams(params.textDocument.uri,
204 params.textDocument.version);
205 server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text,
206 params.textDocument.version,
207 diagParams.diagnostics);
208
209 // Publish any recorded diagnostics.
210 publishDiagnostics(diagParams);
211}
212void LSPServer::onDocumentDidClose(const DidCloseTextDocumentParams &params) {
213 std::optional<int64_t> version =
214 server.removeDocument(params.textDocument.uri);
215 if (!version)
216 return;
217
218 // Empty out the diagnostics shown for this document. This will clear out
219 // anything currently displayed by the client for this document (e.g. in the
220 // "Problems" pane of VSCode).
221 publishDiagnostics(
222 PublishDiagnosticsParams(params.textDocument.uri, *version));
223}
224void LSPServer::onDocumentDidChange(const DidChangeTextDocumentParams &params) {
225 // TODO: We currently only support full document updates, we should refactor
226 // to avoid this.
227 if (params.contentChanges.size() != 1)
228 return;
229 PublishDiagnosticsParams diagParams(params.textDocument.uri,
230 params.textDocument.version);
231 server.addOrUpdateDocument(
232 params.textDocument.uri, params.contentChanges.front().text,
233 params.textDocument.version, diagParams.diagnostics);
234
235 // Publish any recorded diagnostics.
236 publishDiagnostics(diagParams);
237}
238
239//===----------------------------------------------------------------------===//
240// Definitions and References
241//===----------------------------------------------------------------------===//
242
243void LSPServer::onGoToDefinition(const TextDocumentPositionParams &params,
244 Callback<std::vector<Location>> reply) {
245 std::vector<Location> locations;
246 server.getLocationsOf(params.textDocument.uri, params.position, locations);
247 reply(std::move(locations));
248}
249
250void LSPServer::onReference(const ReferenceParams &params,
251 Callback<std::vector<Location>> reply) {
252 std::vector<Location> locations;
253 server.findReferencesOf(params.textDocument.uri, params.position, locations);
254 reply(std::move(locations));
255}
256
257//===----------------------------------------------------------------------===//
258// Hover
259//===----------------------------------------------------------------------===//
260
261void LSPServer::onHover(const TextDocumentPositionParams &params,
262 Callback<std::optional<Hover>> reply) {
263 reply(server.findHover(params.textDocument.uri, params.position));
264}
265
266//===----------------------------------------------------------------------===//
267// Document Symbols
268//===----------------------------------------------------------------------===//
269
270void LSPServer::onDocumentSymbol(const DocumentSymbolParams &params,
271 Callback<std::vector<DocumentSymbol>> reply) {
272 std::vector<DocumentSymbol> symbols;
273 server.findDocumentSymbols(params.textDocument.uri, symbols);
274 reply(std::move(symbols));
275}
276
277//===----------------------------------------------------------------------===//
278// Code Completion
279//===----------------------------------------------------------------------===//
280
281void LSPServer::onCompletion(const CompletionParams &params,
282 Callback<CompletionList> reply) {
283 reply(server.getCodeCompletion(params.textDocument.uri, params.position));
284}
285
286//===----------------------------------------------------------------------===//
287// Code Action
288//===----------------------------------------------------------------------===//
289
290void LSPServer::onCodeAction(const CodeActionParams &params,
291 Callback<llvm::json::Value> reply) {
292 URIForFile uri = params.textDocument.uri;
293
294 // Check whether a particular CodeActionKind is included in the response.
295 auto isKindAllowed = [only(params.context.only)](StringRef kind) {
296 if (only.empty())
297 return true;
298 return llvm::any_of(only, [&](StringRef base) {
299 return kind.consume_front(base) &&
300 (kind.empty() || kind.starts_with("."));
301 });
302 };
303
304 // We provide a code action for fixes on the specified diagnostics.
305 std::vector<CodeAction> actions;
306 if (isKindAllowed(CodeAction::kQuickFix))
307 server.getCodeActions(uri, params.range.start, params.context, actions);
308 reply(std::move(actions));
309}
310
311//===----------------------------------------------------------------------===//
312// Bytecode
313//===----------------------------------------------------------------------===//
314
315void LSPServer::onConvertFromBytecode(
316 const MLIRConvertBytecodeParams &params,
317 Callback<MLIRConvertBytecodeResult> reply) {
318 reply(server.convertFromBytecode(params.uri));
319}
320
321void LSPServer::onConvertToBytecode(const MLIRConvertBytecodeParams &params,
322 Callback<MLIRConvertBytecodeResult> reply) {
323 reply(server.convertToBytecode(params.uri));
324}
325
326//===----------------------------------------------------------------------===//
327// Entry point
328//===----------------------------------------------------------------------===//
329
330LogicalResult lsp::runMlirLSPServer(MLIRServer &server,
331 JSONTransport &transport) {
332 LSPServer lspServer(server);
333 MessageHandler messageHandler(transport);
334
335 // Initialization
336 messageHandler.method("initialize", &lspServer, &LSPServer::onInitialize);
337 messageHandler.notification("initialized", &lspServer,
338 &LSPServer::onInitialized);
339 messageHandler.method("shutdown", &lspServer, &LSPServer::onShutdown);
340
341 // Document Changes
342 messageHandler.notification("textDocument/didOpen", &lspServer,
343 &LSPServer::onDocumentDidOpen);
344 messageHandler.notification("textDocument/didClose", &lspServer,
345 &LSPServer::onDocumentDidClose);
346 messageHandler.notification("textDocument/didChange", &lspServer,
347 &LSPServer::onDocumentDidChange);
348
349 // Definitions and References
350 messageHandler.method("textDocument/definition", &lspServer,
351 &LSPServer::onGoToDefinition);
352 messageHandler.method("textDocument/references", &lspServer,
353 &LSPServer::onReference);
354
355 // Hover
356 messageHandler.method("textDocument/hover", &lspServer, &LSPServer::onHover);
357
358 // Document Symbols
359 messageHandler.method("textDocument/documentSymbol", &lspServer,
360 &LSPServer::onDocumentSymbol);
361
362 // Code Completion
363 messageHandler.method("textDocument/completion", &lspServer,
364 &LSPServer::onCompletion);
365
366 // Code Action
367 messageHandler.method("textDocument/codeAction", &lspServer,
368 &LSPServer::onCodeAction);
369
370 // Bytecode
371 messageHandler.method("mlir/convertFromBytecode", &lspServer,
372 &LSPServer::onConvertFromBytecode);
373 messageHandler.method("mlir/convertToBytecode", &lspServer,
374 &LSPServer::onConvertToBytecode);
375
376 // Diagnostics
377 lspServer.publishDiagnostics =
378 messageHandler.outgoingNotification<PublishDiagnosticsParams>(
379 "textDocument/publishDiagnostics");
380
381 // Run the main loop of the transport.
382 LogicalResult result = success();
383 if (llvm::Error error = transport.run(messageHandler)) {
384 Logger::error("Transport error: {0}", error);
385 llvm::consumeError(std::move(error));
386 result = failure();
387 } else {
388 result = success(lspServer.shutdownRequestReceived);
389 }
390 return result;
391}
return success()
This class implements all of the MLIR related functionality necessary for a language server.
Definition MLIRServer.h:38
void addOrUpdateDocument(const URIForFile &uri, StringRef contents, int64_t version, std::vector< Diagnostic > &diagnostics)
Add or update the document, with the provided version, at the given URI.
std::optional< int64_t > removeDocument(const URIForFile &uri)
Remove the document with the given uri.
void findReferencesOf(const URIForFile &uri, const Position &pos, std::vector< Location > &references)
Find all references of the object pointed at by the given position.
void getLocationsOf(const URIForFile &uri, const Position &defPos, std::vector< Location > &locations)
Return the locations of the object pointed at by the given position.
std::optional< Hover > findHover(const URIForFile &uri, const Position &hoverPos)
Find a hover description for the given hover position, or std::nullopt if one couldn't be found.
llvm::Expected< MLIRConvertBytecodeResult > convertFromBytecode(const URIForFile &uri)
Convert the given bytecode file to the textual format.
llvm::Expected< MLIRConvertBytecodeResult > convertToBytecode(const URIForFile &uri)
Convert the given textual file to the bytecode format.
void setWorkspaceRoot(StringRef root)
Set the workspace root for the server.
CompletionList getCodeCompletion(const URIForFile &uri, const Position &completePos)
Get the code completion list for the position within the given file.
void findDocumentSymbols(const URIForFile &uri, std::vector< DocumentSymbol > &symbols)
Find all of the document symbols within the given file.
void getCodeActions(const URIForFile &uri, const Range &pos, const CodeActionContext &context, std::vector< CodeAction > &actions)
Get the set of code actions within the file.
llvm::LogicalResult runMlirLSPServer(MLIRServer &server, llvm::lsp::JSONTransport &transport)
Run the main loop of the LSP server using the given MLIR server and transport.
Include the generated interface declarations.
This class represents the parameters used when converting between MLIR's bytecode and textual format.
Definition Protocol.h:33
URIForFile uri
The input file containing the bytecode or textual format.
Definition Protocol.h:35
This class represents the result of converting between MLIR's bytecode and textual format.
Definition Protocol.h:48