1 | // Copyright (C) 2024 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "qqmllsquickplugin_p.h" |
5 | #include <QtQmlLS/private/qqmllsutils_p.h> |
6 | #include <QtQmlLS/private/qqmllscompletion_p.h> |
7 | |
8 | using namespace QLspSpecification; |
9 | using namespace QQmlJS::Dom; |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | std::unique_ptr<QQmlLSCompletionPlugin> QQmlLSQuickPlugin::createCompletionPlugin() const |
14 | { |
15 | return std::make_unique<QQmlLSQuickCompletionPlugin>(); |
16 | } |
17 | |
18 | void QQmlLSQuickCompletionPlugin::suggestSnippetsForLeftHandSideOfBinding( |
19 | const DomItem &itemAtPosition, BackInsertIterator result) const |
20 | { |
21 | auto file = itemAtPosition.containingFile().as<QmlFile>(); |
22 | if (!file) |
23 | return; |
24 | |
25 | // check if QtQuick has been imported |
26 | const auto &imports = file->imports(); |
27 | auto it = std::find_if(first: imports.constBegin(), last: imports.constEnd(), pred: [](const Import &import) { |
28 | return import.uri.moduleUri() == u"QtQuick" ; |
29 | }); |
30 | if (it == imports.constEnd()) { |
31 | return; |
32 | } |
33 | |
34 | // for default bindings: |
35 | suggestSnippetsForRightHandSideOfBinding(items: itemAtPosition, result); |
36 | |
37 | // check if the user already typed some qualifier, remove its dot and compare it to QtQuick's |
38 | // qualified name |
39 | const QString userTypedQualifier = QQmlLSUtils::qualifiersFrom(el: itemAtPosition); |
40 | if (!userTypedQualifier.isEmpty() |
41 | && !it->importId.startsWith(s: QStringView(userTypedQualifier).chopped(n: 1))) { |
42 | return; |
43 | } |
44 | |
45 | const QByteArray prefixForSnippet = |
46 | userTypedQualifier.isEmpty() ? it->importId.toUtf8() : QByteArray(); |
47 | const QByteArray prefixWithDotForSnippet = |
48 | it->importId.isEmpty() ? QByteArray() : it->importId.toUtf8().append(c: u'.'); |
49 | |
50 | auto resolver = file->typeResolver(); |
51 | if (!resolver) |
52 | return; |
53 | const auto qquickItemScope = resolver->typeForName(name: prefixWithDotForSnippet + u"Item"_s ); |
54 | const QQmlJSScope::ConstPtr ownerScope = itemAtPosition.qmlObject().semanticScope(); |
55 | if (!ownerScope || !qquickItemScope) |
56 | return; |
57 | |
58 | if (ownerScope->inherits(base: qquickItemScope)) { |
59 | result = QQmlLSCompletion::makeSnippet( |
60 | label: "states binding with PropertyChanges in State" , |
61 | insertText: "states: [\n" |
62 | "\t"_ba .append(a: prefixWithDotForSnippet) |
63 | .append(a: "State {\n" |
64 | "\t\tname: \"${1:name}\"\n" |
65 | "\t\t"_ba .append(a: prefixWithDotForSnippet) |
66 | .append(s: "PropertyChanges {\n" |
67 | "\t\t\ttarget: ${2:object}\n" |
68 | "\t\t}\n" |
69 | "\t}\n" |
70 | "]" ))); |
71 | result = QQmlLSCompletion::makeSnippet(label: "transitions binding with Transition" , |
72 | insertText: "transitions: [\n" |
73 | "\t"_ba .append(a: prefixWithDotForSnippet) |
74 | .append(s: "Transition {\n" |
75 | "\t\tfrom: \"${1:fromState}\"\n" |
76 | "\t\tto: \"${2:fromState}\"\n" |
77 | "\t}\n" |
78 | "]" )); |
79 | } |
80 | } |
81 | |
82 | void QQmlLSQuickCompletionPlugin::suggestSnippetsForRightHandSideOfBinding( |
83 | const DomItem &itemAtPosition, BackInsertIterator result) const |
84 | { |
85 | auto file = itemAtPosition.containingFile().as<QmlFile>(); |
86 | if (!file) |
87 | return; |
88 | |
89 | // check if QtQuick has been imported |
90 | const auto &imports = file->imports(); |
91 | auto it = std::find_if(first: imports.constBegin(), last: imports.constEnd(), pred: [](const Import &import) { |
92 | return import.uri.moduleUri() == u"QtQuick" ; |
93 | }); |
94 | if (it == imports.constEnd()) { |
95 | return; |
96 | } |
97 | |
98 | // check if the user already typed some qualifier, remove its dot and compare it to QtQuick's |
99 | // qualified name |
100 | const QString userTypedQualifier = QQmlLSUtils::qualifiersFrom(el: itemAtPosition); |
101 | if (!userTypedQualifier.isEmpty() |
102 | && !it->importId.startsWith(s: QStringView(userTypedQualifier).chopped(n: 1))) { |
103 | return; |
104 | } |
105 | |
106 | const QByteArray prefixForSnippet = |
107 | userTypedQualifier.isEmpty() ? it->importId.toUtf8() : QByteArray(); |
108 | const QByteArray prefixWithDotForSnippet = |
109 | it->importId.isEmpty() ? QByteArray() : it->importId.toUtf8().append(c: u'.'); |
110 | |
111 | // Quick completions from Qt Creator's code model |
112 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "BorderImage snippet" , |
113 | insertText: "BorderImage {\n" |
114 | "\tid: ${1:name}\n" |
115 | "\tsource: \"${2:file}\"\n" |
116 | "\twidth: ${3:100}; height: ${4:100}\n" |
117 | "\tborder.left: ${5: 5}; border.top: ${5}\n" |
118 | "\tborder.right: ${5}; border.bottom: ${5}\n" |
119 | "}" ); |
120 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "ColorAnimation snippet" , |
121 | insertText: "ColorAnimation {\n" |
122 | "\tfrom: \"${1:white}\"\n" |
123 | "\tto: \"${2:black}\"\n" |
124 | "\tduration: ${3:200}\n" |
125 | "}" ); |
126 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "Image snippet" , |
127 | insertText: "Image {\n" |
128 | "\tid: ${1:name}\n" |
129 | "\tsource: \"${2:file}\"\n" |
130 | "}" ); |
131 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "Item snippet" , |
132 | insertText: "Item {\n" |
133 | "\tid: ${1:name}\n" |
134 | "}" ); |
135 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "NumberAnimation snippet" , |
136 | insertText: "NumberAnimation {\n" |
137 | "\ttarget: ${1:object}\n" |
138 | "\tproperty: \"${2:name}\"\n" |
139 | "\tduration: ${3:200}\n" |
140 | "\teasing.type: "_ba .append(a: prefixWithDotForSnippet) |
141 | .append(s: "Easing.${4:InOutQuad}\n" |
142 | "}" )); |
143 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "NumberAnimation with targets snippet" , |
144 | insertText: "NumberAnimation {\n" |
145 | "\ttargets: [${1:object}]\n" |
146 | "\tproperties: \"${2:name}\"\n" |
147 | "\tduration: ${3:200}\n" |
148 | "}" ); |
149 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "PauseAnimation snippet" , |
150 | insertText: "PauseAnimation {\n" |
151 | "\tduration: ${1:200}\n" |
152 | "}" ); |
153 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "PropertyAction snippet" , |
154 | insertText: "PropertyAction {\n" |
155 | "\ttarget: ${1:object}\n" |
156 | "\tproperty: \"${2:name}\"\n" |
157 | "}" ); |
158 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "PropertyAction with targets snippet" , |
159 | insertText: "PropertyAction {\n" |
160 | "\ttargets: [${1:object}]\n" |
161 | "\tproperties: \"${2:name}\"\n" |
162 | "}" ); |
163 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "PropertyChanges snippet" , |
164 | insertText: "PropertyChanges {\n" |
165 | "\ttarget: ${1:object}\n" |
166 | "}" ); |
167 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "State snippet" , |
168 | insertText: "State {\n" |
169 | "\tname: ${1:name}\n" |
170 | "\t"_ba .append(a: prefixWithDotForSnippet) |
171 | .append(s: "PropertyChanges {\n" |
172 | "\t\ttarget: ${2:object}\n" |
173 | "\t}\n" |
174 | "}" )); |
175 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "Text snippet" , |
176 | insertText: "Text {\n" |
177 | "\tid: ${1:name}\n" |
178 | "\ttext: qsTr(\"${2:text}\")\n" |
179 | "}" ); |
180 | result = QQmlLSCompletion::makeSnippet(qualifier: prefixForSnippet, label: "Transition snippet" , |
181 | insertText: "Transition {\n" |
182 | "\tfrom: \"${1:fromState}\"\n" |
183 | "\tto: \"${2:toState}\"\n" |
184 | "}" ); |
185 | } |
186 | |
187 | QT_END_NAMESPACE |
188 | |