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