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 "qlanguageserverbase_p_p.h"
5
6QT_BEGIN_NAMESPACE
7
8using namespace Qt::StringLiterals;
9
10Q_LOGGING_CATEGORY(lspLog, "qt.languageserver.protocol");
11
12namespace QLspSpecification {
13
14ProtocolBase::ProtocolBase(std::unique_ptr<ProtocolBasePrivate> &&priv) : d_ptr(std::move(priv))
15{
16 Q_D(ProtocolBase);
17 d->typedRpc.setTransport(&d->transport);
18 registerMethods(&d->typedRpc);
19}
20
21ProtocolBase::~ProtocolBase() = default;
22
23void ProtocolBase::registerMethods(QJsonRpc::TypedRpc *typedRpc)
24{
25 auto defaultHandler = new QJsonRpc::TypedHandler(
26 QByteArray(),
27 [this, typedRpc](const QJsonRpcProtocol::Request &req,
28 const QJsonRpcProtocol::ResponseHandler &handler) {
29 QJsonRpc::IdType id =
30 ((req.id.isDouble()) ? QJsonRpc::IdType(req.id.toInt())
31 : QJsonRpc::IdType(req.id.toString().toUtf8()));
32 QByteArray method = req.method.toUtf8();
33 QJsonRpc::TypedResponse response(id, typedRpc, handler);
34 handleUndispatchedRequest(id, method, params: req.params, response: std::move(response));
35 },
36 [this](const QJsonRpcProtocol::Notification &notif) {
37 QByteArray method = notif.method.toUtf8();
38 handleUndispatchedNotification(method, params: notif.params);
39 });
40 typedRpc->setDefaultMessageHandler(defaultHandler); // typedRpc gets ownership
41 typedRpc->setInvalidResponseHandler([this](const QJsonRpcProtocol::Response &response) {
42 handleResponseError(err: ResponseError { .code: response.errorCode.toInt(),
43 .message: response.errorMessage.toUtf8(), .data: response.data });
44 });
45}
46
47void ProtocolBase::defaultUndispatchedRequestHandler(const QJsonRpc::IdType &id,
48 const QByteArray &method,
49 const QLspSpecification::RequestParams &params,
50 QJsonRpc::TypedResponse &&response)
51{
52 Q_UNUSED(id);
53 Q_UNUSED(params);
54 QByteArray msg;
55 QByteArray cppBaseName = requestMethodToBaseCppName(method);
56 if (cppBaseName.isEmpty()) {
57 msg.append(s: "Ignoring unknown request with method ");
58 msg.append(a: method);
59 } else {
60 msg.append(s: "There was no handler registered with register");
61 msg.append(a: cppBaseName);
62 msg.append(s: "Handler to handle a requests with method ");
63 msg.append(a: method);
64 }
65 response.sendErrorResponse(code: int(QJsonRpcProtocol::ErrorCode::MethodNotFound), message: msg);
66 qCWarning(lspLog) << QString::fromUtf8(ba: msg);
67}
68
69void ProtocolBase::defaultUndispatchedNotificationHandler(
70 const QByteArray &method, const QLspSpecification::NotificationParams &params)
71{
72 Q_UNUSED(params);
73 QByteArray msg;
74 QByteArray cppBaseName = notificationMethodToBaseCppName(method);
75 if (cppBaseName.isEmpty()) {
76 msg.append(s: "Unknown notification with method ");
77 msg.append(a: method);
78 } else {
79 msg.append(s: "There was no handler registered with register");
80 msg.append(a: cppBaseName);
81 msg.append(s: "NotificationHandler to handle the \"");
82 msg.append(a: method);
83 msg.append(s: "\" notification");
84 }
85 if (method.startsWith(bv: "$"))
86 qCDebug(lspLog) << QString::fromUtf8(ba: msg);
87 else
88 qCWarning(lspLog) << QString::fromUtf8(ba: msg);
89}
90
91void ProtocolBase::defaultResponseErrorHandler(const QLspSpecification::ResponseError &err)
92{
93 qCWarning(lspLog) << u"ERROR" << err.code << u":" << QString::fromUtf8(ba: err.message)
94 << ((!err.data) ? QString()
95 : (err.data->isObject())
96 ? QString::fromUtf8(ba: QJsonDocument(err.data->toObject()).toJson())
97 : (err.data->isArray())
98 ? QString::fromUtf8(ba: QJsonDocument(err.data->toArray()).toJson())
99 : (err.data->isDouble()) ? QString::number(err.data->toDouble())
100 : (err.data->isString()) ? err.data->toString()
101 : (err.data->isNull()) ? u"null"_s
102 : QString());
103}
104
105void ProtocolBase::registerResponseErrorHandler(const ResponseErrorHandler &handler)
106{
107 Q_D(ProtocolBase);
108 Q_ASSERT(!d->errorHandler || !handler);
109 d->errorHandler = handler;
110}
111
112void ProtocolBase::registerUndispatchedRequestHandler(const GenericRequestHandler &handler)
113{
114 Q_D(ProtocolBase);
115 Q_ASSERT(!d->undispachedRequestHandler || !handler);
116 d->undispachedRequestHandler = handler;
117}
118
119void ProtocolBase::registerUndispatchedNotificationHandler(
120 const GenericNotificationHandler &handler)
121{
122 Q_D(ProtocolBase);
123 Q_ASSERT(!d->undispachedNotificationHandler || !handler);
124 d->undispachedNotificationHandler = handler;
125}
126
127void ProtocolBase::handleUndispatchedRequest(const QJsonRpc::IdType &id, const QByteArray &method,
128 const QLspSpecification::RequestParams &params,
129 QJsonRpc::TypedResponse &&response)
130{
131 Q_D(ProtocolBase);
132 if (d->undispachedRequestHandler)
133 d->undispachedRequestHandler(id, method, params, std::move(response));
134 else
135 defaultUndispatchedRequestHandler(id, method, params, response: std::move(response));
136}
137
138void ProtocolBase::handleUndispatchedNotification(const QByteArray &method,
139 const NotificationParams &params)
140{
141 Q_D(ProtocolBase);
142 if (d->undispachedNotificationHandler)
143 d->undispachedNotificationHandler(method, params);
144 else
145 defaultUndispatchedNotificationHandler(method, params);
146}
147
148void ProtocolBase::handleResponseError(const ResponseError &err)
149{
150 Q_D(ProtocolBase);
151 if (d->errorHandler)
152 d->errorHandler(err);
153 else
154 defaultResponseErrorHandler(err);
155}
156
157QJsonRpc::TypedRpc *ProtocolBase::typedRpc()
158{
159 Q_D(ProtocolBase);
160 return &d->typedRpc;
161}
162
163QJsonRpcTransport *ProtocolBase::transport()
164{
165 Q_D(ProtocolBase);
166 return &d->transport;
167}
168
169} // namespace QLspSpecification
170QT_END_NAMESPACE
171

source code of qtlanguageserver/src/languageserver/qlanguageserverbase.cpp