1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qlanguageserver_p_p.h"
5
6#include <QtLanguageServer/private/qlspnotifysignals_p.h>
7#include <QtJsonRpc/private/qjsonrpcprotocol_p_p.h>
8
9QT_BEGIN_NAMESPACE
10
11using namespace QLspSpecification;
12using namespace Qt::StringLiterals;
13
14Q_LOGGING_CATEGORY(lspServerLog, "qt.languageserver.server")
15
16QLanguageServerPrivate::QLanguageServerPrivate(const QJsonRpcTransport::DataHandler &h)
17 : protocol(h)
18{
19}
20
21/*!
22\internal
23\class QLanguageServer
24\brief Implements a server for the language server protocol
25
26QLanguageServer is a class that uses the QLanguageServerProtocol to
27provide a server implementation.
28It handles the lifecycle management, and can be extended via
29QLanguageServerModule subclasses.
30
31The language server keeps a strictly monotonically increasing runState that can be queried
32from any thread (and is thus mutex gated), the normal run state is DidInitialize.
33
34The language server also keeps track of the task canceled by the client (or implicitly when
35shutting down, and isRequestCanceled can be called from any thread.
36*/
37
38QLanguageServer::QLanguageServer(const QJsonRpcTransport::DataHandler &h, QObject *parent)
39 : QObject(*new QLanguageServerPrivate(h), parent)
40{
41 Q_D(QLanguageServer);
42 registerMethods(typedRpc&: *d->protocol.typedRpc());
43 d->notifySignals.registerHandlers(protocol: &d->protocol);
44}
45
46QLanguageServerProtocol *QLanguageServer::protocol()
47{
48 Q_D(QLanguageServer);
49 return &d->protocol;
50}
51
52QLanguageServer::RunStatus QLanguageServer::runStatus() const
53{
54 const Q_D(QLanguageServer);
55 QMutexLocker l(&d->mutex);
56 return d->runStatus;
57}
58
59void QLanguageServer::finishSetup()
60{
61 Q_D(QLanguageServer);
62 RunStatus rStatus;
63 {
64 QMutexLocker l(&d->mutex);
65 rStatus = d->runStatus;
66 if (rStatus == RunStatus::NotSetup)
67 d->runStatus = RunStatus::SettingUp;
68 }
69 if (rStatus != RunStatus::NotSetup) {
70 emit lifecycleError();
71 return;
72 }
73 emit runStatusChanged(RunStatus::SettingUp);
74
75 registerHandlers(protocol: &d->protocol);
76 for (auto module : d->modules)
77 module->registerHandlers(server: this, protocol: &d->protocol);
78
79 {
80 QMutexLocker l(&d->mutex);
81 rStatus = d->runStatus;
82 if (rStatus == RunStatus::SettingUp)
83 d->runStatus = RunStatus::DidSetup;
84 }
85 if (rStatus != RunStatus::SettingUp) {
86 emit lifecycleError();
87 return;
88 }
89 emit runStatusChanged(RunStatus::DidSetup);
90}
91
92void QLanguageServer::addServerModule(QLanguageServerModule *serverModule)
93{
94 Q_D(QLanguageServer);
95 Q_ASSERT(serverModule);
96 RunStatus rStatus;
97 {
98 QMutexLocker l(&d->mutex);
99 rStatus = d->runStatus;
100 if (rStatus == RunStatus::NotSetup) {
101 if (d->modules.contains(key: serverModule->name())) {
102 d->modules.insert(key: serverModule->name(), value: serverModule);
103 qCWarning(lspServerLog) << "Duplicate add of QLanguageServerModule named"
104 << serverModule->name() << ", overwriting.";
105 } else {
106 d->modules.insert(key: serverModule->name(), value: serverModule);
107 }
108 }
109 }
110 if (rStatus != RunStatus::NotSetup) {
111 qCWarning(lspServerLog) << "Called QLanguageServer::addServerModule after setup";
112 emit lifecycleError();
113 return;
114 }
115}
116
117QLanguageServerModule *QLanguageServer::moduleByName(const QString &n) const
118{
119 const Q_D(QLanguageServer);
120 QMutexLocker l(&d->mutex);
121 return d->modules.value(key: n);
122}
123
124QLspNotifySignals *QLanguageServer::notifySignals()
125{
126 Q_D(QLanguageServer);
127 return &d->notifySignals;
128}
129
130void QLanguageServer::registerMethods(QJsonRpc::TypedRpc &typedRpc)
131{
132 typedRpc.installMessagePreprocessor(
133 preHandler: [this](const QJsonDocument &doc, const QJsonParseError &err,
134 const QJsonRpcProtocol::Handler<QJsonRpcProtocol::Response> &responder) {
135 Q_D(QLanguageServer);
136 if (!doc.isObject()) {
137 qCWarning(lspServerLog)
138 << "non object jsonrpc message" << doc << err.errorString();
139 return QJsonRpcProtocol::Processing::Stop;
140 }
141 bool sendErrorResponse = false;
142 RunStatus rState;
143 QJsonValue id = doc.object()[u"id"];
144 {
145 QMutexLocker l(&d->mutex);
146 // the normal case is d->runStatus == RunStatus::DidInitialize
147 if (d->runStatus != RunStatus::DidInitialize) {
148 if (d->runStatus == RunStatus::DidSetup && !doc.isNull()
149 && doc.object()[u"method"].toString()
150 == QString::fromUtf8(
151 utf8: QLspSpecification::Requests::InitializeMethod)) {
152 return QJsonRpcProtocol::Processing::Continue;
153 } else if (!doc.isNull()
154 && doc.object()[u"method"].toString()
155 == QString::fromUtf8(
156 utf8: QLspSpecification::Notifications::ExitMethod)) {
157 return QJsonRpcProtocol::Processing::Continue;
158 }
159 if (id.isString() || id.isDouble()) {
160 sendErrorResponse = true;
161 rState = d->runStatus;
162 } else {
163 return QJsonRpcProtocol::Processing::Stop;
164 }
165 }
166 }
167 if (!sendErrorResponse) {
168 if (id.isString() || id.isDouble()) {
169 QMutexLocker l(&d->mutex);
170 d->requestsInProgress.insert(key: id, value: QRequestInProgress {});
171 }
172 return QJsonRpcProtocol::Processing::Continue;
173 }
174 if (rState == RunStatus::NotSetup || rState == RunStatus::DidSetup)
175 responder(QJsonRpcProtocol::MessageHandler::error(
176 code: int(QLspSpecification::ErrorCodes::ServerNotInitialized),
177 message: u"Request on non initialized Language Server (runStatus %1): %2"_s
178 .arg(a: int(rState))
179 .arg(a: QString::fromUtf8(ba: doc.toJson()))));
180 else
181 responder(QJsonRpcProtocol::MessageHandler::error(
182 code: int(QLspSpecification::ErrorCodes::InvalidRequest),
183 message: u"Method called on stopping Language Server (runStatus %1)"_s.arg(
184 a: int(rState))));
185 return QJsonRpcProtocol::Processing::Stop;
186 });
187 typedRpc.installOnCloseAction(closeAction: [this](QJsonRpc::TypedResponse::Status,
188 const QJsonRpc::IdType &id, QJsonRpc::TypedRpc &) {
189 Q_D(QLanguageServer);
190 QJsonValue idValue = QTypedJson::toJsonValue(params: id);
191 bool lastReq;
192 {
193 QMutexLocker l(&d->mutex);
194 d->requestsInProgress.remove(key: idValue);
195 lastReq = d->runStatus == RunStatus::WaitPending && d->requestsInProgress.size() <= 1;
196 if (lastReq)
197 d->runStatus = RunStatus::Stopping;
198 }
199 if (lastReq)
200 executeShutdown();
201 });
202}
203
204void QLanguageServer::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
205 QLspSpecification::InitializeResult &serverInfo)
206{
207 Q_D(QLanguageServer);
208 for (auto module : std::as_const(t&: d->modules))
209 module->setupCapabilities(clientInfo, serverInfo);
210}
211
212const QLspSpecification::InitializeParams &QLanguageServer::clientInfo() const
213{
214 const Q_D(QLanguageServer);
215
216 if (int(runStatus()) < int(RunStatus::DidInitialize))
217 qCWarning(lspServerLog) << "asked for Language Server clientInfo before initialization";
218 return d->clientInfo;
219}
220
221const QLspSpecification::InitializeResult &QLanguageServer::serverInfo() const
222{
223 const Q_D(QLanguageServer);
224 if (int(runStatus()) < int(RunStatus::DidInitialize))
225 qCWarning(lspServerLog) << "asked for Language Server serverInfo before initialization";
226 return d->serverInfo;
227}
228
229void QLanguageServer::receiveData(const QByteArray &data, bool isEndOfMessage)
230{
231 if (!data.isEmpty())
232 protocol()->receiveData(data);
233
234 const Q_D(QLanguageServer);
235 // read next message if not shutting down
236 if (isEndOfMessage && d->runStatus != RunStatus::Stopped)
237 emit readNextMessage();
238}
239
240void QLanguageServer::registerHandlers(QLanguageServerProtocol *protocol)
241{
242 QObject::connect(sender: notifySignals(), signal: &QLspNotifySignals::receivedCancelNotification, context: this,
243 slot: [this](const QLspSpecification::Notifications::CancelParamsType &params) {
244 Q_D(QLanguageServer);
245 QJsonValue id = QTypedJson::toJsonValue(params: params.id);
246 QMutexLocker l(&d->mutex);
247 if (d->requestsInProgress.contains(key: id))
248 d->requestsInProgress[id].canceled = true;
249 else
250 qCWarning(lspServerLog)
251 << "Ignoring cancellation of non in progress request" << id;
252 });
253
254 protocol->registerInitializeRequestHandler(
255 handler: [this](const QByteArray &,
256 const QLspSpecification::Requests::InitializeParamsType &params,
257 QLspSpecification::Responses::InitializeResponseType &&response) {
258 qCDebug(lspServerLog) << "init";
259 Q_D(QLanguageServer);
260 RunStatus rStatus;
261 {
262 QMutexLocker l(&d->mutex);
263 rStatus = d->runStatus;
264 if (rStatus == RunStatus::DidSetup)
265 d->runStatus = RunStatus::Initializing;
266 }
267 if (rStatus != RunStatus::DidSetup) {
268 if (rStatus == RunStatus::NotSetup || rStatus == RunStatus::SettingUp)
269 response.sendErrorResponse(
270 code: int(QLspSpecification::ErrorCodes::InvalidRequest),
271 message: u"Initialization request received on non setup language server"_s
272 .toUtf8());
273 else
274 response.sendErrorResponse(
275 code: int(QLspSpecification::ErrorCodes::InvalidRequest),
276 message: u"Received multiple initialization requests"_s.toUtf8());
277 emit lifecycleError();
278 return;
279 }
280 emit runStatusChanged(RunStatus::Initializing);
281 d->clientInfo = params;
282 setupCapabilities(clientInfo: d->clientInfo, serverInfo&: d->serverInfo);
283 {
284 QMutexLocker l(&d->mutex);
285 d->runStatus = RunStatus::DidInitialize;
286 }
287 emit runStatusChanged(RunStatus::DidInitialize);
288 response.sendResponse(r: d->serverInfo);
289 });
290
291 QObject::connect(sender: notifySignals(), signal: &QLspNotifySignals::receivedInitializedNotification, context: this,
292 slot: [this](const QLspSpecification::Notifications::InitializedParamsType &) {
293 Q_D(QLanguageServer);
294 {
295 QMutexLocker l(&d->mutex);
296 d->clientInitialized = true;
297 }
298 emit clientInitialized(server: this);
299 });
300
301 protocol->registerShutdownRequestHandler(
302 handler: [this](const QByteArray &, const QLspSpecification::Requests::ShutdownParamsType &,
303 QLspSpecification::Responses::ShutdownResponseType &&response) {
304 Q_D(QLanguageServer);
305 RunStatus rStatus;
306 bool shouldExecuteShutdown = false;
307 {
308 QMutexLocker l(&d->mutex);
309 rStatus = d->runStatus;
310 if (rStatus == RunStatus::DidInitialize) {
311 d->shutdownResponse = std::move(response);
312 if (d->requestsInProgress.size() <= 1) {
313 d->runStatus = RunStatus::Stopping;
314 shouldExecuteShutdown = true;
315 } else {
316 d->runStatus = RunStatus::WaitPending;
317 }
318 }
319 }
320 if (rStatus != RunStatus::DidInitialize)
321 emit lifecycleError();
322 else if (shouldExecuteShutdown)
323 executeShutdown();
324 });
325
326 QObject::connect(sender: notifySignals(), signal: &QLspNotifySignals::receivedExitNotification, context: this,
327 slot: [this](const QLspSpecification::Notifications::ExitParamsType &) {
328 if (runStatus() != RunStatus::Stopped)
329 emit lifecycleError();
330 else
331 emit exit();
332 });
333}
334
335void QLanguageServer::executeShutdown()
336{
337 RunStatus rStatus = runStatus();
338 if (rStatus != RunStatus::Stopping) {
339 emit lifecycleError();
340 return;
341 }
342 emit shutdown();
343 QLspSpecification::Responses::ShutdownResponseType shutdownResponse;
344 {
345 Q_D(QLanguageServer);
346 QMutexLocker l(&d->mutex);
347 rStatus = d->runStatus;
348 if (rStatus == RunStatus::Stopping) {
349 shutdownResponse = std::move(d->shutdownResponse);
350 d->runStatus = RunStatus::Stopped;
351 }
352 }
353 if (rStatus != RunStatus::Stopping)
354 emit lifecycleError();
355 else
356 shutdownResponse.sendResponse(r: nullptr);
357}
358
359bool QLanguageServer::isRequestCanceled(const QJsonRpc::IdType &id) const
360{
361 const Q_D(QLanguageServer);
362 QJsonValue idVal = QTypedJson::toJsonValue(params: id);
363 QMutexLocker l(&d->mutex);
364 return d->requestsInProgress.value(key: idVal).canceled || d->runStatus != RunStatus::DidInitialize;
365}
366
367bool QLanguageServer::isInitialized() const
368{
369 switch (runStatus()) {
370 case RunStatus::NotSetup:
371 case RunStatus::SettingUp:
372 case RunStatus::DidSetup:
373 case RunStatus::Initializing:
374 return false;
375 case RunStatus::DidInitialize:
376 case RunStatus::WaitPending:
377 case RunStatus::Stopping:
378 case RunStatus::Stopped:
379 break;
380 }
381 return true;
382}
383
384QT_END_NAMESPACE
385

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/src/qmlls/qlanguageserver.cpp