1 //===- LSPServer.cpp - TableGen 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 
11 #include "../lsp-server-support/Logging.h"
12 #include "../lsp-server-support/Protocol.h"
13 #include "../lsp-server-support/Transport.h"
14 #include "TableGenServer.h"
15 #include "llvm/ADT/FunctionExtras.h"
16 #include "llvm/ADT/StringMap.h"
17 
18 using namespace mlir;
19 using namespace mlir::lsp;
20 
21 //===----------------------------------------------------------------------===//
22 // LSPServer
23 //===----------------------------------------------------------------------===//
24 
25 namespace {
26 struct LSPServer {
LSPServer__anon15a5396a0111::LSPServer27   LSPServer(TableGenServer &server, JSONTransport &transport)
28       : server(server), transport(transport) {}
29 
30   //===--------------------------------------------------------------------===//
31   // Initialization
32 
33   void onInitialize(const InitializeParams &params,
34                     Callback<llvm::json::Value> reply);
35   void onInitialized(const InitializedParams &params);
36   void onShutdown(const NoParams &params, Callback<std::nullptr_t> reply);
37 
38   //===--------------------------------------------------------------------===//
39   // Document Change
40 
41   void onDocumentDidOpen(const DidOpenTextDocumentParams &params);
42   void onDocumentDidClose(const DidCloseTextDocumentParams &params);
43   void onDocumentDidChange(const DidChangeTextDocumentParams &params);
44 
45   //===--------------------------------------------------------------------===//
46   // Definitions and References
47 
48   void onGoToDefinition(const TextDocumentPositionParams &params,
49                         Callback<std::vector<Location>> reply);
50   void onReference(const ReferenceParams &params,
51                    Callback<std::vector<Location>> reply);
52 
53   //===----------------------------------------------------------------------===//
54   // DocumentLink
55 
56   void onDocumentLink(const DocumentLinkParams &params,
57                       Callback<std::vector<DocumentLink>> reply);
58 
59   //===--------------------------------------------------------------------===//
60   // Hover
61 
62   void onHover(const TextDocumentPositionParams &params,
63                Callback<Optional<Hover>> reply);
64 
65   //===--------------------------------------------------------------------===//
66   // Fields
67   //===--------------------------------------------------------------------===//
68 
69   TableGenServer &server;
70   JSONTransport &transport;
71 
72   /// An outgoing notification used to send diagnostics to the client when they
73   /// are ready to be processed.
74   OutgoingNotification<PublishDiagnosticsParams> publishDiagnostics;
75 
76   /// Used to indicate that the 'shutdown' request was received from the
77   /// Language Server client.
78   bool shutdownRequestReceived = false;
79 };
80 } // namespace
81 
82 //===----------------------------------------------------------------------===//
83 // Initialization
84 
onInitialize(const InitializeParams & params,Callback<llvm::json::Value> reply)85 void LSPServer::onInitialize(const InitializeParams &params,
86                              Callback<llvm::json::Value> reply) {
87   // Send a response with the capabilities of this server.
88   llvm::json::Object serverCaps{
89       {"textDocumentSync",
90        llvm::json::Object{
91            {"openClose", true},
92            {"change", (int)TextDocumentSyncKind::Incremental},
93            {"save", true},
94        }},
95       {"definitionProvider", true},
96       {"referencesProvider", true},
97       {"documentLinkProvider",
98        llvm::json::Object{
99            {"resolveProvider", false},
100        }},
101       {"hoverProvider", true},
102   };
103 
104   llvm::json::Object result{
105       {{"serverInfo", llvm::json::Object{{"name", "tblgen-lsp-server"},
106                                          {"version", "0.0.1"}}},
107        {"capabilities", std::move(serverCaps)}}};
108   reply(std::move(result));
109 }
onInitialized(const InitializedParams &)110 void LSPServer::onInitialized(const InitializedParams &) {}
onShutdown(const NoParams &,Callback<std::nullptr_t> reply)111 void LSPServer::onShutdown(const NoParams &, Callback<std::nullptr_t> reply) {
112   shutdownRequestReceived = true;
113   reply(nullptr);
114 }
115 
116 //===----------------------------------------------------------------------===//
117 // Document Change
118 
onDocumentDidOpen(const DidOpenTextDocumentParams & params)119 void LSPServer::onDocumentDidOpen(const DidOpenTextDocumentParams &params) {
120   PublishDiagnosticsParams diagParams(params.textDocument.uri,
121                                       params.textDocument.version);
122   server.addDocument(params.textDocument.uri, params.textDocument.text,
123                      params.textDocument.version, diagParams.diagnostics);
124 
125   // Publish any recorded diagnostics.
126   publishDiagnostics(diagParams);
127 }
onDocumentDidClose(const DidCloseTextDocumentParams & params)128 void LSPServer::onDocumentDidClose(const DidCloseTextDocumentParams &params) {
129   Optional<int64_t> version = server.removeDocument(params.textDocument.uri);
130   if (!version)
131     return;
132 
133   // Empty out the diagnostics shown for this document. This will clear out
134   // anything currently displayed by the client for this document (e.g. in the
135   // "Problems" pane of VSCode).
136   publishDiagnostics(
137       PublishDiagnosticsParams(params.textDocument.uri, *version));
138 }
onDocumentDidChange(const DidChangeTextDocumentParams & params)139 void LSPServer::onDocumentDidChange(const DidChangeTextDocumentParams &params) {
140   PublishDiagnosticsParams diagParams(params.textDocument.uri,
141                                       params.textDocument.version);
142   server.updateDocument(params.textDocument.uri, params.contentChanges,
143                         params.textDocument.version, diagParams.diagnostics);
144 
145   // Publish any recorded diagnostics.
146   publishDiagnostics(diagParams);
147 }
148 
149 //===----------------------------------------------------------------------===//
150 // Definitions and References
151 
onGoToDefinition(const TextDocumentPositionParams & params,Callback<std::vector<Location>> reply)152 void LSPServer::onGoToDefinition(const TextDocumentPositionParams &params,
153                                  Callback<std::vector<Location>> reply) {
154   std::vector<Location> locations;
155   server.getLocationsOf(params.textDocument.uri, params.position, locations);
156   reply(std::move(locations));
157 }
158 
onReference(const ReferenceParams & params,Callback<std::vector<Location>> reply)159 void LSPServer::onReference(const ReferenceParams &params,
160                             Callback<std::vector<Location>> reply) {
161   std::vector<Location> locations;
162   server.findReferencesOf(params.textDocument.uri, params.position, locations);
163   reply(std::move(locations));
164 }
165 
166 //===----------------------------------------------------------------------===//
167 // DocumentLink
168 
onDocumentLink(const DocumentLinkParams & params,Callback<std::vector<DocumentLink>> reply)169 void LSPServer::onDocumentLink(const DocumentLinkParams &params,
170                                Callback<std::vector<DocumentLink>> reply) {
171   std::vector<DocumentLink> links;
172   server.getDocumentLinks(params.textDocument.uri, links);
173   reply(std::move(links));
174 }
175 
176 //===----------------------------------------------------------------------===//
177 // Hover
178 
onHover(const TextDocumentPositionParams & params,Callback<Optional<Hover>> reply)179 void LSPServer::onHover(const TextDocumentPositionParams &params,
180                         Callback<Optional<Hover>> reply) {
181   reply(server.findHover(params.textDocument.uri, params.position));
182 }
183 
184 //===----------------------------------------------------------------------===//
185 // Entry Point
186 //===----------------------------------------------------------------------===//
187 
runTableGenLSPServer(TableGenServer & server,JSONTransport & transport)188 LogicalResult mlir::lsp::runTableGenLSPServer(TableGenServer &server,
189                                               JSONTransport &transport) {
190   LSPServer lspServer(server, transport);
191   MessageHandler messageHandler(transport);
192 
193   // Initialization
194   messageHandler.method("initialize", &lspServer, &LSPServer::onInitialize);
195   messageHandler.notification("initialized", &lspServer,
196                               &LSPServer::onInitialized);
197   messageHandler.method("shutdown", &lspServer, &LSPServer::onShutdown);
198 
199   // Document Changes
200   messageHandler.notification("textDocument/didOpen", &lspServer,
201                               &LSPServer::onDocumentDidOpen);
202   messageHandler.notification("textDocument/didClose", &lspServer,
203                               &LSPServer::onDocumentDidClose);
204   messageHandler.notification("textDocument/didChange", &lspServer,
205                               &LSPServer::onDocumentDidChange);
206 
207   // Definitions and References
208   messageHandler.method("textDocument/definition", &lspServer,
209                         &LSPServer::onGoToDefinition);
210   messageHandler.method("textDocument/references", &lspServer,
211                         &LSPServer::onReference);
212 
213   // Document Link
214   messageHandler.method("textDocument/documentLink", &lspServer,
215                         &LSPServer::onDocumentLink);
216 
217   // Hover
218   messageHandler.method("textDocument/hover", &lspServer, &LSPServer::onHover);
219 
220   // Diagnostics
221   lspServer.publishDiagnostics =
222       messageHandler.outgoingNotification<PublishDiagnosticsParams>(
223           "textDocument/publishDiagnostics");
224 
225   // Run the main loop of the transport.
226   if (llvm::Error error = transport.run(messageHandler)) {
227     Logger::error("Transport error: {0}", error);
228     llvm::consumeError(std::move(error));
229     return failure();
230   }
231   return success(lspServer.shutdownRequestReceived);
232 }
233