Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlbasemodule_p.h
Go to the documentation of this file.
1// Copyright (C) 2023 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// Qt-Security score:significant reason:default
4
5#ifndef QQMLBASEMODULE_P_H
6#define QQMLBASEMODULE_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
21#include "qqmllsutils_p.h"
22#include <QtQmlDom/private/qqmldom_utils_p.h>
23
24#include <QObject>
25#include <type_traits>
26#include <unordered_map>
27
28template<typename ParametersT, typename ResponseT>
30{
31 // allow using Parameters and Response type aliases in the
32 // implementations of the different requests.
33 using Parameters = ParametersT;
34 using Response = ResponseT;
35
36 // The version of the code on which the typedefinition request was made.
37 // Request is received: mark it with the current version of the textDocument.
38 // Then, wait for the codemodel to finish creating a snapshot version that is newer or equal to
39 // the textDocument version at request-received-time.
43
44 bool fillFrom(QmlLsp::OpenDocument doc, const Parameters &params, Response &&response);
45};
46
47/*!
48\internal
49\brief This class sends a result or an error when going out of scope.
50
51It has a helper method \c setErrorFrom that sets an error from variant and optionals.
52*/
53
54template<typename Result, typename ResponseCallback>
56{
58
60 ResponseCallback &m_callback;
61
62 ResponseScopeGuard(Result &results, ResponseCallback &callback)
63 : m_response(&results), m_callback(callback)
64 {
65 }
66
67 // note: discards the current result or error message, if there is any
68 void setError(const QQmlLSUtils::ErrorMessage &error) { m_response = error; }
69
71 {
72 std::visit(qOverloadedVisitor{ [this](Result *result) { m_callback.sendResponse(*result); },
73 [this](const QQmlLSUtils::ErrorMessage &error) {
74 m_callback.sendErrorResponse(error.code,
75 error.message.toUtf8());
76 } },
77 m_response);
78 }
79};
80
81template<typename RequestType>
83{
84 using RequestParameters = typename RequestType::Parameters;
85 using RequestResponse = typename RequestType::Response;
88 using BaseT = QQmlBaseModule<RequestType>;
89
92
93 void requestHandler(const RequestParameters &parameters, RequestResponse &&response);
94 decltype(auto) getRequestHandler();
95 // processes a request in a different thread.
96 virtual void process(RequestPointerArgument toBeProcessed) = 0;
98 itemsForRequest(const RequestPointer &request);
99
101 tryLocateItems(const QmlLsp::OpenDocument &doc, const QLspSpecification::Position &position);
102
104 tryOpenDocument(const QByteArray &uri);
105
106public Q_SLOTS:
108
109protected:
113};
114
115template<typename Parameters, typename Response>
116bool BaseRequest<Parameters, Response>::fillFrom(QmlLsp::OpenDocument doc, const Parameters &params,
117 Response &&response)
118{
119 Q_UNUSED(doc);
120 m_parameters = params;
121 m_response = std::move(response);
122
123 if (!doc.textDocument) {
124 qDebug() << "Cannot find document in qmlls's codemodel, did you open it before accessing "
125 "it?";
126 return false;
127 }
128
129 {
130 QMutexLocker l(doc.textDocument->mutex());
131 m_minVersion = doc.textDocument->version().value_or(0);
132 }
133 return true;
134}
135
136template <typename RequestType>
138 : m_codeModelManager(codeModelManager)
139{
140 QObject::connect(m_codeModelManager, &QmlLsp::QQmlCodeModelManager::updatedSnapshot, this,
141 &QQmlBaseModule<RequestType>::updatedSnapshot);
142}
143
144template<typename RequestType>
146{
147 QMutexLocker l(&m_pending_mutex);
148 m_pending.clear(); // empty the m_pending while the mutex is hold
149}
150
151template<typename RequestType>
152decltype(auto) QQmlBaseModule<RequestType>::getRequestHandler()
153{
154 auto handler = [this](const QByteArray &, const RequestParameters &parameters,
155 RequestResponse &&response) {
156 requestHandler(parameters, std::move(response));
157 };
158 return handler;
159}
160
161template<typename RequestType>
162void QQmlBaseModule<RequestType>::requestHandler(const RequestParameters &parameters,
163 RequestResponse &&response)
164{
165 auto req = std::make_unique<RequestType>();
166 QmlLsp::OpenDocument doc = m_codeModelManager->openDocumentByUrl(
167 QQmlLSUtils::lspUriToQmlUrl(parameters.textDocument.uri));
168
169 if (!req->fillFrom(doc, parameters, std::move(response))) {
170 req->m_response.sendErrorResponse(0, "Received invalid request.", parameters);
171 return;
172 }
173 const int minVersion = req->m_minVersion;
174 {
175 QMutexLocker l(&m_pending_mutex);
176 m_pending.insert({ QString::fromUtf8(req->m_parameters.textDocument.uri), std::move(req) });
177 }
178
179 if (doc.snapshot.docVersion && *doc.snapshot.docVersion >= minVersion)
180 updatedSnapshot(QQmlLSUtils::lspUriToQmlUrl(parameters.textDocument.uri));
181}
182
183template<typename RequestType>
184void QQmlBaseModule<RequestType>::updatedSnapshot(const QByteArray &url)
185{
186 QmlLsp::OpenDocumentSnapshot doc = m_codeModelManager->snapshotByUrl(url);
187 std::vector<RequestPointer> toCompl;
188 {
189 QMutexLocker l(&m_pending_mutex);
190 for (auto [it, end] = m_pending.equal_range(QString::fromUtf8(url)); it != end;) {
191 if (auto &[key, value] = *it;
192 doc.docVersion && value->m_minVersion <= *doc.docVersion) {
193 toCompl.push_back(std::move(value));
194 it = m_pending.erase(it);
195 } else {
196 ++it;
197 }
198 }
199 }
200 for (auto it = toCompl.rbegin(), end = toCompl.rend(); it != end; ++it) {
201 process(std::move(*it));
202 }
203}
204
205template <typename RequestType>
207QQmlBaseModule<RequestType>::itemsForRequest(const RequestPointer &request)
208{
209 return tryOpenDocument(request->m_parameters.textDocument.uri).and_then([&](const auto &doc) {
210 return tryLocateItems(doc, request->m_parameters.position);
211 });
212}
213
214template <typename RequestType>
216QQmlBaseModule<RequestType>::tryOpenDocument(const QByteArray &uri)
217{
218 QmlLsp::OpenDocument doc =
219 m_codeModelManager->openDocumentByUrl(QQmlLSUtils::lspUriToQmlUrl(uri));
220
221 if (!doc.snapshot.validDocVersion || doc.snapshot.validDocVersion != doc.snapshot.docVersion) {
222 return q23::unexpected(
223 QQmlLSUtils::ErrorMessage{ 0,
224 u"Cannot proceed: current QML document is invalid! Fix"
225 u" all the errors in your QML code and try again."_s });
226 }
227 return doc;
228}
229
230template <typename RequestType>
232QQmlBaseModule<RequestType>::tryLocateItems(const QmlLsp::OpenDocument &doc,
233 const QLspSpecification::Position &position)
234{
235 QQmlJS::Dom::DomItem file = doc.snapshot.validDoc.fileObject(QQmlJS::Dom::GoTo::MostLikely);
236 // clear reference cache to resolve latest versions (use a local env instead?)
237 if (auto envPtr = file.environment().ownerAs<QQmlJS::Dom::DomEnvironment>())
238 envPtr->clearReferenceCache();
239 if (!file) {
240 return q23::unexpected(QQmlLSUtils::ErrorMessage{
241 0,
242 u"Could not find file %1 in project."_s.arg(doc.snapshot.doc.toString()),
243 });
244 }
245
246 auto itemsFound = QQmlLSUtils::itemsFromTextLocation(file, position.line, position.character);
247
248 if (itemsFound.isEmpty()) {
249 return q23::unexpected(QQmlLSUtils::ErrorMessage{
250 0,
251 u"Could not find any items at given text location."_s,
252 });
253 }
254 return itemsFound;
255}
256
257#endif // QQMLBASEMODULE_P_H
virtual void setupCapabilities(QLspSpecification::ServerCapabilities &)=0
Implements a server for the language server protocol.
void registerHandlers(QLanguageServerProtocol *protocol)
const QLspSpecification::InitializeParams & clientInfo() const
void lifecycleError()
void registerModule(QLanguageServerModule *serverModule)
QLspNotifySignals * notifySignals()
RunStatus runStatus() const
void readNextMessage()
void typeDefinitionRequestHandler(const QByteArray &, const QLspSpecification::TypeDefinitionParams &params, ReferencesRequest::Response &&response)
void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override
void process(RequestPointerArgument request) override
void setupCapabilities(QLspSpecification::ServerCapabilities &caps) override
Combined button and popup list for selecting options.
Parameters m_parameters
ResponseT Response
ParametersT Parameters
Response m_response
bool fillFrom(QmlLsp::OpenDocument doc, const Parameters &params, Response &&response)
QQmlBaseModule< RequestType > BaseT
q23::expected< QList< QQmlLSUtils::ItemLocation >, QQmlLSUtils::ErrorMessage > tryLocateItems(const QmlLsp::OpenDocument &doc, const QLspSpecification::Position &position)
decltype(auto) getRequestHandler()
q23::expected< QList< QQmlLSUtils::ItemLocation >, QQmlLSUtils::ErrorMessage > itemsForRequest(const RequestPointer &request)
QmlLsp::QQmlCodeModelManager * m_codeModelManager
q23::expected< QmlLsp::OpenDocument, QQmlLSUtils::ErrorMessage > tryOpenDocument(const QByteArray &uri)
void requestHandler(const RequestParameters &parameters, RequestResponse &&response)
typename RequestType::Response RequestResponse
virtual void process(RequestPointerArgument toBeProcessed)=0
QQmlBaseModule(QmlLsp::QQmlCodeModelManager *codeModel)
typename RequestType::Parameters RequestParameters
std::unordered_multimap< QString, RequestPointer > m_pending
This class sends a result or an error when going out of scope.
ResponseCallback & m_callback
void setError(const QQmlLSUtils::ErrorMessage &error)
ResponseScopeGuard(Result &results, ResponseCallback &callback)