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
qqmlsemantictokens.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
5
6#include <QtQmlLS/private/qqmllsutils_p.h>
7#include <QtQmlDom/private/qqmldomscriptelements_p.h>
8#include <QtQmlDom/private/qqmldomfieldfilter_p.h>
9
10#include <QtLanguageServer/private/qlanguageserverprotocol_p.h>
11
13
14Q_LOGGING_CATEGORY(semanticTokens, "qt.languageserver.semanticTokens")
15
16using namespace QQmlJS::AST;
17using namespace QQmlJS::Dom;
18using namespace QLspSpecification;
19
21{
22 switch (region) {
23 case AsTokenRegion:
25 case DoKeywordRegion:
36 case OnTokenRegion:
44 case InOfTokenRegion:
48 case IfKeywordRegion:
50 return int(SemanticTokenTypes::Keyword);
54 return int(SemanticTokenTypes::Operator);
56 return int(SemanticTokenTypes::Type);
59 case IdNameRegion:
60 return int(SemanticTokenTypes::Variable);
61 case ImportUriRegion:
62 return int(SemanticTokenTypes::Namespace);
63 case IdTokenRegion:
64 case OnTargetRegion:
65 return int(SemanticTokenTypes::Property);
66 case VersionRegion:
67 case EnumValueRegion:
68 return int(SemanticTokenTypes::Number);
69 default:
70 return int(SemanticTokenTypes::Variable);
71 }
72 Q_UNREACHABLE_RETURN({});
73}
74
76{
77 QMultiMap<QString, QString> fieldFilterAdd{};
78 QMultiMap<QString, QString> fieldFilterRemove{
79 { QString(), QString::fromUtf16(Fields::propertyInfos) },
80 { QString(), QString::fromUtf16(Fields::fileLocationsTree) },
81 { QString(), QString::fromUtf16(Fields::importScope) },
82 { QString(), QString::fromUtf16(Fields::defaultPropertyName) },
83 { QString(), QString::fromUtf16(Fields::get) },
84 };
85 return FieldFilter{ fieldFilterAdd, fieldFilterRemove };
86}
87
89 const std::optional<HighlightsRange> &range)
90 : m_highlights(highlights), m_range(range)
91{
92}
93
95{
96 if (m_range.has_value()) {
97 const auto fLocs = FileLocations::treeOf(item);
98 if (!fLocs)
99 return true;
100 const auto regions = fLocs->info().regions;
102 m_range.value()))
103 return true;
104 }
105 switch (item.internalKind()) {
106 case DomType::Comment: {
107 highlightComment(item);
108 return true;
109 }
110 case DomType::Import: {
111 highlightImport(item);
112 return true;
113 }
114 case DomType::Binding: {
115 highlightBinding(item);
116 return true;
117 }
118 case DomType::Pragma: {
119 highlightPragma(item);
120 return true;
121 }
122 case DomType::EnumDecl: {
123 highlightEnumDecl(item);
124 return true;
125 }
126 case DomType::EnumItem: {
127 highlightEnumItem(item);
128 return true;
129 }
130 case DomType::QmlObject: {
131 highlightQmlObject(item);
132 return true;
133 }
134 case DomType::QmlComponent: {
135 highlightComponent(item);
136 return true;
137 }
138 case DomType::PropertyDefinition: {
139 highlightPropertyDefinition(item);
140 return true;
141 }
142 case DomType::MethodInfo: {
143 highlightMethod(item);
144 return true;
145 }
146 case DomType::ScriptLiteral: {
147 highlightScriptLiteral(item);
148 return true;
149 }
150 case DomType::ScriptIdentifierExpression: {
151 highlightIdentifier(item);
152 return true;
153 }
154 default:
155 if (item.ownerAs<ScriptExpression>())
156 highlightScriptExpressions(item);
157 return true;
158 }
159 Q_UNREACHABLE_RETURN(false);
160}
161
162void HighlightingVisitor::highlightComment(const DomItem &item)
163{
164 const auto comment = item.as<Comment>();
165 Q_ASSERT(comment);
167 comment->info().comment(), comment->info().sourceLocation());
168 for (const auto &loc : locs)
169 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Comment));
170}
171
172void HighlightingVisitor::highlightImport(const DomItem &item)
173{
174 const auto fLocs = FileLocations::treeOf(item);
175 if (!fLocs)
176 return;
177 const auto regions = fLocs->info().regions;
178 const auto import = item.as<Import>();
179 Q_ASSERT(import);
180 m_highlights.addHighlight(regions, ImportTokenRegion);
181 if (import->uri.isModule())
182 m_highlights.addHighlight(regions[ImportUriRegion], int(SemanticTokenTypes::Namespace));
183 else
184 m_highlights.addHighlight(regions[ImportUriRegion], int(SemanticTokenTypes::String));
185 if (regions.contains(VersionRegion))
186 m_highlights.addHighlight(regions, VersionRegion);
187 if (regions.contains(AsTokenRegion)) {
188 m_highlights.addHighlight(regions, AsTokenRegion);
189 m_highlights.addHighlight(regions[IdNameRegion], int(SemanticTokenTypes::Namespace));
190 }
191}
192
193void HighlightingVisitor::highlightBinding(const DomItem &item)
194{
195 const auto binding = item.as<Binding>();
196 Q_ASSERT(binding);
197 const auto fLocs = FileLocations::treeOf(item);
198 if (!fLocs) {
199 qCDebug(semanticTokens) << "Can't find the locations for" << item.internalKind();
200 return;
201 }
202 const auto regions = fLocs->info().regions;
203 // If dotted name, then defer it to be handled in ScriptIdentifierExpression
204 if (binding->name().contains("."_L1))
205 return;
206
207 if (binding->bindingType() != BindingType::Normal) {
208 m_highlights.addHighlight(regions, OnTokenRegion);
209 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Property));
210 return;
211 }
212
213 return highlightBySemanticAnalysis(item, regions[IdentifierRegion]);
214}
215
216void HighlightingVisitor::highlightPragma(const DomItem &item)
217{
218 const auto fLocs = FileLocations::treeOf(item);
219 if (!fLocs)
220 return;
221 const auto regions = fLocs->info().regions;
222 m_highlights.addHighlight(regions, PragmaKeywordRegion);
223 m_highlights.addHighlight(regions, IdentifierRegion);
224 const auto pragma = item.as<Pragma>();
225 for (auto i = 0; i < pragma->values.size(); ++i) {
226 DomItem value = item.field(Fields::values).index(i);
227 const auto valueRegions = FileLocations::treeOf(value)->info().regions;
228 m_highlights.addHighlight(valueRegions, PragmaValuesRegion);
229 }
230 return;
231}
232
233void HighlightingVisitor::highlightEnumDecl(const DomItem &item)
234{
235 const auto fLocs = FileLocations::treeOf(item);
236 if (!fLocs)
237 return;
238 const auto regions = fLocs->info().regions;
239 m_highlights.addHighlight(regions, EnumKeywordRegion);
240 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Enum));
241}
242
243void HighlightingVisitor::highlightEnumItem(const DomItem &item)
244{
245 const auto fLocs = FileLocations::treeOf(item);
246 if (!fLocs)
247 return;
248 const auto regions = fLocs->info().regions;
249 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::EnumMember));
250 if (regions.contains(EnumValueRegion))
251 m_highlights.addHighlight(regions, EnumValueRegion);
252}
253
254void HighlightingVisitor::highlightQmlObject(const DomItem &item)
255{
256 const auto qmlObject = item.as<QmlObject>();
257 Q_ASSERT(qmlObject);
258 const auto fLocs = FileLocations::treeOf(item);
259 if (!fLocs)
260 return;
261 const auto regions = fLocs->info().regions;
262 // Handle ids here
263 if (!qmlObject->idStr().isEmpty()) {
264 m_highlights.addHighlight(regions, IdTokenRegion);
265 m_highlights.addHighlight(regions, IdNameRegion);
266 }
267 // If dotted name, then defer it to be handled in ScriptIdentifierExpression
268 if (qmlObject->name().contains("."_L1))
269 return;
270
271 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Type));
272}
273
274void HighlightingVisitor::highlightComponent(const DomItem &item)
275{
276 const auto fLocs = FileLocations::treeOf(item);
277 if (!fLocs)
278 return;
279 const auto regions = fLocs->info().regions;
280 m_highlights.addHighlight(regions, ComponentKeywordRegion);
281 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Type));
282}
283
284void HighlightingVisitor::highlightPropertyDefinition(const DomItem &item)
285{
286 const auto propertyDef = item.as<PropertyDefinition>();
287 Q_ASSERT(propertyDef);
288 const auto fLocs = FileLocations::treeOf(item);
289 if (!fLocs)
290 return;
291 const auto regions = fLocs->info().regions;
292 int modifier = 0;
293 HighlightingUtils::addModifier(SemanticTokenModifiers::Definition, &modifier);
294 if (propertyDef->isDefaultMember) {
295 HighlightingUtils::addModifier(SemanticTokenModifiers::DefaultLibrary,
296 &modifier);
297 m_highlights.addHighlight(regions[DefaultKeywordRegion],
298 int(SemanticTokenTypes::Keyword));
299 }
300 if (propertyDef->isRequired) {
301 HighlightingUtils::addModifier(SemanticTokenModifiers::Abstract, &modifier);
302 m_highlights.addHighlight(regions[RequiredKeywordRegion],
303 int(SemanticTokenTypes::Keyword));
304 }
305 if (propertyDef->isReadonly) {
306 HighlightingUtils::addModifier(SemanticTokenModifiers::Readonly, &modifier);
307 m_highlights.addHighlight(regions[ReadonlyKeywordRegion],
308 int(SemanticTokenTypes::Keyword));
309 }
310 m_highlights.addHighlight(regions, PropertyKeywordRegion);
311 if (propertyDef->isAlias())
312 m_highlights.addHighlight(regions[TypeIdentifierRegion],
313 int(SemanticTokenTypes::Keyword));
314 else
315 m_highlights.addHighlight(regions, TypeIdentifierRegion);
316 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Property),
317 modifier);
318}
319
320void HighlightingVisitor::highlightMethod(const DomItem &item)
321{
322 const auto method = item.as<MethodInfo>();
324 const auto fLocs = FileLocations::treeOf(item);
325 if (!fLocs)
326 return;
327 const auto regions = fLocs->info().regions;
328 switch (method->methodType) {
329 case MethodInfo::Signal: {
330 m_highlights.addHighlight(regions, SignalKeywordRegion);
331 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Method));
332 break;
333 }
334 case MethodInfo::Method: {
335 m_highlights.addHighlight(regions, FunctionKeywordRegion);
336 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Method));
337 m_highlights.addHighlight(regions[TypeIdentifierRegion], int(SemanticTokenTypes::Type));
338 break;
339 }
340 default:
341 Q_UNREACHABLE();
342 }
343
344 for (auto i = 0; i < method->parameters.size(); ++i) {
345 DomItem parameter = item.field(Fields::parameters).index(i);
346 const auto paramRegions = FileLocations::treeOf(parameter)->info().regions;
347 m_highlights.addHighlight(paramRegions[IdentifierRegion],
348 int(SemanticTokenTypes::Parameter));
349 m_highlights.addHighlight(paramRegions[TypeIdentifierRegion], int(SemanticTokenTypes::Type));
350 }
351 return;
352}
353
354void HighlightingVisitor::highlightScriptLiteral(const DomItem &item)
355{
356 const auto literal = item.as<ScriptElements::Literal>();
357 Q_ASSERT(literal);
358 const auto fLocs = FileLocations::treeOf(item);
359 if (!fLocs)
360 return;
361 const auto regions = fLocs->info().regions;
362 if (std::holds_alternative<QString>(literal->literalValue())) {
363 const QString value = u'\"' + std::get<QString>(literal->literalValue()) + u'\"';
365 value, regions[MainRegion]);
366 for (const auto &loc : locs)
367 m_highlights.addHighlight(loc, int(SemanticTokenTypes::String));
368 } else if (std::holds_alternative<double>(literal->literalValue()))
369 m_highlights.addHighlight(regions[MainRegion], int(SemanticTokenTypes::Number));
370 else if (std::holds_alternative<bool>(literal->literalValue()))
371 m_highlights.addHighlight(regions[MainRegion], int(SemanticTokenTypes::Keyword));
372 else if (std::holds_alternative<std::nullptr_t>(literal->literalValue()))
373 m_highlights.addHighlight(regions[MainRegion], int(SemanticTokenTypes::Keyword));
374 else
375 qCWarning(semanticTokens) << "Invalid literal variant";
376}
377
378void HighlightingVisitor::highlightIdentifier(const DomItem &item)
379{
380 using namespace QLspSpecification;
381 const auto id = item.as<ScriptElements::IdentifierExpression>();
382 Q_ASSERT(id);
383 const auto loc = id->mainRegionLocation();
384 // Many of the scriptIdentifiers expressions are already handled by
385 // other cases. In those cases, if the location offset is already in the list
386 // we don't need to perform expensive resolveExpressionType operation.
387 if (m_highlights.highlights().contains(loc.offset))
388 return;
389
390 highlightBySemanticAnalysis(item, loc);
391}
392
393void HighlightingVisitor::highlightBySemanticAnalysis(const DomItem &item, QQmlJS::SourceLocation loc)
394{
395 const auto expression = QQmlLSUtils::resolveExpressionType(
397
398 if (!expression) {
399 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Variable));
400 return;
401 }
402 switch (expression->type) {
404 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Type));
405 return;
407 SemanticTokenTypes tokenType = SemanticTokenTypes::Variable;
408 int modifier = 0;
409 if (const auto jsIdentifier
410 = expression->semanticScope->jsIdentifier(expression->name.value())) {
411 switch (jsIdentifier.value().kind) {
413 tokenType = SemanticTokenTypes::Parameter;
414 break;
418 default:
419 tokenType = SemanticTokenTypes::Variable;
420 break;
421 }
422 if (jsIdentifier.value().isConst) {
423 HighlightingUtils::addModifier(SemanticTokenModifiers::Readonly,
424 &modifier);
425 }
426 }
427 m_highlights.addHighlight(loc, int(tokenType), modifier);
428 return;
429 }
431 if (const auto scope = expression->semanticScope) {
432 const auto property = scope->property(expression->name.value());
433 int modifier = 0;
434 if (!property.isWritable()) {
435 HighlightingUtils::addModifier(SemanticTokenModifiers::Readonly,
436 &modifier);
437 }
438 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Property), modifier);
439 }
440 return;
441 }
443 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
444 return;
446 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
447 return;
449 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
450 return;
452 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
453 return;
455 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Method));
456 return;
458 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Variable));
459 return;
461 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Type));
462 return;
464 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Enum));
465 return;
467 m_highlights.addHighlight(loc, int(SemanticTokenTypes::EnumMember));
468 return;
470 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Type));
471 return;
473 m_highlights.addHighlight(loc, int(SemanticTokenTypes::Property));
474 return;
475 default:
476 qCWarning(semanticTokens)
477 << QString::fromLatin1("Semantic token for %1 has not been implemented yet")
478 .arg(int(expression->type));
479 }
480 Q_UNREACHABLE_RETURN();
481}
482
483void HighlightingVisitor::highlightScriptExpressions(const DomItem &item)
484{
485 const auto fLocs = FileLocations::treeOf(item);
486 if (!fLocs)
487 return;
488 const auto regions = fLocs->info().regions;
489 switch (item.internalKind()) {
490 case DomType::ScriptLiteral:
491 highlightScriptLiteral(item);
492 return;
493 case DomType::ScriptForStatement:
494 m_highlights.addHighlight(regions, ForKeywordRegion);
495 m_highlights.addHighlight(regions[TypeIdentifierRegion],
496 int(SemanticTokenTypes::Keyword));
497 return;
498
499 case DomType::ScriptVariableDeclaration: {
500 m_highlights.addHighlight(regions[TypeIdentifierRegion],
501 int(SemanticTokenTypes::Keyword));
502 return;
503 }
504 case DomType::ScriptReturnStatement:
505 m_highlights.addHighlight(regions, ReturnKeywordRegion);
506 return;
507 case DomType::ScriptCaseClause:
508 m_highlights.addHighlight(regions, CaseKeywordRegion);
509 return;
510 case DomType::ScriptDefaultClause:
511 m_highlights.addHighlight(regions, DefaultKeywordRegion);
512 return;
513 case DomType::ScriptSwitchStatement:
514 m_highlights.addHighlight(regions, SwitchKeywordRegion);
515 return;
516 case DomType::ScriptWhileStatement:
517 m_highlights.addHighlight(regions, WhileKeywordRegion);
518 return;
519 case DomType::ScriptDoWhileStatement:
520 m_highlights.addHighlight(regions, DoKeywordRegion);
521 m_highlights.addHighlight(regions, WhileKeywordRegion);
522 return;
523 case DomType::ScriptTryCatchStatement:
524 m_highlights.addHighlight(regions, TryKeywordRegion);
525 m_highlights.addHighlight(regions, CatchKeywordRegion);
526 m_highlights.addHighlight(regions, FinallyKeywordRegion);
527 return;
528 case DomType::ScriptForEachStatement:
529 m_highlights.addHighlight(regions[TypeIdentifierRegion],
530 int(SemanticTokenTypes::Keyword));
531 m_highlights.addHighlight(regions, ForKeywordRegion);
532 m_highlights.addHighlight(regions, InOfTokenRegion);
533 return;
534 case DomType::ScriptThrowStatement:
535 m_highlights.addHighlight(regions, ThrowKeywordRegion);
536 return;
537 case DomType::ScriptBreakStatement:
538 m_highlights.addHighlight(regions, BreakKeywordRegion);
539 return;
540 case DomType::ScriptContinueStatement:
541 m_highlights.addHighlight(regions, ContinueKeywordRegion);
542 return;
543 case DomType::ScriptIfStatement:
544 m_highlights.addHighlight(regions, IfKeywordRegion);
545 m_highlights.addHighlight(regions, ElseKeywordRegion);
546 return;
547 case DomType::ScriptLabelledStatement:
548 m_highlights.addHighlight(regions, IdentifierRegion);
549 return;
550 case DomType::ScriptConditionalExpression:
551 m_highlights.addHighlight(regions, QuestionMarkTokenRegion);
552 m_highlights.addHighlight(regions, ColonTokenRegion);
553 return;
554 case DomType::ScriptUnaryExpression:
555 case DomType::ScriptPostExpression:
556 m_highlights.addHighlight(regions, OperatorTokenRegion);
557 return;
558 case DomType::ScriptType:
559 m_highlights.addHighlight(regions[IdentifierRegion], int(SemanticTokenTypes::Type));
560 m_highlights.addHighlight(regions[TypeIdentifierRegion], int(SemanticTokenTypes::Type));
561 return;
562 default:
563 qCDebug(semanticTokens)
564 << "Script Expressions with kind" << item.internalKind() << "not implemented";
565 return;
566 }
567 Q_UNREACHABLE_RETURN();
568}
569
579QList<QQmlJS::SourceLocation>
581 const QQmlJS::SourceLocation &locationInDocument)
582{
583 auto lineBreakLength = qsizetype(std::char_traits<char>::length("\n"));
584 const auto lineLengths = [&lineBreakLength](QStringView literal) {
585 std::vector<qsizetype> lineLengths;
586 qsizetype startIndex = 0;
587 qsizetype pos = literal.indexOf(u'\n');
588 while (pos != -1) {
589 // TODO: QTBUG-106813
590 // Since a document could be opened in normalized form
591 // we can't use platform dependent newline handling here.
592 // Thus, we check manually if the literal contains \r so that we split
593 // the literal at the correct offset.
594 if (pos - 1 > 0 && literal[pos - 1] == u'\r') {
595 // Handle Windows line endings
596 lineBreakLength = qsizetype(std::char_traits<char>::length("\r\n"));
597 // Move pos to the index of '\r'
598 pos = pos - 1;
599 }
600 lineLengths.push_back(pos - startIndex);
601 // Advance the lookup index, so it won't find the same index.
602 startIndex = pos + lineBreakLength;
603 pos = literal.indexOf('\n'_L1, startIndex);
604 }
605 // Push the last line
606 if (startIndex < literal.length()) {
607 lineLengths.push_back(literal.length() - startIndex);
608 }
609 return lineLengths;
610 };
611
612 QList<QQmlJS::SourceLocation> result;
613 // First token location should start from the "stringLiteral"'s
614 // location in the qml document.
615 QQmlJS::SourceLocation lineLoc = locationInDocument;
616 for (const auto lineLength : lineLengths(stringLiteral)) {
617 lineLoc.length = lineLength;
618 result.push_back(lineLoc);
619
620 // update for the next line
621 lineLoc.offset += lineLoc.length + lineBreakLength;
622 ++lineLoc.startLine;
623 lineLoc.startColumn = 1;
624 }
625 return result;
626}
627
629{
630 QList<int> result;
631 const auto highlightingTokens = highlights.highlights();
632 constexpr auto tokenEncodingLength = 5;
633 result.reserve(tokenEncodingLength * highlightingTokens.size());
634
635 int prevLine = 0;
636 int prevColumn = 0;
637
638 std::for_each(highlightingTokens.constBegin(), highlightingTokens.constEnd(), [&](const auto &token) {
639 Q_ASSERT(token.startLine >= prevLine);
640 if (token.startLine != prevLine)
641 prevColumn = 0;
642 result.emplace_back(token.startLine - prevLine);
643 result.emplace_back(token.startColumn - prevColumn);
644 result.emplace_back(token.length);
645 result.emplace_back(token.tokenType);
646 result.emplace_back(token.tokenModifier);
647 prevLine = token.startLine;
648 prevColumn = token.startColumn;
649 });
650
651 return result;
652}
653
664void HighlightingUtils::addModifier(SemanticTokenModifiers modifier, int *baseModifier)
665{
666 if (!baseModifier)
667 return;
668 *baseModifier |= (1 << int(modifier));
669}
670
676 const HighlightsRange &r)
677{
678 int startOffsetItem = int(loc.offset);
679 int endOffsetItem = startOffsetItem + int(loc.length);
680 return (startOffsetItem <= r.endOffset) && (r.startOffset <= endOffsetItem);
681}
682
683/*
684\internal
685Increments the resultID by one.
686*/
688{
689 int length = resultID.length();
690 for (int i = length - 1; i >= 0; --i) {
691 if (resultID[i] == '9') {
692 resultID[i] = '0';
693 } else {
694 resultID[i] = resultID[i] + 1;
695 return;
696 }
697 }
698 resultID.prepend('1');
699}
700
701/*
702\internal
703A utility method that computes the difference of two list. The first argument is the encoded token data
704of the file before edited. The second argument is the encoded token data after the file is edited. Returns
705a list of SemanticTokensEdit as expected by the protocol.
706*/
707QList<SemanticTokensEdit> HighlightingUtils::computeDiff(const QList<int> &oldData, const QList<int> &newData)
708{
709 // Find the iterators pointing the first mismatch, from the start
710 const auto [oldStart, newStart] =
711 std::mismatch(oldData.cbegin(), oldData.cend(), newData.cbegin(), newData.cend());
712
713 // Find the iterators pointing the first mismatch, from the end
714 // but the iterators shouldn't pass over the start iterators found above.
715 const auto [r1, r2] = std::mismatch(oldData.crbegin(), std::make_reverse_iterator(oldStart),
716 newData.crbegin(), std::make_reverse_iterator(newStart));
717 const auto oldEnd = r1.base();
718 const auto newEnd = r2.base();
719
720 // no change
721 if (oldStart == oldEnd && newStart == newEnd)
722 return {};
723
724 SemanticTokensEdit edit;
725 edit.start = int(std::distance(newData.cbegin(), newStart));
726 edit.deleteCount = int(std::distance(oldStart, oldEnd));
727
728 if (newStart >= newData.cbegin() && newEnd <= newData.cend() && newStart < newEnd)
729 edit.data.emplace(newStart, newEnd);
730
731 return { std::move(edit) };
732}
733
734
735void Highlights::addHighlight(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier)
736{
737 if (!loc.isValid()) {
738 qCDebug(semanticTokens) << "Invalid locations: Cannot add highlight to token";
739 return;
740 }
741
742 if (!m_highlights.contains(loc.offset))
743 m_highlights.insert(loc.offset, QT_PREPEND_NAMESPACE(Token)(loc, tokenType, tokenModifier));
744}
745
746void Highlights::addHighlight(const QMap<FileLocationRegion, QQmlJS::SourceLocation> &regions,
747 FileLocationRegion region, int modifier)
748{
749 if (!regions.contains(region)) {
750 qCDebug(semanticTokens) << "Invalid region: Cannot add highlight to token";
751 return;
752 }
753
754 const auto loc = regions.value(region);
755 return addHighlight(loc, tokenTypeFromRegion(region), modifier);
756}
757
759 const std::optional<HighlightsRange> &range)
760{
761 using namespace QQmlJS::Dom;
762 HighlightingVisitor highlightDomElements(*this, range);
763 // In QmlFile level, visitTree visits even FileLocations tree which takes quite a time to
764 // finish. HighlightingFilter is added to prevent unnecessary visits.
765 item.visitTree(Path(), highlightDomElements, VisitOption::Default, emptyChildrenVisitor,
767
769}
770
HighlightingVisitor(Highlights &highlights, const std::optional< HighlightsRange > &range)
bool operator()(QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &item, bool)
void addHighlight(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier=0)
QList< int > collectTokens(const QQmlJS::Dom::DomItem &item, const std::optional< HighlightsRange > &range)
HighlightsContainer & highlights()
\inmodule QtCore
Definition qbytearray.h:57
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool contains(const Key &key) const
Definition qmap.h:341
Represents a comment.
static FileLocations::Tree treeOf(const DomItem &)
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5873
static QString fromUtf16(const char16_t *, qsizetype size=-1)
Definition qstring.cpp:6047
Token token
Definition keywords.cpp:444
bool emptyChildrenVisitor(Path, const DomItem &, bool)
std::optional< ExpressionType > resolveExpressionType(const QQmlJS::Dom::DomItem &item, ResolveOptions options)
@ EnumeratorValueIdentifier
@ GroupedPropertyIdentifier
@ PropertyChangedHandlerIdentifier
@ PropertyChangedSignalIdentifier
@ SignalHandlerIdentifier
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLboolean r
[2]
GLenum GLuint GLenum GLsizei length
GLsizei range
GLuint64EXT * result
[6]
static int tokenTypeFromRegion(QQmlJS::Dom::FileLocationRegion region)
static FieldFilter highlightingFilter()
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
ptrdiff_t qsizetype
Definition qtypes.h:165
const char property[13]
Definition qwizard.cpp:101
QRect r1(100, 200, 11, 16)
[0]
QRect r2(QPoint(100, 200), QSize(11, 16))
QGraphicsItem * item
static QList< QQmlJS::SourceLocation > sourceLocationsFromMultiLineToken(QStringView code, const QQmlJS::SourceLocation &tokenLocation)
Returns multiple source locations for a given raw comment.
static QList< int > encodeSemanticTokens(Highlights &highlights)
static void updateResultID(QByteArray &resultID)
static QList< QLspSpecification::SemanticTokensEdit > computeDiff(const QList< int > &, const QList< int > &)
static bool rangeOverlapsWithSourceLocation(const QQmlJS::SourceLocation &loc, const HighlightsRange &r)
static void addModifier(QLspSpecification::SemanticTokenModifiers modifier, int *baseModifier)