aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2024-01-17 16:08:12 +0100
committerSami Shalayel <sami.shalayel@qt.io>2024-02-23 09:24:23 +0100
commit4de69daf82a278bea54dd62fd073449b0d8937f9 (patch)
tree10460603b20709f0e7c4eedd857fab99032d31ee
parent49165e9643ee6ad8684b975a2168b8e13cd80e43 (diff)
qmlls: add Qt Quick completion snippets
Add snippets for Qt Quick types, for feature parity with the Qt Creator code model. Do not propose them when QtQuick is not imported, and add the qualifier to the snippets in case QtQuick is imported with a qualified name, like `import QtQuick as QQ` for example. TODO: move the snippets to their own plugin. Task-number: QTBUG-119969 Change-Id: I874989e28c27d65fcde90b0ccaa4c447c44a5b30 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Dmitrii Akshintsev <dmitrii.akshintsev@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qmlls/qqmllsutils.cpp225
-rw-r--r--src/qmlls/qqmllsutils_p.h1
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp1
-rw-r--r--tests/auto/qmlls/utils/data/Yyy.qml3
-rw-r--r--tests/auto/qmlls/utils/tst_qmlls_utils.cpp293
5 files changed, 457 insertions, 66 deletions
diff --git a/src/qmlls/qqmllsutils.cpp b/src/qmlls/qqmllsutils.cpp
index b71fcb1349..17cf113290 100644
--- a/src/qmlls/qqmllsutils.cpp
+++ b/src/qmlls/qqmllsutils.cpp
@@ -171,6 +171,33 @@ static QStringList fieldMemberExpressionBits(const DomItem &item, const DomItem
return result;
}
+static CompletionItem makeSnippet(QUtf8StringView qualifier, QUtf8StringView label,
+ QUtf8StringView insertText)
+{
+ CompletionItem res;
+ if (!qualifier.isEmpty()) {
+ res.label = qualifier.data();
+ res.label += '.';
+ }
+ res.label += label.data();
+ res.insertTextFormat = InsertTextFormat::Snippet;
+ if (!qualifier.isEmpty()) {
+ res.insertText = qualifier.data();
+ *res.insertText += '.';
+ *res.insertText += insertText.data();
+ } else {
+ res.insertText = insertText.data();
+ }
+ res.kind = int(CompletionItemKind::Snippet);
+ res.insertTextMode = InsertTextMode::AdjustIndentation;
+ return res;
+}
+
+static CompletionItem makeSnippet(QUtf8StringView label, QUtf8StringView insertText)
+{
+ return makeSnippet(QByteArray(), label, insertText);
+}
+
/*!
\internal
The language server protocol calls "URI" what QML calls "URL".
@@ -1927,6 +1954,146 @@ static QList<CompletionItem> signalHandlerCompletion(const QQmlJSScope::ConstPtr
return res;
}
+static QList<CompletionItem> suggestQuickSnippetsCompletion(const DomItem &itemAtPosition)
+{
+ QList<CompletionItem> res;
+ auto file = itemAtPosition.containingFile().as<QmlFile>();
+ if (!file)
+ return {};
+
+ // check if QtQuick has been imported
+ const auto &imports = file->imports();
+ auto it = std::find_if(imports.constBegin(), imports.constEnd(), [](const Import &import) {
+ return import.uri.moduleUri() == u"QtQuick";
+ });
+ if (it == imports.constEnd()) {
+ return res;
+ }
+
+ // check if the user already typed some qualifier, remove its dot and compare it to QtQuick's
+ // qualified name
+ const QString userTypedQualifier = QQmlLSUtils::qualifiersFrom(itemAtPosition);
+ if (!userTypedQualifier.isEmpty()
+ && !it->importId.startsWith(QStringView(userTypedQualifier).chopped(1))) {
+ return res;
+ }
+
+ const QByteArray prefixForSnippet =
+ userTypedQualifier.isEmpty() ? it->importId.toUtf8() : QByteArray();
+ const QByteArray prefixWithDotForSnippet =
+ prefixForSnippet.isEmpty() ? QByteArray() : QByteArray(prefixForSnippet).append(u'.');
+
+ // Quick completions from Qt Creator's code model
+ res << makeSnippet(prefixForSnippet, "BorderImage snippet",
+ "BorderImage {\n"
+ "\tid: ${1:name}\n"
+ "\tsource: \"${2:file}\"\n"
+ "\twidth: ${3:100}; height: ${4:100}\n"
+ "\tborder.left: ${5: 5}; border.top: ${5}\n"
+ "\tborder.right: ${5}; border.bottom: ${5}\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "ColorAnimation snippet",
+ "ColorAnimation {\n"
+ "\tfrom: \"${1:white}\"\n"
+ "\tto: \"${2:black}\"\n"
+ "\tduration: ${3:200}\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "Image snippet",
+ "Image {\n"
+ "\tid: ${1:name}\n"
+ "\tsource: \"${2:file}\"\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "Item snippet",
+ "Item {\n"
+ "\tid: ${1:name}\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "NumberAnimation snippet",
+ "NumberAnimation {\n"
+ "\ttarget: ${1:object}\n"
+ "\tproperty: \"${2:name}\"\n"
+ "\tduration: ${3:200}\n"
+ "\teasing.type: "_ba.append(prefixWithDotForSnippet)
+ .append("Easing.${4:InOutQuad}\n"
+ "}"));
+ res << makeSnippet(prefixForSnippet, "NumberAnimation with targets snippet",
+ "NumberAnimation {\n"
+ "\ttargets: [${1:object}]\n"
+ "\tproperties: \"${2:name}\"\n"
+ "\tduration: ${3:200}\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "PauseAnimation snippet",
+ "PauseAnimation {\n"
+ "\tduration: ${1:200}\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "PropertyAction snippet",
+ "PropertyAction {\n"
+ "\ttarget: ${1:object}\n"
+ "\tproperty: \"${2:name}\"\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "PropertyAction with targets snippet",
+ "PropertyAction {\n"
+ "\ttargets: [${1:object}]\n"
+ "\tproperties: \"${2:name}\"\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "PropertyChanges snippet",
+ "PropertyChanges {\n"
+ "\ttarget: ${1:object}\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "State snippet",
+ "State {\n"
+ "\tname: ${1:name}\n"
+ "\t"_ba.append(prefixWithDotForSnippet)
+ .append("PropertyChanges {\n"
+ "\t\ttarget: ${2:object}\n"
+ "\t}\n"
+ "}"));
+ res << makeSnippet(prefixForSnippet, "Text snippet",
+ "Text {\n"
+ "\tid: ${1:name}\n"
+ "\ttext: qsTr(\"${2:text}\")\n"
+ "}");
+ res << makeSnippet(prefixForSnippet, "Transition snippet",
+ "Transition {\n"
+ "\tfrom: \"${1:fromState}\"\n"
+ "\tto: \"${2:toState}\"\n"
+ "}");
+
+ if (!userTypedQualifier.isEmpty())
+ return res;
+
+ auto resolver = file->typeResolver();
+ if (!resolver)
+ return res;
+ const auto qquickItemScope = resolver->typeForName(prefixWithDotForSnippet + u"Item"_s);
+ const QQmlJSScope::ConstPtr ownerScope = itemAtPosition.qmlObject().semanticScope();
+ if (!ownerScope || !qquickItemScope)
+ return res;
+
+ if (ownerScope->inherits(qquickItemScope)) {
+ res << makeSnippet("states binding with PropertyChanges in State",
+ "states: [\n"
+ "\t"_ba.append(prefixWithDotForSnippet)
+ .append("State {\n"
+ "\t\tname: \"${1:name}\"\n"
+ "\t\t"_ba.append(prefixWithDotForSnippet)
+ .append("PropertyChanges {\n"
+ "\t\t\ttarget: ${2:object}\n"
+ "\t\t}\n"
+ "\t}\n"
+ "]")));
+ res << makeSnippet("transitions binding with Transition",
+ "transitions: [\n"
+ "\t"_ba.append(prefixWithDotForSnippet)
+ .append("Transition {\n"
+ "\t\tfrom: \"${1:fromState}\"\n"
+ "\t\tto: \"${2:fromState}\"\n"
+ "\t}\n"
+ "]"));
+ }
+
+ return res;
+}
+
static QList<CompletionItem> suggestBindingCompletion(const DomItem &itemAtPosition)
{
QList<CompletionItem> res;
@@ -2075,6 +2242,29 @@ static bool testScopeSymbol(const QQmlJSScope::ConstPtr &scope, LocalSymbolsType
return false;
}
+QString QQmlLSUtils::qualifiersFrom(const DomItem &el)
+{
+ const bool isAccess = isFieldMemberAccess(el);
+ if (!isAccess && !isFieldMemberExpression(el))
+ return {};
+
+ const DomItem fieldMemberExpressionBeginning =
+ el.filterUp([](DomType, const DomItem &item) { return !isFieldMemberAccess(item); },
+ FilterUpOptions::ReturnOuter);
+ QStringList qualifiers = fieldMemberExpressionBits(fieldMemberExpressionBeginning, el);
+
+ QString result;
+ for (const QString &qualifier : qualifiers)
+ result.append(qualifier).append(QChar(u'.'));
+ return result;
+}
+
+/*!
+\internal
+Obtain the types reachable from \c{el}.
+
+The parameter \c{qualifiers} is re-computed from \c{el} when empty.
+*/
QList<CompletionItem> QQmlLSUtils::reachableTypes(const DomItem &el, LocalSymbolsTypes options,
CompletionItemKind kind)
{
@@ -2085,22 +2275,7 @@ QList<CompletionItem> QQmlLSUtils::reachableTypes(const DomItem &el, LocalSymbol
if (!resolver)
return {};
- const QString requiredQualifiers = [&el]() -> QString {
- const bool isAccess = isFieldMemberAccess(el);
- if (!isAccess && !isFieldMemberExpression(el))
- return {};
-
- const DomItem fieldMemberExpressionBeginning =
- el.filterUp([](DomType, const DomItem &item) { return !isFieldMemberAccess(item); },
- FilterUpOptions::ReturnOuter);
- QStringList qualifiers = fieldMemberExpressionBits(fieldMemberExpressionBeginning, el);
-
- QString result;
- for (const QString &qualifier : qualifiers)
- result.append(qualifier).append(u'.');
- return result;
- }();
-
+ const QString requiredQualifiers = qualifiersFrom(el);
QList<CompletionItem> res;
const auto keyValueRange = resolver->importedTypes().asKeyValueRange();
for (const auto &type : keyValueRange) {
@@ -2459,17 +2634,6 @@ static QList<CompletionItem> insidePragmaCompletion(QQmlJS::Dom::DomItem current
return res;
}
-static CompletionItem makeSnippet(QUtf8StringView label, QUtf8StringView insertText)
-{
- CompletionItem res;
- res.label = label.data();
- res.insertTextFormat = InsertTextFormat::Snippet;
- res.insertText = insertText.data();
- res.kind = int(CompletionItemKind::Snippet);
- res.insertTextMode = InsertTextMode::AdjustIndentation;
- return res;
-}
-
static QList<CompletionItem> insideQmlObjectCompletion(const DomItem &parentForContext,
const QQmlLSCompletionPosition &positionInfo)
{
@@ -2485,6 +2649,7 @@ static QList<CompletionItem> insideQmlObjectCompletion(const DomItem &parentForC
options.setFlag(LocalSymbolsType::ObjectType);
res << QQmlLSUtils::reachableTypes(positionInfo.itemAtPosition, options,
CompletionItemKind::Constructor);
+ res << suggestQuickSnippetsCompletion(positionInfo.itemAtPosition);
if (isFieldMemberExpression(positionInfo.itemAtPosition)) {
/*!
@@ -2564,6 +2729,7 @@ static QList<CompletionItem> insideQmlObjectCompletion(const DomItem &parentForC
const DomItem containingFile = parentForContext.containingFile();
res += QQmlLSUtils::reachableTypes(containingFile, LocalSymbolsType::ObjectType,
CompletionItemKind::Constructor);
+ res << suggestQuickSnippetsCompletion(positionInfo.itemAtPosition);
return res;
}
return {};
@@ -2653,6 +2819,7 @@ static QList<CompletionItem> insideBindingCompletion(const DomItem &currentItem,
options.setFlag(LocalSymbolsType::ObjectType);
res << QQmlLSUtils::reachableTypes(positionInfo.itemAtPosition, options,
CompletionItemKind::Constructor);
+ res << suggestQuickSnippetsCompletion(positionInfo.itemAtPosition);
}
}
return res;
@@ -2671,6 +2838,7 @@ static QList<CompletionItem> insideBindingCompletion(const DomItem &currentItem,
// add Qml Types for default binding
res += QQmlLSUtils::reachableTypes(positionInfo.itemAtPosition, LocalSymbolsType::ObjectType,
CompletionItemKind::Constructor);
+ res << suggestQuickSnippetsCompletion(positionInfo.itemAtPosition);
return res;
}
@@ -2682,9 +2850,10 @@ static QList<CompletionItem> insideImportCompletion(const DomItem &currentItem,
res += insideImportCompletionHelper(containingFile, positionInfo);
// when in front of the import statement: propose types for root Qml Object completion
- if (cursorInFrontOfItem(currentItem, positionInfo))
+ if (cursorInFrontOfItem(currentItem, positionInfo)) {
res += QQmlLSUtils::reachableTypes(containingFile, LocalSymbolsType::ObjectType,
CompletionItemKind::Constructor);
+ }
return res;
}
diff --git a/src/qmlls/qqmllsutils_p.h b/src/qmlls/qqmllsutils_p.h
index a50256ac8b..1c5ad7598e 100644
--- a/src/qmlls/qqmllsutils_p.h
+++ b/src/qmlls/qqmllsutils_p.h
@@ -162,6 +162,7 @@ public:
using CompletionItem = QLspSpecification::CompletionItem;
static QList<CompletionItem> idsCompletions(const DomItem& component);
+ static QString qualifiersFrom(const DomItem &el);
static QList<CompletionItem> reachableTypes(const DomItem &context,
QQmlJS::Dom::LocalSymbolsTypes typeCompletionType,
QLspSpecification::CompletionItemKind kind);
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 9d85fec982..c30e62536f 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -144,6 +144,7 @@ void TestQmlformat::initTestCase()
m_invalidFiles << "tests/auto/qmlls/utils/data/completions/afterDots.qml";
m_invalidFiles << "tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml";
m_invalidFiles << "tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/qualifiedModule.qml";
// Files that get changed:
// rewrite of import "bla/bla/.." to import "bla"
diff --git a/tests/auto/qmlls/utils/data/Yyy.qml b/tests/auto/qmlls/utils/data/Yyy.qml
index 80d2812505..6c3886c0a4 100644
--- a/tests/auto/qmlls/utils/data/Yyy.qml
+++ b/tests/auto/qmlls/utils/data/Yyy.qml
@@ -137,4 +137,7 @@ Zzz {
function qualifiedScriptIdentifiers() {
console.l()
}
+ QtObject {
+
+ }
}
diff --git a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
index 8b602a301d..ea6b31e8ef 100644
--- a/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
+++ b/tests/auto/qmlls/utils/tst_qmlls_utils.cpp
@@ -1664,48 +1664,265 @@ void tst_qmlls_utils::completions_data()
const QString propertyCompletion = u"property type name: value;"_s;
const QString functionCompletion = u"function name(args...): returnType { statements...}"_s;
+
+ const ExpectedCompletions quickSnippetsWithQualifier{
+ { u"QQ.BorderImage snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.BorderImage {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"\twidth: ${3:100}; height: ${4:100}\n"
+ u"\tborder.left: ${5: 5}; border.top: ${5}\n"
+ u"\tborder.right: ${5}; border.bottom: ${5}\n"
+ u"}"_s },
+ { u"QQ.ColorAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.ColorAnimation {\n"
+ u"\tfrom: \"${1:white}\"\n"
+ u"\tto: \"${2:black}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"QQ.Image snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Image {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"}"_s },
+ { u"QQ.Item snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Item {\n"
+ u"\tid: ${1:name}\n"
+ u"}"_s },
+ { u"QQ.NumberAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.NumberAnimation {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"\teasing.type: QQ.Easing.${4:InOutQuad}\n"
+ u"}"_s },
+ { u"QQ.NumberAnimation with targets snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.NumberAnimation {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"QQ.PauseAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PauseAnimation {\n"
+ u"\tduration: ${1:200}\n"
+ u"}"_s },
+ { u"QQ.PropertyAction snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyAction {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ "}"_s },
+ { u"QQ.PropertyAction with targets snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyAction {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"}"_s },
+ { u"QQ.PropertyChanges snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.PropertyChanges {\n"
+ u"\ttarget: ${1:object}\n"
+ u"}"_s },
+ { u"QQ.State snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.State {\n"
+ u"\tname: ${1:name}\n"
+ u"\tQQ.PropertyChanges {\n"
+ u"\t\ttarget: ${2:object}\n"
+ u"\t}\n"
+ u"}"_s },
+ { u"QQ.Text snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Text {\n"
+ u"\tid: ${1:name}\n"
+ u"\ttext: qsTr(\"${2:text}\")\n"
+ u"}"_s },
+ { u"QQ.Transition snippet"_s, CompletionItemKind::Snippet,
+ u"QQ.Transition {\n"
+ u"\tfrom: \"${1:fromState}\"\n"
+ u"\tto: \"${2:toState}\"\n"
+ u"}"_s },
+ { u"states binding with PropertyChanges in State"_s, CompletionItemKind::Snippet,
+ u"states: [\n"
+ u"\tQQ.State {\n"
+ u"\t\tname: \"${1:name}\"\n"
+ u"\t\tQQ.PropertyChanges {\n"
+ u"\t\t\ttarget: ${2:object}\n"
+ u"\t\t}\n"
+ u"\t}\n"
+ u"]"_s },
+ { u"transitions binding with Transition"_s, CompletionItemKind::Snippet,
+ u"transitions: [\n"
+ u"\tQQ.Transition {\n"
+ u"\t\tfrom: \"${1:fromState}\"\n"
+ u"\t\tto: \"${2:fromState}\"\n"
+ u"\t}\n"
+ u"]"_s }
+ };
+ const ExpectedCompletions quickSnippetsWithoutQualifier{
+ { { u"BorderImage snippet"_s, CompletionItemKind::Snippet,
+ u"BorderImage {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"\twidth: ${3:100}; height: ${4:100}\n"
+ u"\tborder.left: ${5: 5}; border.top: ${5}\n"
+ u"\tborder.right: ${5}; border.bottom: ${5}\n"
+ u"}"_s },
+ { u"ColorAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"ColorAnimation {\n"
+ u"\tfrom: \"${1:white}\"\n"
+ u"\tto: \"${2:black}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"Image snippet"_s, CompletionItemKind::Snippet,
+ u"Image {\n"
+ u"\tid: ${1:name}\n"
+ u"\tsource: \"${2:file}\"\n"
+ u"}"_s },
+ { u"Item snippet"_s, CompletionItemKind::Snippet,
+ u"Item {\n"
+ u"\tid: ${1:name}\n"
+ u"}"_s },
+ { u"NumberAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"NumberAnimation {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"\teasing.type: Easing.${4:InOutQuad}\n"
+ u"}"_s },
+ { u"NumberAnimation with targets snippet"_s, CompletionItemKind::Snippet,
+ u"NumberAnimation {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"\tduration: ${3:200}\n"
+ u"}"_s },
+ { u"PauseAnimation snippet"_s, CompletionItemKind::Snippet,
+ u"PauseAnimation {\n"
+ u"\tduration: ${1:200}\n"
+ u"}"_s },
+ { u"PropertyAction snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyAction {\n"
+ u"\ttarget: ${1:object}\n"
+ u"\tproperty: \"${2:name}\"\n"
+ "}"_s },
+ { u"PropertyAction with targets snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyAction {\n"
+ u"\ttargets: [${1:object}]\n"
+ u"\tproperties: \"${2:name}\"\n"
+ u"}"_s },
+ { u"PropertyChanges snippet"_s, CompletionItemKind::Snippet,
+ u"PropertyChanges {\n"
+ u"\ttarget: ${1:object}\n"
+ u"}"_s },
+ { u"State snippet"_s, CompletionItemKind::Snippet,
+ u"State {\n"
+ u"\tname: ${1:name}\n"
+ u"\tPropertyChanges {\n"
+ u"\t\ttarget: ${2:object}\n"
+ u"\t}\n"
+ u"}"_s },
+ { u"Text snippet"_s, CompletionItemKind::Snippet,
+ u"Text {\n"
+ u"\tid: ${1:name}\n"
+ u"\ttext: qsTr(\"${2:text}\")\n"
+ u"}"_s },
+ { u"Transition snippet"_s, CompletionItemKind::Snippet,
+ u"Transition {\n"
+ u"\tfrom: \"${1:fromState}\"\n"
+ u"\tto: \"${2:toState}\"\n"
+ u"}"_s } }
+ };
+ const ExpectedCompletions quickSnippetsWithoutQualifierWithBindings = ExpectedCompletions{
+ { { u"states binding with PropertyChanges in State"_s, CompletionItemKind::Snippet,
+ u"states: [\n"
+ u"\tState {\n"
+ u"\t\tname: \"${1:name}\"\n"
+ u"\t\tPropertyChanges {\n"
+ u"\t\t\ttarget: ${2:object}\n"
+ u"\t\t}\n"
+ u"\t}\n"
+ u"]"_s },
+ { u"transitions binding with Transition"_s, CompletionItemKind::Snippet,
+ u"transitions: [\n"
+ u"\tTransition {\n"
+ u"\t\tfrom: \"${1:fromState}\"\n"
+ u"\t\tto: \"${2:fromState}\"\n"
+ u"\t}\n"
+ u"]"_s } }
+ } += quickSnippetsWithoutQualifier;
QTest::newRow("objEmptyLineSnippets")
<< file << 9 << 1
- << ExpectedCompletions({
- { propertyCompletion, CompletionItemKind::Snippet,
- u"property ${1:type} ${2:name}: ${0:value};"_s },
- { u"readonly property type name: value;"_s, CompletionItemKind::Snippet,
- u"readonly property ${1:type} ${2:name}: ${0:value};"_s },
- { u"default property type name: value;"_s, CompletionItemKind::Snippet,
- u"default property ${1:type} ${2:name}: ${0:value};"_s },
- { u"default required property type name: value;"_s,
- CompletionItemKind::Snippet,
- u"default required property ${1:type} ${2:name}: ${0:value};"_s },
- { u"required default property type name: value;"_s,
- CompletionItemKind::Snippet,
- u"required default property ${1:type} ${2:name}: ${0:value};"_s },
- { u"required property type name: value;"_s, CompletionItemKind::Snippet,
- u"required property ${1:type} ${2:name}: ${0:value};"_s },
- { u"property type name;"_s, CompletionItemKind::Snippet,
- u"property ${1:type} ${0:name};"_s },
- { u"required property type name;"_s, CompletionItemKind::Snippet,
- u"required property ${1:type} ${0:name};"_s },
- { u"default property type name;"_s, CompletionItemKind::Snippet,
- u"default property ${1:type} ${0:name};"_s },
- { u"default required property type name;"_s, CompletionItemKind::Snippet,
- u"default required property ${1:type} ${0:name};"_s },
- { u"required default property type name;"_s, CompletionItemKind::Snippet,
- u"required default property ${1:type} ${0:name};"_s },
- { u"signal name(arg1:type1, ...)"_s, CompletionItemKind::Snippet,
- u"signal ${1:name}($0)"_s },
- { u"signal name;"_s, CompletionItemKind::Snippet, u"signal ${0:name};"_s },
- { u"required name;"_s, CompletionItemKind::Snippet,
- u"required ${0:name};"_s },
- { functionCompletion, CompletionItemKind::Snippet,
- u"function ${1:name}($2): ${3:returnType} {\n\t$0\n}"_s },
- { u"enum name { Values...}"_s, CompletionItemKind::Snippet,
- u"enum ${1:name} {\n\t${0:values}\n}"_s },
- { u"component Name: BaseType { ... }"_s, CompletionItemKind::Snippet,
- u"component ${1:name}: ${2:baseType} {\n\t$0\n}"_s },
- })
+ << (ExpectedCompletions({
+ { propertyCompletion, CompletionItemKind::Snippet,
+ u"property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"readonly property type name: value;"_s, CompletionItemKind::Snippet,
+ u"readonly property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"default property type name: value;"_s, CompletionItemKind::Snippet,
+ u"default property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"default required property type name: value;"_s,
+ CompletionItemKind::Snippet,
+ u"default required property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"required default property type name: value;"_s,
+ CompletionItemKind::Snippet,
+ u"required default property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"required property type name: value;"_s, CompletionItemKind::Snippet,
+ u"required property ${1:type} ${2:name}: ${0:value};"_s },
+ { u"property type name;"_s, CompletionItemKind::Snippet,
+ u"property ${1:type} ${0:name};"_s },
+ { u"required property type name;"_s, CompletionItemKind::Snippet,
+ u"required property ${1:type} ${0:name};"_s },
+ { u"default property type name;"_s, CompletionItemKind::Snippet,
+ u"default property ${1:type} ${0:name};"_s },
+ { u"default required property type name;"_s, CompletionItemKind::Snippet,
+ u"default required property ${1:type} ${0:name};"_s },
+ { u"required default property type name;"_s, CompletionItemKind::Snippet,
+ u"required default property ${1:type} ${0:name};"_s },
+ { u"signal name(arg1:type1, ...)"_s, CompletionItemKind::Snippet,
+ u"signal ${1:name}($0)"_s },
+ { u"signal name;"_s, CompletionItemKind::Snippet, u"signal ${0:name};"_s },
+ { u"required name;"_s, CompletionItemKind::Snippet,
+ u"required ${0:name};"_s },
+ { functionCompletion, CompletionItemKind::Snippet,
+ u"function ${1:name}($2): ${3:returnType} {\n\t$0\n}"_s },
+ { u"enum name { Values...}"_s, CompletionItemKind::Snippet,
+ u"enum ${1:name} {\n\t${0:values}\n}"_s },
+ { u"component Name: BaseType { ... }"_s, CompletionItemKind::Snippet,
+ u"component ${1:name}: ${2:baseType} {\n\t$0\n}"_s },
+ }) += quickSnippetsWithoutQualifierWithBindings)
// not allowed because required properties need an initializer
<< QStringList({ u"readonly property type name;"_s });
+ QTest::newRow("quickSnippetsForQualifiedQuickImport")
+ << testFile("qualifiedModule.qml") << 5 << 1
+ << quickSnippetsWithQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportBeforeDot")
+ << testFile("qualifiedModule.qml") << 5 << 7
+ << quickSnippetsWithQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportAfterDot")
+ << testFile("qualifiedModule.qml") << 5 << 8
+ << quickSnippetsWithoutQualifier
+ // not allowed because required properties need an initializer
+ << QStringList({ u"readonly property type name;"_s,
+ u"states binding with PropertyChanges in State"_s,
+ u"transitions binding with Transition"_s });
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportBeforeDotInBinding")
+ << testFile("qualifiedModule.qml") << 4 << 33 << quickSnippetsWithQualifier
+ << QStringList();
+
+ QTest::newRow("quickSnippetsForQualifiedQuickImportAfterDotInBinding")
+ << testFile("qualifiedModule.qml") << 4 << 34 << quickSnippetsWithoutQualifier
+ << QStringList({ u"states binding with PropertyChanges in State"_s,
+ u"transitions binding with Transition"_s });
+
+ // forbid transitions and states because QtObject does not inherit from Item
+ QTest::newRow("qtObjectEmptyLineSnippets")
+ << file << 141 << 8
+ << ExpectedCompletions{ { u"Item"_s, CompletionItemKind::Constructor } }
+ << QStringList({ u"transitions binding with Transition"_s,
+ u"states binding with PropertyChanges in State"_s });
+
QTest::newRow("handlers") << file << 5 << 1
<< ExpectedCompletions{ {
{ u"onHandleMe"_s, CompletionItemKind::Method },
@@ -1849,7 +2066,7 @@ void tst_qmlls_utils::completions_data()
<< ExpectedCompletions({
{ u"QQ.Rectangle"_s, CompletionItemKind::Constructor },
})
- << QStringList({ u"foo"_s, u"import"_s, u"lala"_s, u"width"_s });
+ << QStringList({ u"foo"_s, u"import"_s, u"lala"_s, });
QTest::newRow("qualifiedTypeCompletionAfterDot")
<< testFile(u"qualifiedModule.qml"_s) << 4 << 35