9Q_LOGGING_CATEGORY(QQmlLSCompletionLog,
"qt.languageserver.completions")
11using namespace QLspSpecification;
12using namespace QQmlJS::Dom;
13using namespace Qt::StringLiterals;
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
42CompletionItem
QQmlLSCompletion::makeSnippet(QUtf8StringView qualifier, QUtf8StringView label,
43 QUtf8StringView insertText)
46 if (!qualifier.isEmpty()) {
47 res.label = qualifier.data();
50 res.label += label.data();
51 res.insertTextFormat = InsertTextFormat::Snippet;
52 if (!qualifier.isEmpty()) {
53 res.insertText = qualifier.data();
54 *res.insertText +=
'.';
55 *res.insertText += insertText.data();
57 res.insertText = insertText.data();
59 res.kind =
int(CompletionItemKind::Snippet);
60 res.insertTextMode = InsertTextMode::AdjustIndentation;
64CompletionItem
QQmlLSCompletion::makeSnippet(QUtf8StringView label, QUtf8StringView insertText)
66 return makeSnippet(QByteArray(), label, insertText);
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
90 const QQmlLSCompletionPosition &positionInfo,
91 QQmlJS::SourceLocation right)
const
96 if (!(left.end() <= positionInfo.offset()))
102 return positionInfo.offset() <= right.begin();
106
107
108
110 const QQmlLSCompletionPosition &positionInfo)
const
112 return betweenLocations(left, positionInfo, QQmlJS::SourceLocation{});
116
117
118
120 QQmlJS::SourceLocation right)
const
122 if (!right.isValid())
126 if (ctx.offset() <= right.begin())
132bool QQmlLSCompletion::ctxBeforeStatement(
const QQmlLSCompletionPosition &positionInfo,
133 const DomItem &parentForContext,
134 FileLocationRegion firstRegion)
const
136 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
137 const bool result = beforeLocation(positionInfo, regions[firstRegion]);
142QQmlLSCompletion::suggestBindingCompletion(
const DomItem &itemAtPosition, BackInsertIterator it)
const
144 suggestReachableTypes(itemAtPosition, LocalSymbolsType::AttachedType, CompletionItemKind::Class,
147 const auto scope = [&]() -> QQmlJSScope::ConstPtr {
148 const DomItem owner = ownerOfQualifiedExpression(itemAtPosition);
150 return itemAtPosition.qmlObject().semanticScope();
152 const auto expressionType = QQmlLSUtils::resolveExpressionType(
153 owner, QQmlLSUtils::ResolveActualTypeForFieldMemberExpression);
156 if (!expressionType || expressionType->type == QQmlLSUtils::QualifiedModuleIdentifier)
159 return expressionType->semanticScope;
165 propertyCompletion(scope,
nullptr, it);
166 signalHandlerCompletion(scope,
nullptr, it);
170 const QQmlLSCompletionPosition &positionInfo,
171 BackInsertIterator it)
const
174 const CompletionContextStrings &ctx = positionInfo.cursorPosition;
176 QRegularExpression spaceRe(uR"(\s+)"_s);
177 QList<QStringView> linePieces = ctx.preLine().split(spaceRe, Qt::SkipEmptyParts);
178 qsizetype effectiveLength = linePieces.size()
179 + ((!ctx.preLine().isEmpty() && ctx.preLine().last().isSpace()) ? 1 : 0);
180 if (effectiveLength < 2) {
182 comp.label =
"import";
183 comp.kind =
int(CompletionItemKind::Keyword);
186 if (linePieces.isEmpty() || linePieces.first() != u"import")
188 if (effectiveLength == 2) {
191 }
else if (effectiveLength == 3) {
192 if (linePieces.last() != u"as") {
196 comp.kind =
int(CompletionItemKind::Keyword);
201 DomItem env = file.environment();
202 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
203 switch (importCompletionType) {
207 QDuplicateTracker<QString> modulesSeen;
208 for (
const QString &uri : envPtr->moduleIndexUris(env)) {
209 QStringView base = ctx.base();
210 if (uri.startsWith(base)) {
211 QStringList rest = uri.mid(base.size()).split(u'.');
215 const QString label = rest.first();
216 if (!modulesSeen.hasSeen(label)) {
218 comp.label = label.toUtf8();
219 comp.kind =
int(CompletionItemKind::Module);
227 if (ctx.base().isEmpty()) {
229 envPtr->moduleIndexMajorVersions(env, linePieces.at(1).toString())) {
231 comp.label = QString::number(majorV).toUtf8();
232 comp.kind =
int(CompletionItemKind::Constant);
236 bool hasMajorVersion = ctx.base().endsWith(u'.');
239 majorV = ctx.base().mid(0, ctx.base().size() - 1).toInt(&hasMajorVersion);
240 if (!hasMajorVersion)
242 if (std::shared_ptr<ModuleIndex> mIndex =
243 envPtr->moduleIndexWithUri(env, linePieces.at(1).toString(), majorV)) {
244 for (
int minorV : mIndex->minorVersions()) {
246 comp.label = QString::number(minorV).toUtf8();
247 comp.kind =
int(CompletionItemKind::Constant);
257void QQmlLSCompletion::idsCompletions(
const DomItem &component, BackInsertIterator it)
const
259 qCDebug(QQmlLSCompletionLog) <<
"adding ids completions";
260 for (
const QString &k : component.field(Fields::ids).keys()) {
262 comp.label = k.toUtf8();
263 comp.kind =
int(CompletionItemKind::Value);
268static bool testScopeSymbol(
const QQmlJSScope::ConstPtr &scope, LocalSymbolsTypes options,
269 CompletionItemKind kind)
271 const bool currentIsSingleton = scope->isSingleton();
272 const bool currentIsAttached = !scope->attachedType().isNull();
273 if ((options & LocalSymbolsType::Singleton) && currentIsSingleton) {
276 if ((options & LocalSymbolsType::AttachedType) && currentIsAttached) {
279 const bool isObjectType = scope->isReferenceType();
280 if (options & LocalSymbolsType::ObjectType && !currentIsSingleton && isObjectType) {
281 return kind != CompletionItemKind::Constructor || scope->isCreatable();
283 if (options & LocalSymbolsType::ValueType && !currentIsSingleton && !isObjectType) {
290
291
292
293void QQmlLSCompletion::suggestReachableTypes(
const DomItem &el, LocalSymbolsTypes options,
294 CompletionItemKind kind, BackInsertIterator it)
const
296 auto file = el.containingFile().as<QmlFile>();
299 auto resolver = file->typeResolver();
303 const QString requiredQualifiers = QQmlLSUtils::qualifiersFrom(el);
304 const auto keyValueRange = resolver->importedTypes().asKeyValueRange();
305 for (
const auto &type : keyValueRange) {
307 const bool isMarkerType = type.first.contains(u"$internal$.")
308 || type.first.contains(u"$anonymous$.") || type.first.contains(u"$module$.");
309 if (isMarkerType || !type.first.startsWith(requiredQualifiers))
312 auto &scope = type.second.scope;
316 if (!testScopeSymbol(scope, options, kind))
319 CompletionItem completion;
320 completion.label = QStringView(type.first).sliced(requiredQualifiers.size()).toUtf8();
321 completion.kind =
int(kind);
326void QQmlLSCompletion::jsIdentifierCompletion(
const QQmlJSScope::ConstPtr &scope,
327 QDuplicateTracker<QString> *usedNames,
328 BackInsertIterator it)
const
330 for (
const auto &[name, jsIdentifier] : scope->ownJSIdentifiers().asKeyValueRange()) {
331 CompletionItem completion;
332 if (usedNames && usedNames->hasSeen(name)) {
335 completion.label = name.toUtf8();
336 completion.kind =
int(CompletionItemKind::Variable);
337 QString detail = u"has type "_s;
338 if (jsIdentifier.typeName) {
339 if (jsIdentifier.isConst) {
340 detail.append(u"const ");
342 detail.append(*jsIdentifier.typeName);
344 detail.append(jsIdentifier.isConst ? u"const"_s : u"var"_s);
346 completion.detail = detail.toUtf8();
352 QDuplicateTracker<QString> *usedNames,
353 BackInsertIterator it)
const
356 for (
const auto &[name, method] : scope->methods().asKeyValueRange()) {
357 if (method.access() != QQmlJSMetaMethod::Public)
359 if (usedNames && usedNames->hasSeen(name)) {
362 CompletionItem completion;
363 completion.label = name.toUtf8();
364 completion.kind =
int(CompletionItemKind::Method);
371void QQmlLSCompletion::propertyCompletion(
const QQmlJSScope::ConstPtr &scope,
372 QDuplicateTracker<QString> *usedNames,
373 BackInsertIterator it)
const
375 for (
const auto &[name, property] : scope->properties().asKeyValueRange()) {
376 if (usedNames && usedNames->hasSeen(name)) {
379 CompletionItem completion;
380 completion.label = name.toUtf8();
381 completion.kind =
int(CompletionItemKind::Property);
382 QString detail{ u"has type "_s };
383 if (!property.isWritable())
384 detail.append(u"readonly "_s);
385 detail.append(property.typeName().isEmpty() ? u"var"_s : property.typeName());
386 completion.detail = detail.toUtf8();
391void QQmlLSCompletion::enumerationCompletion(
const QQmlJSScope::ConstPtr &scope,
392 QDuplicateTracker<QString> *usedNames,
393 BackInsertIterator it)
const
395 for (
const QQmlJSMetaEnum &enumerator : scope->enumerations()) {
396 if (usedNames && usedNames->hasSeen(enumerator.name())) {
399 CompletionItem completion;
400 completion.label = enumerator.name().toUtf8();
401 completion.kind =
static_cast<
int>(CompletionItemKind::Enum);
406void QQmlLSCompletion::enumerationValueCompletionHelper(
const QStringList &enumeratorKeys,
407 BackInsertIterator it)
const
409 for (
const QString &enumeratorKey : enumeratorKeys) {
410 CompletionItem completion;
411 completion.label = enumeratorKey.toUtf8();
412 completion.kind =
static_cast<
int>(CompletionItemKind::EnumMember);
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
437void QQmlLSCompletion::enumerationValueCompletion(
const QQmlJSScope::ConstPtr &scope,
438 const QString &enumeratorName,
439 BackInsertIterator result)
const
441 auto enumerator = scope->enumeration(enumeratorName);
442 if (enumerator.isValid()) {
443 enumerationValueCompletionHelper(enumerator.keys(), result);
447 for (
const QQmlJSMetaEnum &enumerator : scope->enumerations()) {
448 enumerationValueCompletionHelper(enumerator.keys(), result);
453
454
455
456
457
458
459
460
461
462
463
464
468 for (QQmlJSScope::ConstPtr current = scope; current; current = current->parentScope()) {
470 if (current->scopeType() == QQmlSA::ScopeType::QMLScope)
476
477
478
479
480
481
482
483
484
486 const QQmlJSScope::ConstPtr &scope,
const QString &enumName,
487 QDuplicateTracker<QString> &usedNames, BackInsertIterator result)
const
489 enumerationValueCompletion(scope, enumName, result);
492 if (
auto enumerator = scope->enumeration(enumName); !enumerator.isValid()) {
493 enumerationCompletion(scope, &usedNames, result);
498
499
500
501
502
503
504
505
506
507DomItem
QQmlLSCompletion::ownerOfQualifiedExpression(
const DomItem &qualifiedExpression)
const
512 const bool askForCompletionOnDot = QQmlLSUtils::isFieldMemberExpression(qualifiedExpression);
513 const bool hasQualifier =
514 QQmlLSUtils::isFieldMemberAccess(qualifiedExpression) || askForCompletionOnDot;
519 const DomItem owner =
520 (askForCompletionOnDot ? qualifiedExpression : qualifiedExpression.directParent())
521 .field(Fields::left);
526
527
528
529
530
531
532
533
534void QQmlLSCompletion::suggestJSExpressionCompletion(
const DomItem &scriptIdentifier,
535 BackInsertIterator result)
const
537 QDuplicateTracker<QString> usedNames;
538 QQmlJSScope::ConstPtr nearestScope;
540 const DomItem owner = ownerOfQualifiedExpression(scriptIdentifier);
543 for (QUtf8StringView view : std::array<QUtf8StringView, 3>{
"null",
"false",
"true" }) {
544 CompletionItem completion;
545 completion.label = view.data();
546 completion.kind =
int(CompletionItemKind::Value);
549 idsCompletions(scriptIdentifier.component(), result);
550 suggestReachableTypes(scriptIdentifier,
551 LocalSymbolsType::Singleton | LocalSymbolsType::AttachedType,
552 CompletionItemKind::Class, result);
554 auto scope = scriptIdentifier.nearestSemanticScope();
557 nearestScope = scope;
559 enumerationCompletion(nearestScope, &usedNames, result);
561 auto ownerExpressionType = QQmlLSUtils::resolveExpressionType(
562 owner, QQmlLSUtils::ResolveActualTypeForFieldMemberExpression);
563 if (!ownerExpressionType || !ownerExpressionType->semanticScope)
565 nearestScope = ownerExpressionType->semanticScope;
567 switch (ownerExpressionType->type) {
568 case QQmlLSUtils::EnumeratorValueIdentifier:
570 case QQmlLSUtils::EnumeratorIdentifier:
571 suggestEnumerationsAndEnumerationValues(nearestScope, *ownerExpressionType->name,
574 case QQmlLSUtils::QmlComponentIdentifier:
577 if (QQmlJSScope::ConstPtr attachedType =
578 ownerExpressionType->semanticScope->attachedType()) {
579 methodCompletion(attachedType, &usedNames, result);
580 propertyCompletion(attachedType, &usedNames, result);
581 suggestEnumerationsAndEnumerationValues(
582 attachedType, *ownerExpressionType->name, usedNames, result);
585 case QQmlLSUtils::SingletonIdentifier:
586 if (ownerExpressionType->name)
587 suggestEnumerationsAndEnumerationValues(nearestScope, *ownerExpressionType->name,
595 Q_ASSERT(nearestScope);
597 methodCompletion(nearestScope, &usedNames, result);
598 propertyCompletion(nearestScope, &usedNames, result);
602 collectFromAllJavaScriptParents(
603 [
this, &usedNames, result](
const QQmlJSScope::ConstPtr &scope) {
604 jsIdentifierCompletion(scope, &usedNames, result);
607 collectFromAllJavaScriptParents(
608 [
this, &usedNames, result](
const QQmlJSScope::ConstPtr &scope) {
609 methodCompletion(scope, &usedNames, result);
612 collectFromAllJavaScriptParents(
613 [
this, &usedNames, result](
const QQmlJSScope::ConstPtr &scope) {
614 propertyCompletion(scope, &usedNames, result);
618 auto file = scriptIdentifier.containingFile().as<QmlFile>();
621 auto resolver = file->typeResolver();
625 const auto globals = resolver->jsGlobalObject();
626 methodCompletion(globals, &usedNames, result);
627 propertyCompletion(globals, &usedNames, result);
633 for (
const QString &name : names) {
634 if (
auto property = current->property(name); property.isValid()) {
635 if (
auto propertyType = property.type().get()) {
636 current = propertyType;
646 const QQmlLSCompletionPosition &positionInfo)
648 auto fileLocations = FileLocations::treeOf(parentForContext)->info().fullRegion;
649 return positionInfo.offset() <= fileLocations.begin();
653 const QQmlLSCompletionPosition &positionInfo)
655 auto location = FileLocations::treeOf(currentItem)->info();
656 auto region = location.regions.constFind(ColonTokenRegion);
658 if (region == location.regions.constEnd())
661 if (region.value().isValid() && region.value().begin() < positionInfo.offset()) {
668
669
670
671
672
673
674
675
676
677
678static const QMap<QString, QList<QString>> valuesForPragmas{
679 { u"ComponentBehavior"_s, { u"Unbound"_s, u"Bound"_s } },
680 { u"NativeMethodBehavior"_s, { u"AcceptThisObject"_s, u"RejectThisObject"_s } },
681 { u"ListPropertyAssignBehavior"_s, { u"Append"_s, u"Replace"_s, u"ReplaceIfNotDefault"_s } },
682 { u"Singleton"_s, {} },
683 { u"ValueTypeBehavior"_s, { u"Addressable"_s, u"Inaddressable"_s } },
686void QQmlLSCompletion::insidePragmaCompletion(QQmlJS::Dom::DomItem currentItem,
687 const QQmlLSCompletionPosition &positionInfo,
688 BackInsertIterator result)
const
690 if (cursorAfterColon(currentItem, positionInfo)) {
691 const QString name = currentItem.field(Fields::name).value().toString();
692 auto values = valuesForPragmas.constFind(name);
693 if (values == valuesForPragmas.constEnd())
696 for (
const auto &value : *values) {
698 comp.label = value.toUtf8();
699 comp.kind =
static_cast<
int>(CompletionItemKind::Value);
705 for (
const auto &pragma : valuesForPragmas.asKeyValueRange()) {
707 comp.label = pragma.first.toUtf8();
708 if (!pragma.second.isEmpty()) {
709 comp.insertText = QString(pragma.first).append(u": ").toUtf8();
711 comp.kind =
static_cast<
int>(CompletionItemKind::Value);
716void QQmlLSCompletion::insideQmlObjectCompletion(
const DomItem &parentForContext,
717 const QQmlLSCompletionPosition &positionInfo,
718 BackInsertIterator result)
const
721 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
723 const QQmlJS::SourceLocation leftBrace = regions[LeftBraceRegion];
724 const QQmlJS::SourceLocation rightBrace = regions[RightBraceRegion];
726 if (beforeLocation(positionInfo, leftBrace)) {
727 LocalSymbolsTypes options;
728 options.setFlag(LocalSymbolsType::ObjectType);
729 suggestReachableTypes(positionInfo.itemAtPosition, options, CompletionItemKind::Constructor,
731 if (parentForContext.directParent().internalKind() == DomType::Binding)
732 suggestSnippetsForRightHandSideOfBinding(positionInfo.itemAtPosition, result);
734 suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
736 if (QQmlLSUtils::isFieldMemberExpression(positionInfo.itemAtPosition)) {
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
763 if (betweenLocations(leftBrace, positionInfo, rightBrace)) {
767 constexpr static std::array completions{
""_L1,
775 "default virtual "_L1,
776 "default override "_L1,
777 "default required "_L1,
778 "final required "_L1,
779 "final readonly "_L1,
780 "virtual required "_L1,
781 "virtual readonly "_L1,
782 "override required "_L1,
783 "override readonly "_L1,
784 "default final required "_L1,
785 "default virtual required "_L1,
786 "default override required "_L1 };
787 for (QLatin1StringView completion : completions) {
792 if (!completion.contains(QLatin1StringView(
"readonly"))) {
793 result = makeSnippet(
794 QByteArray(completion.data()).append(
"property type name;"),
795 QByteArray(completion.data()).append(
"property ${1:type} ${0:name};"));
798 if (!completion.contains(QLatin1StringView(
"required"))) {
799 result = makeSnippet(
800 QByteArray(completion.data()).append(
"property type name: value;"),
801 QByteArray(completion.data())
802 .append(
"property ${1:type} ${2:name}: ${0:value};"));
807 result = makeSnippet(
"signal name(arg1:type1, ...)",
"signal ${1:name}($0)");
810 result = makeSnippet(
"signal name;",
"signal ${0:name};");
813 result = makeSnippet(
"required name;",
"required ${0:name};");
816 result = makeSnippet(
"function name(args...): returnType { statements...}",
817 "function ${1:name}($2): ${3:returnType} {\n\t$0\n}");
820 result = makeSnippet(
"enum name { Values...}",
"enum ${1:name} {\n\t${0:values}\n}");
823 result = makeSnippet(
"component Name: BaseType { ... }",
824 "component ${1:name}: ${2:baseType} {\n\t$0\n}");
826 suggestBindingCompletion(positionInfo.itemAtPosition, result);
829 const DomItem containingFile = parentForContext.containingFile();
830 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
831 CompletionItemKind::Constructor, result);
832 suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
839 const DomItem ¤tItem,
const QQmlLSCompletionPosition &positionInfo,
840 BackInsertIterator result)
const
842 auto info = FileLocations::treeOf(currentItem)->info();
843 const QQmlJS::SourceLocation propertyKeyword = info.regions[PropertyKeywordRegion];
846 if (positionInfo.offset() < propertyKeyword.end()) {
847 auto addCompletionKeyword = [&result](QUtf8StringView view) {
849 item.label = view.data();
850 item.kind =
int(CompletionItemKind::Keyword);
854 const auto alreadyTyped = [&info,
855 &positionInfo](
const FileLocationRegion &keywordRegion) ->
bool {
856 const auto &keywordSLoc = info.regions[keywordRegion];
857 return keywordSLoc.isValid() && keywordSLoc.begin() < positionInfo.offset();
860 const bool alreadyTypedReadonlyOrRequired =
861 alreadyTyped(ReadonlyKeywordRegion) || alreadyTyped(RequiredKeywordRegion);
862 if (!alreadyTypedReadonlyOrRequired) {
867 addCompletionKeyword(u8"readonly");
868 addCompletionKeyword(u8"required");
871 if (!alreadyTyped(DefaultKeywordRegion)) {
872 addCompletionKeyword(u8"default");
875 const bool alreadyTypedVirtualOrOverrideOrFinal = alreadyTyped(VirtualKeywordRegion)
876 || alreadyTyped(OverrideKeywordRegion) || alreadyTyped(FinalKeywordRegion);
877 if (!alreadyTypedVirtualOrOverrideOrFinal) {
882 addCompletionKeyword(u8"final");
883 addCompletionKeyword(u8"virtual");
884 addCompletionKeyword(u8"override");
887 addCompletionKeyword(u8"property");
892 const QQmlJS::SourceLocation propertyIdentifier = info.regions[IdentifierRegion];
893 if (propertyKeyword.end() <= positionInfo.offset()
894 && positionInfo.offset() < propertyIdentifier.begin()) {
895 suggestReachableTypes(currentItem,
896 LocalSymbolsType::ObjectType | LocalSymbolsType::ValueType,
897 CompletionItemKind::Class, result);
904 const QQmlLSCompletionPosition &positionInfo,
905 BackInsertIterator result)
const
907 const DomItem containingBinding = currentItem.filterUp(
908 [](DomType type,
const QQmlJS::Dom::DomItem &) {
return type == DomType::Binding; },
909 FilterUpOptions::ReturnOuter);
912 if (cursorAfterColon(containingBinding, positionInfo)) {
913 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
915 if (
auto type = QQmlLSUtils::resolveExpressionType(currentItem,
916 QQmlLSUtils::ResolveOwnerType)) {
917 const QStringList names = currentItem.field(Fields::name).toString().split(u'.');
918 const QQmlJSScope *current = resolve(type->semanticScope.get(), names);
920 if (!current || current->accessSemantics() == QQmlSA::AccessSemantics::Reference) {
921 LocalSymbolsTypes options;
922 options.setFlag(LocalSymbolsType::ObjectType);
923 suggestReachableTypes(positionInfo.itemAtPosition, options,
924 CompletionItemKind::Constructor, result);
925 suggestSnippetsForRightHandSideOfBinding(positionInfo.itemAtPosition, result);
932 if (cursorInFrontOfItem(containingBinding, positionInfo)) {
933 insideQmlObjectCompletion(currentItem.containingObject(), positionInfo, result);
937 const DomItem containingObject = currentItem.qmlObject();
939 suggestBindingCompletion(positionInfo.itemAtPosition, result);
942 suggestReachableTypes(positionInfo.itemAtPosition, LocalSymbolsType::ObjectType,
943 CompletionItemKind::Constructor, result);
944 suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
948 const QQmlLSCompletionPosition &positionInfo,
949 BackInsertIterator result)
const
951 const DomItem containingFile = currentItem.containingFile();
952 insideImportCompletionHelper(containingFile, positionInfo, result);
955 if (cursorInFrontOfItem(currentItem, positionInfo)) {
956 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
957 CompletionItemKind::Constructor, result);
962 const QQmlLSCompletionPosition &positionInfo,
963 BackInsertIterator result)
const
965 const DomItem containingFile = currentItem.containingFile();
968 if (positionInfo.cursorPosition.atLineStart()) {
969 if (positionInfo.cursorPosition.base().isEmpty()) {
970 for (
const QStringView &s : std::array<QStringView, 2>({ u"pragma", u"import" })) {
972 comp.label = s.toUtf8();
973 comp.kind =
int(CompletionItemKind::Keyword);
979 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
980 CompletionItemKind::Constructor, result);
984
985
986
991 for (
auto view : std::array<QUtf8StringView, 3>{
"let",
"var",
"const" }) {
992 auto snippet = makeSnippet(QByteArray(view.data()).append(
" variable = value"),
993 QByteArray(view.data()).append(
" ${1:variable} = $0"));
994 if (option == AppendSemicolon) {
995 snippet.insertText->append(
";");
996 snippet.label.append(
";");
1003
1004
1005
1006void QQmlLSCompletion::suggestCaseAndDefaultStatementCompletion(BackInsertIterator result)
const
1009 result = makeSnippet(
"case value: statements...",
"case ${1:value}:\n\t$0");
1011 result = makeSnippet(
"case value: { statements... }",
"case ${1:value}: {\n\t$0\n}");
1014 result = makeSnippet(
"default: statements...",
"default:\n\t$0");
1016 result = makeSnippet(
"default: { statements... }",
"default: {\n\t$0\n}");
1020
1021
1022
1023
1024
1025
1026
1027
1028void QQmlLSCompletion::suggestContinueAndBreakStatementIfNeeded(
const DomItem &itemAtPosition,
1029 BackInsertIterator result)
const
1031 bool alreadyInLabel =
false;
1032 bool alreadyInSwitch =
false;
1033 for (DomItem current = itemAtPosition; current; current = current.directParent()) {
1034 switch (current.internalKind()) {
1035 case DomType::ScriptExpression:
1039 case DomType::ScriptForStatement:
1040 case DomType::ScriptForEachStatement:
1041 case DomType::ScriptWhileStatement:
1042 case DomType::ScriptDoWhileStatement: {
1043 CompletionItem continueKeyword;
1044 continueKeyword.label =
"continue";
1045 continueKeyword.kind =
int(CompletionItemKind::Keyword);
1046 result = continueKeyword;
1049 if (!alreadyInSwitch && !alreadyInLabel) {
1050 CompletionItem breakKeyword;
1051 breakKeyword.label =
"break";
1052 breakKeyword.kind =
int(CompletionItemKind::Keyword);
1053 result = breakKeyword;
1058 case DomType::ScriptSwitchStatement: {
1060 if (alreadyInSwitch || alreadyInLabel)
1062 alreadyInSwitch =
true;
1064 CompletionItem breakKeyword;
1065 breakKeyword.label =
"break";
1066 breakKeyword.kind =
int(CompletionItemKind::Keyword);
1067 result = breakKeyword;
1070 case DomType::ScriptLabelledStatement: {
1072 if (alreadyInSwitch || alreadyInLabel)
1074 alreadyInLabel =
true;
1076 CompletionItem breakKeyword;
1077 breakKeyword.label =
"break";
1078 breakKeyword.kind =
int(CompletionItemKind::Keyword);
1079 result = breakKeyword;
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104void QQmlLSCompletion::suggestJSStatementCompletion(
const DomItem &itemAtPosition,
1105 BackInsertIterator result)
const
1107 suggestJSExpressionCompletion(itemAtPosition, result);
1109 if (QQmlLSUtils::isFieldMemberAccess(itemAtPosition)
1110 || QQmlLSUtils::isFieldMemberExpression(itemAtPosition))
1114 suggestVariableDeclarationStatementCompletion(result);
1116 result = makeSnippet(
"{ statements... }",
"{\n\t$0\n}");
1119 result = makeSnippet(
"if (condition) { statements }",
"if ($1) {\n\t$0\n}");
1122 result = makeSnippet(
"do { statements } while (condition);",
"do {\n\t$1\n} while ($0);");
1125 result = makeSnippet(
"while (condition) { statements...}",
"while ($1) {\n\t$0\n}");
1128 result = makeSnippet(
"for (initializer; condition; increment) { statements... }",
1129 "for ($1;$2;$3) {\n\t$0\n}");
1132 result = makeSnippet(
"for (property in object) { statements... }",
"for ($1 in $2) {\n\t$0\n}");
1135 result = makeSnippet(
"for (element of array) { statements... }",
"for ($1 of $2) {\n\t$0\n}");
1138 result = makeSnippet(
"try { statements... } catch(error) { statements... }",
1139 "try {\n\t$1\n} catch($2) {\n\t$0\n}");
1142 result = makeSnippet(
"try { statements... } finally { statements... }",
1143 "try {\n\t$1\n} finally {\n\t$0\n}");
1146 result = makeSnippet(
1147 "try { statements... } catch(error) { statements... } finally { statements... }",
1148 "try {\n\t$1\n} catch($2) {\n\t$3\n} finally {\n\t$0\n}");
1151 for (
auto &&view : {
"return"_ba,
"throw"_ba }) {
1152 CompletionItem item;
1153 item.label = std::move(view);
1154 item.kind =
int(CompletionItemKind::Keyword);
1172 const DomType currentKind = itemAtPosition.internalKind();
1173 const DomType parentKind = itemAtPosition.directParent().internalKind();
1174 if (currentKind == DomType::ScriptCaseBlock || currentKind == DomType::ScriptCaseClause
1175 || currentKind == DomType::ScriptDefaultClause
1176 || (currentKind == DomType::List
1177 && (parentKind == DomType::ScriptCaseClause
1178 || parentKind == DomType::ScriptDefaultClause))) {
1179 suggestCaseAndDefaultStatementCompletion(result);
1181 suggestContinueAndBreakStatementIfNeeded(itemAtPosition, result);
1184void QQmlLSCompletion::insideForStatementCompletion(
const DomItem &parentForContext,
1185 const QQmlLSCompletionPosition &positionInfo,
1186 BackInsertIterator result)
const
1188 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1190 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1191 const QQmlJS::SourceLocation firstSemicolon = regions[FirstSemicolonTokenRegion];
1192 const QQmlJS::SourceLocation secondSemicolon = regions[SecondSemicolonRegion];
1193 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1195 if (betweenLocations(leftParenthesis, positionInfo, firstSemicolon)) {
1196 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1197 suggestVariableDeclarationStatementCompletion(result,
1198 AppendOption::AppendNothing);
1201 if (betweenLocations(firstSemicolon, positionInfo, secondSemicolon)
1202 || betweenLocations(secondSemicolon, positionInfo, rightParenthesis)) {
1203 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1207 if (afterLocation(rightParenthesis, positionInfo)) {
1208 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1213void QQmlLSCompletion::insideScriptLiteralCompletion(
const DomItem ¤tItem,
1214 const QQmlLSCompletionPosition &positionInfo,
1215 BackInsertIterator result)
const
1217 Q_UNUSED(currentItem);
1218 if (positionInfo.cursorPosition.base().isEmpty()) {
1219 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1225 const QQmlLSCompletionPosition &positionInfo,
1226 BackInsertIterator result)
const
1228 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1229 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1230 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1231 if (beforeLocation(positionInfo, leftParenthesis)) {
1232 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1235 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1236 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1242 const QQmlLSCompletionPosition &positionInfo,
1243 BackInsertIterator result)
const
1245 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1246 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1247 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1248 const QQmlJS::SourceLocation elseKeyword = regions[ElseKeywordRegion];
1250 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1251 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1254 if (betweenLocations(rightParenthesis, positionInfo, elseKeyword)) {
1255 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1258 if (afterLocation(elseKeyword, positionInfo)) {
1259 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1265 const QQmlLSCompletionPosition &positionInfo,
1266 BackInsertIterator result)
const
1268 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1269 const QQmlJS::SourceLocation returnKeyword = regions[ReturnKeywordRegion];
1271 if (afterLocation(returnKeyword, positionInfo)) {
1272 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1278 const QQmlLSCompletionPosition &positionInfo,
1279 BackInsertIterator result)
const
1281 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1282 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1283 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1285 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1286 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1289 if (afterLocation(rightParenthesis, positionInfo)) {
1290 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1295void QQmlLSCompletion::insideDoWhileStatement(
const DomItem &parentForContext,
1296 const QQmlLSCompletionPosition &positionInfo,
1297 BackInsertIterator result)
const
1299 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1300 const QQmlJS::SourceLocation doKeyword = regions[DoKeywordRegion];
1301 const QQmlJS::SourceLocation whileKeyword = regions[WhileKeywordRegion];
1302 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1303 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1305 if (betweenLocations(doKeyword, positionInfo, whileKeyword)) {
1306 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1309 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1310 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1315void QQmlLSCompletion::insideForEachStatement(
const DomItem &parentForContext,
1316 const QQmlLSCompletionPosition &positionInfo,
1317 BackInsertIterator result)
const
1319 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1321 const QQmlJS::SourceLocation inOf = regions[InOfTokenRegion];
1322 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1323 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1325 if (betweenLocations(leftParenthesis, positionInfo, inOf)) {
1326 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1327 suggestVariableDeclarationStatementCompletion(result);
1330 if (betweenLocations(inOf, positionInfo, rightParenthesis)) {
1331 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1335 if (afterLocation(rightParenthesis, positionInfo)) {
1336 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1341void QQmlLSCompletion::insideSwitchStatement(
const DomItem &parentForContext,
1342 const QQmlLSCompletionPosition positionInfo,
1343 BackInsertIterator result)
const
1345 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1347 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1348 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1350 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1351 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1357 const QQmlLSCompletionPosition &positionInfo,
1358 BackInsertIterator result)
const
1360 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1362 const QQmlJS::SourceLocation caseKeyword = regions[CaseKeywordRegion];
1363 const QQmlJS::SourceLocation colonToken = regions[ColonTokenRegion];
1365 if (betweenLocations(caseKeyword, positionInfo, colonToken)) {
1366 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1369 if (afterLocation(colonToken, positionInfo)) {
1370 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1377
1378
1379
1380bool QQmlLSCompletion::isCaseOrDefaultBeforeCtx(
const DomItem ¤tClause,
1381 const QQmlLSCompletionPosition &positionInfo,
1382 FileLocationRegion keywordRegion)
const
1384 Q_ASSERT(keywordRegion == QQmlJS::Dom::CaseKeywordRegion
1385 || keywordRegion == QQmlJS::Dom::DefaultKeywordRegion);
1390 const auto token = FileLocations::treeOf(currentClause)->info().regions[keywordRegion];
1391 if (afterLocation(token, positionInfo))
1398
1399
1400
1401
1402
1403
1404
1407 const QQmlLSCompletionPosition &positionInfo)
const
1409 const DomItem caseClauses = parentForContext.field(Fields::caseClauses);
1410 for (
int i = 0; i < caseClauses.indexes(); ++i) {
1411 const DomItem currentClause = caseClauses.index(i);
1412 if (isCaseOrDefaultBeforeCtx(currentClause, positionInfo, QQmlJS::Dom::CaseKeywordRegion)) {
1413 return currentClause;
1417 const DomItem defaultClause = parentForContext.field(Fields::defaultClause);
1418 if (isCaseOrDefaultBeforeCtx(defaultClause, positionInfo, QQmlJS::Dom::DefaultKeywordRegion))
1419 return parentForContext.field(Fields::defaultClause);
1421 const DomItem moreCaseClauses = parentForContext.field(Fields::moreCaseClauses);
1422 for (
int i = 0; i < moreCaseClauses.indexes(); ++i) {
1423 const DomItem currentClause = moreCaseClauses.index(i);
1424 if (isCaseOrDefaultBeforeCtx(currentClause, positionInfo, QQmlJS::Dom::CaseKeywordRegion)) {
1425 return currentClause;
1433 const QQmlLSCompletionPosition &positionInfo,
1434 BackInsertIterator result)
const
1436 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1438 const QQmlJS::SourceLocation leftBrace = regions[LeftBraceRegion];
1439 const QQmlJS::SourceLocation rightBrace = regions[RightBraceRegion];
1441 if (!betweenLocations(leftBrace, positionInfo, rightBrace))
1446 if (
const auto previousCase = previousCaseOfCaseBlock(parentForContext, positionInfo)) {
1447 suggestJSStatementCompletion(previousCase, result);
1452 suggestCaseAndDefaultStatementCompletion(result);
1456 const QQmlLSCompletionPosition &positionInfo,
1457 BackInsertIterator result)
const
1459 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1461 const QQmlJS::SourceLocation colonToken = regions[ColonTokenRegion];
1463 if (afterLocation(colonToken, positionInfo)) {
1464 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1470 const DomItem &parentForContext,
const QQmlLSCompletionPosition &positionInfo,
1471 BackInsertIterator result)
const
1473 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1475 const QQmlJS::SourceLocation operatorLocation = regions[OperatorTokenRegion];
1477 if (beforeLocation(positionInfo, operatorLocation)) {
1478 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1481 if (afterLocation(operatorLocation, positionInfo)) {
1482 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1528 const QQmlLSCompletionPosition &positionInfo,
1529 BackInsertIterator result)
const
1531 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1533 const QQmlJS::SourceLocation equal = regions[EqualTokenRegion];
1535 if (!afterLocation(equal, positionInfo))
1539 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1543
1544
1545
1546void QQmlLSCompletion::insideVariableDeclarationEntry(
const DomItem &parentForContext,
1547 const QQmlLSCompletionPosition &positionInfo,
1548 BackInsertIterator result)
const
1550 insideScriptPattern(parentForContext, positionInfo, result);
1553void QQmlLSCompletion::insideThrowStatement(
const DomItem &parentForContext,
1554 const QQmlLSCompletionPosition &positionInfo,
1555 BackInsertIterator result)
const
1557 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1559 const QQmlJS::SourceLocation throwKeyword = regions[ThrowKeywordRegion];
1561 if (afterLocation(throwKeyword, positionInfo)) {
1562 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1567void QQmlLSCompletion::insideLabelledStatement(
const DomItem &parentForContext,
1568 const QQmlLSCompletionPosition &positionInfo,
1569 BackInsertIterator result)
const
1571 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1573 const QQmlJS::SourceLocation colon = regions[ColonTokenRegion];
1575 if (afterLocation(colon, positionInfo)) {
1576 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1587
1588
1589
1590static void collectLabels(
const DomItem &context, QQmlLSCompletion::BackInsertIterator result)
1592 for (DomItem current = context; current; current = current.directParent()) {
1593 if (current.internalKind() == DomType::ScriptLabelledStatement) {
1594 const QString label = current.field(Fields::label).value().toString();
1595 if (label.isEmpty())
1597 CompletionItem item;
1598 item.label = label.toUtf8();
1599 item.kind =
int(CompletionItemKind::Value);
1602 }
else if (current.internalKind() == DomType::ScriptExpression) {
1610void QQmlLSCompletion::insideContinueStatement(
const DomItem &parentForContext,
1611 const QQmlLSCompletionPosition &positionInfo,
1612 BackInsertIterator result)
const
1614 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1616 const QQmlJS::SourceLocation continueKeyword = regions[ContinueKeywordRegion];
1618 if (afterLocation(continueKeyword, positionInfo)) {
1619 collectLabels(parentForContext, result);
1624void QQmlLSCompletion::insideBreakStatement(
const DomItem &parentForContext,
1625 const QQmlLSCompletionPosition &positionInfo,
1626 BackInsertIterator result)
const
1628 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1630 const QQmlJS::SourceLocation breakKeyword = regions[BreakKeywordRegion];
1632 if (afterLocation(breakKeyword, positionInfo)) {
1633 collectLabels(parentForContext, result);
1638void QQmlLSCompletion::insideConditionalExpression(
const DomItem &parentForContext,
1639 const QQmlLSCompletionPosition &positionInfo,
1640 BackInsertIterator result)
const
1642 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1644 const QQmlJS::SourceLocation questionMark = regions[QuestionMarkTokenRegion];
1645 const QQmlJS::SourceLocation colon = regions[ColonTokenRegion];
1647 if (beforeLocation(positionInfo, questionMark)) {
1648 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1651 if (betweenLocations(questionMark, positionInfo, colon)) {
1652 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1655 if (afterLocation(colon, positionInfo)) {
1656 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1661void QQmlLSCompletion::insideUnaryExpression(
const DomItem &parentForContext,
1662 const QQmlLSCompletionPosition &positionInfo,
1663 BackInsertIterator result)
const
1665 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1667 const QQmlJS::SourceLocation operatorToken = regions[OperatorTokenRegion];
1669 if (afterLocation(operatorToken, positionInfo)) {
1670 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1675void QQmlLSCompletion::insidePostExpression(
const DomItem &parentForContext,
1676 const QQmlLSCompletionPosition &positionInfo,
1677 BackInsertIterator result)
const
1679 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1681 const QQmlJS::SourceLocation operatorToken = regions[OperatorTokenRegion];
1683 if (beforeLocation(positionInfo, operatorToken)) {
1684 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1689void QQmlLSCompletion::insideParenthesizedExpression(
const DomItem &parentForContext,
1690 const QQmlLSCompletionPosition &positionInfo,
1691 BackInsertIterator result)
const
1693 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1695 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1696 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1698 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1699 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1704void QQmlLSCompletion::insideTemplateLiteral(
const DomItem &parentForContext,
1705 const QQmlLSCompletionPosition &positionInfo,
1706 BackInsertIterator result)
const
1708 Q_UNUSED(parentForContext);
1709 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1713 const QQmlLSCompletionPosition &positionInfo,
1714 BackInsertIterator result)
const
1716 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1717 const QQmlJS::SourceLocation newKeyword = regions[NewKeywordRegion];
1719 if (afterLocation(newKeyword, positionInfo)) {
1720 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1725void QQmlLSCompletion::insideNewMemberExpression(
const DomItem &parentForContext,
1726 const QQmlLSCompletionPosition &positionInfo,
1727 BackInsertIterator result)
const
1729 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1730 const QQmlJS::SourceLocation newKeyword = regions[NewKeywordRegion];
1731 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1732 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1734 if (betweenLocations(newKeyword, positionInfo, leftParenthesis)
1735 || betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1736 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1741void QQmlLSCompletion::signalHandlerCompletion(
const QQmlJSScope::ConstPtr &scope,
1742 QDuplicateTracker<QString> *usedNames,
1743 BackInsertIterator result)
const
1745 const auto keyValues = scope->methods().asKeyValueRange();
1746 for (
const auto &[name, method] : keyValues) {
1747 if (method.access() != QQmlJSMetaMethod::Public
1748 || method.methodType() != QQmlJSMetaMethodType::Signal) {
1751 if (usedNames && usedNames->hasSeen(name)) {
1755 CompletionItem completion;
1756 completion.label = QQmlSignalNames::signalNameToHandlerName(name).toUtf8();
1757 completion.kind =
int(CompletionItemKind::Method);
1758 result = completion;
1763
1764
1765
1768 const CompletionContextStrings &contextStrings)
const
1770 QList<CompletionItem> result;
1771 collectCompletions(currentItem, contextStrings, std::back_inserter(result));
1776 const CompletionContextStrings &contextStrings,
1777 BackInsertIterator result)
const
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800 const QQmlLSCompletionPosition positionInfo{ currentItem, contextStrings };
1801 for (DomItem currentParent = currentItem; currentParent;
1802 currentParent = currentParent.directParent()) {
1803 const DomType currentType = currentParent.internalKind();
1805 switch (currentType) {
1809 case DomType::Pragma:
1810 insidePragmaCompletion(currentParent, positionInfo, result);
1812 case DomType::ScriptType: {
1813 if (currentParent.directParent().internalKind() == DomType::QmlObject) {
1814 insideQmlObjectCompletion(currentParent.directParent(), positionInfo, result);
1818 LocalSymbolsTypes options;
1819 options.setFlag(LocalSymbolsType::ObjectType);
1820 options.setFlag(LocalSymbolsType::ValueType);
1821 suggestReachableTypes(currentItem, options, CompletionItemKind::Class, result);
1824 case DomType::ScriptFormalParameter:
1827 case DomType::Binding:
1828 insideBindingCompletion(currentParent, positionInfo, result);
1830 case DomType::Import:
1831 insideImportCompletion(currentParent, positionInfo, result);
1833 case DomType::ScriptForStatement:
1834 insideForStatementCompletion(currentParent, positionInfo, result);
1836 case DomType::ScriptBlockStatement:
1837 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1839 case DomType::QmlFile:
1840 insideQmlFileCompletion(currentParent, positionInfo, result);
1842 case DomType::QmlObject:
1843 insideQmlObjectCompletion(currentParent, positionInfo, result);
1845 case DomType::MethodInfo:
1848 case DomType::PropertyDefinition:
1849 insidePropertyDefinitionCompletion(currentParent, positionInfo, result);
1851 case DomType::ScriptBinaryExpression:
1853 if (QQmlLSUtils::isFieldMemberExpression(currentParent))
1855 insideBinaryExpressionCompletion(currentParent, positionInfo, result);
1857 case DomType::ScriptLiteral:
1858 insideScriptLiteralCompletion(currentParent, positionInfo, result);
1860 case DomType::ScriptRegExpLiteral:
1863 case DomType::ScriptCallExpression:
1864 insideCallExpression(currentParent, positionInfo, result);
1866 case DomType::ScriptIfStatement:
1867 insideIfStatement(currentParent, positionInfo, result);
1869 case DomType::ScriptReturnStatement:
1870 insideReturnStatement(currentParent, positionInfo, result);
1872 case DomType::ScriptWhileStatement:
1873 insideWhileStatement(currentParent, positionInfo, result);
1875 case DomType::ScriptDoWhileStatement:
1876 insideDoWhileStatement(currentParent, positionInfo, result);
1878 case DomType::ScriptForEachStatement:
1879 insideForEachStatement(currentParent, positionInfo, result);
1881 case DomType::ScriptTryCatchStatement:
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1901 case DomType::ScriptSwitchStatement:
1902 insideSwitchStatement(currentParent, positionInfo, result);
1904 case DomType::ScriptCaseClause:
1905 insideCaseClause(currentParent, positionInfo, result);
1907 case DomType::ScriptDefaultClause:
1908 if (ctxBeforeStatement(positionInfo, currentParent, QQmlJS::Dom::DefaultKeywordRegion))
1910 insideDefaultClause(currentParent, positionInfo, result);
1912 case DomType::ScriptCaseBlock:
1913 insideCaseBlock(currentParent, positionInfo, result);
1915 case DomType::ScriptVariableDeclaration:
1920 case DomType::ScriptVariableDeclarationEntry:
1921 insideVariableDeclarationEntry(currentParent, positionInfo, result);
1923 case DomType::ScriptProperty:
1926 case DomType::ScriptPattern:
1927 insideScriptPattern(currentParent, positionInfo, result);
1929 case DomType::ScriptThrowStatement:
1930 insideThrowStatement(currentParent, positionInfo, result);
1932 case DomType::ScriptLabelledStatement:
1933 insideLabelledStatement(currentParent, positionInfo, result);
1935 case DomType::ScriptContinueStatement:
1936 insideContinueStatement(currentParent, positionInfo, result);
1938 case DomType::ScriptBreakStatement:
1939 insideBreakStatement(currentParent, positionInfo, result);
1941 case DomType::ScriptConditionalExpression:
1942 insideConditionalExpression(currentParent, positionInfo, result);
1944 case DomType::ScriptUnaryExpression:
1945 insideUnaryExpression(currentParent, positionInfo, result);
1947 case DomType::ScriptPostExpression:
1948 insidePostExpression(currentParent, positionInfo, result);
1950 case DomType::ScriptParenthesizedExpression:
1951 insideParenthesizedExpression(currentParent, positionInfo, result);
1953 case DomType::ScriptTemplateLiteral:
1954 insideTemplateLiteral(currentParent, positionInfo, result);
1956 case DomType::ScriptTemplateStringPart:
1959 case DomType::ScriptNewExpression:
1960 insideNewExpression(currentParent, positionInfo, result);
1962 case DomType::ScriptNewMemberExpression:
1963 insideNewMemberExpression(currentParent, positionInfo, result);
1965 case DomType::ScriptThisExpression:
1968 case DomType::ScriptSuperLiteral:
1971 case DomType::Comment:
1977 case DomType::ScriptArray:
1978 case DomType::ScriptObject:
1979 case DomType::ScriptElision:
1980 case DomType::ScriptArrayEntry:
1990 qCDebug(QQmlLSUtilsLog) <<
"No completion was found for current request.";
1996 const auto keys = pluginLoader.metaDataKeys();
1997 for (qsizetype i = 0; i < keys.size(); ++i) {
1998 auto instance = std::unique_ptr<QQmlLSPlugin>(
1999 qobject_cast<QQmlLSPlugin *>(pluginLoader.instance(i)));
2002 if (
auto completionInstance = instance->createCompletionPlugin())
2003 m_plugins.push_back(std::move(completionInstance));
2008
2009
2010
2011void QQmlLSCompletion::collectFromPlugins(qxp::function_ref<CompletionFromPluginFunction> f,
2012 BackInsertIterator result)
const
2014 for (
const auto &plugin : m_plugins) {
2016 f(plugin.get(), result);
2020void QQmlLSCompletion::suggestSnippetsForLeftHandSideOfBinding(
const DomItem &itemAtPosition,
2021 BackInsertIterator result)
const
2024 [&itemAtPosition](QQmlLSCompletionPlugin *p, BackInsertIterator result) {
2025 p->suggestSnippetsForLeftHandSideOfBinding(itemAtPosition, result);
2030void QQmlLSCompletion::suggestSnippetsForRightHandSideOfBinding(
const DomItem &itemAtPosition,
2031 BackInsertIterator result)
const
2034 [&itemAtPosition](QQmlLSCompletionPlugin *p, BackInsertIterator result) {
2035 p->suggestSnippetsForRightHandSideOfBinding(itemAtPosition, result);
QQmlLSCompletion provides completions for all kinds of QML and JS constructs.
QList< CompletionItem > completions(const DomItem ¤tItem, const CompletionContextStrings &ctx) const
QQmlLSCompletion(const QFactoryLoader &pluginLoader)
Combined button and popup list for selecting options.
void collectFromAllJavaScriptParents(const F &&f, const QQmlJSScope::ConstPtr &scope)
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
static void collectLabels(const DomItem &context, QQmlLSCompletion::BackInsertIterator result)
static bool testScopeSymbol(const QQmlJSScope::ConstPtr &scope, LocalSymbolsTypes options, CompletionItemKind kind)