27 switch (highlightKind) {
92 switch (highlightKind) {
152
153
154
155
156
157
162 static QSet<QString> noConstructorObjects = { u"Math"_s, u"JSON"_s, u"Atomics"_s, u"Reflect"_s,
166 if (noConstructorObjects.contains(name))
169 if (item.directParent().internalKind() == DomType::ScriptNewMemberExpression) {
172 if (DomItem containingCallExpression = item.filterUp(
173 [](DomType k,
const DomItem &) {
return k == DomType::ScriptCallExpression; },
174 FilterUpOptions::ReturnOuter)) {
177 const auto callee = containingCallExpression.field(Fields::callee);
178 if (callee.internalKind() == DomType::ScriptBinaryExpression) {
179 const auto right = callee.field(Fields::right);
180 if (right.internalKind() == DomType::ScriptIdentifierExpression
181 && right.field(Fields::identifier).value().toString() == name) {
195 using namespace QLspSpecification;
196 using namespace Utils;
200 addModifier(SemanticTokenModifiers::Definition, &modifier);
203 addModifier(SemanticTokenModifiers::DefaultLibrary, &modifier);
206 addModifier(SemanticTokenModifiers::Static, &modifier);
209 addModifier(SemanticTokenModifiers::Static, &modifier);
212 addModifier(SemanticTokenModifiers::Static, &modifier);
215 addModifier(SemanticTokenModifiers::Abstract, &modifier);
218 addModifier(SemanticTokenModifiers::Readonly, &modifier);
225 QMultiMap<QString, QString> fieldFilterAdd{};
226 QMultiMap<QString, QString> fieldFilterRemove{
227 { QString(), Fields::propertyInfos.toString() },
228 { QString(), Fields::fileLocationsTree.toString() },
229 { QString(), Fields::importScope.toString() },
230 { QString(), Fields::defaultPropertyName.toString() },
231 { QString(), Fields::get.toString() },
233 return FieldFilter{ fieldFilterAdd, fieldFilterRemove };
238 QmlHighlightModifiers modifiers)
244 const std::optional<HighlightsRange> &range)
249 [
this](
const Path &path,
const DomItem &item,
bool b) {
250 return this->visitor(path, item, b);
252 VisitOption::Default | VisitOption::NoPath, emptyChildrenVisitor, emptyChildrenVisitor,
253 highlightingFilter());
258 if (m_range.has_value()) {
259 const auto fLocs = FileLocations::treeOf(item);
262 const auto regions = fLocs->info().regions;
263 if (!Utils::rangeOverlapsWithSourceLocation(regions[MainRegion],
267 switch (item.internalKind()) {
268 case DomType::Comment: {
269 highlightComment(item);
272 case DomType::Import: {
273 highlightImport(item);
276 case DomType::Binding: {
277 highlightBinding(item);
280 case DomType::Pragma: {
281 highlightPragma(item);
284 case DomType::EnumDecl: {
285 highlightEnumDecl(item);
288 case DomType::EnumItem: {
289 highlightEnumItem(item);
292 case DomType::QmlObject: {
293 highlightQmlObject(item);
296 case DomType::QmlComponent: {
297 highlightComponent(item);
300 case DomType::PropertyDefinition: {
301 highlightPropertyDefinition(item);
304 case DomType::MethodInfo: {
305 highlightMethod(item);
308 case DomType::ScriptLiteral: {
309 highlightScriptLiteral(item);
312 case DomType::ScriptCallExpression: {
313 highlightCallExpression(item);
316 case DomType::ScriptIdentifierExpression: {
317 highlightIdentifier(item);
321 if (item.ownerAs<ScriptExpression>())
322 highlightScriptExpressions(item);
325 Q_UNREACHABLE_RETURN(
false);
330 const auto comment = item.as<Comment>();
332 const auto locs = Utils::sourceLocationsFromMultiLineToken(
333 comment->info().comment(), comment->info().sourceLocation());
334 for (
const auto &loc : locs)
335 addHighlight(loc, QmlHighlightKind::Comment);
340 const auto fLocs = FileLocations::treeOf(item);
343 const auto regions = fLocs->info().regions;
344 const auto import = item.as<Import>();
346 addHighlight(regions[ImportTokenRegion], QmlHighlightKind::QmlKeyword);
347 if (import->uri.isModule())
348 addHighlight(regions[ImportUriRegion], QmlHighlightKind::QmlImportId);
350 addHighlight(regions[ImportUriRegion], QmlHighlightKind::String);
351 if (regions.contains(VersionRegion))
352 addHighlight(regions[VersionRegion], QmlHighlightKind::Number);
353 if (regions.contains(AsTokenRegion)) {
354 addHighlight(regions[AsTokenRegion], QmlHighlightKind::QmlKeyword);
355 addHighlight(regions[IdNameRegion], QmlHighlightKind::QmlNamespace);
361 const auto binding = item.as<Binding>();
363 const auto fLocs = FileLocations::treeOf(item);
365 qCDebug(semanticTokens) <<
"Can't find the locations for" << item.internalKind();
368 const auto regions = fLocs->info().regions;
370 if (binding->name().contains(
"."_L1))
373 if (binding->bindingType() != BindingType::Normal) {
374 addHighlight(regions[OnTokenRegion], QmlHighlightKind::QmlKeyword);
375 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlProperty);
379 return addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlProperty);
384 const auto fLocs = FileLocations::treeOf(item);
387 const auto regions = fLocs->info().regions;
388 addHighlight(regions[PragmaKeywordRegion], QmlHighlightKind::QmlKeyword);
389 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlPragmaName );
390 const auto pragma = item.as<Pragma>();
391 for (
auto i = 0; i < pragma->values.size(); ++i) {
392 DomItem value = item.field(Fields::values).index(i);
393 const auto valueRegions = FileLocations::treeOf(value)->info().regions;
394 addHighlight(valueRegions[PragmaValuesRegion], QmlHighlightKind::QmlPragmaValue);
401 const auto fLocs = FileLocations::treeOf(item);
404 const auto regions = fLocs->info().regions;
405 addHighlight(regions[EnumKeywordRegion], QmlHighlightKind::QmlKeyword);
406 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlEnumName);
411 const auto fLocs = FileLocations::treeOf(item);
414 const auto regions = fLocs->info().regions;
415 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlEnumMember);
416 if (regions.contains(EnumValueRegion))
417 addHighlight(regions[EnumValueRegion], QmlHighlightKind::Number);
422 const auto qmlObject = item.as<QmlObject>();
424 const auto fLocs = FileLocations::treeOf(item);
427 const auto regions = fLocs->info().regions;
429 if (!qmlObject->idStr().isEmpty()) {
430 addHighlight(regions[IdTokenRegion], QmlHighlightKind::QmlProperty);
431 addHighlight(regions[IdNameRegion], QmlHighlightKind::QmlLocalId);
434 if (qmlObject->name().contains(
"."_L1))
437 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlType);
442 const auto fLocs = FileLocations::treeOf(item);
445 const auto regions = fLocs->info().regions;
446 const auto componentKeywordIt = regions.constFind(ComponentKeywordRegion);
447 if (componentKeywordIt == regions.constEnd())
449 addHighlight(*componentKeywordIt, QmlHighlightKind::QmlKeyword);
450 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlType);
455 const auto propertyDef = item.as<PropertyDefinition>();
456 Q_ASSERT(propertyDef);
457 const auto fLocs = FileLocations::treeOf(item);
460 const auto regions = fLocs->info().regions;
462 if (propertyDef->isDefaultMember) {
464 addHighlight(regions[DefaultKeywordRegion], QmlHighlightKind::QmlKeyword);
466 if (propertyDef->isVirtual) {
468 addHighlight(regions[VirtualKeywordRegion], QmlHighlightKind::QmlKeyword);
470 if (propertyDef->isOverride) {
472 addHighlight(regions[OverrideKeywordRegion], QmlHighlightKind::QmlKeyword);
474 if (propertyDef->isFinal) {
476 addHighlight(regions[FinalKeywordRegion], QmlHighlightKind::QmlKeyword);
478 if (propertyDef->isRequired) {
480 addHighlight(regions[RequiredKeywordRegion], QmlHighlightKind::QmlKeyword);
482 if (propertyDef->isReadonly) {
484 addHighlight(regions[ReadonlyKeywordRegion], QmlHighlightKind::QmlKeyword);
486 addHighlight(regions[PropertyKeywordRegion], QmlHighlightKind::QmlKeyword);
487 if (propertyDef->isAlias())
488 addHighlight(regions[TypeIdentifierRegion], QmlHighlightKind::QmlKeyword);
490 addHighlight(regions[TypeIdentifierRegion], QmlHighlightKind::QmlType);
492 addHighlight(regions[TypeModifierRegion], QmlHighlightKind::QmlTypeModifier);
493 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlProperty,
499 const auto method = item.as<MethodInfo>();
501 const auto fLocs = FileLocations::treeOf(item);
504 const auto regions = fLocs->info().regions;
505 switch (method->methodType) {
506 case MethodInfo::Signal: {
507 addHighlight(regions[SignalKeywordRegion], QmlHighlightKind::QmlKeyword);
508 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlMethod);
511 case MethodInfo::Method: {
512 addHighlight(regions[FunctionKeywordRegion], QmlHighlightKind::QmlKeyword);
513 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlMethod);
514 addHighlight(regions[TypeIdentifierRegion], QmlHighlightKind::QmlType);
521 for (
auto i = 0; i < method->parameters.size(); ++i) {
522 DomItem parameter = item.field(Fields::parameters).index(i);
523 const auto paramRegions = FileLocations::treeOf(parameter)->info().regions;
524 addHighlight(paramRegions[IdentifierRegion],
525 QmlHighlightKind::QmlMethodParameter);
526 addHighlight(paramRegions[TypeIdentifierRegion], QmlHighlightKind::QmlType);
533 const auto literal = item.as<ScriptElements::Literal>();
535 const auto fLocs = FileLocations::treeOf(item);
538 const auto regions = fLocs->info().regions;
539 if (std::holds_alternative<QString>(literal->literalValue())) {
540 const auto file = item.containingFile().as<QmlFile>();
543 const auto &code = file->engine()->code();
544 const auto offset = regions[MainRegion].offset;
545 const auto length = regions[MainRegion].length;
546 const QStringView literalCode = QStringView{code}.mid(offset, length);
547 const auto &locs = Utils::sourceLocationsFromMultiLineToken(
548 literalCode, regions[MainRegion]);
549 for (
const auto &loc : locs)
550 addHighlight(loc, QmlHighlightKind::String);
551 }
else if (std::holds_alternative<
double>(literal->literalValue()))
552 addHighlight(regions[MainRegion], QmlHighlightKind::Number);
553 else if (std::holds_alternative<
bool>(literal->literalValue()))
554 addHighlight(regions[MainRegion], QmlHighlightKind::QmlKeyword);
555 else if (std::holds_alternative<std::nullptr_t>(literal->literalValue()))
556 addHighlight(regions[MainRegion], QmlHighlightKind::QmlKeyword);
558 qCWarning(semanticTokens) <<
"Invalid literal variant";
563 using namespace QLspSpecification;
564 const auto id = item.as<ScriptElements::IdentifierExpression>();
566 const auto loc = id->mainRegionLocation();
570 if (m_highlights.contains(loc.offset))
576 if (QQmlLSUtils::isFieldMemberAccess(item))
577 highlightFieldMemberAccess(item, loc);
579 highlightBySemanticAnalysis(item, loc);
584 const auto highlight = [
this](
const DomItem &item) {
585 if (item.internalKind() == DomType::ScriptIdentifierExpression) {
586 const auto id = item.as<ScriptElements::IdentifierExpression>();
588 const auto loc = id->mainRegionLocation();
589 addHighlight(loc, QmlHighlightKind::QmlMethod);
593 if (item.internalKind() == DomType::ScriptCallExpression) {
595 const auto callee = item.field(Fields::callee);
596 if (callee.internalKind() == DomType::ScriptIdentifierExpression) {
599 }
else if (callee.internalKind() == DomType::ScriptBinaryExpression) {
601 const auto right = callee.field(Fields::right);
602 if (right.internalKind() == DomType::ScriptIdentifierExpression)
610 QQmlJS::SourceLocation loc)
614 const auto name = item.field(Fields::identifier).value().toString();
615 if (!name.isEmpty() && name.at(0).category() == QChar::Letter_Uppercase) {
618 return highlightBySemanticAnalysis(item, loc);
621 const auto expression =
622 QQmlLSUtils::resolveExpressionType(item, QQmlLSUtils::ResolveOptions::ResolveOwnerType);
625 addHighlight(loc, QmlHighlightKind::Field);
629 if (expression->type == QQmlLSUtils::MethodIdentifier
630 || expression->type == QQmlLSUtils::LambdaMethodIdentifier) {
631 addHighlight(loc, QmlHighlightKind::QmlMethod);
634 return addHighlight(loc, QmlHighlightKind::Field);
638void HighlightingVisitor::highlightBySemanticAnalysis(
const DomItem &item, QQmlJS::SourceLocation loc)
640 const auto expression = QQmlLSUtils::resolveExpressionType(
641 item, QQmlLSUtils::ResolveOptions::ResolveOwnerType);
644 addHighlight(loc, QmlHighlightKind::Unknown);
647 switch (expression->type) {
648 case QQmlLSUtils::QmlComponentIdentifier:
649 addHighlight(loc, QmlHighlightKind::QmlType);
651 case QQmlLSUtils::JavaScriptIdentifier: {
654 if (
const auto scope = expression->semanticScope) {
655 if (
const auto jsIdentifier = scope->jsIdentifier(*expression->name)) {
656 if (jsIdentifier->kind == QQmlJSScope::JavaScriptIdentifier::Parameter)
658 if (jsIdentifier->isConst) {
661 addHighlight(loc, tokenType, modifier);
665 if (
const auto name = expression->name) {
666 if (
const auto highlightKind = resolveJsGlobalObjectKind(item, *name))
667 return addHighlight(loc, *highlightKind);
671 case QQmlLSUtils::PropertyIdentifier: {
672 if (
const auto scope = expression->semanticScope) {
674 if (scope == item.qmlObject().semanticScope()) {
676 }
else if (scope == item.rootQmlObject(GoTo::MostLikely).semanticScope()) {
681 const auto property = scope->property(expression->name.value());
683 if (!property.isWritable())
685 addHighlight(loc, tokenType, modifier);
689 case QQmlLSUtils::PropertyChangedSignalIdentifier:
690 addHighlight(loc, QmlHighlightKind::QmlSignal);
692 case QQmlLSUtils::PropertyChangedHandlerIdentifier:
693 addHighlight(loc, QmlHighlightKind::QmlSignalHandler);
695 case QQmlLSUtils::SignalIdentifier:
696 addHighlight(loc, QmlHighlightKind::QmlSignal);
698 case QQmlLSUtils::SignalHandlerIdentifier:
699 addHighlight(loc, QmlHighlightKind::QmlSignalHandler);
701 case QQmlLSUtils::MethodIdentifier:
702 addHighlight(loc, QmlHighlightKind::QmlMethod);
704 case QQmlLSUtils::QmlObjectIdIdentifier: {
705 if (!expression->semanticScope) {
708 addHighlight(loc, QmlHighlightKind::Unknown);
711 const auto qmlfile = item.fileObject().as<QmlFile>();
713 addHighlight(loc, QmlHighlightKind::Unknown);
716 const auto resolver = qmlfile->typeResolver();
718 addHighlight(loc, QmlHighlightKind::Unknown);
721 const auto &objects = resolver->objectsById();
722 if (expression->name.has_value()) {
723 const auto &name = expression->name.value();
724 const auto boundName =
725 objects.id(expression->semanticScope, item.qmlObject().semanticScope());
726 if (!boundName.isEmpty() && name == boundName) {
728 addHighlight(loc, QmlHighlightKind::QmlLocalId);
731 addHighlight(loc, QmlHighlightKind::QmlExternalId);
735 addHighlight(loc, QmlHighlightKind::QmlExternalId);
739 case QQmlLSUtils::SingletonIdentifier:
740 addHighlight(loc, QmlHighlightKind::QmlType);
742 case QQmlLSUtils::EnumeratorIdentifier:
743 addHighlight(loc, QmlHighlightKind::QmlEnumName);
745 case QQmlLSUtils::EnumeratorValueIdentifier:
746 addHighlight(loc, QmlHighlightKind::QmlEnumMember);
748 case QQmlLSUtils::AttachedTypeIdentifier:
749 case QQmlLSUtils::AttachedTypeIdentifierInBindingTarget:
750 addHighlight(loc, QmlHighlightKind::QmlType);
752 case QQmlLSUtils::GroupedPropertyIdentifier:
753 addHighlight(loc, QmlHighlightKind::QmlProperty);
755 case QQmlLSUtils::QualifiedModuleIdentifier:
756 addHighlight(loc, QmlHighlightKind::QmlNamespace);
759 qCWarning(semanticTokens)
760 << QString::fromLatin1(
"Semantic token for %1 has not been implemented yet")
761 .arg(
int(expression->type));
767 const auto fLocs = FileLocations::treeOf(item);
770 const auto regions = fLocs->info().regions;
771 switch (item.internalKind()) {
772 case DomType::ScriptLiteral:
773 highlightScriptLiteral(item);
775 case DomType::ScriptForStatement:
776 addHighlight(regions[ForKeywordRegion], QmlHighlightKind::QmlKeyword);
777 addHighlight(regions[TypeIdentifierRegion],
778 QmlHighlightKind::QmlKeyword);
781 case DomType::ScriptVariableDeclaration: {
782 addHighlight(regions[TypeIdentifierRegion],
783 QmlHighlightKind::QmlKeyword);
786 case DomType::ScriptReturnStatement:
787 addHighlight(regions[ReturnKeywordRegion], QmlHighlightKind::QmlKeyword);
789 case DomType::ScriptCaseClause:
790 addHighlight(regions[CaseKeywordRegion], QmlHighlightKind::QmlKeyword);
792 case DomType::ScriptDefaultClause:
793 addHighlight(regions[DefaultKeywordRegion], QmlHighlightKind::QmlKeyword);
795 case DomType::ScriptSwitchStatement:
796 addHighlight(regions[SwitchKeywordRegion], QmlHighlightKind::QmlKeyword);
798 case DomType::ScriptWhileStatement:
799 addHighlight(regions[WhileKeywordRegion], QmlHighlightKind::QmlKeyword);
801 case DomType::ScriptDoWhileStatement:
802 addHighlight(regions[DoKeywordRegion], QmlHighlightKind::QmlKeyword);
803 addHighlight(regions[WhileKeywordRegion], QmlHighlightKind::QmlKeyword);
805 case DomType::ScriptTryCatchStatement:
806 addHighlight(regions[TryKeywordRegion], QmlHighlightKind::QmlKeyword);
807 addHighlight(regions[CatchKeywordRegion], QmlHighlightKind::QmlKeyword);
808 addHighlight(regions[FinallyKeywordRegion], QmlHighlightKind::QmlKeyword);
810 case DomType::ScriptForEachStatement:
811 addHighlight(regions[TypeIdentifierRegion], QmlHighlightKind::QmlKeyword);
812 addHighlight(regions[ForKeywordRegion], QmlHighlightKind::QmlKeyword);
813 addHighlight(regions[InOfTokenRegion], QmlHighlightKind::QmlKeyword);
815 case DomType::ScriptThrowStatement:
816 addHighlight(regions[ThrowKeywordRegion], QmlHighlightKind::QmlKeyword);
818 case DomType::ScriptBreakStatement:
819 addHighlight(regions[BreakKeywordRegion], QmlHighlightKind::QmlKeyword);
821 case DomType::ScriptContinueStatement:
822 addHighlight(regions[ContinueKeywordRegion], QmlHighlightKind::QmlKeyword);
824 case DomType::ScriptIfStatement:
825 addHighlight(regions[IfKeywordRegion], QmlHighlightKind::QmlKeyword);
826 addHighlight(regions[ElseKeywordRegion], QmlHighlightKind::QmlKeyword);
828 case DomType::ScriptLabelledStatement:
829 addHighlight(regions[IdentifierRegion], QmlHighlightKind::JsLabel);
831 case DomType::ScriptConditionalExpression:
832 addHighlight(regions[QuestionMarkTokenRegion], QmlHighlightKind::Operator);
833 addHighlight(regions[ColonTokenRegion], QmlHighlightKind::Operator);
835 case DomType::ScriptUnaryExpression:
836 case DomType::ScriptPostExpression:
837 addHighlight(regions[OperatorTokenRegion], QmlHighlightKind::Operator);
839 case DomType::ScriptType:
840 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlType);
841 addHighlight(regions[TypeIdentifierRegion], QmlHighlightKind::QmlType);
843 case DomType::ScriptFunctionExpression: {
844 addHighlight(regions[FunctionKeywordRegion], QmlHighlightKind::QmlKeyword);
845 addHighlight(regions[IdentifierRegion], QmlHighlightKind::QmlMethod);
848 case DomType::ScriptYieldExpression:
849 addHighlight(regions[YieldKeywordRegion], QmlHighlightKind::QmlKeyword);
851 case DomType::ScriptThisExpression:
852 addHighlight(regions[ThisKeywordRegion], QmlHighlightKind::QmlKeyword);
854 case DomType::ScriptSuperLiteral:
855 addHighlight(regions[SuperKeywordRegion], QmlHighlightKind::QmlKeyword);
857 case DomType::ScriptNewMemberExpression:
858 case DomType::ScriptNewExpression:
859 addHighlight(regions[NewKeywordRegion], QmlHighlightKind::QmlKeyword);
861 case DomType::ScriptTemplateExpressionPart:
862 addHighlight(regions[DollarLeftBraceTokenRegion], QmlHighlightKind::Operator);
863 visitor(Path(), item.field(Fields::expression),
false);
864 addHighlight(regions[RightBraceRegion], QmlHighlightKind::Operator);
866 case DomType::ScriptTemplateLiteral:
867 addHighlight(regions[LeftBacktickTokenRegion], QmlHighlightKind::String);
868 addHighlight(regions[RightBacktickTokenRegion], QmlHighlightKind::String);
870 case DomType::ScriptTemplateStringPart: {
872 QString code = item.field(Fields::value).value().toString();
873 const auto &locs = Utils::sourceLocationsFromMultiLineToken(
874 code, regions[MainRegion]);
875 for (
const auto &loc : locs)
876 addHighlight(loc, QmlHighlightKind::String);
880 qCDebug(semanticTokens) <<
"Script Expressions with kind" << item.internalKind()
881 <<
"not implemented";
886 QmlHighlightModifiers modifierKind)
888 return Utils::addHighlight(m_highlights, loc, highlightKind, modifierKind);
908 std::vector<LineSegment> segments;
909 qsizetype segmentStart = start;
910 qsizetype pos = text.indexOf(u'\n', start);
911 while (pos != -1 && pos < end) {
912 qsizetype contentEnd = pos;
913 if (contentEnd > start + 1 && text[contentEnd - 1] == u'\r')
915 segments.push_back({ segmentStart, contentEnd - segmentStart });
916 segmentStart = pos + 1;
917 pos = text.indexOf(u'\n', segmentStart);
920 if (segmentStart < end)
921 segments.push_back({ segmentStart, end - segmentStart });
926
927
928
929
930
931
932
933
936 const QQmlJS::SourceLocation &locationInDocument)
938 QList<QQmlJS::SourceLocation> result;
941 QQmlJS::SourceLocation lineLoc = locationInDocument;
942 for (
const auto &segment : splitIntoLineSegments(stringLiteral, 0, stringLiteral.size())) {
943 lineLoc.offset = locationInDocument.offset + quint32(segment.offset);
944 lineLoc.length = quint32(segment.length);
945 result.push_back(lineLoc);
948 lineLoc.startColumn = 1;
954 HighlightingMode mode)
956 QList<
unsigned> result;
957 constexpr auto tokenEncodingLength = 5;
958 result.reserve(tokenEncodingLength * highlights.size());
960 unsigned prevLine = 0;
961 unsigned prevColumn = 0;
962 const auto m_mapToProtocol = mode == HighlightingMode::Default
963 ? mapToProtocolDefault
964 : mapToProtocolForQtCreator;
965 std::for_each(highlights.constBegin(), highlights.constEnd(), [&](
const auto &token) {
966 unsigned length = token.loc.length;
967 unsigned line = token.loc.startLine - 1u;
968 unsigned col = token.loc.startColumn - 1u;
969 Q_ASSERT(line >= prevLine);
970 if (line != prevLine)
972 result.emplace_back(line - prevLine);
973 result.emplace_back(col - prevColumn);
974 result.emplace_back(length);
975 result.emplace_back(m_mapToProtocol(token.kind));
976 result.emplace_back(fromQmlModifierKindToLspTokenType(token.modifiers));
985
986
987
988
989
990
991
992
993
998 *baseModifier |= (1 <<
int(modifier));
1002
1003
1004
1008 int startOffsetItem =
int(loc.offset);
1009 int endOffsetItem = startOffsetItem +
int(loc.length);
1014
1015
1016
1019 int length = resultID.length();
1020 for (
int i = length - 1; i >= 0; --i) {
1021 if (resultID[i] ==
'9') {
1024 resultID[i] = resultID[i] + 1;
1028 resultID.prepend(
'1');
1032
1033
1034
1035
1036
1038 const QList<
unsigned> &newData)
1041 const auto [oldStart, newStart] =
1042 std::mismatch(oldData.cbegin(), oldData.cend(), newData.cbegin(), newData.cend());
1046 const auto [r1, r2] = std::mismatch(oldData.crbegin(), std::make_reverse_iterator(oldStart),
1047 newData.crbegin(), std::make_reverse_iterator(newStart));
1048 const auto oldEnd = r1.base();
1049 const auto newEnd = r2.base();
1052 if (oldStart == oldEnd && newStart == newEnd)
1055 SemanticTokensEdit edit;
1056 edit.start =
int(std::distance(newData.cbegin(), newStart));
1057 edit.deleteCount =
int(std::distance(oldStart, oldEnd));
1059 if (newStart >= newData.cbegin() && newEnd <= newData.cend() && newStart < newEnd)
1060 edit.data.emplace(newStart, newEnd);
1062 return { std::move(edit) };
1066 const QQmlJS::SourceLocation &loc,
1068 QmlHighlightModifiers modifierKind)
1070 if (!loc.isValid() || loc.length == 0) {
1071 qCDebug(semanticTokens)
1072 <<
"Invalid locations: Cannot add highlight to token";
1075 if (!out.contains(loc.offset))
1076 out.insert(loc.offset,
HighlightToken(loc, highlightKind, modifierKind));
1080 const std::optional<HighlightsRange> &range)
1082 using namespace QQmlJS::Dom;
1084 return highlightDomElements.highlights();
1088 const QString &lastValidCode,
const QString ¤tCode)
1092 const QList<Diff> diffs = differ.diff(lastValidCode, currentCode);
1093 HighlightsContainer shifts = cachedHighlights;
1094 applyDiffs(shifts, diffs);
1102 qsizetype offset = 0;
1108 QRegularExpression regex;
1109 void (*action)(HighlightsContainer &,
const QRegularExpressionMatch &, LineAnchor line);
1112static void addFallbackToken(HighlightsContainer &out, LineAnchor line, qsizetype offset,
1116 Q_ASSERT(length > 0);
1117 const auto loc = QQmlJS::SourceLocation::fromQSizeType(offset, length, line.number,
1118 offset - line.offset + 1);
1122static void addGroup(HighlightsContainer &out,
const QRegularExpressionMatch &m,
int group,
1126 if (!m.hasCaptured(group))
1128 addFallbackToken(out, line, line.offset + m.capturedStart(group), m.capturedLength(group), kind,
1132static void highlightSignalParameters(HighlightsContainer &out,
const QRegularExpressionMatch &m,
1135 static const QRegularExpression paramRe(
1136 uR"re((?:([\w.<>]+)\s+(\w+))|(?:(\w+)\s*:\s*([\w.<>]+)))re"_s);
1137 const qsizetype paramsStart = m.capturedStart(3);
1138 Q_ASSERT(paramsStart >= 0);
1139 auto it = paramRe.globalMatchView(m.capturedView(3));
1140 while (it.hasNext()) {
1141 const auto pm = it.next();
1142 if (pm.capturedStart(1) >= 0) {
1143 addFallbackToken(out, line, line.offset + paramsStart + pm.capturedStart(1),
1145 addFallbackToken(out, line, line.offset + paramsStart + pm.capturedStart(2),
1148 addFallbackToken(out, line, line.offset + paramsStart + pm.capturedStart(3),
1150 addFallbackToken(out, line, line.offset + paramsStart + pm.capturedStart(4),
1156static QmlHighlightModifiers propertyModifiers(QStringView modifierWord)
1159 if (modifierWord == u"readonly")
1161 else if (modifierWord == u"required")
1163 else if (modifierWord == u"default")
1165 else if (modifierWord == u"final")
1167 else if (modifierWord == u"virtual")
1169 else if (modifierWord == u"override")
1178const QList<FallbackRule> &fallbackRules()
1180 static const QList<FallbackRule> rules = [] {
1181 QList<FallbackRule> r;
1183 r.append({ QRegularExpression(uR"re(//.*)re"_s),
1184 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1188 r.append({ QRegularExpression(uR"re("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')re"_s),
1189 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1195 { QRegularExpression(
1196 uR"re(\b(import)\b\s+(?:("(?:[^"\\]|\\.)*")|([\w.]+))(?:\s+(\d+\.\d+))?(?:\s+(as)\s+(\w+))?)re"_s),
1197 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1207 { QRegularExpression(
1208 uR"re(\b(?:(default|readonly|required|final|virtual|override)\s+)*(property)\s+(alias|[A-Za-z_][\w.<>]*)\s+(\w+))re"_s),
1209 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1212 const QStringView type = m.capturedView(3);
1213 addGroup(out, m, 3, line,
1217 propertyModifiers(m.capturedView(1)));
1220 r.append({ QRegularExpression(uR"re((?:^|[{;])\s*(required)\s+(?!property\b)(\w+))re"_s),
1221 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1227 r.append({ QRegularExpression(uR"re(\b(signal)\s+(\w+)\s*\(([^)]*)\))re"_s),
1228 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1231 highlightSignalParameters(out, m, line);
1234 r.append({ QRegularExpression(uR"re(\b(function)\s+(\w+)\s*(?=\())re"_s),
1235 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1240 r.append({ QRegularExpression(uR"re((?:^|[{;])\s*(id)\s*:\s*(\w+))re"_s),
1241 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1246 r.append({ QRegularExpression(uR"re(\bon[A-Z]\w*(?=\s*:))re"_s),
1247 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1251 r.append({ QRegularExpression(uR"re((?:^|[{;])\s*(pragma)\s+(\w+)(?:\s+(\w+))?)re"_s),
1252 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1258 r.append({ QRegularExpression(uR"re((?:^|[{;])\s*([a-z_]\w*(?:\.[a-z_]\w*)*)\s*(?=:))re"_s),
1259 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1264 { QRegularExpression(
1265 uR"re(\b(?:as|async|await|break|case|catch|class|component|const|continue|
1266 debugger|default|delete|do|else|enum|export|extends|false|final|finally|
1267 for|function|id|if|import|in|instanceof|let|new|null|on|override|pragma|
1268 property|readonly|required|return|signal|super|switch|this|throw|true|
1269 try|typeof|undefined|var|virtual|void|while|with|yield)\b)re"_s),
1270 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1274 r.append({ QRegularExpression(uR"re(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b)re"_s),
1275 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1279 r.append({ QRegularExpression(uR"re((?<=\.)\w+)re"_s),
1280 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1284 r.append({ QRegularExpression(uR"re(\b[A-Z]\w*\b)re"_s),
1285 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1289 r.append({ QRegularExpression(uR"re(\b[A-Za-z_]\w*(?=\s*[(`]))re"_s),
1290 [](HighlightsContainer &out,
const QRegularExpressionMatch &m, LineAnchor line) {
1301static void addMultilineToken(HighlightsContainer &out, QStringView text, qsizetype start,
1304 const auto segments = splitIntoLineSegments(text, start, end);
1305 for (size_t i = 0; i < segments.size(); ++i) {
1306 if (segments[i].length > 0)
1307 addFallbackToken(out, line, segments[i].offset, segments[i].length, kind);
1308 if (i + 1 < segments.size()) {
1310 line.offset = segments[i + 1].offset;
1313 if (!segments.empty() && segments.back().offset + segments.back().length < end) {
1322static qsizetype scanBlockComment(HighlightsContainer &out, QStringView text, qsizetype pos,
1325 const qsizetype closeIdx = text.indexOf(u"*/", pos + 2);
1326 const qsizetype end = closeIdx == -1 ? text.size() : closeIdx + 2;
1333static qsizetype scanSpan(HighlightsContainer &out, QStringView text, qsizetype pos,
1334 LineAnchor &line,
bool stopAtUnbalancedBrace);
1340static qsizetype scanTemplateLiteral(HighlightsContainer &out, QStringView text, qsizetype pos,
1343 const qsizetype size = text.size();
1344 qsizetype segmentStart = pos;
1346 while (pos < size) {
1347 const QChar c = text[pos];
1357 if (c == u'$' && pos + 1 < size && text[pos + 1] == u'{') {
1360 pos = scanSpan(out, text, pos + 2, line,
true);
1375enum class SpecialChar { None, BlockComment, TemplateLiteral, OpenBrace, CloseBrace };
1376struct SpecialCharMatch
1379 SpecialChar kind = SpecialChar::None;
1382static SpecialCharMatch nextSpecialChar(QStringView lineView, qsizetype col,
bool trackBraces)
1384 for (qsizetype i = col; i < lineView.size(); ++i) {
1385 const QChar c = lineView[i];
1387 return { i, SpecialChar::TemplateLiteral };
1388 if (c == u'/' && i + 1 < lineView.size() && lineView[i + 1] == u'*')
1389 return { i, SpecialChar::BlockComment };
1390 if (trackBraces && c == u'{')
1391 return { i, SpecialChar::OpenBrace };
1392 if (trackBraces && c == u'}')
1393 return { i, SpecialChar::CloseBrace };
1403static qsizetype scanSpan(HighlightsContainer &out, QStringView text, qsizetype pos,
1404 LineAnchor &line,
bool stopAtUnbalancedBrace)
1406 const qsizetype size = text.size();
1407 const QList<FallbackRule> &rules = fallbackRules();
1410 while (pos <= size) {
1411 qsizetype physLineEnd = text.indexOf(u'\n', pos);
1412 if (physLineEnd == -1)
1414 const QStringView lineView = text.mid(line.offset, physLineEnd - line.offset);
1415 const qsizetype col = pos - line.offset;
1417 const FallbackRule *bestRule =
nullptr;
1418 QRegularExpressionMatch bestMatch;
1419 for (
const auto &rule : rules) {
1420 const auto m = rule.regex.matchView(lineView, col);
1423 if (!bestRule || m.capturedStart(0) < bestMatch.capturedStart(0)) {
1429 const SpecialCharMatch special = nextSpecialChar(lineView, col, stopAtUnbalancedBrace);
1430 const bool ruleWins = bestRule
1431 && (special.kind == SpecialChar::None || bestMatch.capturedStart(0) <= special.col);
1433 if (!bestRule && special.kind == SpecialChar::None) {
1434 if (physLineEnd >= size)
1436 line.offset = physLineEnd + 1;
1443 bestRule->action(out, bestMatch, line);
1444 pos = line.offset + qMax(bestMatch.capturedEnd(0), col + 1);
1448 const qsizetype absPos = line.offset + special.col;
1449 switch (special.kind) {
1450 case SpecialChar::BlockComment:
1451 pos = scanBlockComment(out, text, absPos, line);
1453 case SpecialChar::TemplateLiteral:
1454 pos = scanTemplateLiteral(out, text, absPos, line);
1456 case SpecialChar::OpenBrace:
1460 case SpecialChar::CloseBrace:
1461 if (braceDepth == 0)
1466 case SpecialChar::None:
1479 const std::optional<HighlightsRange> &range)
1481 HighlightsContainer highlights;
1483 scanSpan(highlights, code, 0, line,
false);
1486 for (
auto it = highlights.begin(); it != highlights.end();) {
1487 if (!rangeOverlapsWithSourceLocation(it->loc, *range))
1488 it = highlights.erase(it);
1499 auto [row, col] = QQmlJS::SourceLocation::rowAndColumnFrom(text, text.size());
1500 return { row - 1, col - 1 };
1505 auto [newLines, lastLineLength] = newlineCountAndLastLineLength(text);
1507 cursor.startLine += newLines;
1508 cursor.startColumn = lastLineLength + 1;
1510 cursor.startColumn += text.size();
1512 cursor.offset += text.size();
1521 return t.end() < offset;
1526 return t.begin() > offset;
1530 const QQmlJS::SourceLocation &cursor)
1532 return token.begin() < cursor.begin() && token.end() >= cursor.begin();
1536 const QQmlJS::SourceLocation &cursor)
1538 return token.begin() >= cursor.begin() && token.begin() <= cursor.end();
1542 int newlines,
int lastLen,
int diffLen)
1544 if (t.startLine == cursor.startLine) {
1546 t.startColumn = lastLen + t.startColumn - cursor.startColumn + 1;
1548 t.startColumn += lastLen;
1551 t.startLine += newlines;
1552 t.offset += diffLen;
1556 const QQmlJS::SourceLocation &cursor)
1558 auto begin = diff.text.cbegin();
1559 auto end = diff.text.cend();
1561 auto ptr = std::find_if(begin, end, [](QChar c) {
return c.isSpace(); });
1564 t.length = cursor.begin() - t.begin() + std::distance(begin, ptr);
1566 t.length += diff.text.size();
1571 const QQmlJS::SourceLocation &cursor,
int newlines,
1574 const int diffLen = diff.text.size();
1575 t.offset = cursor.begin();
1576 t.length += diffLen;
1577 t.startLine = cursor.startLine;
1578 t.startColumn = cursor.startColumn;
1581 auto rbegin = diff.text.rbegin();
1582 auto rend = diff.text.rend();
1583 auto ptr = std::find_if(rbegin, rend, [](QChar c) {
return c.isSpace(); });
1586 std::ptrdiff_t omitted = std::distance(ptr, rend);
1587 t.offset += omitted;
1588 t.length -= omitted;
1589 t.startColumn += omitted;
1594 t.startLine += newlines;
1595 t.startColumn = lastLen - std::distance(ptr.base(), diff.text.end()) + 1;
1602 const auto [newlines, lastLen] = newlineCountAndLastLineLength(diff.text);
1603 const auto diffLen = diff.text.size();
1604 cursor.length = quint32(diffLen);
1606 HighlightsContainer shifted;
1608 for (
auto item : highlights) {
1609 auto &token = item.loc;
1610 if (tokenBeforeOffset(token, cursor.begin())) {
1611 shifted.insert(token.offset, item);
1615 if (tokenAfterOffset(token, cursor.begin())) {
1616 shiftTokenAfterInsert(token, cursor, newlines, lastLen, diffLen);
1617 shifted.insert(token.offset, item);
1622 if (insertionInsideToken(token, cursor)) {
1623 expandTokenForMiddleInsert(token, diff, cursor);
1624 }
else if (insertionTouchesTokenLeft(token, cursor)) {
1625 expandTokenForLeftOverlap(token, diff, cursor, newlines, lastLen);
1628 shifted.insert(token.offset, item);
1631 highlights.swap(shifted);
1634 updateCursorPositionByDiff(diff.text, cursor);
1642 return t.begin() < delStart && t.end() > delEnd;
1647 return t.begin() < delStart && t.end() <= delEnd;
1652 return t.begin() >= delStart && t.end() > delEnd;
1659 const QQmlJS::SourceLocation &cursor,
int diffLen)
1661 t.offset -= diffLen;
1664 if (t.startLine == cursor.startLine + newlines) {
1666 t.startColumn = cursor.startColumn + (t.startColumn - lastLen) - 1;
1668 t.startColumn -= lastLen;
1673 t.startLine -= newlines;
1680 int newlines, quint32 delStartLine, quint32 delStartColumn)
1682 const quint32 deletedLen = delEnd - delStart;
1684 if (spansAcrossDeletion(t, delStart, delEnd)) {
1686 t.length -= deletedLen;
1690 if (leftFragmentRemains(t, delStart, delEnd)) {
1692 t.length = delStart - t.begin();
1696 if (rightFragmentRemains(t, delStart, delEnd)) {
1698 quint32 overlap = delEnd - t.begin();
1699 t.offset = delStart;
1700 t.length -= overlap;
1702 t.startColumn = delStartColumn;
1704 t.startLine = delStartLine;
1716 const auto [newlines, lastLen] = newlineCountAndLastLineLength(diff.text);
1717 const int diffLen = diff.text.size();
1719 cursor.length = diffLen;
1721 const quint32 delStart = cursor.offset;
1722 const quint32 delEnd = cursor.offset + diffLen;
1724 HighlightsContainer shifts;
1726 for (
auto item : highlights) {
1727 auto &token = item.loc;
1732 if (tokenBeforeOffset(token, delStart)) {
1733 shifts.insert(token.offset, item);
1740 if (tokenAfterOffset(token, delEnd)) {
1741 shiftTokenAfterDelete(token, newlines, lastLen, cursor, diffLen);
1742 shifts.insert(token.offset, item);
1749 applyDeletionOverlap(token, delStart, delEnd, newlines, cursor.startLine,
1750 cursor.startColumn);
1752 if (token.length == 0)
1755 shifts.insert(token.offset, item);
1758 highlights.swap(shifts);
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787void Utils::
applyDiffs(HighlightsContainer &highlights,
const QList<QQmlLSUtils::Diff> &diffs)
1790 if (highlights.isEmpty())
1793 QQmlJS::SourceLocation cursor;
1796 cursor.startLine = 1;
1797 cursor.startColumn = 1;
1799 for (
const Diff &diff : diffs) {
1800 switch (diff.command) {
1803 updateCursorPositionByDiff(diff.text, cursor);
1805 case Diff::Insert: {
1806 updateHighlightsOnInsert(highlights, cursor, diff);
1809 case Diff::Delete: {
1810 updateHighlightsOnDelete(highlights, cursor, diff);