Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
4#ifndef QQMLBASEMODULE_P_H
5#define QQMLBASEMODULE_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qlanguageserver_p.h"
19#include "qqmlcodemodel_p.h"
20#include "qqmllsutils_p.h"
21#include <QtQmlDom/private/qqmldom_utils_p.h>
22
23#include <QObject>
24#include <type_traits>
25#include <unordered_map>
26
27template<typename ParametersT, typename ResponseT>
29{
30 // allow using Parameters and Response type aliases in the
31 // implementations of the different requests.
32 using Parameters = ParametersT;
33 using Response = ResponseT;
34
35 // The version of the code on which the typedefinition request was made.
36 // Request is received: mark it with the current version of the textDocument.
37 // Then, wait for the codemodel to finish creating a snapshot version that is newer or equal to
38 // the textDocument version at request-received-time.
42
44};
45
53template<typename Result, typename ResponseCallback>
55{
56 Q_DISABLE_COPY_MOVE(ResponseScopeGuard)
57
59 ResponseCallback &m_callback;
60
61 ResponseScopeGuard(Result &results, ResponseCallback &callback)
62 : m_response(&results), m_callback(callback)
63 {
64 }
65
66 // note: discards the current result or error message, if there is any
68
69 template<typename... T>
70 bool setErrorFrom(const std::variant<T...> &variant)
71 {
72 static_assert(std::disjunction_v<std::is_same<T, QQmlLSUtilsErrorMessage>...>,
73 "ResponseScopeGuard::setErrorFrom was passed a variant that never contains"
74 " an error message.");
75 if (auto x = std::get_if<QQmlLSUtilsErrorMessage>(&variant)) {
76 setError(*x);
77 return true;
78 }
79 return false;
80 }
81
92 bool setErrorFrom(const std::optional<QQmlLSUtilsErrorMessage> &error)
93 {
94 if (error) {
96 return true;
97 }
98 return false;
99 }
100
102 {
103 std::visit(qOverloadedVisitor{ [this](Result *result) { m_callback.sendResponse(*result); },
104 [this](const QQmlLSUtilsErrorMessage &error) {
105 m_callback.sendErrorResponse(error.code,
106 error.message.toUtf8());
107 } },
108 m_response);
109 }
110};
111
112template<typename RequestType>
114{
115 using RequestParameters = typename RequestType::Parameters;
116 using RequestResponse = typename RequestType::Response;
117 using RequestPointer = std::unique_ptr<RequestType>;
119 using BaseT = QQmlBaseModule<RequestType>;
120
123
124 void requestHandler(const RequestParameters &parameters, RequestResponse &&response);
125 decltype(auto) getRequestHandler();
126 // processes a request in a different thread.
127 virtual void process(RequestPointerArgument toBeProcessed) = 0;
128 std::variant<QList<QQmlLSUtilsItemLocation>, QQmlLSUtilsErrorMessage>
130
131public Q_SLOTS:
132 void updatedSnapshot(const QByteArray &uri);
133
134protected:
136 std::unordered_multimap<QString, RequestPointer> m_pending;
138};
139
140template<typename Parameters, typename Response>
142 Response &&response)
143{
144 Q_UNUSED(doc);
145 m_parameters = params;
146 m_response = std::move(response);
147
148 if (!doc.textDocument) {
149 qDebug() << "Cannot find document in qmlls's codemodel, did you open it before accessing "
150 "it?";
151 return false;
152 }
153
154 {
155 QMutexLocker l(doc.textDocument->mutex());
156 m_minVersion = doc.textDocument->version().value_or(0);
157 }
158 return true;
159}
160
161template<typename RequestType>
168
169template<typename RequestType>
171{
172 QMutexLocker l(&m_pending_mutex);
173 m_pending.clear(); // empty the m_pending while the mutex is hold
174}
175
176template<typename RequestType>
178{
179 auto handler = [this](const QByteArray &, const RequestParameters &parameters,
180 RequestResponse &&response) {
181 requestHandler(parameters, std::move(response));
182 };
183 return handler;
184}
185
186template<typename RequestType>
188 RequestResponse &&response)
189{
190 auto req = std::make_unique<RequestType>();
191 QmlLsp::OpenDocument doc = m_codeModel->openDocumentByUrl(
192 QQmlLSUtils::lspUriToQmlUrl(parameters.textDocument.uri));
193
194 if (!req->fillFrom(doc, parameters, std::move(response))) {
195 req->m_response.sendErrorResponse(0, "Received invalid request", parameters);
196 return;
197 }
198 const int minVersion = req->m_minVersion;
199 {
200 QMutexLocker l(&m_pending_mutex);
201 m_pending.insert({ QString::fromUtf8(req->m_parameters.textDocument.uri), std::move(req) });
202 }
203
204 if (doc.snapshot.docVersion && *doc.snapshot.docVersion >= minVersion)
205 updatedSnapshot(QQmlLSUtils::lspUriToQmlUrl(parameters.textDocument.uri));
206}
207
208template<typename RequestType>
210{
211 QmlLsp::OpenDocumentSnapshot doc = m_codeModel->snapshotByUrl(url);
212 std::vector<RequestPointer> toCompl;
213 {
214 QMutexLocker l(&m_pending_mutex);
215 for (auto [it, end] = m_pending.equal_range(QString::fromUtf8(url)); it != end;) {
216 if (auto &[key, value] = *it;
217 doc.docVersion && value->m_minVersion <= *doc.docVersion) {
218 toCompl.push_back(std::move(value));
219 it = m_pending.erase(it);
220 } else {
221 ++it;
222 }
223 }
224 }
225 for (auto it = toCompl.rbegin(), end = toCompl.rend(); it != end; ++it) {
226 process(std::move(*it));
227 }
228}
229
230template<typename RequestType>
231std::variant<QList<QQmlLSUtilsItemLocation>, QQmlLSUtilsErrorMessage>
233{
234
235 QmlLsp::OpenDocument doc = m_codeModel->openDocumentByUrl(
236 QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri));
237
239 return QQmlLSUtilsErrorMessage{ 0,
240 u"Cannot proceed: current QML document is invalid! Fix"
241 u" all the errors in your QML code and try again."_s };
242 }
243
245 // clear reference cache to resolve latest versions (use a local env instead?)
246 if (auto envPtr = file.environment().ownerAs<QQmlJS::Dom::DomEnvironment>())
247 envPtr->clearReferenceCache();
248 if (!file) {
250 0,
251 u"Could not find file %1 in project."_s.arg(doc.snapshot.doc.toString()),
252 };
253 }
254
255 auto itemsFound = QQmlLSUtils::itemsFromTextLocation(file, request->m_parameters.position.line,
256 request->m_parameters.position.character);
257
258 if (itemsFound.isEmpty()) {
260 0,
261 u"Could not find any items at given text location."_s,
262 };
263 }
264 return itemsFound;
265}
266
267#endif // QQMLBASEMODULE_P_H
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
Represents a consistent set of types organized in modules, it is the top level of the DOM.
DomItem fileObject(GoTo option=GoTo::Strict) const
QString toString() const
static QList< QQmlLSUtilsItemLocation > itemsFromTextLocation(const DomItem &file, int line, int character)
Find the DomItem representing the object situated in file at given line and character/column.
static QByteArray lspUriToQmlUrl(const QByteArray &uri)
iterator erase(const_iterator i)
Definition qset.h:145
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
std::optional< int > docVersion
std::optional< int > validDocVersion
QQmlJS::Dom::DomItem validDoc
std::shared_ptr< Utils::TextDocument > textDocument
OpenDocumentSnapshot snapshot
void updatedSnapshot(const QByteArray &url)
QSet< QString >::iterator it
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:164
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLuint GLuint end
void ** params
GLuint64EXT * result
[6]
#define Q_SLOTS
#define Q_UNUSED(x)
QFile file
[0]
QUrl url("example.com")
[constructor-url-reference]
QVariant variant
[1]
QNetworkRequest request(url)
Parameters m_parameters
ResponseT Response
ParametersT Parameters
Response m_response
bool fillFrom(QmlLsp::OpenDocument doc, const Parameters &params, Response &&response)
QQmlBaseModule< RequestType > BaseT
decltype(auto) getRequestHandler()
QQmlBaseModule(QmlLsp::QQmlCodeModel *codeModel)
std::variant< QList< QQmlLSUtilsItemLocation >, QQmlLSUtilsErrorMessage > itemsForRequest(const RequestPointer &request)
QmlLsp::QQmlCodeModel * m_codeModel
void updatedSnapshot(const QByteArray &uri)
void requestHandler(const RequestParameters &parameters, RequestResponse &&response)
RequestPointer && RequestPointerArgument
typename RequestType::Response RequestResponse
virtual void process(RequestPointerArgument toBeProcessed)=0
std::unique_ptr< RequestType > RequestPointer
typename RequestType::Parameters RequestParameters
std::unordered_multimap< QString, RequestPointer > m_pending
This class sends a result or an error when going out of scope.
bool setErrorFrom(const std::optional< QQmlLSUtilsErrorMessage > &error)
ResponseCallback & m_callback
void setError(const QQmlLSUtilsErrorMessage &error)
std::variant< Result *, QQmlLSUtilsErrorMessage > m_response
bool setErrorFrom(const std::variant< T... > &variant)