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
89bool QQmlLSCompletion::betweenLocations(QQmlJS::SourceLocation left,
90 const QQmlLSCompletionPosition &positionInfo,
91 QQmlJS::SourceLocation right)
const
96 if (!(left.end() <= positionInfo.offset()))
102 return positionInfo.offset() <= right.begin();
106
107
108
109bool QQmlLSCompletion::afterLocation(QQmlJS::SourceLocation left,
110 const QQmlLSCompletionPosition &positionInfo)
const
112 return betweenLocations(left, positionInfo, QQmlJS::SourceLocation{});
116
117
118
119bool QQmlLSCompletion::beforeLocation(
const QQmlLSCompletionPosition &ctx,
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);
169void QQmlLSCompletion::insideImportCompletionHelper(
const DomItem &file,
170 const QQmlLSCompletionPosition &positionInfo,
171 BackInsertIterator it)
const
174 const CompletionContextStrings &ctx = positionInfo.cursorPosition;
175 ImportCompletionType importCompletionType = ImportCompletionType::None;
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) {
190 importCompletionType = ImportCompletionType::Module;
191 }
else if (effectiveLength == 3) {
192 if (linePieces.last() != u"as") {
196 comp.kind =
int(CompletionItemKind::Keyword);
198 importCompletionType = ImportCompletionType::Version;
201 DomItem env = file.environment();
202 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
203 switch (importCompletionType) {
204 case ImportCompletionType::None:
206 case ImportCompletionType::Module: {
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);
226 case ImportCompletionType::Version:
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();
351void QQmlLSCompletion::methodCompletion(
const QQmlJSScope::ConstPtr &scope,
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
485void QQmlLSCompletion::suggestEnumerationsAndEnumerationValues(
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;
645bool QQmlLSCompletion::cursorInFrontOfItem(
const DomItem &parentForContext,
646 const QQmlLSCompletionPosition &positionInfo)
648 auto fileLocations = FileLocations::treeOf(parentForContext)->info().fullRegion;
649 return positionInfo.offset() <= fileLocations.begin();
652bool QQmlLSCompletion::cursorAfterColon(
const DomItem ¤tItem,
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)) {
765 constexpr static std::array completions {
766 ""_L1,
"default "_L1,
"final "_L1,
"required "_L1,
"readonly "_L1,
"default final "_L1,
767 "default required "_L1,
"final required "_L1,
"final readonly "_L1,
768 "default final required "_L1 };
769 for (QLatin1StringView view : completions) {
771 if (view != QUtf8StringView(
"readonly ")) {
772 result = makeSnippet(
773 QByteArray(view.data()).append(
"property type name;"),
774 QByteArray(view.data()).append(
"property ${1:type} ${0:name};"));
777 result = makeSnippet(
778 QByteArray(view.data()).append(
"property type name: value;"),
779 QByteArray(view.data()).append(
"property ${1:type} ${2:name}: ${0:value};"));
783 result = makeSnippet(
"signal name(arg1:type1, ...)",
"signal ${1:name}($0)");
786 result = makeSnippet(
"signal name;",
"signal ${0:name};");
789 result = makeSnippet(
"required name;",
"required ${0:name};");
792 result = makeSnippet(
"function name(args...): returnType { statements...}",
793 "function ${1:name}($2): ${3:returnType} {\n\t$0\n}");
796 result = makeSnippet(
"enum name { Values...}",
"enum ${1:name} {\n\t${0:values}\n}");
799 result = makeSnippet(
"component Name: BaseType { ... }",
800 "component ${1:name}: ${2:baseType} {\n\t$0\n}");
802 suggestBindingCompletion(positionInfo.itemAtPosition, result);
805 const DomItem containingFile = parentForContext.containingFile();
806 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
807 CompletionItemKind::Constructor, result);
808 suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
813void QQmlLSCompletion::insidePropertyDefinitionCompletion(
814 const DomItem ¤tItem,
const QQmlLSCompletionPosition &positionInfo,
815 BackInsertIterator result)
const
817 auto info = FileLocations::treeOf(currentItem)->info();
818 const QQmlJS::SourceLocation propertyKeyword = info.regions[PropertyKeywordRegion];
821 if (positionInfo.offset() < propertyKeyword.end()) {
822 const QQmlJS::SourceLocation readonlyKeyword = info.regions[ReadonlyKeywordRegion];
823 const QQmlJS::SourceLocation defaultKeyword = info.regions[DefaultKeywordRegion];
824 const QQmlJS::SourceLocation requiredKeyword = info.regions[RequiredKeywordRegion];
825 const QQmlJS::SourceLocation finalKeyword = info.regions[FinalKeywordRegion];
827 bool completeReadonly =
true;
828 bool completeRequired =
true;
829 bool completeDefault =
true;
830 bool completeFinal =
true;
833 if (readonlyKeyword.isValid() && readonlyKeyword.begin() < positionInfo.offset()) {
834 completeReadonly =
false;
836 completeRequired =
false;
840 if (requiredKeyword.isValid() && requiredKeyword.begin() < positionInfo.offset()) {
841 completeRequired =
false;
843 completeReadonly =
false;
847 if (defaultKeyword.isValid() && defaultKeyword.begin() < positionInfo.offset()) {
848 completeDefault =
false;
852 if (finalKeyword.isValid() && finalKeyword.begin() < positionInfo.offset())
853 completeFinal =
false;
855 auto addCompletionKeyword = [&result](QUtf8StringView view,
bool complete) {
859 item.label = view.data();
860 item.kind =
int(CompletionItemKind::Keyword);
863 addCompletionKeyword(u8"readonly", completeReadonly);
864 addCompletionKeyword(u8"required", completeRequired);
865 addCompletionKeyword(u8"default", completeDefault);
866 addCompletionKeyword(u8"final", completeFinal);
867 addCompletionKeyword(u8"property",
true);
872 const QQmlJS::SourceLocation propertyIdentifier = info.regions[IdentifierRegion];
873 if (propertyKeyword.end() <= positionInfo.offset()
874 && positionInfo.offset() < propertyIdentifier.begin()) {
875 suggestReachableTypes(currentItem,
876 LocalSymbolsType::ObjectType | LocalSymbolsType::ValueType,
877 CompletionItemKind::Class, result);
883void QQmlLSCompletion::insideBindingCompletion(
const DomItem ¤tItem,
884 const QQmlLSCompletionPosition &positionInfo,
885 BackInsertIterator result)
const
887 const DomItem containingBinding = currentItem.filterUp(
888 [](DomType type,
const QQmlJS::Dom::DomItem &) {
return type == DomType::Binding; },
889 FilterUpOptions::ReturnOuter);
892 if (cursorAfterColon(containingBinding, positionInfo)) {
893 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
895 if (
auto type = QQmlLSUtils::resolveExpressionType(currentItem,
896 QQmlLSUtils::ResolveOwnerType)) {
897 const QStringList names = currentItem.field(Fields::name).toString().split(u'.');
898 const QQmlJSScope *current = resolve(type->semanticScope.get(), names);
900 if (!current || current->accessSemantics() == QQmlSA::AccessSemantics::Reference) {
901 LocalSymbolsTypes options;
902 options.setFlag(LocalSymbolsType::ObjectType);
903 suggestReachableTypes(positionInfo.itemAtPosition, options,
904 CompletionItemKind::Constructor, result);
905 suggestSnippetsForRightHandSideOfBinding(positionInfo.itemAtPosition, result);
912 if (cursorInFrontOfItem(containingBinding, positionInfo)) {
913 insideQmlObjectCompletion(currentItem.containingObject(), positionInfo, result);
917 const DomItem containingObject = currentItem.qmlObject();
919 suggestBindingCompletion(positionInfo.itemAtPosition, result);
922 suggestReachableTypes(positionInfo.itemAtPosition, LocalSymbolsType::ObjectType,
923 CompletionItemKind::Constructor, result);
924 suggestSnippetsForLeftHandSideOfBinding(positionInfo.itemAtPosition, result);
927void QQmlLSCompletion::insideImportCompletion(
const DomItem ¤tItem,
928 const QQmlLSCompletionPosition &positionInfo,
929 BackInsertIterator result)
const
931 const DomItem containingFile = currentItem.containingFile();
932 insideImportCompletionHelper(containingFile, positionInfo, result);
935 if (cursorInFrontOfItem(currentItem, positionInfo)) {
936 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
937 CompletionItemKind::Constructor, result);
941void QQmlLSCompletion::insideQmlFileCompletion(
const DomItem ¤tItem,
942 const QQmlLSCompletionPosition &positionInfo,
943 BackInsertIterator result)
const
945 const DomItem containingFile = currentItem.containingFile();
948 if (positionInfo.cursorPosition.atLineStart()) {
949 if (positionInfo.cursorPosition.base().isEmpty()) {
950 for (
const QStringView &s : std::array<QStringView, 2>({ u"pragma", u"import" })) {
952 comp.label = s.toUtf8();
953 comp.kind =
int(CompletionItemKind::Keyword);
959 suggestReachableTypes(containingFile, LocalSymbolsType::ObjectType,
960 CompletionItemKind::Constructor, result);
964
965
966
967void QQmlLSCompletion::suggestVariableDeclarationStatementCompletion(
968 BackInsertIterator result, AppendOption option)
const
971 for (
auto view : std::array<QUtf8StringView, 3>{
"let",
"var",
"const" }) {
972 auto snippet = makeSnippet(QByteArray(view.data()).append(
" variable = value"),
973 QByteArray(view.data()).append(
" ${1:variable} = $0"));
974 if (option == AppendSemicolon) {
975 snippet.insertText->append(
";");
976 snippet.label.append(
";");
983
984
985
986void QQmlLSCompletion::suggestCaseAndDefaultStatementCompletion(BackInsertIterator result)
const
989 result = makeSnippet(
"case value: statements...",
"case ${1:value}:\n\t$0");
991 result = makeSnippet(
"case value: { statements... }",
"case ${1:value}: {\n\t$0\n}");
994 result = makeSnippet(
"default: statements...",
"default:\n\t$0");
996 result = makeSnippet(
"default: { statements... }",
"default: {\n\t$0\n}");
1000
1001
1002
1003
1004
1005
1006
1007
1008void QQmlLSCompletion::suggestContinueAndBreakStatementIfNeeded(
const DomItem &itemAtPosition,
1009 BackInsertIterator result)
const
1011 bool alreadyInLabel =
false;
1012 bool alreadyInSwitch =
false;
1013 for (DomItem current = itemAtPosition; current; current = current.directParent()) {
1014 switch (current.internalKind()) {
1015 case DomType::ScriptExpression:
1019 case DomType::ScriptForStatement:
1020 case DomType::ScriptForEachStatement:
1021 case DomType::ScriptWhileStatement:
1022 case DomType::ScriptDoWhileStatement: {
1023 CompletionItem continueKeyword;
1024 continueKeyword.label =
"continue";
1025 continueKeyword.kind =
int(CompletionItemKind::Keyword);
1026 result = continueKeyword;
1029 if (!alreadyInSwitch && !alreadyInLabel) {
1030 CompletionItem breakKeyword;
1031 breakKeyword.label =
"break";
1032 breakKeyword.kind =
int(CompletionItemKind::Keyword);
1033 result = breakKeyword;
1038 case DomType::ScriptSwitchStatement: {
1040 if (alreadyInSwitch || alreadyInLabel)
1042 alreadyInSwitch =
true;
1044 CompletionItem breakKeyword;
1045 breakKeyword.label =
"break";
1046 breakKeyword.kind =
int(CompletionItemKind::Keyword);
1047 result = breakKeyword;
1050 case DomType::ScriptLabelledStatement: {
1052 if (alreadyInSwitch || alreadyInLabel)
1054 alreadyInLabel =
true;
1056 CompletionItem breakKeyword;
1057 breakKeyword.label =
"break";
1058 breakKeyword.kind =
int(CompletionItemKind::Keyword);
1059 result = breakKeyword;
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084void QQmlLSCompletion::suggestJSStatementCompletion(
const DomItem &itemAtPosition,
1085 BackInsertIterator result)
const
1087 suggestJSExpressionCompletion(itemAtPosition, result);
1089 if (QQmlLSUtils::isFieldMemberAccess(itemAtPosition)
1090 || QQmlLSUtils::isFieldMemberExpression(itemAtPosition))
1094 suggestVariableDeclarationStatementCompletion(result);
1096 result = makeSnippet(
"{ statements... }",
"{\n\t$0\n}");
1099 result = makeSnippet(
"if (condition) { statements }",
"if ($1) {\n\t$0\n}");
1102 result = makeSnippet(
"do { statements } while (condition);",
"do {\n\t$1\n} while ($0);");
1105 result = makeSnippet(
"while (condition) { statements...}",
"while ($1) {\n\t$0\n}");
1108 result = makeSnippet(
"for (initializer; condition; increment) { statements... }",
1109 "for ($1;$2;$3) {\n\t$0\n}");
1112 result = makeSnippet(
"for (property in object) { statements... }",
"for ($1 in $2) {\n\t$0\n}");
1115 result = makeSnippet(
"for (element of array) { statements... }",
"for ($1 of $2) {\n\t$0\n}");
1118 result = makeSnippet(
"try { statements... } catch(error) { statements... }",
1119 "try {\n\t$1\n} catch($2) {\n\t$0\n}");
1122 result = makeSnippet(
"try { statements... } finally { statements... }",
1123 "try {\n\t$1\n} finally {\n\t$0\n}");
1126 result = makeSnippet(
1127 "try { statements... } catch(error) { statements... } finally { statements... }",
1128 "try {\n\t$1\n} catch($2) {\n\t$3\n} finally {\n\t$0\n}");
1131 for (
auto &&view : {
"return"_ba,
"throw"_ba }) {
1132 CompletionItem item;
1133 item.label = std::move(view);
1134 item.kind =
int(CompletionItemKind::Keyword);
1152 const DomType currentKind = itemAtPosition.internalKind();
1153 const DomType parentKind = itemAtPosition.directParent().internalKind();
1154 if (currentKind == DomType::ScriptCaseBlock || currentKind == DomType::ScriptCaseClause
1155 || currentKind == DomType::ScriptDefaultClause
1156 || (currentKind == DomType::List
1157 && (parentKind == DomType::ScriptCaseClause
1158 || parentKind == DomType::ScriptDefaultClause))) {
1159 suggestCaseAndDefaultStatementCompletion(result);
1161 suggestContinueAndBreakStatementIfNeeded(itemAtPosition, result);
1164void QQmlLSCompletion::insideForStatementCompletion(
const DomItem &parentForContext,
1165 const QQmlLSCompletionPosition &positionInfo,
1166 BackInsertIterator result)
const
1168 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1170 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1171 const QQmlJS::SourceLocation firstSemicolon = regions[FirstSemicolonTokenRegion];
1172 const QQmlJS::SourceLocation secondSemicolon = regions[SecondSemicolonRegion];
1173 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1175 if (betweenLocations(leftParenthesis, positionInfo, firstSemicolon)) {
1176 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1177 suggestVariableDeclarationStatementCompletion(result,
1178 AppendOption::AppendNothing);
1181 if (betweenLocations(firstSemicolon, positionInfo, secondSemicolon)
1182 || betweenLocations(secondSemicolon, positionInfo, rightParenthesis)) {
1183 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1187 if (afterLocation(rightParenthesis, positionInfo)) {
1188 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1193void QQmlLSCompletion::insideScriptLiteralCompletion(
const DomItem ¤tItem,
1194 const QQmlLSCompletionPosition &positionInfo,
1195 BackInsertIterator result)
const
1197 Q_UNUSED(currentItem);
1198 if (positionInfo.cursorPosition.base().isEmpty()) {
1199 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1204void QQmlLSCompletion::insideCallExpression(
const DomItem ¤tItem,
1205 const QQmlLSCompletionPosition &positionInfo,
1206 BackInsertIterator result)
const
1208 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1209 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1210 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1211 if (beforeLocation(positionInfo, leftParenthesis)) {
1212 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1215 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1216 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1221void QQmlLSCompletion::insideIfStatement(
const DomItem ¤tItem,
1222 const QQmlLSCompletionPosition &positionInfo,
1223 BackInsertIterator result)
const
1225 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1226 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1227 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1228 const QQmlJS::SourceLocation elseKeyword = regions[ElseKeywordRegion];
1230 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1231 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1234 if (betweenLocations(rightParenthesis, positionInfo, elseKeyword)) {
1235 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1238 if (afterLocation(elseKeyword, positionInfo)) {
1239 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1244void QQmlLSCompletion::insideReturnStatement(
const DomItem ¤tItem,
1245 const QQmlLSCompletionPosition &positionInfo,
1246 BackInsertIterator result)
const
1248 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1249 const QQmlJS::SourceLocation returnKeyword = regions[ReturnKeywordRegion];
1251 if (afterLocation(returnKeyword, positionInfo)) {
1252 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1257void QQmlLSCompletion::insideWhileStatement(
const DomItem ¤tItem,
1258 const QQmlLSCompletionPosition &positionInfo,
1259 BackInsertIterator result)
const
1261 const auto regions = FileLocations::treeOf(currentItem)->info().regions;
1262 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1263 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1265 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1266 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1269 if (afterLocation(rightParenthesis, positionInfo)) {
1270 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1275void QQmlLSCompletion::insideDoWhileStatement(
const DomItem &parentForContext,
1276 const QQmlLSCompletionPosition &positionInfo,
1277 BackInsertIterator result)
const
1279 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1280 const QQmlJS::SourceLocation doKeyword = regions[DoKeywordRegion];
1281 const QQmlJS::SourceLocation whileKeyword = regions[WhileKeywordRegion];
1282 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1283 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1285 if (betweenLocations(doKeyword, positionInfo, whileKeyword)) {
1286 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1289 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1290 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1295void QQmlLSCompletion::insideForEachStatement(
const DomItem &parentForContext,
1296 const QQmlLSCompletionPosition &positionInfo,
1297 BackInsertIterator result)
const
1299 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1301 const QQmlJS::SourceLocation inOf = regions[InOfTokenRegion];
1302 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1303 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1305 if (betweenLocations(leftParenthesis, positionInfo, inOf)) {
1306 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1307 suggestVariableDeclarationStatementCompletion(result);
1310 if (betweenLocations(inOf, positionInfo, rightParenthesis)) {
1311 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1315 if (afterLocation(rightParenthesis, positionInfo)) {
1316 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1321void QQmlLSCompletion::insideSwitchStatement(
const DomItem &parentForContext,
1322 const QQmlLSCompletionPosition positionInfo,
1323 BackInsertIterator result)
const
1325 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1327 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1328 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1330 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1331 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1336void QQmlLSCompletion::insideCaseClause(
const DomItem &parentForContext,
1337 const QQmlLSCompletionPosition &positionInfo,
1338 BackInsertIterator result)
const
1340 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1342 const QQmlJS::SourceLocation caseKeyword = regions[CaseKeywordRegion];
1343 const QQmlJS::SourceLocation colonToken = regions[ColonTokenRegion];
1345 if (betweenLocations(caseKeyword, positionInfo, colonToken)) {
1346 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1349 if (afterLocation(colonToken, positionInfo)) {
1350 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1357
1358
1359
1360bool QQmlLSCompletion::isCaseOrDefaultBeforeCtx(
const DomItem ¤tClause,
1361 const QQmlLSCompletionPosition &positionInfo,
1362 FileLocationRegion keywordRegion)
const
1364 Q_ASSERT(keywordRegion == QQmlJS::Dom::CaseKeywordRegion
1365 || keywordRegion == QQmlJS::Dom::DefaultKeywordRegion);
1370 const auto token = FileLocations::treeOf(currentClause)->info().regions[keywordRegion];
1371 if (afterLocation(token, positionInfo))
1378
1379
1380
1381
1382
1383
1384
1386QQmlLSCompletion::previousCaseOfCaseBlock(
const DomItem &parentForContext,
1387 const QQmlLSCompletionPosition &positionInfo)
const
1389 const DomItem caseClauses = parentForContext.field(Fields::caseClauses);
1390 for (
int i = 0; i < caseClauses.indexes(); ++i) {
1391 const DomItem currentClause = caseClauses.index(i);
1392 if (isCaseOrDefaultBeforeCtx(currentClause, positionInfo, QQmlJS::Dom::CaseKeywordRegion)) {
1393 return currentClause;
1397 const DomItem defaultClause = parentForContext.field(Fields::defaultClause);
1398 if (isCaseOrDefaultBeforeCtx(defaultClause, positionInfo, QQmlJS::Dom::DefaultKeywordRegion))
1399 return parentForContext.field(Fields::defaultClause);
1401 const DomItem moreCaseClauses = parentForContext.field(Fields::moreCaseClauses);
1402 for (
int i = 0; i < moreCaseClauses.indexes(); ++i) {
1403 const DomItem currentClause = moreCaseClauses.index(i);
1404 if (isCaseOrDefaultBeforeCtx(currentClause, positionInfo, QQmlJS::Dom::CaseKeywordRegion)) {
1405 return currentClause;
1412void QQmlLSCompletion::insideCaseBlock(
const DomItem &parentForContext,
1413 const QQmlLSCompletionPosition &positionInfo,
1414 BackInsertIterator result)
const
1416 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1418 const QQmlJS::SourceLocation leftBrace = regions[LeftBraceRegion];
1419 const QQmlJS::SourceLocation rightBrace = regions[RightBraceRegion];
1421 if (!betweenLocations(leftBrace, positionInfo, rightBrace))
1426 if (
const auto previousCase = previousCaseOfCaseBlock(parentForContext, positionInfo)) {
1427 suggestJSStatementCompletion(previousCase, result);
1432 suggestCaseAndDefaultStatementCompletion(result);
1435void QQmlLSCompletion::insideDefaultClause(
const DomItem &parentForContext,
1436 const QQmlLSCompletionPosition &positionInfo,
1437 BackInsertIterator result)
const
1439 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1441 const QQmlJS::SourceLocation colonToken = regions[ColonTokenRegion];
1443 if (afterLocation(colonToken, positionInfo)) {
1444 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1449void QQmlLSCompletion::insideBinaryExpressionCompletion(
1450 const DomItem &parentForContext,
const QQmlLSCompletionPosition &positionInfo,
1451 BackInsertIterator result)
const
1453 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1455 const QQmlJS::SourceLocation operatorLocation = regions[OperatorTokenRegion];
1457 if (beforeLocation(positionInfo, operatorLocation)) {
1458 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1461 if (afterLocation(operatorLocation, positionInfo)) {
1462 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507void QQmlLSCompletion::insideScriptPattern(
const DomItem &parentForContext,
1508 const QQmlLSCompletionPosition &positionInfo,
1509 BackInsertIterator result)
const
1511 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1513 const QQmlJS::SourceLocation equal = regions[EqualTokenRegion];
1515 if (!afterLocation(equal, positionInfo))
1519 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1523
1524
1525
1526void QQmlLSCompletion::insideVariableDeclarationEntry(
const DomItem &parentForContext,
1527 const QQmlLSCompletionPosition &positionInfo,
1528 BackInsertIterator result)
const
1530 insideScriptPattern(parentForContext, positionInfo, result);
1533void QQmlLSCompletion::insideThrowStatement(
const DomItem &parentForContext,
1534 const QQmlLSCompletionPosition &positionInfo,
1535 BackInsertIterator result)
const
1537 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1539 const QQmlJS::SourceLocation throwKeyword = regions[ThrowKeywordRegion];
1541 if (afterLocation(throwKeyword, positionInfo)) {
1542 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1547void QQmlLSCompletion::insideLabelledStatement(
const DomItem &parentForContext,
1548 const QQmlLSCompletionPosition &positionInfo,
1549 BackInsertIterator result)
const
1551 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1553 const QQmlJS::SourceLocation colon = regions[ColonTokenRegion];
1555 if (afterLocation(colon, positionInfo)) {
1556 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1567
1568
1569
1570static void collectLabels(
const DomItem &context, QQmlLSCompletion::BackInsertIterator result)
1572 for (DomItem current = context; current; current = current.directParent()) {
1573 if (current.internalKind() == DomType::ScriptLabelledStatement) {
1574 const QString label = current.field(Fields::label).value().toString();
1575 if (label.isEmpty())
1577 CompletionItem item;
1578 item.label = label.toUtf8();
1579 item.kind =
int(CompletionItemKind::Value);
1582 }
else if (current.internalKind() == DomType::ScriptExpression) {
1590void QQmlLSCompletion::insideContinueStatement(
const DomItem &parentForContext,
1591 const QQmlLSCompletionPosition &positionInfo,
1592 BackInsertIterator result)
const
1594 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1596 const QQmlJS::SourceLocation continueKeyword = regions[ContinueKeywordRegion];
1598 if (afterLocation(continueKeyword, positionInfo)) {
1599 collectLabels(parentForContext, result);
1604void QQmlLSCompletion::insideBreakStatement(
const DomItem &parentForContext,
1605 const QQmlLSCompletionPosition &positionInfo,
1606 BackInsertIterator result)
const
1608 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1610 const QQmlJS::SourceLocation breakKeyword = regions[BreakKeywordRegion];
1612 if (afterLocation(breakKeyword, positionInfo)) {
1613 collectLabels(parentForContext, result);
1618void QQmlLSCompletion::insideConditionalExpression(
const DomItem &parentForContext,
1619 const QQmlLSCompletionPosition &positionInfo,
1620 BackInsertIterator result)
const
1622 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1624 const QQmlJS::SourceLocation questionMark = regions[QuestionMarkTokenRegion];
1625 const QQmlJS::SourceLocation colon = regions[ColonTokenRegion];
1627 if (beforeLocation(positionInfo, questionMark)) {
1628 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1631 if (betweenLocations(questionMark, positionInfo, colon)) {
1632 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1635 if (afterLocation(colon, positionInfo)) {
1636 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1641void QQmlLSCompletion::insideUnaryExpression(
const DomItem &parentForContext,
1642 const QQmlLSCompletionPosition &positionInfo,
1643 BackInsertIterator result)
const
1645 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1647 const QQmlJS::SourceLocation operatorToken = regions[OperatorTokenRegion];
1649 if (afterLocation(operatorToken, positionInfo)) {
1650 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1655void QQmlLSCompletion::insidePostExpression(
const DomItem &parentForContext,
1656 const QQmlLSCompletionPosition &positionInfo,
1657 BackInsertIterator result)
const
1659 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1661 const QQmlJS::SourceLocation operatorToken = regions[OperatorTokenRegion];
1663 if (beforeLocation(positionInfo, operatorToken)) {
1664 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1669void QQmlLSCompletion::insideParenthesizedExpression(
const DomItem &parentForContext,
1670 const QQmlLSCompletionPosition &positionInfo,
1671 BackInsertIterator result)
const
1673 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1675 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1676 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1678 if (betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1679 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1684void QQmlLSCompletion::insideTemplateLiteral(
const DomItem &parentForContext,
1685 const QQmlLSCompletionPosition &positionInfo,
1686 BackInsertIterator result)
const
1688 Q_UNUSED(parentForContext);
1689 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1692void QQmlLSCompletion::insideNewExpression(
const DomItem &parentForContext,
1693 const QQmlLSCompletionPosition &positionInfo,
1694 BackInsertIterator result)
const
1696 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1697 const QQmlJS::SourceLocation newKeyword = regions[NewKeywordRegion];
1699 if (afterLocation(newKeyword, positionInfo)) {
1700 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1705void QQmlLSCompletion::insideNewMemberExpression(
const DomItem &parentForContext,
1706 const QQmlLSCompletionPosition &positionInfo,
1707 BackInsertIterator result)
const
1709 const auto regions = FileLocations::treeOf(parentForContext)->info().regions;
1710 const QQmlJS::SourceLocation newKeyword = regions[NewKeywordRegion];
1711 const QQmlJS::SourceLocation leftParenthesis = regions[LeftParenthesisRegion];
1712 const QQmlJS::SourceLocation rightParenthesis = regions[RightParenthesisRegion];
1714 if (betweenLocations(newKeyword, positionInfo, leftParenthesis)
1715 || betweenLocations(leftParenthesis, positionInfo, rightParenthesis)) {
1716 suggestJSExpressionCompletion(positionInfo.itemAtPosition, result);
1721void QQmlLSCompletion::signalHandlerCompletion(
const QQmlJSScope::ConstPtr &scope,
1722 QDuplicateTracker<QString> *usedNames,
1723 BackInsertIterator result)
const
1725 const auto keyValues = scope->methods().asKeyValueRange();
1726 for (
const auto &[name, method] : keyValues) {
1727 if (method.access() != QQmlJSMetaMethod::Public
1728 || method.methodType() != QQmlJSMetaMethodType::Signal) {
1731 if (usedNames && usedNames->hasSeen(name)) {
1735 CompletionItem completion;
1736 completion.label = QQmlSignalNames::signalNameToHandlerName(name).toUtf8();
1737 completion.kind =
int(CompletionItemKind::Method);
1738 result = completion;
1743
1744
1745
1746QList<CompletionItem>
1747QQmlLSCompletion::completions(
const DomItem ¤tItem,
1748 const CompletionContextStrings &contextStrings)
const
1750 QList<CompletionItem> result;
1751 collectCompletions(currentItem, contextStrings, std::back_inserter(result));
1755void QQmlLSCompletion::collectCompletions(
const DomItem ¤tItem,
1756 const CompletionContextStrings &contextStrings,
1757 BackInsertIterator result)
const
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780 const QQmlLSCompletionPosition positionInfo{ currentItem, contextStrings };
1781 for (DomItem currentParent = currentItem; currentParent;
1782 currentParent = currentParent.directParent()) {
1783 const DomType currentType = currentParent.internalKind();
1785 switch (currentType) {
1789 case DomType::Pragma:
1790 insidePragmaCompletion(currentParent, positionInfo, result);
1792 case DomType::ScriptType: {
1793 if (currentParent.directParent().internalKind() == DomType::QmlObject) {
1794 insideQmlObjectCompletion(currentParent.directParent(), positionInfo, result);
1798 LocalSymbolsTypes options;
1799 options.setFlag(LocalSymbolsType::ObjectType);
1800 options.setFlag(LocalSymbolsType::ValueType);
1801 suggestReachableTypes(currentItem, options, CompletionItemKind::Class, result);
1804 case DomType::ScriptFormalParameter:
1807 case DomType::Binding:
1808 insideBindingCompletion(currentParent, positionInfo, result);
1810 case DomType::Import:
1811 insideImportCompletion(currentParent, positionInfo, result);
1813 case DomType::ScriptForStatement:
1814 insideForStatementCompletion(currentParent, positionInfo, result);
1816 case DomType::ScriptBlockStatement:
1817 suggestJSStatementCompletion(positionInfo.itemAtPosition, result);
1819 case DomType::QmlFile:
1820 insideQmlFileCompletion(currentParent, positionInfo, result);
1822 case DomType::QmlObject:
1823 insideQmlObjectCompletion(currentParent, positionInfo, result);
1825 case DomType::MethodInfo:
1828 case DomType::PropertyDefinition:
1829 insidePropertyDefinitionCompletion(currentParent, positionInfo, result);
1831 case DomType::ScriptBinaryExpression:
1833 if (QQmlLSUtils::isFieldMemberExpression(currentParent))
1835 insideBinaryExpressionCompletion(currentParent, positionInfo, result);
1837 case DomType::ScriptLiteral:
1838 insideScriptLiteralCompletion(currentParent, positionInfo, result);
1840 case DomType::ScriptRegExpLiteral:
1843 case DomType::ScriptCallExpression:
1844 insideCallExpression(currentParent, positionInfo, result);
1846 case DomType::ScriptIfStatement:
1847 insideIfStatement(currentParent, positionInfo, result);
1849 case DomType::ScriptReturnStatement:
1850 insideReturnStatement(currentParent, positionInfo, result);
1852 case DomType::ScriptWhileStatement:
1853 insideWhileStatement(currentParent, positionInfo, result);
1855 case DomType::ScriptDoWhileStatement:
1856 insideDoWhileStatement(currentParent, positionInfo, result);
1858 case DomType::ScriptForEachStatement:
1859 insideForEachStatement(currentParent, positionInfo, result);
1861 case DomType::ScriptTryCatchStatement:
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1881 case DomType::ScriptSwitchStatement:
1882 insideSwitchStatement(currentParent, positionInfo, result);
1884 case DomType::ScriptCaseClause:
1885 insideCaseClause(currentParent, positionInfo, result);
1887 case DomType::ScriptDefaultClause:
1888 if (ctxBeforeStatement(positionInfo, currentParent, QQmlJS::Dom::DefaultKeywordRegion))
1890 insideDefaultClause(currentParent, positionInfo, result);
1892 case DomType::ScriptCaseBlock:
1893 insideCaseBlock(currentParent, positionInfo, result);
1895 case DomType::ScriptVariableDeclaration:
1900 case DomType::ScriptVariableDeclarationEntry:
1901 insideVariableDeclarationEntry(currentParent, positionInfo, result);
1903 case DomType::ScriptProperty:
1906 case DomType::ScriptPattern:
1907 insideScriptPattern(currentParent, positionInfo, result);
1909 case DomType::ScriptThrowStatement:
1910 insideThrowStatement(currentParent, positionInfo, result);
1912 case DomType::ScriptLabelledStatement:
1913 insideLabelledStatement(currentParent, positionInfo, result);
1915 case DomType::ScriptContinueStatement:
1916 insideContinueStatement(currentParent, positionInfo, result);
1918 case DomType::ScriptBreakStatement:
1919 insideBreakStatement(currentParent, positionInfo, result);
1921 case DomType::ScriptConditionalExpression:
1922 insideConditionalExpression(currentParent, positionInfo, result);
1924 case DomType::ScriptUnaryExpression:
1925 insideUnaryExpression(currentParent, positionInfo, result);
1927 case DomType::ScriptPostExpression:
1928 insidePostExpression(currentParent, positionInfo, result);
1930 case DomType::ScriptParenthesizedExpression:
1931 insideParenthesizedExpression(currentParent, positionInfo, result);
1933 case DomType::ScriptTemplateLiteral:
1934 insideTemplateLiteral(currentParent, positionInfo, result);
1936 case DomType::ScriptTemplateStringPart:
1939 case DomType::ScriptNewExpression:
1940 insideNewExpression(currentParent, positionInfo, result);
1942 case DomType::ScriptNewMemberExpression:
1943 insideNewMemberExpression(currentParent, positionInfo, result);
1945 case DomType::ScriptThisExpression:
1948 case DomType::ScriptSuperLiteral:
1951 case DomType::Comment:
1957 case DomType::ScriptArray:
1958 case DomType::ScriptObject:
1959 case DomType::ScriptElision:
1960 case DomType::ScriptArrayEntry:
1970 qCDebug(QQmlLSUtilsLog) <<
"No completion was found for current request.";
1974QQmlLSCompletion::QQmlLSCompletion(
const QFactoryLoader &pluginLoader)
1976 const auto keys = pluginLoader.metaDataKeys();
1977 for (qsizetype i = 0; i < keys.size(); ++i) {
1978 auto instance = std::unique_ptr<QQmlLSPlugin>(
1979 qobject_cast<QQmlLSPlugin *>(pluginLoader.instance(i)));
1982 if (
auto completionInstance = instance->createCompletionPlugin())
1983 m_plugins.push_back(std::move(completionInstance));
1988
1989
1990
1991void QQmlLSCompletion::collectFromPlugins(qxp::function_ref<CompletionFromPluginFunction> f,
1992 BackInsertIterator result)
const
1994 for (
const auto &plugin : m_plugins) {
1996 f(plugin.get(), result);
2000void QQmlLSCompletion::suggestSnippetsForLeftHandSideOfBinding(
const DomItem &itemAtPosition,
2001 BackInsertIterator result)
const
2004 [&itemAtPosition](QQmlLSCompletionPlugin *p, BackInsertIterator result) {
2005 p->suggestSnippetsForLeftHandSideOfBinding(itemAtPosition, result);
2010void QQmlLSCompletion::suggestSnippetsForRightHandSideOfBinding(
const DomItem &itemAtPosition,
2011 BackInsertIterator result)
const
2014 [&itemAtPosition](QQmlLSCompletionPlugin *p, BackInsertIterator result) {
2015 p->suggestSnippetsForRightHandSideOfBinding(itemAtPosition, result);
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)