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 = 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 = 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 = CompletionItemKind::Keyword;
201 DomItem env = file.environment();
202 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
203 switch (importCompletionType) {
215void QQmlLSCompletion::idsCompletions(
const DomItem &component, BackInsertIterator it)
const
217 qCDebug(QQmlLSCompletionLog) <<
"adding ids completions";
218 for (
const QString &k : component.field(Fields::ids).keys()) {
220 comp.label = k.toUtf8();
221 comp.kind = CompletionItemKind::Value;
226static bool testScopeSymbol(
const QQmlJSScope::ConstPtr &scope, LocalSymbolsTypes options,
227 CompletionItemKind kind)
229 const bool currentIsSingleton = scope->isSingleton();
230 const bool currentIsAttached = !scope->attachedType().isNull();
231 if ((options & LocalSymbolsType::Singleton) && currentIsSingleton) {
234 if ((options & LocalSymbolsType::AttachedType) && currentIsAttached) {
237 const bool isObjectType = scope->isReferenceType();
238 if (options & LocalSymbolsType::ObjectType && !currentIsSingleton && isObjectType) {
239 return kind != CompletionItemKind::Constructor || scope->isCreatable();
241 if (options & LocalSymbolsType::ValueType && !currentIsSingleton && !isObjectType) {
248
249
250
251void QQmlLSCompletion::suggestReachableTypes(
const DomItem &el, LocalSymbolsTypes options,
252 CompletionItemKind kind, BackInsertIterator it)
const
254 auto file = el.containingFile().as<QmlFile>();
257 auto resolver = file->typeResolver();
261 const QString requiredQualifiers = QQmlLSUtils::qualifiersFrom(el);
262 const auto keyValueRange = resolver->importedTypes().asKeyValueRange();
263 for (
const auto &type : keyValueRange) {
265 const bool isMarkerType = type.first.contains(u"$internal$.")
266 || type.first.contains(u"$anonymous$.") || type.first.contains(u"$module$.");
267 if (isMarkerType || !type.first.startsWith(requiredQualifiers))
270 auto &scope = type.second.scope;
274 if (!testScopeSymbol(scope, options, kind))
277 CompletionItem completion;
278 completion.label = QStringView(type.first).sliced(requiredQualifiers.size()).toUtf8();
279 completion.kind = kind;
284void QQmlLSCompletion::jsIdentifierCompletion(
const QQmlJSScope::ConstPtr &scope,
285 QDuplicateTracker<QString> *usedNames,
286 BackInsertIterator it)
const
288 for (
const auto &[name, jsIdentifier] : scope->ownJSIdentifiers().asKeyValueRange()) {
289 CompletionItem completion;
290 if (usedNames && usedNames->hasSeen(name)) {
293 completion.label = name.toUtf8();
294 completion.kind = CompletionItemKind::Variable;
295 QString detail = u"has type "_s;
296 if (jsIdentifier.typeName) {
297 if (jsIdentifier.isConst) {
298 detail.append(u"const ");
300 detail.append(*jsIdentifier.typeName);
302 detail.append(jsIdentifier.isConst ? u"const"_s : u"var"_s);
304 completion.detail = detail.toUtf8();
310 QDuplicateTracker<QString> *usedNames,
311 BackInsertIterator it)
const
314 for (
const auto &[name, method] : scope->methods().asKeyValueRange()) {
315 if (method.access() != QQmlJSMetaMethod::Public)
317 if (usedNames && usedNames->hasSeen(name)) {
320 CompletionItem completion;
321 completion.label = name.toUtf8();
322 completion.kind = CompletionItemKind::Method;
329void QQmlLSCompletion::propertyCompletion(
const QQmlJSScope::ConstPtr &scope,
330 QDuplicateTracker<QString> *usedNames,
331 BackInsertIterator it)
const
333 for (
const auto &[name, property] : scope->properties().asKeyValueRange()) {
334 if (usedNames && usedNames->hasSeen(name)) {
337 CompletionItem completion;
338 completion.label = name.toUtf8();
339 completion.kind = CompletionItemKind::Property;
340 QString detail{ u"has type "_s };
341 if (!property.isWritable())
342 detail.append(u"readonly "_s);
343 detail.append(property.typeName().isEmpty() ? u"var"_s : property.typeName());
344 completion.detail = detail.toUtf8();
349void QQmlLSCompletion::enumerationCompletion(
const QQmlJSScope::ConstPtr &scope,
350 QDuplicateTracker<QString> *usedNames,
351 BackInsertIterator it)
const
353 for (
const QQmlJSMetaEnum &enumerator : scope->enumerations()) {
354 if (usedNames && usedNames->hasSeen(enumerator.name())) {
357 CompletionItem completion;
358 completion.label = enumerator.name().toUtf8();
359 completion.kind = CompletionItemKind::Enum;
364void QQmlLSCompletion::enumerationValueCompletionHelper(
const QStringList &enumeratorKeys,
365 BackInsertIterator it)
const
367 for (
const QString &enumeratorKey : enumeratorKeys) {
368 CompletionItem completion;
369 completion.label = enumeratorKey.toUtf8();
370 completion.kind = CompletionItemKind::EnumMember;
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
395void QQmlLSCompletion::enumerationValueCompletion(
const QQmlJSScope::ConstPtr &scope,
396 const QString &enumeratorName,
397 BackInsertIterator result)
const
399 auto enumerator = scope->enumeration(enumeratorName);
400 if (enumerator.isValid()) {
401 enumerationValueCompletionHelper(enumerator.keys(), result);
405 const bool enforcesScopedEnums = scope->enforcesScopedEnums();
406 for (
const QQmlJSMetaEnum &enumerator : scope->enumerations()) {
407 if (enforcesScopedEnums && enumerator.isScoped())
409 enumerationValueCompletionHelper(enumerator.keys(), result);
414
415
416
417
418
419
420
421
422
423
424
425
429 for (QQmlJSScope::ConstPtr current = scope; current; current = current->parentScope()) {
431 if (current->scopeType() == QQmlSA::ScopeType::QMLScope)
437
438
439
440
441
442
443
444
445
447 const QQmlJSScope::ConstPtr &scope,
const QString &enumName,
448 QDuplicateTracker<QString> &usedNames, BackInsertIterator result)
const
450 enumerationValueCompletion(scope, enumName, result);
453 if (
auto enumerator = scope->enumeration(enumName); !enumerator.isValid()) {
454 enumerationCompletion(scope, &usedNames, result);
459
460
461
462
463
464
465
466
467
468DomItem
QQmlLSCompletion::ownerOfQualifiedExpression(
const DomItem &qualifiedExpression)
const
473 const bool askForCompletionOnDot = QQmlLSUtils::isFieldMemberExpression(qualifiedExpression);
474 const bool hasQualifier =
475 QQmlLSUtils::isFieldMemberAccess(qualifiedExpression) || askForCompletionOnDot;
480 const DomItem owner =
481 (askForCompletionOnDot ? qualifiedExpression : qualifiedExpression.directParent())
482 .field(Fields::left);
487
488
489
490
491
492
493
494
495void QQmlLSCompletion::suggestJSExpressionCompletion(
const DomItem &scriptIdentifier,
496 BackInsertIterator result)
const
498 QDuplicateTracker<QString> usedNames;
499 QQmlJSScope::ConstPtr nearestScope;
501 const DomItem owner = ownerOfQualifiedExpression(scriptIdentifier);
504 for (QUtf8StringView view : std::array<QUtf8StringView, 3>{
"null",
"false",
"true" }) {
505 CompletionItem completion;
506 completion.label = view.data();
507 completion.kind = CompletionItemKind::Value;
510 idsCompletions(scriptIdentifier.component(), result);
511 suggestReachableTypes(scriptIdentifier,
512 LocalSymbolsType::Singleton | LocalSymbolsType::AttachedType,
513 CompletionItemKind::Class, result);
515 auto scope = scriptIdentifier.nearestSemanticScope();
518 nearestScope = scope;
520 enumerationCompletion(nearestScope, &usedNames, result);
522 auto ownerExpressionType = QQmlLSUtils::resolveExpressionType(
523 owner, QQmlLSUtils::ResolveActualTypeForFieldMemberExpression);
524 if (!ownerExpressionType || !ownerExpressionType->semanticScope)
526 nearestScope = ownerExpressionType->semanticScope;
528 switch (ownerExpressionType->type) {
529 case QQmlLSUtils::EnumeratorValueIdentifier:
531 case QQmlLSUtils::EnumeratorIdentifier:
532 suggestEnumerationsAndEnumerationValues(nearestScope, *ownerExpressionType->name,
535 case QQmlLSUtils::QmlComponentIdentifier:
538 if (QQmlJSScope::ConstPtr attachedType =
539 ownerExpressionType->semanticScope->attachedType()) {
540 methodCompletion(attachedType, &usedNames, result);
541 propertyCompletion(attachedType, &usedNames, result);
542 suggestEnumerationsAndEnumerationValues(
543 attachedType, *ownerExpressionType->name, usedNames, result);
546 case QQmlLSUtils::SingletonIdentifier:
547 if (ownerExpressionType->name)
548 suggestEnumerationsAndEnumerationValues(nearestScope, *ownerExpressionType->name,
556 Q_ASSERT(nearestScope);
558 methodCompletion(nearestScope, &usedNames, result);
559 propertyCompletion(nearestScope, &usedNames, result);
563 collectFromAllJavaScriptParents(
564 [
this, &usedNames, result](
const QQmlJSScope::ConstPtr &scope) {
565 jsIdentifierCompletion(scope, &usedNames, result);
568 collectFromAllJavaScriptParents(
569 [
this, &usedNames, result](
const QQmlJSScope::ConstPtr &scope) {
570 methodCompletion(scope, &usedNames, result);
573 collectFromAllJavaScriptParents(
574 [
this, &usedNames, result](
const QQmlJSScope::ConstPtr &scope) {
575 propertyCompletion(scope, &usedNames, result);
579 auto file = scriptIdentifier.containingFile().as<QmlFile>();
582 auto resolver = file->typeResolver();
586 const auto globals = resolver->jsGlobalObject();
587 methodCompletion(globals, &usedNames, result);
588 propertyCompletion(globals, &usedNames, result);
594 for (
const QString &name : names) {
595 if (
auto property = current->property(name); property.isValid()) {
596 if (
auto propertyType = property.type().get()) {
597 current = propertyType;
607 const QQmlLSCompletionPosition &positionInfo)
609 auto fileLocations = FileLocations::treeOf(parentForContext)->info().fullRegion;
610 return positionInfo.offset() <= fileLocations.begin();
614 const QQmlLSCompletionPosition &positionInfo)
616 auto location = FileLocations::treeOf(currentItem)->info();
617 auto region = location.regions.constFind(ColonTokenRegion);
619 if (region == location.regions.constEnd())
622 if (region.value().isValid() && region.value().begin() < positionInfo.offset()) {
629
630
631
632
633
634
635
636
637
638
639static const QMap<QString, QList<QString>> valuesForPragmas{
640 { u"ComponentBehavior"_s, { u"Unbound"_s, u"Bound"_s } },
641 { u"NativeMethodBehavior"_s, { u"AcceptThisObject"_s, u"RejectThisObject"_s } },
642 { u"ListPropertyAssignBehavior"_s, { u"Append"_s, u"Replace"_s, u"ReplaceIfNotDefault"_s } },
643 { u"Singleton"_s, {} },
644 { u"ValueTypeBehavior"_s, { u"Addressable"_s, u"Inaddressable"_s } },
647void QQmlLSCompletion::insidePragmaCompletion(QQmlJS::Dom::DomItem currentItem,
648 const QQmlLSCompletionPosition &positionInfo,
649 BackInsertIterator result)
const
651 if (cursorAfterColon(currentItem, positionInfo)) {
652 const QString name = currentItem.field(Fields::name).value().toString();
653 auto values = valuesForPragmas.constFind(name);
654 if (values == valuesForPragmas.constEnd())
657 for (
const auto &value : *values) {
659 comp.label = value.toUtf8();
660 comp.kind = CompletionItemKind::Value;
666 for (
const auto &pragma : valuesForPragmas.asKeyValueRange()) {
668 comp.label = pragma.first.toUtf8();
669 if (!pragma.second.isEmpty()) {
670 comp.insertText = QString(pragma.first).append(u": ").toUtf8();
672 comp.kind = CompletionItemKind::Value;
677void QQmlLSCompletion::insideQmlObjectCompletion(
const DomItem &parentForContext,
678 const QQmlLSCompletionPosition &positionInfo,
679 BackInsertIterator result)
const
682 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
684 const QQmlJS::SourceLocation leftBrace = regions[LeftBraceRegion];
685 const QQmlJS::SourceLocation rightBrace = regions[RightBraceRegion];
687 if (beforeLocation(positionInfo, leftBrace)) {
688 LocalSymbolsTypes options;
689 options.setFlag(LocalSymbolsType::ObjectType);
690 suggestReachableTypes(positionInfo.itemAtPosition, options, CompletionItemKind::Constructor,
692 if (parentForContext.directParent().internalKind() == DomType::Binding)
693 suggestSnippetsForRightHandSideOfBinding(positionInfo.itemAtPosition, result);
695 suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
697 if (QQmlLSUtils::isFieldMemberExpression(positionInfo.itemAtPosition)) {
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
724 if (betweenLocations(leftBrace, positionInfo, rightBrace)) {
728 constexpr static std::array completions{
""_L1,
736 "default virtual "_L1,
737 "default override "_L1,
738 "default required "_L1,
739 "final required "_L1,
740 "final readonly "_L1,
741 "virtual required "_L1,
742 "virtual readonly "_L1,
743 "override required "_L1,
744 "override readonly "_L1,
745 "default final required "_L1,
746 "default virtual required "_L1,
747 "default override required "_L1 };
748 for (QLatin1StringView completion : completions) {
753 if (!completion.contains(QLatin1StringView(
"readonly"))) {
754 result = makeSnippet(
755 QByteArray(completion.data()).append(
"property type name;"),
756 QByteArray(completion.data()).append(
"property ${1:type} ${0:name};"));
759 if (!completion.contains(QLatin1StringView(
"required"))) {
760 result = makeSnippet(
761 QByteArray(completion.data()).append(
"property type name: value;"),
762 QByteArray(completion.data())
763 .append(
"property ${1:type} ${2:name}: ${0:value};"));
768 result = makeSnippet(
"signal name(arg1:type1, ...)",
"signal ${1:name}($0)");
771 result = makeSnippet(
"signal name;",
"signal ${0:name};");
774 result = makeSnippet(
"required name;",
"required ${0:name};");
777 result = makeSnippet(
"function name(args...): returnType { statements...}",
778 "function ${1:name}($2): ${3:returnType} {\n\t$0\n}");
781 result = makeSnippet(
"enum name { Values...}",
"enum ${1:name} {\n\t${0:values}\n}");
784 result = makeSnippet(
"component Name: BaseType { ... }",
785 "component ${1:name}: ${2:baseType} {\n\t$0\n}");
787 suggestBindingCompletion(positionInfo.itemAtPosition, result);
790 const DomItem containingFile = parentForContext.containingFile();
791 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
792 CompletionItemKind::Constructor, result);
793 suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
800 const DomItem ¤tItem,
const QQmlLSCompletionPosition &positionInfo,
801 BackInsertIterator result)
const
803 auto info = FileLocations::treeOf(currentItem)->info();
804 const QQmlJS::SourceLocation propertyKeyword = info.regions[PropertyKeywordRegion];
807 if (positionInfo.offset() < propertyKeyword.end()) {
808 auto addCompletionKeyword = [&result](QUtf8StringView view) {
810 item.label = view.data();
811 item.kind = CompletionItemKind::Keyword;
815 const auto alreadyTyped = [&info,
816 &positionInfo](
const FileLocationRegion &keywordRegion) ->
bool {
817 const auto &keywordSLoc = info.regions[keywordRegion];
818 return keywordSLoc.isValid() && keywordSLoc.begin() < positionInfo.offset();
821 const bool alreadyTypedReadonlyOrRequired =
822 alreadyTyped(ReadonlyKeywordRegion) || alreadyTyped(RequiredKeywordRegion);
823 if (!alreadyTypedReadonlyOrRequired) {
828 addCompletionKeyword(u8"readonly");
829 addCompletionKeyword(u8"required");
832 if (!alreadyTyped(DefaultKeywordRegion)) {
833 addCompletionKeyword(u8"default");
836 const bool alreadyTypedVirtualOrOverrideOrFinal = alreadyTyped(VirtualKeywordRegion)
837 || alreadyTyped(OverrideKeywordRegion) || alreadyTyped(FinalKeywordRegion);
838 if (!alreadyTypedVirtualOrOverrideOrFinal) {
843 addCompletionKeyword(u8"final");
844 addCompletionKeyword(u8"virtual");
845 addCompletionKeyword(u8"override");
848 addCompletionKeyword(u8"property");
853 const QQmlJS::SourceLocation propertyIdentifier = info.regions[IdentifierRegion];
854 if (propertyKeyword.end() <= positionInfo.offset()
855 && positionInfo.offset() < propertyIdentifier.begin()) {
856 suggestReachableTypes(currentItem,
857 LocalSymbolsType::ObjectType | LocalSymbolsType::ValueType,
858 CompletionItemKind::Class, result);
865 const QQmlLSCompletionPosition &positionInfo,
866 BackInsertIterator result)
const
868 const DomItem containingBinding = currentItem.filterUp(
869 [](DomType type,
const QQmlJS::Dom::DomItem &) {
return type == DomType::Binding; },
870 FilterUpOptions::ReturnOuter);
873 if (cursorAfterColon(containingBinding, positionInfo)) {
874 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
876 if (
auto type = QQmlLSUtils::resolveExpressionType(currentItem,
877 QQmlLSUtils::ResolveOwnerType)) {
878 const QStringList names = currentItem.field(Fields::name).toString().split(u'.');
879 const QQmlJSScope *current = resolve(type->semanticScope.get(), names);
881 if (!current || current->accessSemantics() == QQmlSA::AccessSemantics::Reference) {
882 LocalSymbolsTypes options;
883 options.setFlag(LocalSymbolsType::ObjectType);
884 suggestReachableTypes(positionInfo.itemAtPosition, options,
885 CompletionItemKind::Constructor, result);
886 suggestSnippetsForRightHandSideOfBinding(positionInfo.itemAtPosition, result);
893 if (cursorInFrontOfItem(containingBinding, positionInfo)) {
894 insideQmlObjectCompletion(currentItem.containingObject(), positionInfo, result);
898 const DomItem containingObject = currentItem.qmlObject();
900 suggestBindingCompletion(positionInfo.itemAtPosition, result);
903 suggestReachableTypes(positionInfo.itemAtPosition, LocalSymbolsType::ObjectType,
904 CompletionItemKind::Constructor, result);
905 suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
909 const QQmlLSCompletionPosition &positionInfo,
910 BackInsertIterator result)
const
912 const DomItem containingFile = currentItem.containingFile();
913 insideImportCompletionHelper(containingFile, positionInfo, result);
916 if (cursorInFrontOfItem(currentItem, positionInfo)) {
917 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
918 CompletionItemKind::Constructor, result);
923 const QQmlLSCompletionPosition &positionInfo,
924 BackInsertIterator result)
const
926 const DomItem containingFile = currentItem.containingFile();
929 if (positionInfo.cursorPosition.atLineStart()) {
930 if (positionInfo.cursorPosition.base().isEmpty()) {
931 for (
const QStringView &s : std::array<QStringView, 2>({ u"pragma", u"import" })) {
933 comp.label = s.toUtf8();
934 comp.kind = CompletionItemKind::Keyword;
940 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
941 CompletionItemKind::Constructor, result);
945
946
947
952 for (
auto view : std::array<QUtf8StringView, 3>{
"let",
"var",
"const" }) {
953 auto snippet = makeSnippet(QByteArray(view.data()).append(
" variable = value"),
954 QByteArray(view.data()).append(
" ${1:variable} = $0"));
955 if (option == AppendSemicolon) {
956 snippet.insertText->append(
";");
957 snippet.label.append(
";");
964
965
966
967void QQmlLSCompletion::suggestCaseAndDefaultStatementCompletion(BackInsertIterator result)
const
970 result = makeSnippet(
"case value: statements...",
"case ${1:value}:\n\t$0");
972 result = makeSnippet(
"case value: { statements... }",
"case ${1:value}: {\n\t$0\n}");
975 result = makeSnippet(
"default: statements...",
"default:\n\t$0");
977 result = makeSnippet(
"default: { statements... }",
"default: {\n\t$0\n}");
981
982
983
984
985
986
987
988
989void QQmlLSCompletion::suggestContinueAndBreakStatementIfNeeded(
const DomItem &itemAtPosition,
990 BackInsertIterator result)
const
992 bool alreadyInLabel =
false;
993 bool alreadyInSwitch =
false;
994 for (DomItem current = itemAtPosition; current; current = current.directParent()) {
995 switch (current.internalKind()) {
996 case DomType::ScriptExpression:
1000 case DomType::ScriptForStatement:
1001 case DomType::ScriptForEachStatement:
1002 case DomType::ScriptWhileStatement:
1003 case DomType::ScriptDoWhileStatement: {
1004 CompletionItem continueKeyword;
1005 continueKeyword.label =
"continue";
1006 continueKeyword.kind = CompletionItemKind::Keyword;
1007 result = continueKeyword;
1010 if (!alreadyInSwitch && !alreadyInLabel) {
1011 CompletionItem breakKeyword;
1012 breakKeyword.label =
"break";
1013 breakKeyword.kind = CompletionItemKind::Keyword;
1014 result = breakKeyword;
1019 case DomType::ScriptSwitchStatement: {
1021 if (alreadyInSwitch || alreadyInLabel)
1023 alreadyInSwitch =
true;
1025 CompletionItem breakKeyword;
1026 breakKeyword.label =
"break";
1027 breakKeyword.kind = CompletionItemKind::Keyword;
1028 result = breakKeyword;
1031 case DomType::ScriptLabelledStatement: {
1033 if (alreadyInSwitch || alreadyInLabel)
1035 alreadyInLabel =
true;
1037 CompletionItem breakKeyword;
1038 breakKeyword.label =
"break";
1039 breakKeyword.kind = CompletionItemKind::Keyword;
1040 result = breakKeyword;
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065void QQmlLSCompletion::suggestJSStatementCompletion(
const DomItem &itemAtPosition,
1066 BackInsertIterator result)
const
1068 suggestJSExpressionCompletion(itemAtPosition, result);
1070 if (QQmlLSUtils::isFieldMemberAccess(itemAtPosition)
1071 || QQmlLSUtils::isFieldMemberExpression(itemAtPosition))
1075 suggestVariableDeclarationStatementCompletion(result);
1077 result = makeSnippet(
"{ statements... }",
"{\n\t$0\n}");
1080 result = makeSnippet(
"if (condition) { statements }",
"if ($1) {\n\t$0\n}");
1083 result = makeSnippet(
"do { statements } while (condition);",
"do {\n\t$1\n} while ($0);");
1086 result = makeSnippet(
"while (condition) { statements...}",
"while ($1) {\n\t$0\n}");
1089 result = makeSnippet(
"for (initializer; condition; increment) { statements... }",
1090 "for ($1;$2;$3) {\n\t$0\n}");
1093 result = makeSnippet(
"for (property in object) { statements... }",
"for ($1 in $2) {\n\t$0\n}");
1096 result = makeSnippet(
"for (element of array) { statements... }",
"for ($1 of $2) {\n\t$0\n}");
1099 result = makeSnippet(
"try { statements... } catch(error) { statements... }",
1100 "try {\n\t$1\n} catch($2) {\n\t$0\n}");
1103 result = makeSnippet(
"try { statements... } finally { statements... }",
1104 "try {\n\t$1\n} finally {\n\t$0\n}");
1107 result = makeSnippet(
1108 "try { statements... } catch(error) { statements... } finally { statements... }",
1109 "try {\n\t$1\n} catch($2) {\n\t$3\n} finally {\n\t$0\n}");
1112 for (
const QByteArray &view : {
"return"_ba,
"throw"_ba }) {
1113 CompletionItem item;
1115 item.kind = CompletionItemKind::Keyword;
1133 const DomType currentKind = itemAtPosition.internalKind();
1134 const DomType parentKind = itemAtPosition.directParent().internalKind();
1135 if (currentKind == DomType::ScriptCaseBlock || currentKind == DomType::ScriptCaseClause
1136 || currentKind == DomType::ScriptDefaultClause
1137 || (currentKind == DomType::List
1138 && (parentKind == DomType::ScriptCaseClause
1139 || parentKind == DomType::ScriptDefaultClause))) {
1140 suggestCaseAndDefaultStatementCompletion(result);
1142 suggestContinueAndBreakStatementIfNeeded(itemAtPosition, result);
1145void QQmlLSCompletion::insideForStatementCompletion(
const DomItem &parentForContext,
1146 const QQmlLSCompletionPosition &positionInfo,
1147 BackInsertIterator result)
const
1149 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1151 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1152 const QQmlJS::SourceLocation firstSemicolon = regions[FirstSemicolonTokenRegion];
1153 const QQmlJS::SourceLocation secondSemicolon = regions[SecondSemicolonRegion];
1154 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1156 if (betweenLocations(leftParenthesis, positionInfo, firstSemicolon)) {
1157 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1158 suggestVariableDeclarationStatementCompletion(result,
1159 AppendOption::AppendNothing);
1162 if (betweenLocations(firstSemicolon, positionInfo, secondSemicolon)
1163 || betweenLocations(secondSemicolon, positionInfo, rightParenthesis)) {
1164 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1168 if (afterLocation(rightParenthesis, positionInfo)) {
1169 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1174void QQmlLSCompletion::insideScriptLiteralCompletion(
const DomItem ¤tItem,
1175 const QQmlLSCompletionPosition &positionInfo,
1176 BackInsertIterator result)
const
1178 Q_UNUSED(currentItem);
1179 if (positionInfo.cursorPosition.base().isEmpty()) {
1180 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1186 const QQmlLSCompletionPosition &positionInfo,
1187 BackInsertIterator result)
const
1189 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1190 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1191 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1192 if (beforeLocation(positionInfo, leftParenthesis)) {
1193 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1196 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1197 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1203 const QQmlLSCompletionPosition &positionInfo,
1204 BackInsertIterator result)
const
1206 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1207 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1208 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1209 const QQmlJS::SourceLocation elseKeyword = regions[ElseKeywordRegion];
1211 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1212 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1215 if (betweenLocations(rightParenthesis, positionInfo, elseKeyword)) {
1216 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1219 if (afterLocation(elseKeyword, positionInfo)) {
1220 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1226 const QQmlLSCompletionPosition &positionInfo,
1227 BackInsertIterator result)
const
1229 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1230 const QQmlJS::SourceLocation returnKeyword = regions[ReturnKeywordRegion];
1232 if (afterLocation(returnKeyword, positionInfo)) {
1233 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1239 const QQmlLSCompletionPosition &positionInfo,
1240 BackInsertIterator result)
const
1242 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1243 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1244 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1246 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1247 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1250 if (afterLocation(rightParenthesis, positionInfo)) {
1251 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1256void QQmlLSCompletion::insideDoWhileStatement(
const DomItem &parentForContext,
1257 const QQmlLSCompletionPosition &positionInfo,
1258 BackInsertIterator result)
const
1260 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1261 const QQmlJS::SourceLocation doKeyword = regions[DoKeywordRegion];
1262 const QQmlJS::SourceLocation whileKeyword = regions[WhileKeywordRegion];
1263 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1264 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1266 if (betweenLocations(doKeyword, positionInfo, whileKeyword)) {
1267 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1270 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1271 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1276void QQmlLSCompletion::insideForEachStatement(
const DomItem &parentForContext,
1277 const QQmlLSCompletionPosition &positionInfo,
1278 BackInsertIterator result)
const
1280 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1282 const QQmlJS::SourceLocation inOf = regions[InOfTokenRegion];
1283 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1284 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1286 if (betweenLocations(leftParenthesis, positionInfo, inOf)) {
1287 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1288 suggestVariableDeclarationStatementCompletion(result);
1291 if (betweenLocations(inOf, positionInfo, rightParenthesis)) {
1292 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1296 if (afterLocation(rightParenthesis, positionInfo)) {
1297 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1302void QQmlLSCompletion::insideSwitchStatement(
const DomItem &parentForContext,
1303 const QQmlLSCompletionPosition positionInfo,
1304 BackInsertIterator result)
const
1306 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1308 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1309 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1311 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1312 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1318 const QQmlLSCompletionPosition &positionInfo,
1319 BackInsertIterator result)
const
1321 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1323 const QQmlJS::SourceLocation caseKeyword = regions[CaseKeywordRegion];
1324 const QQmlJS::SourceLocation colonToken = regions[ColonTokenRegion];
1326 if (betweenLocations(caseKeyword, positionInfo, colonToken)) {
1327 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1330 if (afterLocation(colonToken, positionInfo)) {
1331 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1338
1339
1340
1341bool QQmlLSCompletion::isCaseOrDefaultBeforeCtx(
const DomItem ¤tClause,
1342 const QQmlLSCompletionPosition &positionInfo,
1343 FileLocationRegion keywordRegion)
const
1345 Q_ASSERT(keywordRegion == QQmlJS::Dom::CaseKeywordRegion
1346 || keywordRegion == QQmlJS::Dom::DefaultKeywordRegion);
1351 const auto token = FileLocations::treeOf(currentClause)->info().regions[keywordRegion];
1352 if (afterLocation(token, positionInfo))
1359
1360
1361
1362
1363
1364
1365
1368 const QQmlLSCompletionPosition &positionInfo)
const
1370 const DomItem caseClauses = parentForContext.field(Fields::caseClauses);
1371 for (
int i = 0; i < caseClauses.indexes(); ++i) {
1372 const DomItem currentClause = caseClauses.index(i);
1373 if (isCaseOrDefaultBeforeCtx(currentClause, positionInfo, QQmlJS::Dom::CaseKeywordRegion)) {
1374 return currentClause;
1378 const DomItem defaultClause = parentForContext.field(Fields::defaultClause);
1379 if (isCaseOrDefaultBeforeCtx(defaultClause, positionInfo, QQmlJS::Dom::DefaultKeywordRegion))
1380 return parentForContext.field(Fields::defaultClause);
1382 const DomItem moreCaseClauses = parentForContext.field(Fields::moreCaseClauses);
1383 for (
int i = 0; i < moreCaseClauses.indexes(); ++i) {
1384 const DomItem currentClause = moreCaseClauses.index(i);
1385 if (isCaseOrDefaultBeforeCtx(currentClause, positionInfo, QQmlJS::Dom::CaseKeywordRegion)) {
1386 return currentClause;
1394 const QQmlLSCompletionPosition &positionInfo,
1395 BackInsertIterator result)
const
1397 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1399 const QQmlJS::SourceLocation leftBrace = regions[LeftBraceRegion];
1400 const QQmlJS::SourceLocation rightBrace = regions[RightBraceRegion];
1402 if (!betweenLocations(leftBrace, positionInfo, rightBrace))
1407 if (
const auto previousCase = previousCaseOfCaseBlock(parentForContext, positionInfo)) {
1408 suggestJSStatementCompletion(previousCase, result);
1413 suggestCaseAndDefaultStatementCompletion(result);
1417 const QQmlLSCompletionPosition &positionInfo,
1418 BackInsertIterator result)
const
1420 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1422 const QQmlJS::SourceLocation colonToken = regions[ColonTokenRegion];
1424 if (afterLocation(colonToken, positionInfo)) {
1425 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1431 const DomItem &parentForContext,
const QQmlLSCompletionPosition &positionInfo,
1432 BackInsertIterator result)
const
1434 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1436 const QQmlJS::SourceLocation operatorLocation = regions[OperatorTokenRegion];
1438 if (beforeLocation(positionInfo, operatorLocation)) {
1439 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1442 if (afterLocation(operatorLocation, positionInfo)) {
1443 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1489 const QQmlLSCompletionPosition &positionInfo,
1490 BackInsertIterator result)
const
1492 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1494 const QQmlJS::SourceLocation equal = regions[EqualTokenRegion];
1496 if (!afterLocation(equal, positionInfo))
1500 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1504
1505
1506
1507void QQmlLSCompletion::insideVariableDeclarationEntry(
const DomItem &parentForContext,
1508 const QQmlLSCompletionPosition &positionInfo,
1509 BackInsertIterator result)
const
1511 insideScriptPattern(parentForContext, positionInfo, result);
1514void QQmlLSCompletion::insideThrowStatement(
const DomItem &parentForContext,
1515 const QQmlLSCompletionPosition &positionInfo,
1516 BackInsertIterator result)
const
1518 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1520 const QQmlJS::SourceLocation throwKeyword = regions[ThrowKeywordRegion];
1522 if (afterLocation(throwKeyword, positionInfo)) {
1523 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1528void QQmlLSCompletion::insideLabelledStatement(
const DomItem &parentForContext,
1529 const QQmlLSCompletionPosition &positionInfo,
1530 BackInsertIterator result)
const
1532 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1534 const QQmlJS::SourceLocation colon = regions[ColonTokenRegion];
1536 if (afterLocation(colon, positionInfo)) {
1537 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1548
1549
1550
1551static void collectLabels(
const DomItem &context, QQmlLSCompletion::BackInsertIterator result)
1553 for (DomItem current = context; current; current = current.directParent()) {
1554 if (current.internalKind() == DomType::ScriptLabelledStatement) {
1555 const QString label = current.field(Fields::label).value().toString();
1556 if (label.isEmpty())
1558 CompletionItem item;
1559 item.label = label.toUtf8();
1560 item.kind = CompletionItemKind::Value;
1563 }
else if (current.internalKind() == DomType::ScriptExpression) {
1571void QQmlLSCompletion::insideContinueStatement(
const DomItem &parentForContext,
1572 const QQmlLSCompletionPosition &positionInfo,
1573 BackInsertIterator result)
const
1575 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1577 const QQmlJS::SourceLocation continueKeyword = regions[ContinueKeywordRegion];
1579 if (afterLocation(continueKeyword, positionInfo)) {
1580 collectLabels(parentForContext, result);
1585void QQmlLSCompletion::insideBreakStatement(
const DomItem &parentForContext,
1586 const QQmlLSCompletionPosition &positionInfo,
1587 BackInsertIterator result)
const
1589 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1591 const QQmlJS::SourceLocation breakKeyword = regions[BreakKeywordRegion];
1593 if (afterLocation(breakKeyword, positionInfo)) {
1594 collectLabels(parentForContext, result);
1599void QQmlLSCompletion::insideConditionalExpression(
const DomItem &parentForContext,
1600 const QQmlLSCompletionPosition &positionInfo,
1601 BackInsertIterator result)
const
1603 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1605 const QQmlJS::SourceLocation questionMark = regions[QuestionMarkTokenRegion];
1606 const QQmlJS::SourceLocation colon = regions[ColonTokenRegion];
1608 if (beforeLocation(positionInfo, questionMark)) {
1609 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1612 if (betweenLocations(questionMark, positionInfo, colon)) {
1613 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1616 if (afterLocation(colon, positionInfo)) {
1617 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1622void QQmlLSCompletion::insideUnaryExpression(
const DomItem &parentForContext,
1623 const QQmlLSCompletionPosition &positionInfo,
1624 BackInsertIterator result)
const
1626 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1628 const QQmlJS::SourceLocation operatorToken = regions[OperatorTokenRegion];
1630 if (afterLocation(operatorToken, positionInfo)) {
1631 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1636void QQmlLSCompletion::insidePostExpression(
const DomItem &parentForContext,
1637 const QQmlLSCompletionPosition &positionInfo,
1638 BackInsertIterator result)
const
1640 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1642 const QQmlJS::SourceLocation operatorToken = regions[OperatorTokenRegion];
1644 if (beforeLocation(positionInfo, operatorToken)) {
1645 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1650void QQmlLSCompletion::insideParenthesizedExpression(
const DomItem &parentForContext,
1651 const QQmlLSCompletionPosition &positionInfo,
1652 BackInsertIterator result)
const
1654 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1656 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1657 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1659 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1660 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1665void QQmlLSCompletion::insideTemplateLiteral(
const DomItem &parentForContext,
1666 const QQmlLSCompletionPosition &positionInfo,
1667 BackInsertIterator result)
const
1669 Q_UNUSED(parentForContext);
1670 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1674 const QQmlLSCompletionPosition &positionInfo,
1675 BackInsertIterator result)
const
1677 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1678 const QQmlJS::SourceLocation newKeyword = regions[NewKeywordRegion];
1680 if (afterLocation(newKeyword, positionInfo)) {
1681 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1686void QQmlLSCompletion::insideNewMemberExpression(
const DomItem &parentForContext,
1687 const QQmlLSCompletionPosition &positionInfo,
1688 BackInsertIterator result)
const
1690 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1691 const QQmlJS::SourceLocation newKeyword = regions[NewKeywordRegion];
1692 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1693 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1695 if (betweenLocations(newKeyword, positionInfo, leftParenthesis)
1696 || betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1697 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1702void QQmlLSCompletion::signalHandlerCompletion(
const QQmlJSScope::ConstPtr &scope,
1703 QDuplicateTracker<QString> *usedNames,
1704 BackInsertIterator result)
const
1706 const auto keyValues = scope->methods().asKeyValueRange();
1707 for (
const auto &[name, method] : keyValues) {
1708 if (method.access() != QQmlJSMetaMethod::Public
1709 || method.methodType() != QQmlJSMetaMethodType::Signal) {
1712 if (usedNames && usedNames->hasSeen(name)) {
1716 CompletionItem completion;
1717 completion.label = QQmlSignalNames::signalNameToHandlerName(name).toUtf8();
1718 completion.kind = CompletionItemKind::Method;
1719 result = completion;
1724
1725
1726
1729 const CompletionContextStrings &contextStrings)
const
1731 QList<CompletionItem> result;
1732 collectCompletions(currentItem, contextStrings, std::back_inserter(result));
1737 const CompletionContextStrings &contextStrings,
1738 BackInsertIterator result)
const
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761 const QQmlLSCompletionPosition positionInfo{ currentItem, contextStrings };
1762 for (DomItem currentParent = currentItem; currentParent;
1763 currentParent = currentParent.directParent()) {
1764 const DomType currentType = currentParent.internalKind();
1766 switch (currentType) {
1770 case DomType::Pragma:
1771 insidePragmaCompletion(currentParent, positionInfo, result);
1773 case DomType::ScriptType: {
1774 if (currentParent.directParent().internalKind() == DomType::QmlObject) {
1775 insideQmlObjectCompletion(currentParent.directParent(), positionInfo, result);
1779 LocalSymbolsTypes options;
1780 options.setFlag(LocalSymbolsType::ObjectType);
1781 options.setFlag(LocalSymbolsType::ValueType);
1782 suggestReachableTypes(currentItem, options, CompletionItemKind::Class, result);
1785 case DomType::ScriptFormalParameter:
1788 case DomType::Binding:
1789 insideBindingCompletion(currentParent, positionInfo, result);
1791 case DomType::Import:
1792 insideImportCompletion(currentParent, positionInfo, result);
1794 case DomType::ScriptForStatement:
1795 insideForStatementCompletion(currentParent, positionInfo, result);
1797 case DomType::ScriptBlockStatement:
1798 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1800 case DomType::QmlFile:
1801 insideQmlFileCompletion(currentParent, positionInfo, result);
1803 case DomType::QmlObject:
1804 insideQmlObjectCompletion(currentParent, positionInfo, result);
1806 case DomType::MethodInfo:
1809 case DomType::PropertyDefinition:
1810 insidePropertyDefinitionCompletion(currentParent, positionInfo, result);
1812 case DomType::ScriptBinaryExpression:
1814 if (QQmlLSUtils::isFieldMemberExpression(currentParent))
1816 insideBinaryExpressionCompletion(currentParent, positionInfo, result);
1818 case DomType::ScriptLiteral:
1819 insideScriptLiteralCompletion(currentParent, positionInfo, result);
1821 case DomType::ScriptRegExpLiteral:
1824 case DomType::ScriptCallExpression:
1825 insideCallExpression(currentParent, positionInfo, result);
1827 case DomType::ScriptIfStatement:
1828 insideIfStatement(currentParent, positionInfo, result);
1830 case DomType::ScriptReturnStatement:
1831 insideReturnStatement(currentParent, positionInfo, result);
1833 case DomType::ScriptWhileStatement:
1834 insideWhileStatement(currentParent, positionInfo, result);
1836 case DomType::ScriptDoWhileStatement:
1837 insideDoWhileStatement(currentParent, positionInfo, result);
1839 case DomType::ScriptForEachStatement:
1840 insideForEachStatement(currentParent, positionInfo, result);
1842 case DomType::ScriptTryCatchStatement:
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1862 case DomType::ScriptSwitchStatement:
1863 insideSwitchStatement(currentParent, positionInfo, result);
1865 case DomType::ScriptCaseClause:
1866 insideCaseClause(currentParent, positionInfo, result);
1868 case DomType::ScriptDefaultClause:
1869 if (ctxBeforeStatement(positionInfo, currentParent, QQmlJS::Dom::DefaultKeywordRegion))
1871 insideDefaultClause(currentParent, positionInfo, result);
1873 case DomType::ScriptCaseBlock:
1874 insideCaseBlock(currentParent, positionInfo, result);
1876 case DomType::ScriptVariableDeclaration:
1881 case DomType::ScriptVariableDeclarationEntry:
1882 insideVariableDeclarationEntry(currentParent, positionInfo, result);
1884 case DomType::ScriptProperty:
1887 case DomType::ScriptPattern:
1888 insideScriptPattern(currentParent, positionInfo, result);
1890 case DomType::ScriptThrowStatement:
1891 insideThrowStatement(currentParent, positionInfo, result);
1893 case DomType::ScriptLabelledStatement:
1894 insideLabelledStatement(currentParent, positionInfo, result);
1896 case DomType::ScriptContinueStatement:
1897 insideContinueStatement(currentParent, positionInfo, result);
1899 case DomType::ScriptBreakStatement:
1900 insideBreakStatement(currentParent, positionInfo, result);
1902 case DomType::ScriptConditionalExpression:
1903 insideConditionalExpression(currentParent, positionInfo, result);
1905 case DomType::ScriptUnaryExpression:
1906 insideUnaryExpression(currentParent, positionInfo, result);
1908 case DomType::ScriptPostExpression:
1909 insidePostExpression(currentParent, positionInfo, result);
1911 case DomType::ScriptParenthesizedExpression:
1912 insideParenthesizedExpression(currentParent, positionInfo, result);
1914 case DomType::ScriptTemplateLiteral:
1915 insideTemplateLiteral(currentParent, positionInfo, result);
1917 case DomType::ScriptTemplateStringPart:
1920 case DomType::ScriptNewExpression:
1921 insideNewExpression(currentParent, positionInfo, result);
1923 case DomType::ScriptNewMemberExpression:
1924 insideNewMemberExpression(currentParent, positionInfo, result);
1926 case DomType::ScriptThisExpression:
1929 case DomType::ScriptSuperLiteral:
1932 case DomType::Comment:
1938 case DomType::ScriptArray:
1939 case DomType::ScriptObject:
1940 case DomType::ScriptElision:
1941 case DomType::ScriptArrayEntry:
1951 qCDebug(QQmlLSUtilsLog) <<
"No completion was found for current request.";
1957 const auto keys = pluginLoader.metaDataKeys();
1958 for (qsizetype i = 0; i < keys.size(); ++i) {
1959 auto instance = std::unique_ptr<QQmlLSPlugin>(
1960 qobject_cast<QQmlLSPlugin *>(pluginLoader.instance(i)));
1963 if (
auto completionInstance = instance->createCompletionPlugin())
1964 m_plugins.push_back(std::move(completionInstance));
1969
1970
1971
1972void QQmlLSCompletion::collectFromPlugins(qxp::function_ref<CompletionFromPluginFunction> f,
1973 BackInsertIterator result)
const
1975 for (
const auto &plugin : m_plugins) {
1977 f(plugin.get(), result);
1981void QQmlLSCompletion::suggestSnippetsForLeftHandSideOfBinding(
const DomItem &itemAtPosition,
1982 BackInsertIterator result)
const
1985 [&itemAtPosition](QQmlLSCompletionPlugin *p, BackInsertIterator result) {
1986 p->suggestSnippetsForLeftHandSideOfBinding(itemAtPosition, result);
1991void QQmlLSCompletion::suggestSnippetsForRightHandSideOfBinding(
const DomItem &itemAtPosition,
1992 BackInsertIterator result)
const
1995 [&itemAtPosition](QQmlLSCompletionPlugin *p, BackInsertIterator result) {
1996 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)