// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // Qt-Security score:significant reason:default #include "qqmllsquickplugin_p.h" #include #include using namespace QLspSpecification; using namespace QQmlJS::Dom; QT_BEGIN_NAMESPACE std::unique_ptr QQmlLSQuickPlugin::createCompletionPlugin() const { return std::make_unique(); } void QQmlLSQuickCompletionPlugin::suggestSnippetsForLeftHandSideOfBinding( const DomItem &itemAtPosition, BackInsertIterator result) const { auto file = itemAtPosition.containingFile().as(); 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; } // for default bindings: suggestSnippetsForRightHandSideOfBinding(itemAtPosition, result); // 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; } const QByteArray prefixForSnippet = userTypedQualifier.isEmpty() ? it->importId.toUtf8() : QByteArray(); const QByteArray prefixWithDotForSnippet = it->importId.isEmpty() ? QByteArray() : it->importId.toUtf8().append(u'.'); auto resolver = file->typeResolver(); if (!resolver) return; const auto qquickItemScope = resolver->typeForName(prefixWithDotForSnippet + u"Item"_s); const QQmlJSScope::ConstPtr ownerScope = itemAtPosition.qmlObject().semanticScope(); if (!ownerScope || !qquickItemScope) return; if (ownerScope->inherits(qquickItemScope)) { result = QQmlLSCompletion::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" "]"))); result = QQmlLSCompletion::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" "]")); } } void QQmlLSQuickCompletionPlugin::suggestSnippetsForRightHandSideOfBinding( const DomItem &itemAtPosition, BackInsertIterator result) const { auto file = itemAtPosition.containingFile().as(); 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; } // 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; } const QByteArray prefixForSnippet = userTypedQualifier.isEmpty() ? it->importId.toUtf8() : QByteArray(); const QByteArray prefixWithDotForSnippet = it->importId.isEmpty() ? QByteArray() : it->importId.toUtf8().append(u'.'); // Quick completions from Qt Creator's code model result = QQmlLSCompletion::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" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "ColorAnimation snippet", "ColorAnimation {\n" "\tfrom: \"${1:white}\"\n" "\tto: \"${2:black}\"\n" "\tduration: ${3:200}\n" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "Image snippet", "Image {\n" "\tid: ${1:name}\n" "\tsource: \"${2:file}\"\n" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "Item snippet", "Item {\n" "\tid: ${1:name}\n" "}"); result = QQmlLSCompletion::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" "}")); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "NumberAnimation with targets snippet", "NumberAnimation {\n" "\ttargets: [${1:object}]\n" "\tproperties: \"${2:name}\"\n" "\tduration: ${3:200}\n" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "PauseAnimation snippet", "PauseAnimation {\n" "\tduration: ${1:200}\n" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "PropertyAction snippet", "PropertyAction {\n" "\ttarget: ${1:object}\n" "\tproperty: \"${2:name}\"\n" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "PropertyAction with targets snippet", "PropertyAction {\n" "\ttargets: [${1:object}]\n" "\tproperties: \"${2:name}\"\n" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "PropertyChanges snippet", "PropertyChanges {\n" "\ttarget: ${1:object}\n" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "State snippet", "State {\n" "\tname: ${1:name}\n" "\t"_ba.append(prefixWithDotForSnippet) .append("PropertyChanges {\n" "\t\ttarget: ${2:object}\n" "\t}\n" "}")); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "Text snippet", "Text {\n" "\tid: ${1:name}\n" "\ttext: qsTr(\"${2:text}\")\n" "}"); result = QQmlLSCompletion::makeSnippet(prefixForSnippet, "Transition snippet", "Transition {\n" "\tfrom: \"${1:fromState}\"\n" "\tto: \"${2:toState}\"\n" "}"); } QT_END_NAMESPACE