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);
45 registerHandlers(&d->protocol);
48QLanguageServerProtocol *QLanguageServer::protocol()
54QLanguageServer::RunStatus QLanguageServer::runStatus()
const
56 const Q_D(QLanguageServer);
57 QMutexLocker l(&d->mutex);
61void QLanguageServer::registerModule(QLanguageServerModule *serverModule)
64 Q_ASSERT(serverModule);
65 serverModule->registerHandlers(
this, &d->protocol);
66 serverModule->setupCapabilities(d->serverInfo.capabilities);
69QLspNotifySignals *QLanguageServer::notifySignals()
72 return &d->notifySignals;
75void QLanguageServer::registerMethods(QJsonRpc::TypedRpc &typedRpc)
77 typedRpc.installMessagePreprocessor(
78 [
this](
const QJsonDocument &doc,
const QJsonParseError &err,
79 const QJsonRpcProtocol::Handler<QJsonRpcProtocol::Response> &responder) {
81 if (!doc.isObject()) {
82 qCWarning(lspServerLog)
83 <<
"non object jsonrpc message" << doc << err.errorString();
84 return QJsonRpcProtocol::Processing::Stop;
86 bool sendErrorResponse =
false;
87 RunStatus rState = RunStatus::NotInitialized;
88 QJsonValue id = doc.object()[u"id"];
90 QMutexLocker l(&d->mutex);
92 if (d->runStatus != RunStatus::Initialized) {
93 if (d->runStatus == RunStatus::NotInitialized && !doc.isNull()
94 && doc.object()[u"method"].toString()
96 QLspSpecification::Requests::InitializeMethod)) {
97 return QJsonRpcProtocol::Processing::Continue;
98 }
else if (!doc.isNull()
99 && doc.object()[u"method"].toString()
100 == QString::fromUtf8(
101 QLspSpecification::Notifications::ExitMethod)) {
102 return QJsonRpcProtocol::Processing::Continue;
104 if (id.isString() || id.isDouble()) {
105 sendErrorResponse =
true;
106 rState = d->runStatus;
108 return QJsonRpcProtocol::Processing::Stop;
112 if (!sendErrorResponse) {
113 if (id.isString() || id.isDouble()) {
114 QMutexLocker l(&d->mutex);
115 d->requestsInProgress.insert(id, QRequestInProgress {});
117 return QJsonRpcProtocol::Processing::Continue;
119 if (rState == RunStatus::NotInitialized)
120 responder(QJsonRpcProtocol::MessageHandler::error(
121 int(QLspSpecification::ErrorCodes::ServerNotInitialized),
122 u"Request on non initialized Language Server (runStatus %1): %2"_s
124 .arg(QString::fromUtf8(doc.toJson()))));
126 responder(QJsonRpcProtocol::MessageHandler::error(
127 int(QLspSpecification::ErrorCodes::InvalidRequest),
128 u"Method called on stopping Language Server (runStatus %1)"_s.arg(
130 return QJsonRpcProtocol::Processing::Stop;
132 typedRpc.installOnCloseAction([
this](QJsonRpc::TypedResponse::Status,
133 const QJsonRpc::IdType &id, QJsonRpc::TypedRpc &) {
134 Q_D(QLanguageServer);
135 QJsonValue idValue = QTypedJson::toJsonValue(id);
138 QMutexLocker l(&d->mutex);
139 d->requestsInProgress.remove(idValue);
140 lastReq = d->runStatus == RunStatus::WaitPending && d->requestsInProgress.size() <= 1;
142 d->runStatus = RunStatus::Stopping;
149const QLspSpecification::InitializeParams &QLanguageServer::clientInfo()
const
151 const Q_D(QLanguageServer);
153 if (
int(runStatus()) <
int(RunStatus::Initialized))
154 qCWarning(lspServerLog) <<
"asked for Language Server clientInfo before initialization";
155 return d->clientInfo;
158void QLanguageServer::receiveData(
const QByteArray &data,
bool isEndOfMessage)
161 protocol()->receiveData(data);
163 const Q_D(QLanguageServer);
165 if (isEndOfMessage && d->runStatus != RunStatus::Stopped)
166 emit readNextMessage();
169void QLanguageServer::registerHandlers(QLanguageServerProtocol *protocol)
171 QObject::connect(notifySignals(), &QLspNotifySignals::receivedCancelNotification,
this,
172 [
this](
const QLspSpecification::Notifications::CancelParamsType ¶ms) {
173 Q_D(QLanguageServer);
174 QJsonValue id = QTypedJson::toJsonValue(params.id);
175 QMutexLocker l(&d->mutex);
176 if (d->requestsInProgress.contains(id))
177 d->requestsInProgress[id].canceled =
true;
179 qCWarning(lspServerLog)
180 <<
"Ignoring cancellation of non in progress request" << id;
183 protocol->registerInitializeRequestHandler(
184 [
this](
const QByteArray &,
185 const QLspSpecification::Requests::InitializeParamsType ¶ms,
186 QLspSpecification::Responses::InitializeResponseType &&response) {
187 Q_D(QLanguageServer);
189 QMutexLocker l(&d->mutex);
190 if (d->runStatus == RunStatus::Initialized) {
191 response.sendErrorResponse(
192 int(QLspSpecification::ErrorCodes::InvalidRequest),
193 u"Received multiple initialization requests"_s.toUtf8());
197 qCDebug(lspServerLog) <<
"init";
198 d->clientInfo = params;
200 QMutexLocker l(&d->mutex);
201 d->runStatus = RunStatus::Initialized;
203 response.sendResponse(d->serverInfo);
206 QObject::connect(notifySignals(), &QLspNotifySignals::receivedInitializedNotification,
this,
207 [
this](
const QLspSpecification::Notifications::InitializedParamsType &) {
208 emit clientInitialized(
this);
211 protocol->registerShutdownRequestHandler(
212 [
this](
const QByteArray &,
const QLspSpecification::Requests::ShutdownParamsType &,
213 QLspSpecification::Responses::ShutdownResponseType &&response) {
214 Q_D(QLanguageServer);
216 bool shouldExecuteShutdown =
false;
218 QMutexLocker l(&d->mutex);
219 rStatus = d->runStatus;
220 if (rStatus == RunStatus::Initialized) {
221 d->shutdownResponse = std::move(response);
222 if (d->requestsInProgress.size() <= 1) {
223 d->runStatus = RunStatus::Stopping;
224 shouldExecuteShutdown =
true;
226 d->runStatus = RunStatus::WaitPending;
230 if (rStatus != RunStatus::Initialized)
231 emit lifecycleError();
232 else if (shouldExecuteShutdown)
236 QObject::connect(notifySignals(), &QLspNotifySignals::receivedExitNotification,
this,
237 [
this](
const QLspSpecification::Notifications::ExitParamsType &) {
238 Q_D(QLanguageServer);
239 QMutexLocker l(&d->mutex);
240 RunStatus runStatus = d->runStatus;
241 if (runStatus != RunStatus::WaitingForExit) {
242 emit lifecycleError();
245 d->runStatus = RunStatus::Stopped;
250void QLanguageServer::executeShutdown()
252 RunStatus rStatus = runStatus();
253 if (rStatus != RunStatus::Stopping) {
254 emit lifecycleError();
257 QLspSpecification::Responses::ShutdownResponseType shutdownResponse;
259 Q_D(QLanguageServer);
260 QMutexLocker l(&d->mutex);
261 rStatus = d->runStatus;
262 if (rStatus == RunStatus::Stopping) {
263 shutdownResponse = std::move(d->shutdownResponse);
264 d->runStatus = RunStatus::WaitingForExit;
267 if (rStatus != RunStatus::Stopping)
268 emit lifecycleError();
270 shutdownResponse.sendResponse(
nullptr);
QLanguageServerPrivate(const QJsonRpcTransport::DataHandler &h)
Combined button and popup list for selecting options.