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 {
27   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 
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::Full},
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 }
110 void LSPServer::onInitialized(const InitializedParams &) {}
111 void LSPServer::onShutdown(const NoParams &, Callback<std::nullptr_t> reply) {
112   shutdownRequestReceived = true;
113   reply(nullptr);
114 }
115 
116 //===----------------------------------------------------------------------===//
117 // Document Change
118 
119 void LSPServer::onDocumentDidOpen(const DidOpenTextDocumentParams &params) {
120   PublishDiagnosticsParams diagParams(params.textDocument.uri,
121                                       params.textDocument.version);
122   server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text,
123                              params.textDocument.version,
124                              diagParams.diagnostics);
125 
126   // Publish any recorded diagnostics.
127   publishDiagnostics(diagParams);
128 }
129 void LSPServer::onDocumentDidClose(const DidCloseTextDocumentParams &params) {
130   Optional<int64_t> version = server.removeDocument(params.textDocument.uri);
131   if (!version)
132     return;
133 
134   // Empty out the diagnostics shown for this document. This will clear out
135   // anything currently displayed by the client for this document (e.g. in the
136   // "Problems" pane of VSCode).
137   publishDiagnostics(
138       PublishDiagnosticsParams(params.textDocument.uri, *version));
139 }
140 void LSPServer::onDocumentDidChange(const DidChangeTextDocumentParams &params) {
141   // TODO: We currently only support full document updates, we should refactor
142   // to avoid this.
143   if (params.contentChanges.size() != 1)
144     return;
145   PublishDiagnosticsParams diagParams(params.textDocument.uri,
146                                       params.textDocument.version);
147   server.addOrUpdateDocument(
148       params.textDocument.uri, params.contentChanges.front().text,
149       params.textDocument.version, diagParams.diagnostics);
150 
151   // Publish any recorded diagnostics.
152   publishDiagnostics(diagParams);
153 }
154 
155 //===----------------------------------------------------------------------===//
156 // Definitions and References
157 
158 void LSPServer::onGoToDefinition(const TextDocumentPositionParams &params,
159                                  Callback<std::vector<Location>> reply) {
160   std::vector<Location> locations;
161   server.getLocationsOf(params.textDocument.uri, params.position, locations);
162   reply(std::move(locations));
163 }
164 
165 void LSPServer::onReference(const ReferenceParams &params,
166                             Callback<std::vector<Location>> reply) {
167   std::vector<Location> locations;
168   server.findReferencesOf(params.textDocument.uri, params.position, locations);
169   reply(std::move(locations));
170 }
171 
172 //===----------------------------------------------------------------------===//
173 // DocumentLink
174 
175 void LSPServer::onDocumentLink(const DocumentLinkParams &params,
176                                Callback<std::vector<DocumentLink>> reply) {
177   std::vector<DocumentLink> links;
178   server.getDocumentLinks(params.textDocument.uri, links);
179   reply(std::move(links));
180 }
181 
182 //===----------------------------------------------------------------------===//
183 // Hover
184 
185 void LSPServer::onHover(const TextDocumentPositionParams &params,
186                         Callback<Optional<Hover>> reply) {
187   reply(server.findHover(params.textDocument.uri, params.position));
188 }
189 
190 //===----------------------------------------------------------------------===//
191 // Entry Point
192 //===----------------------------------------------------------------------===//
193 
194 LogicalResult mlir::lsp::runTableGenLSPServer(TableGenServer &server,
195                                               JSONTransport &transport) {
196   LSPServer lspServer(server, transport);
197   MessageHandler messageHandler(transport);
198 
199   // Initialization
200   messageHandler.method("initialize", &lspServer, &LSPServer::onInitialize);
201   messageHandler.notification("initialized", &lspServer,
202                               &LSPServer::onInitialized);
203   messageHandler.method("shutdown", &lspServer, &LSPServer::onShutdown);
204 
205   // Document Changes
206   messageHandler.notification("textDocument/didOpen", &lspServer,
207                               &LSPServer::onDocumentDidOpen);
208   messageHandler.notification("textDocument/didClose", &lspServer,
209                               &LSPServer::onDocumentDidClose);
210   messageHandler.notification("textDocument/didChange", &lspServer,
211                               &LSPServer::onDocumentDidChange);
212 
213   // Definitions and References
214   messageHandler.method("textDocument/definition", &lspServer,
215                         &LSPServer::onGoToDefinition);
216   messageHandler.method("textDocument/references", &lspServer,
217                         &LSPServer::onReference);
218 
219   // Document Link
220   messageHandler.method("textDocument/documentLink", &lspServer,
221                         &LSPServer::onDocumentLink);
222 
223   // Hover
224   messageHandler.method("textDocument/hover", &lspServer, &LSPServer::onHover);
225 
226   // Diagnostics
227   lspServer.publishDiagnostics =
228       messageHandler.outgoingNotification<PublishDiagnosticsParams>(
229           "textDocument/publishDiagnostics");
230 
231   // Run the main loop of the transport.
232   if (llvm::Error error = transport.run(messageHandler)) {
233     Logger::error("Transport error: {0}", error);
234     llvm::consumeError(std::move(error));
235     return failure();
236   }
237   return success(lspServer.shutdownRequestReceived);
238 }
239