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
qqmlhighlightsupport.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 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#include <qqmlhighlightsupport_p.h>
6#include <qqmldiffer_p.h>
7
9
10using namespace Qt::StringLiterals;
11using namespace QLspSpecification;
12using namespace QQmlJS::Dom;
13using namespace QmlHighlighting;
14
15/*!
16\internal
17Make a list of enum names to register the supported token
18types and modifiers. It is case-sensitive in the protocol
19thus we need to lower the first characters of the enum names.
20*/
21template <typename EnumType>
23{
24 QList<QByteArray> result;
25 QMetaEnum metaEnum = QMetaEnum::fromType<EnumType>();
26 for (auto i = 0; i < metaEnum.keyCount(); ++i) {
27 auto &&enumName = QByteArray(metaEnum.key(i));
28 enumName.front() = std::tolower(enumName.front());
29 result.emplace_back(std::move(enumName));
30 }
31
32 return result;
33}
34
36{
37 return enumToByteArray<QLspSpecification::SemanticTokenModifiers>();
38}
39
41{
42 return enumToByteArray<SemanticTokenProtocolTypes>();
43}
44
45static QList<unsigned> generateHighlights(QmlLsp::RegisteredSemanticTokens &cached,
46 const QmlLsp::OpenDocument &doc,
47 const std::optional<HighlightsRange> &range,
48 HighlightingMode mode)
49{
50 DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely);
51 const auto fileObject = file.ownerAs<QmlFile>();
52 QmlHighlighting::Utils::updateResultID(cached.resultId);
53 if (!fileObject || !(fileObject && fileObject->isValid())) {
54 if (const auto lastValidItem = doc.snapshot.validDoc.ownerAs<QmlFile>()) {
55 const auto shiftedHighlights = QmlHighlighting::Utils::shiftHighlights(
56 cached.highlights, lastValidItem->code(), doc.textDocument->toPlainText());
57 return QmlHighlighting::Utils::encodeSemanticTokens(shiftedHighlights, mode);
58 } else {
59 const QString code = doc.textDocument->toPlainText();
60 const HighlightsContainer highlights =
61 QmlHighlighting::Utils::regexFallbackHighlights(code, range);
62 if (highlights.isEmpty())
63 return {};
64 return QmlHighlighting::Utils::encodeSemanticTokens(highlights, mode);
65 }
66 } else {
67 HighlightsContainer highlights = QmlHighlighting::Utils::visitTokens(file, range);
68 if (highlights.isEmpty())
69 return {};
70 // Record the highlights for future diffs, only record full highlights
71 if (!range.has_value() )
72 cached.highlights = highlights;
73
74 return QmlHighlighting::Utils::encodeSemanticTokens(highlights, mode);
75 }
76}
77
78/*!
79\internal
80A wrapper class that handles the semantic tokens request for a whole file as described in
81https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#semanticTokens_fullRequest
82Sends a QLspSpecification::SemanticTokens data as response that is generated for the entire file.
83*/
88
90 QQmlBaseModule<SemanticTokensRequest>::RequestPointerArgument request)
91{
92 if (!request) {
93 qCWarning(semanticTokens) << "No semantic token request is available!";
94 return;
95 }
96
97 Responses::SemanticTokensResultType result;
98 ResponseScopeGuard guard(result, request->m_response);
99 const QByteArray uri = QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri);
100 const auto doc = m_codeModelManager->openDocumentByUrl(uri);
101 auto &cached = m_codeModelManager->registeredTokens(uri);
102 auto encoded = generateHighlights(cached, doc, std::nullopt, m_mode);
103
104 if (encoded.isEmpty()) {
105 return;
106 } else {
107 result = SemanticTokens{cached.resultId, std::move(encoded)};
108 }
109}
110
111void SemanticTokenFullHandler::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
112{
113 protocol->registerSemanticTokensRequestHandler(getRequestHandler());
114}
115
116/*!
117\internal
118A wrapper class that handles the semantic tokens delta request for a file
119https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#semanticTokens_deltaRequest
120Sends either SemanticTokens or SemanticTokensDelta data as response.
121This is generally requested when the text document is edited after receiving full highlighting data.
122*/
123SemanticTokenDeltaHandler::SemanticTokenDeltaHandler(QmlLsp::QQmlCodeModelManager *codeModelManager)
125{
126}
127
129 QQmlBaseModule<SemanticTokensDeltaRequest>::RequestPointerArgument request)
130{
131 if (!request) {
132 qCWarning(semanticTokens) << "No semantic token request is available!";
133 return;
134 }
135
136 Responses::SemanticTokensDeltaResultType result;
137 ResponseScopeGuard guard(result, request->m_response);
138 const QByteArray uri = QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri);
139 const auto doc = m_codeModelManager->openDocumentByUrl(uri);
140 auto &cached = m_codeModelManager->registeredTokens(uri);
141 if (cached.resultId != request->m_parameters.previousResultId) {
142 // The client is out of sync, send full tokens
143 cached.resultId = request->m_parameters.previousResultId;
144 const auto encoded = generateHighlights(cached, doc, std::nullopt, m_mode);
145 result = QLspSpecification::SemanticTokens{ cached.resultId, encoded };
146 } else {
147 const auto cachedHighlights = QmlHighlighting::Utils::encodeSemanticTokens(cached.highlights);
148 const auto encoded = generateHighlights(cached, doc, std::nullopt, m_mode);
149 result = QLspSpecification::SemanticTokensDelta{
150 cached.resultId,
151 QmlHighlighting::Utils::computeDiff(cachedHighlights, encoded)
152 };
153 }
154}
155
156void SemanticTokenDeltaHandler::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
157{
158 protocol->registerSemanticTokensDeltaRequestHandler(getRequestHandler());
159}
160
161/*!
162\internal
163A wrapper class that handles the semantic tokens range request for a file
164https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#semanticTokens_rangeRequest
165Sends a QLspSpecification::SemanticTokens data as response that is generated for a range of file.
166*/
167SemanticTokenRangeHandler::SemanticTokenRangeHandler(QmlLsp::QQmlCodeModelManager *codeModelManager)
169{
170}
171
173 QQmlBaseModule<SemanticTokensRangeRequest>::RequestPointerArgument request)
174{
175 if (!request) {
176 qCWarning(semanticTokens) << "No semantic token request is available!";
177 return;
178 }
179
180 Responses::SemanticTokensRangeResultType result;
181 ResponseScopeGuard guard(result, request->m_response);
182 const QByteArray uri = QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri);
183 const auto doc = m_codeModelManager->openDocumentByUrl(uri);
184 const QString code = doc.textDocument->toPlainText();
185 const auto range = request->m_parameters.range;
186 int startOffset =
187 int(QQmlLSUtils::textOffsetFrom(code, range.start.line, range.end.character));
188 int endOffset = int(QQmlLSUtils::textOffsetFrom(code, range.end.line, range.end.character));
189 auto &cached = m_codeModelManager->registeredTokens(uri);
190 auto encodedTokens = generateHighlights(
191 cached,
192 doc,
193 QmlHighlighting::HighlightsRange{ startOffset, endOffset },
194 m_mode);
195 if (encodedTokens.isEmpty()) {
196 } else {
197 result = SemanticTokens{ cached.resultId, std::move(encodedTokens) };
198 }
199}
200
201void SemanticTokenRangeHandler::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
202{
203 protocol->registerSemanticTokensRangeRequestHandler(getRequestHandler());
204}
205
206QQmlHighlightSupport::QQmlHighlightSupport(QmlLsp::QQmlCodeModelManager *codeModelManager)
208{
209}
210
211void QQmlHighlightSupport::registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol)
212{
213 m_full.registerHandlers(server, protocol);
214 m_delta.registerHandlers(server, protocol);
215 m_range.registerHandlers(server, protocol);
216
217 QObject::connect(server, &QLanguageServer::clientInitialized, this,
218 &QQmlHighlightSupport::clientInitialized, Qt::SingleShotConnection);
219
220 QObject::connect(
221 m_full.manager(), &QmlLsp::QQmlCodeModelManager::configurationChanged, this,
222 [protocol]() { protocol->requestWorkspaceSemanticTokensRefresh({ }, []() { }); });
223}
224
225void QQmlHighlightSupport::clientInitialized(QLanguageServer *server)
226{
227 if (auto clientInitOptions = server->clientInfo().initializationOptions) {
228 if ((*clientInitOptions)[u"qtCreatorHighlighting"_s].toBool(false)) {
229 const auto mode = HighlightingMode::QtCHighlighting;
230 m_delta.setHighlightingMode(mode);
231 m_full.setHighlightingMode(mode);
232 m_range.setHighlightingMode(mode);
233 }
234 }
235}
236
237void QQmlHighlightSupport::setupCapabilities(QLspSpecification::ServerCapabilities &caps)
238{
239 QLspSpecification::SemanticTokensOptions options;
240 options.range = true;
241 options.full = QJsonObject({ { u"delta"_s, true } });
242
243 options.legend.tokenTypes = extendedTokenTypesList();
244 options.legend.tokenModifiers = defaultTokenModifiersList();
245 caps.semanticTokensProvider = options;
246}
247
248QT_END_NAMESPACE
void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override
void setupCapabilities(QLspSpecification::ServerCapabilities &caps) override
QQmlHighlightSupport(QmlLsp::QQmlCodeModelManager *codeModel)
void process(QQmlBaseModule< SemanticTokensDeltaRequest >::RequestPointerArgument req) override
SemanticTokenDeltaHandler(QmlLsp::QQmlCodeModelManager *codeModel)
void registerHandlers(QLanguageServer *, QLanguageServerProtocol *) override
void process(QQmlBaseModule< SemanticTokensRequest >::RequestPointerArgument req) override
SemanticTokenFullHandler(QmlLsp::QQmlCodeModelManager *codeModel)
void registerHandlers(QLanguageServer *, QLanguageServerProtocol *) override
SemanticTokenRangeHandler(QmlLsp::QQmlCodeModelManager *codeModel)
void process(QQmlBaseModule< SemanticTokensRangeRequest >::RequestPointerArgument req) override
void registerHandlers(QLanguageServer *, QLanguageServerProtocol *) override
Combined button and popup list for selecting options.
QList< QByteArray > extendedTokenTypesList()
QList< QByteArray > defaultTokenModifiersList()
static QList< QByteArray > enumToByteArray()
static QList< unsigned > generateHighlights(QmlLsp::RegisteredSemanticTokens &cached, const QmlLsp::OpenDocument &doc, const std::optional< HighlightsRange > &range, HighlightingMode mode)