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 | |
6 | QT_BEGIN_NAMESPACE |
7 | |
8 | using namespace Qt::StringLiterals; |
9 | |
10 | namespace QLspSpecification { |
11 | |
12 | Q_LOGGING_CATEGORY(lspLog, "qt.languageserver.protocol" ); |
13 | |
14 | ProtocolBase::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 | |
21 | ProtocolBase::~ProtocolBase() = default; |
22 | |
23 | void 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 ¬if) { |
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 | |
47 | void ProtocolBase::defaultUndispatchedRequestHandler(const QJsonRpc::IdType &id, |
48 | const QByteArray &method, |
49 | const QLspSpecification::RequestParams ¶ms, |
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 | |
69 | void ProtocolBase::defaultUndispatchedNotificationHandler( |
70 | const QByteArray &method, const QLspSpecification::NotificationParams ¶ms) |
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 | |
90 | void 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 | |
104 | void ProtocolBase::registerResponseErrorHandler(const ResponseErrorHandler &handler) |
105 | { |
106 | Q_D(ProtocolBase); |
107 | Q_ASSERT(!d->errorHandler || !handler); |
108 | d->errorHandler = handler; |
109 | } |
110 | |
111 | void ProtocolBase::registerUndispatchedRequestHandler(const GenericRequestHandler &handler) |
112 | { |
113 | Q_D(ProtocolBase); |
114 | Q_ASSERT(!d->undispachedRequestHandler || !handler); |
115 | d->undispachedRequestHandler = handler; |
116 | } |
117 | |
118 | void ProtocolBase::registerUndispatchedNotificationHandler( |
119 | const GenericNotificationHandler &handler) |
120 | { |
121 | Q_D(ProtocolBase); |
122 | Q_ASSERT(!d->undispachedNotificationHandler || !handler); |
123 | d->undispachedNotificationHandler = handler; |
124 | } |
125 | |
126 | void ProtocolBase::handleUndispatchedRequest(const QJsonRpc::IdType &id, const QByteArray &method, |
127 | const QLspSpecification::RequestParams ¶ms, |
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 | |
137 | void ProtocolBase::handleUndispatchedNotification(const QByteArray &method, |
138 | const NotificationParams ¶ms) |
139 | { |
140 | Q_D(ProtocolBase); |
141 | if (d->undispachedNotificationHandler) |
142 | d->undispachedNotificationHandler(method, params); |
143 | else |
144 | defaultUndispatchedNotificationHandler(method, params); |
145 | } |
146 | |
147 | void 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 | |
156 | QJsonRpc::TypedRpc *ProtocolBase::typedRpc() |
157 | { |
158 | Q_D(ProtocolBase); |
159 | return &d->typedRpc; |
160 | } |
161 | |
162 | QJsonRpcTransport *ProtocolBase::transport() |
163 | { |
164 | Q_D(ProtocolBase); |
165 | return &d->transport; |
166 | } |
167 | |
168 | } // namespace QLspSpecification |
169 | QT_END_NAMESPACE |
170 | |