7#include <QtLanguageServer/private/qlspnotifysignals_p.h>
8#include <QtJsonRpc/private/qjsonrpcprotocol_p_p.h>
12Q_LOGGING_CATEGORY(lspServerLog,
"qt.languageserver.server")
14using namespace QLspSpecification;
15using namespace Qt::StringLiterals;
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
39QLanguageServer::QLanguageServer(
const QJsonRpcTransport::DataHandler &h, QObject *parent)
40 : QObject(*
new QLanguageServerPrivate(h), parent)
43 registerMethods(*d->protocol.typedRpc());
44 d->notifySignals.registerHandlers(&d->protocol);
55 const Q_D(QLanguageServer);
56 QMutexLocker l(&d->mutex);
65 QMutexLocker l(&d->mutex);
66 rStatus = d->runStatus;
67 if (rStatus == RunStatus::NotSetup)
68 d->runStatus = RunStatus::SettingUp;
71 emit lifecycleError();
76 registerHandlers(&d->protocol);
77 for (
auto module : d->modules)
78 module->registerHandlers(
this, &d->protocol);
81 QMutexLocker l(&d->mutex);
82 rStatus = d->runStatus;
83 if (rStatus == RunStatus::SettingUp)
84 d->runStatus = RunStatus::DidSetup;
87 emit lifecycleError();
96 Q_ASSERT(serverModule);
99 QMutexLocker l(&d->mutex);
100 rStatus = d->runStatus;
102 if (d->modules.contains(serverModule->name())) {
103 d->modules.insert(serverModule->name(), serverModule);
104 qCWarning(lspServerLog) <<
"Duplicate add of QLanguageServerModule named"
105 << serverModule->name() <<
", overwriting.";
107 d->modules.insert(serverModule->name(), serverModule);
112 qCWarning(lspServerLog) <<
"Called QLanguageServer::addServerModule after setup";
113 emit lifecycleError();
120 const Q_D(QLanguageServer);
121 QMutexLocker l(&d->mutex);
122 return d->modules.value(n);
127 Q_D(QLanguageServer);
128 return &d->notifySignals;
133 typedRpc.installMessagePreprocessor(
134 [
this](
const QJsonDocument &doc,
const QJsonParseError &err,
135 const QJsonRpcProtocol::Handler<QJsonRpcProtocol::Response> &responder) {
136 Q_D(QLanguageServer);
137 if (!doc.isObject()) {
138 qCWarning(lspServerLog)
139 <<
"non object jsonrpc message" << doc << err.errorString();
140 return QJsonRpcProtocol::Processing::Stop;
142 bool sendErrorResponse =
false;
144 QJsonValue id = doc.object()[u"id"];
146 QMutexLocker l(&d->mutex);
148 if (d->runStatus != RunStatus::DidInitialize) {
149 if (d->runStatus == RunStatus::DidSetup && !doc.isNull()
150 && doc.object()[u"method"].toString()
151 == QString::fromUtf8(
152 QLspSpecification::Requests::InitializeMethod)) {
153 return QJsonRpcProtocol::Processing::Continue;
154 }
else if (!doc.isNull()
155 && doc.object()[u"method"].toString()
156 == QString::fromUtf8(
157 QLspSpecification::Notifications::ExitMethod)) {
158 return QJsonRpcProtocol::Processing::Continue;
160 if (id.isString() || id.isDouble()) {
161 sendErrorResponse =
true;
162 rState = d->runStatus;
164 return QJsonRpcProtocol::Processing::Stop;
168 if (!sendErrorResponse) {
169 if (id.isString() || id.isDouble()) {
170 QMutexLocker l(&d->mutex);
171 d->requestsInProgress.insert(id, QRequestInProgress {});
173 return QJsonRpcProtocol::Processing::Continue;
175 if (rState == RunStatus::NotSetup || rState == RunStatus::DidSetup)
176 responder(QJsonRpcProtocol::MessageHandler::error(
177 int(QLspSpecification::ErrorCodes::ServerNotInitialized),
178 u"Request on non initialized Language Server (runStatus %1): %2"_s
180 .arg(QString::fromUtf8(doc.toJson()))));
182 responder(QJsonRpcProtocol::MessageHandler::error(
183 int(QLspSpecification::ErrorCodes::InvalidRequest),
184 u"Method called on stopping Language Server (runStatus %1)"_s.arg(
186 return QJsonRpcProtocol::Processing::Stop;
188 typedRpc.installOnCloseAction([
this](QJsonRpc::TypedResponse::Status,
189 const QJsonRpc::IdType &id, QJsonRpc::TypedRpc &) {
190 Q_D(QLanguageServer);
191 QJsonValue idValue = QTypedJson::toJsonValue(id);
194 QMutexLocker l(&d->mutex);
195 d->requestsInProgress.remove(idValue);
196 lastReq = d->runStatus == RunStatus::WaitPending && d->requestsInProgress.size() <= 1;
198 d->runStatus = RunStatus::Stopping;
206 QLspSpecification::InitializeResult &serverInfo)
208 Q_D(QLanguageServer);
209 for (
auto module : std::as_const(d->modules))
210 module->setupCapabilities(clientInfo, serverInfo);
215 const Q_D(QLanguageServer);
218 qCWarning(lspServerLog) <<
"asked for Language Server clientInfo before initialization";
219 return d->clientInfo;
224 const Q_D(QLanguageServer);
226 qCWarning(lspServerLog) <<
"asked for Language Server serverInfo before initialization";
227 return d->serverInfo;
233 protocol()->receiveData(data);
235 const Q_D(QLanguageServer);
237 if (isEndOfMessage && d->runStatus != RunStatus::Stopped)
238 emit readNextMessage();
243 QObject::connect(notifySignals(), &QLspNotifySignals::receivedCancelNotification,
this,
244 [
this](
const QLspSpecification::Notifications::CancelParamsType ¶ms) {
245 Q_D(QLanguageServer);
246 QJsonValue id = QTypedJson::toJsonValue(params.id);
247 QMutexLocker l(&d->mutex);
248 if (d->requestsInProgress.contains(id))
249 d->requestsInProgress[id].canceled =
true;
251 qCWarning(lspServerLog)
252 <<
"Ignoring cancellation of non in progress request" << id;
255 protocol->registerInitializeRequestHandler(
256 [
this](
const QByteArray &,
257 const QLspSpecification::Requests::InitializeParamsType ¶ms,
258 QLspSpecification::Responses::InitializeResponseType &&response) {
259 qCDebug(lspServerLog) <<
"init";
260 Q_D(QLanguageServer);
263 QMutexLocker l(&d->mutex);
264 rStatus = d->runStatus;
265 if (rStatus == RunStatus::DidSetup)
266 d->runStatus = RunStatus::Initializing;
269 if (rStatus == RunStatus::NotSetup || rStatus == RunStatus::SettingUp)
270 response.sendErrorResponse(
271 int(QLspSpecification::ErrorCodes::InvalidRequest),
272 u"Initialization request received on non setup language server"_s
275 response.sendErrorResponse(
276 int(QLspSpecification::ErrorCodes::InvalidRequest),
277 u"Received multiple initialization requests"_s.toUtf8());
278 emit lifecycleError();
282 d->clientInfo = params;
283 setupCapabilities(d->clientInfo, d->serverInfo);
285 QMutexLocker l(&d->mutex);
286 d->runStatus = RunStatus::DidInitialize;
289 response.sendResponse(d->serverInfo);
292 QObject::connect(notifySignals(), &QLspNotifySignals::receivedInitializedNotification,
this,
293 [
this](
const QLspSpecification::Notifications::InitializedParamsType &) {
294 Q_D(QLanguageServer);
296 QMutexLocker l(&d->mutex);
297 d->clientInitialized =
true;
299 emit clientInitialized(
this);
302 protocol->registerShutdownRequestHandler(
303 [
this](
const QByteArray &,
const QLspSpecification::Requests::ShutdownParamsType &,
304 QLspSpecification::Responses::ShutdownResponseType &&response) {
305 Q_D(QLanguageServer);
307 bool shouldExecuteShutdown =
false;
309 QMutexLocker l(&d->mutex);
310 rStatus = d->runStatus;
312 d->shutdownResponse = std::move(response);
313 if (d->requestsInProgress.size() <= 1) {
314 d->runStatus = RunStatus::Stopping;
315 shouldExecuteShutdown =
true;
317 d->runStatus = RunStatus::WaitPending;
322 emit lifecycleError();
323 else if (shouldExecuteShutdown)
327 QObject::connect(notifySignals(), &QLspNotifySignals::receivedExitNotification,
this,
328 [
this](
const QLspSpecification::Notifications::ExitParamsType &) {
329 Q_D(QLanguageServer);
330 QMutexLocker l(&d->mutex);
331 RunStatus runStatus = d->runStatus;
332 if (runStatus != RunStatus::WaitingForExit) {
333 emit lifecycleError();
336 d->runStatus = RunStatus::Stopped;
345 emit lifecycleError();
349 QLspSpecification::Responses::ShutdownResponseType shutdownResponse;
351 Q_D(QLanguageServer);
352 QMutexLocker l(&d->mutex);
353 rStatus = d->runStatus;
355 shutdownResponse = std::move(d->shutdownResponse);
356 d->runStatus = RunStatus::WaitingForExit;
360 emit lifecycleError();
362 shutdownResponse.sendResponse(
nullptr);
367 const Q_D(QLanguageServer);
368 QJsonValue idVal = QTypedJson::toJsonValue(id);
369 QMutexLocker l(&d->mutex);
370 return d->requestsInProgress.value(idVal).canceled || d->runStatus != RunStatus::DidInitialize;
QLanguageServerPrivate(const QJsonRpcTransport::DataHandler &h)
Implements a server for the language server protocol.
void registerHandlers(QLanguageServerProtocol *protocol)
const QLspSpecification::InitializeParams & clientInfo() const
void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo, QLspSpecification::InitializeResult &serverInfo)
bool isRequestCanceled(const QJsonRpc::IdType &id) const
QLspNotifySignals * notifySignals()
const QLspSpecification::InitializeResult & serverInfo() const
void addServerModule(QLanguageServerModule *serverModule)
QLanguageServerModule * moduleByName(const QString &n) const
bool isInitialized() const
RunStatus runStatus() const