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 return QJsonRpcProtocol::Processing::Continue;
115 if (rState == RunStatus::NotInitialized)
116 responder(QJsonRpcProtocol::MessageHandler::error(
117 int(QLspSpecification::ErrorCodes::ServerNotInitialized),
118 u"Request on non initialized Language Server (runStatus %1): %2"_s
120 .arg(QString::fromUtf8(doc.toJson()))));
122 responder(QJsonRpcProtocol::MessageHandler::error(
123 int(QLspSpecification::ErrorCodes::InvalidRequest),
124 u"Method called on stopping Language Server (runStatus %1)"_s.arg(
126 return QJsonRpcProtocol::Processing::Stop;
130const QLspSpecification::InitializeParams &QLanguageServer::clientInfo()
const
132 const Q_D(QLanguageServer);
134 if (
int(runStatus()) <
int(RunStatus::Initialized))
135 qCWarning(lspServerLog) <<
"asked for Language Server clientInfo before initialization";
136 return d->clientInfo;
139void QLanguageServer::receiveData(
const QByteArray &data,
bool isEndOfMessage)
142 protocol()->receiveData(data);
144 const Q_D(QLanguageServer);
146 if (isEndOfMessage && d->runStatus != RunStatus::Stopped)
147 emit readNextMessage();
150void QLanguageServer::registerHandlers(QLanguageServerProtocol *protocol)
152 protocol->registerInitializeRequestHandler(
153 [
this](
const QByteArray &,
154 const QLspSpecification::Requests::InitializeParamsType ¶ms,
155 QLspSpecification::Responses::InitializeResponseType &&response) {
156 Q_D(QLanguageServer);
158 QMutexLocker l(&d->mutex);
159 if (d->runStatus == RunStatus::Initialized) {
160 response.sendErrorResponse(
161 int(QLspSpecification::ErrorCodes::InvalidRequest),
162 u"Received multiple initialization requests"_s.toUtf8());
166 qCDebug(lspServerLog) <<
"init";
167 d->clientInfo = params;
169 QMutexLocker l(&d->mutex);
170 d->runStatus = RunStatus::Initialized;
172 response.sendResponse(d->serverInfo);
175 QObject::connect(notifySignals(), &QLspNotifySignals::receivedInitializedNotification,
this,
176 [
this](
const QLspSpecification::Notifications::InitializedParamsType &) {
177 emit clientInitialized(
this);
180 protocol->registerShutdownRequestHandler(
181 [
this](
const QByteArray &,
const QLspSpecification::Requests::ShutdownParamsType &,
182 QLspSpecification::Responses::ShutdownResponseType &&response) {
183 Q_D(QLanguageServer);
185 bool shouldExecuteShutdown =
false;
187 QMutexLocker l(&d->mutex);
188 rStatus = d->runStatus;
189 if (rStatus == RunStatus::Initialized) {
190 d->shutdownResponse = std::move(response);
191 d->runStatus = RunStatus::Stopping;
192 shouldExecuteShutdown =
true;
195 if (rStatus != RunStatus::Initialized)
196 emit lifecycleError();
197 else if (shouldExecuteShutdown)
201 QObject::connect(notifySignals(), &QLspNotifySignals::receivedExitNotification,
this,
202 [
this](
const QLspSpecification::Notifications::ExitParamsType &) {
203 Q_D(QLanguageServer);
204 QMutexLocker l(&d->mutex);
205 RunStatus runStatus = d->runStatus;
206 if (runStatus != RunStatus::WaitingForExit) {
207 emit lifecycleError();
210 d->runStatus = RunStatus::Stopped;
215void QLanguageServer::executeShutdown()
217 RunStatus rStatus = runStatus();
218 if (rStatus != RunStatus::Stopping) {
219 emit lifecycleError();
222 QLspSpecification::Responses::ShutdownResponseType shutdownResponse;
224 Q_D(QLanguageServer);
225 QMutexLocker l(&d->mutex);
226 rStatus = d->runStatus;
227 if (rStatus == RunStatus::Stopping) {
228 shutdownResponse = std::move(d->shutdownResponse);
229 d->runStatus = RunStatus::WaitingForExit;
232 if (rStatus != RunStatus::Stopping)
233 emit lifecycleError();
235 shutdownResponse.sendResponse(
nullptr);
QLanguageServerPrivate(const QJsonRpcTransport::DataHandler &h)
Combined button and popup list for selecting options.