CIRCT 22.0.0git
Loading...
Searching...
No Matches
LSPServer.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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"
13#include "llvm/Support/JSON.h"
14#include "llvm/Support/LSP/Protocol.h"
15#include "llvm/Support/LSP/Transport.h"
16
17#include <cstdint>
18#include <mutex>
19#include <optional>
20
21#define DEBUG_TYPE "circt-verilog-lsp-server"
22
23using namespace llvm;
24using namespace llvm::lsp;
25
26//===----------------------------------------------------------------------===//
27// LSPServer
28//===----------------------------------------------------------------------===//
29
30namespace {
31
32struct LSPServer {
33
34 LSPServer(const circt::lsp::LSPServerOptions &options,
35 circt::lsp::VerilogServer &server, JSONTransport &transport)
36 : server(server), transport(transport),
37 debounceOptions(circt::lsp::DebounceOptions::fromLSPOptions(options)) {}
38
39 //===--------------------------------------------------------------------===//
40 // Initialization
41 //===--------------------------------------------------------------------===//
42
43 void onInitialize(const InitializeParams &params,
44 Callback<json::Value> reply);
45 void onInitialized(const InitializedParams &params);
46 void onShutdown(const NoParams &params, Callback<std::nullptr_t> reply);
47
48 //===--------------------------------------------------------------------===//
49 // Document Change
50 //===--------------------------------------------------------------------===//
51
52 void onDocumentDidOpen(const DidOpenTextDocumentParams &params);
53 void onDocumentDidClose(const DidCloseTextDocumentParams &params);
54 void onDocumentDidChange(const DidChangeTextDocumentParams &params);
55
56 //===--------------------------------------------------------------------===//
57 // Definitions and References
58 //===--------------------------------------------------------------------===//
59
60 void onGoToDefinition(const TextDocumentPositionParams &params,
61 Callback<std::vector<Location>> reply);
62 void onReference(const ReferenceParams &params,
63 Callback<std::vector<Location>> reply);
64
65 //===--------------------------------------------------------------------===//
66 // Fields
67 //===--------------------------------------------------------------------===//
68
70 JSONTransport &transport;
71
72 /// A thread-safe version of `publishDiagnostics`
73 void sendDiagnostics(const PublishDiagnosticsParams &p) {
74 std::scoped_lock lk(diagnosticsMutex);
75 publishDiagnostics(p); // serialize the write
76 }
77
78 void
79 setPublishDiagnostics(OutgoingNotification<PublishDiagnosticsParams> diag) {
80 std::scoped_lock lk(diagnosticsMutex);
81 publishDiagnostics = std::move(diag);
82 }
83
84 /// Used to indicate that the 'shutdown' request was received from the
85 /// Language Server client.
86 bool shutdownRequestReceived = false;
87
88private:
89 /// A mutex to serialize access to publishing diagnostics
90 std::mutex diagnosticsMutex;
91 /// An outgoing notification used to send diagnostics to the client when they
92 /// are ready to be processed.
93 OutgoingNotification<PublishDiagnosticsParams> publishDiagnostics;
94
95 circt::lsp::PendingChangesMap pendingChanges;
96 circt::lsp::DebounceOptions debounceOptions;
97};
98
99} // namespace
100//===----------------------------------------------------------------------===//
101// Initialization
102//===----------------------------------------------------------------------===//
103
104void LSPServer::onInitialize(const InitializeParams &params,
105 Callback<json::Value> reply) {
106 // Send a response with the capabilities of this server.
107 json::Object serverCaps{
108 {
109 "textDocumentSync",
110 llvm::json::Object{
111 {"openClose", true},
112 {"change", (int)TextDocumentSyncKind::Incremental},
113 {"save", true},
114
115 },
116
117 },
118 {"definitionProvider", true},
119 {"referencesProvider", true},
120 };
121
122 json::Object result{
123 {{"serverInfo", json::Object{{"name", "circt-verilog-lsp-server"},
124 {"version", "0.0.1"}}},
125 {"capabilities", std::move(serverCaps)}}};
126 reply(std::move(result));
127}
128void LSPServer::onInitialized(const InitializedParams &) {}
129void LSPServer::onShutdown(const NoParams &, Callback<std::nullptr_t> reply) {
130 shutdownRequestReceived = true;
131 pendingChanges.abort();
132 reply(nullptr);
133}
134
135//===----------------------------------------------------------------------===//
136// Document Change
137//===----------------------------------------------------------------------===//
138
139void LSPServer::onDocumentDidOpen(const DidOpenTextDocumentParams &params) {
140 PublishDiagnosticsParams diagParams(params.textDocument.uri,
141 params.textDocument.version);
142 server.addDocument(params.textDocument.uri, params.textDocument.text,
143 params.textDocument.version, diagParams.diagnostics);
144
145 // Publish any recorded diagnostics.
146 sendDiagnostics(diagParams);
147}
148
149void LSPServer::onDocumentDidClose(const DidCloseTextDocumentParams &params) {
150 pendingChanges.erase(params.textDocument.uri);
151 std::optional<int64_t> version =
152 server.removeDocument(params.textDocument.uri);
153 if (!version)
154 return;
155
156 // Empty out the diagnostics shown for this document. This will clear out
157 // anything currently displayed by the client for this document (e.g. in the
158 // "Problems" pane of VSCode).
159 sendDiagnostics(PublishDiagnosticsParams(params.textDocument.uri, *version));
160}
161
162void LSPServer::onDocumentDidChange(const DidChangeTextDocumentParams &params) {
163 pendingChanges.debounceAndUpdate(
164 params, debounceOptions,
165 [this, params](std::unique_ptr<circt::lsp::PendingChanges> result) {
166 if (!result)
167 return; // obsolete
168
169 PublishDiagnosticsParams diagParams(params.textDocument.uri,
170 result->version);
171 server.updateDocument(params.textDocument.uri, result->changes,
172 result->version, diagParams.diagnostics);
173
174 sendDiagnostics(diagParams);
175 });
176}
177
178//===----------------------------------------------------------------------===//
179// Definitions and References
180//===----------------------------------------------------------------------===//
181
182void LSPServer::onGoToDefinition(const TextDocumentPositionParams &params,
183 Callback<std::vector<Location>> reply) {
184 std::vector<Location> locations;
185 server.getLocationsOf(params.textDocument.uri, params.position, locations);
186 reply(std::move(locations));
187}
188
189void LSPServer::onReference(const ReferenceParams &params,
190 Callback<std::vector<Location>> reply) {
191 std::vector<Location> locations;
192 server.findReferencesOf(params.textDocument.uri, params.position, locations);
193 reply(std::move(locations));
194}
195
196//===----------------------------------------------------------------------===//
197// Entry Point
198//===----------------------------------------------------------------------===//
199
200LogicalResult
202 VerilogServer &server,
203 JSONTransport &transport) {
204 LSPServer lspServer(options, server, transport);
205 MessageHandler messageHandler(transport);
206
207 // Diagnostics
208 lspServer.setPublishDiagnostics(
209 messageHandler.outgoingNotification<PublishDiagnosticsParams>(
210 "textDocument/publishDiagnostics"));
211
212 // Initialization
213 messageHandler.method("initialize", &lspServer, &LSPServer::onInitialize);
214 messageHandler.notification("initialized", &lspServer,
215 &LSPServer::onInitialized);
216 messageHandler.method("shutdown", &lspServer, &LSPServer::onShutdown);
217
218 // Document Changes
219 messageHandler.notification("textDocument/didOpen", &lspServer,
220 &LSPServer::onDocumentDidOpen);
221 messageHandler.notification("textDocument/didClose", &lspServer,
222 &LSPServer::onDocumentDidClose);
223
224 messageHandler.notification("textDocument/didChange", &lspServer,
225 &LSPServer::onDocumentDidChange);
226 // Definitions and References
227 messageHandler.method("textDocument/definition", &lspServer,
228 &LSPServer::onGoToDefinition);
229 messageHandler.method("textDocument/references", &lspServer,
230 &LSPServer::onReference);
231
232 // Run the main loop of the transport.
233 if (Error error = transport.run(messageHandler)) {
234 Logger::error("Transport error: {0}", error);
235 consumeError(std::move(error));
236 return failure();
237 }
238
239 return success(lspServer.shutdownRequestReceived);
240}
static std::unique_ptr< RpcServer > server
Thread-safe accumulator + debouncer for text document changes.
This class implements all of the Verilog related functionality necessary for a language server.
llvm::LogicalResult runVerilogLSPServer(const LSPServerOptions &lspOptions, VerilogServer &server, llvm::lsp::JSONTransport &transport)
Run the main loop of the LSP server using the given Verilog server and transport.
Debounce tuning for document-change bursts.
static DebounceOptions fromLSPOptions(const circt::lsp::LSPServerOptions &opts)
Factory: build from server options. Keep mapping 1:1 for clarity.