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
10namespace QLspSpecification {
11
12Q_LOGGING_CATEGORY(lspLog, "qt.languageserver.protocol");
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 not handler registered with register");
80 msg.append(a: cppBaseName);
81 msg.append(s: "NotificationHandler to handle notification with method ");
82 msg.append(a: method);
83 }
84 if (method.startsWith(bv: "$"))
85 qCDebug(lspLog) << QString::fromUtf8(ba: msg);
86 else
87 qCWarning(lspLog) << QString::fromUtf8(ba: msg);
88}
89
90void ProtocolBase::defaultResponseErrorHandler(const QLspSpecification::ResponseError &err)
91{
92 qCWarning(lspLog) << u"ERROR" << err.code << u":" << QString::fromUtf8(ba: err.message)
93 << ((!err.data) ? QString()
94 : (err.data->isObject())
95 ? QString::fromUtf8(ba: QJsonDocument(err.data->toObject()).toJson())
96 : (err.data->isArray())
97 ? QString::fromUtf8(ba: QJsonDocument(err.data->toArray()).toJson())
98 : (err.data->isDouble()) ? QString::number(err.data->toDouble())
99 : (err.data->isString()) ? err.data->toString()
100 : (err.data->isNull()) ? u"null"_s
101 : QString());
102}
103
104void ProtocolBase::registerResponseErrorHandler(const ResponseErrorHandler &handler)
105{
106 Q_D(ProtocolBase);
107 Q_ASSERT(!d->errorHandler || !handler);
108 d->errorHandler = handler;
109}
110
111void ProtocolBase::registerUndispatchedRequestHandler(const GenericRequestHandler &handler)
112{
113 Q_D(ProtocolBase);
114 Q_ASSERT(!d->undispachedRequestHandler || !handler);
115 d->undispachedRequestHandler = handler;
116}
117
118void ProtocolBase::registerUndispatchedNotificationHandler(
119 const GenericNotificationHandler &handler)
120{
121 Q_D(ProtocolBase);
122 Q_ASSERT(!d->undispachedNotificationHandler || !handler);
123 d->undispachedNotificationHandler = handler;
124}
125
126void ProtocolBase::handleUndispatchedRequest(const QJsonRpc::IdType &id, const QByteArray &method,
127 const QLspSpecification::RequestParams &params,
128 QJsonRpc::TypedResponse &&response)
129{
130 Q_D(ProtocolBase);
131 if (d->undispachedRequestHandler)
132 d->undispachedRequestHandler(id, method, params, std::move(response));
133 else
134 defaultUndispatchedRequestHandler(id, method, params, response: std::move(response));
135}
136
137void ProtocolBase::handleUndispatchedNotification(const QByteArray &method,
138 const NotificationParams &params)
139{
140 Q_D(ProtocolBase);
141 if (d->undispachedNotificationHandler)
142 d->undispachedNotificationHandler(method, params);
143 else
144 defaultUndispatchedNotificationHandler(method, params);
145}
146
147void ProtocolBase::handleResponseError(const ResponseError &err)
148{
149 Q_D(ProtocolBase);
150 if (d->errorHandler)
151 d->errorHandler(err);
152 else
153 defaultResponseErrorHandler(err);
154}
155
156QJsonRpc::TypedRpc *ProtocolBase::typedRpc()
157{
158 Q_D(ProtocolBase);
159 return &d->typedRpc;
160}
161
162QJsonRpcTransport *ProtocolBase::transport()
163{
164 Q_D(ProtocolBase);
165 return &d->transport;
166}
167
168} // namespace QLspSpecification
169QT_END_NAMESPACE
170

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