From 490f2797f636ff49ce0a778610a2532d1277e4d4 Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Thu, 10 Feb 2011 17:03:52 +0100
Subject: [PATCH] QmlJS: Completion for attached properties.

---
 src/libs/qmljs/qmljscheck.cpp                 |  2 +-
 src/libs/qmljs/qmljsinterpreter.cpp           | 92 ++++++++++++++++++-
 src/libs/qmljs/qmljsinterpreter.h             | 19 ++++
 src/libs/qmljs/qmljsscopebuilder.cpp          |  4 +-
 .../designercore/model/texttomodelmerger.cpp  |  2 +-
 .../qmljseditor/qmljscodecompletion.cpp       | 19 +++-
 .../qmljseditor/qmljsfindreferences.cpp       |  2 +-
 7 files changed, 128 insertions(+), 12 deletions(-)

diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index 8c3d0d1554d..ce0d97f5cbe 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -826,7 +826,7 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
     bool isAttachedProperty = false;
     if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
         isAttachedProperty = true;
-        if (const ObjectValue *qmlTypes = _context.scopeChain().qmlTypes)
+        if (const ObjectValue *qmlTypes = _context.scopeChain().qmlAttachedTypes)
             scopeObjects += qmlTypes;
     }
 
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index 8eee26d03d6..90416d3a6df 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -588,6 +588,7 @@ private:
 QmlObjectValue::QmlObjectValue(FakeMetaObject::ConstPtr metaObject, const QString &className,
                                const QString &packageName, const ComponentVersion version, Engine *engine)
     : ObjectValue(engine),
+      _attachedType(0),
       _metaObject(metaObject),
       _packageName(packageName),
       _componentVersion(version)
@@ -719,6 +720,16 @@ const QmlObjectValue *QmlObjectValue::prototype() const
     return static_cast<const QmlObjectValue *>(_prototype);
 }
 
+const QmlObjectValue *QmlObjectValue::attachedType() const
+{
+    return _attachedType;
+}
+
+void QmlObjectValue::setAttachedType(QmlObjectValue *value)
+{
+    _attachedType = value;
+}
+
 FakeMetaObject::ConstPtr QmlObjectValue::metaObject() const
 {
     return _metaObject;
@@ -1294,6 +1305,7 @@ void StringValue::accept(ValueVisitor *visitor) const
 ScopeChain::ScopeChain()
     : globalScope(0)
     , qmlTypes(0)
+    , qmlAttachedTypes(0)
 {
 }
 
@@ -1353,8 +1365,9 @@ void ScopeChain::update()
     _all += qmlScopeObjects;
     if (ids)
         _all += ids;
-    if (qmlTypes)
-        _all += qmlTypes;
+    if (qmlAttachedTypes)
+        _all += qmlAttachedTypes;
+    // qmlTypes are not added on purpose
     _all += jsScopes;
 }
 
@@ -2091,9 +2104,18 @@ QmlObjectValue *CppQmlTypes::makeObject(
 
 void CppQmlTypes::setPrototypes(QmlObjectValue *object)
 {
-    if (!object || object->metaObject()->superclassName().isEmpty())
+    if (!object)
         return;
 
+    FakeMetaObject::ConstPtr fmo = object->metaObject();
+
+    // resolve attached type
+    if (!fmo->attachedTypeName().isEmpty()) {
+        QmlObjectValue *attachedObject = typeByCppName(fmo->attachedTypeName());
+        if (attachedObject)
+            object->setAttachedType(attachedObject);
+    }
+
     const QString targetPackage = object->packageName();
 
     // set prototypes for whole chain, creating new QmlObjectValues if necessary
@@ -2101,7 +2123,6 @@ void CppQmlTypes::setPrototypes(QmlObjectValue *object)
     // Example: QObject (Qt, QtQuick) -> Positioner (not exported) -> Column (Qt, QtQuick)
     // needs to create Positioner (Qt) and Positioner (QtQuick)
     QmlObjectValue *v = object;
-    FakeMetaObject::ConstPtr fmo = v->metaObject();
     while (!v->prototype() && !fmo->superclassName().isEmpty()) {
         QmlObjectValue *superValue = getOrCreate(targetPackage, fmo->superclassName());
         if (!superValue)
@@ -3404,3 +3425,66 @@ ImportInfo TypeEnvironment::importInfo(const QString &name, const Context *conte
     }
     return ImportInfo();
 }
+
+namespace {
+class AttachedTypeProcessor: public MemberProcessor
+{
+    MemberProcessor *_wrapped;
+
+public:
+    AttachedTypeProcessor(MemberProcessor *wrapped) : _wrapped(wrapped) {}
+
+    static const QmlObjectValue *attachedType(const Value *v)
+    {
+        if (const QmlObjectValue *qmlValue = dynamic_cast<const QmlObjectValue *>(v)) {
+            return qmlValue->attachedType();
+        }
+        return 0;
+    }
+
+    virtual bool processProperty(const QString &name, const Value *value)
+    {
+        const QmlObjectValue *qmlValue = attachedType(value);
+        return qmlValue ? _wrapped->processProperty(name, qmlValue) : true;
+    }
+    virtual bool processEnumerator(const QString &name, const Value *value)
+    {
+        const QmlObjectValue *qmlValue = attachedType(value);
+        return qmlValue ? _wrapped->processEnumerator(name, qmlValue) : true;
+    }
+    virtual bool processSignal(const QString &name, const Value *value)
+    {
+        const QmlObjectValue *qmlValue = attachedType(value);
+        return qmlValue ? _wrapped->processSignal(name, qmlValue) : true;
+    }
+    virtual bool processSlot(const QString &name, const Value *value)
+    {
+        const QmlObjectValue *qmlValue = attachedType(value);
+        return qmlValue ? _wrapped->processSlot(name, qmlValue) : true;
+    }
+    virtual bool processGeneratedSlot(const QString &name, const Value *value)
+    {
+        const QmlObjectValue *qmlValue = attachedType(value);
+        return qmlValue ? _wrapped->processGeneratedSlot(name, qmlValue) : true;
+    }
+};
+} // anonymous namespace
+
+AttachedTypeEnvironment::AttachedTypeEnvironment(const TypeEnvironment *typeEnv)
+    : ObjectValue(typeEnv->engine())
+    , _typeEnvironment(typeEnv)
+{
+}
+
+const Value *AttachedTypeEnvironment::lookupMember(const QString &name, const Context *context,
+                                           const ObjectValue **, bool) const
+{
+    const Value *v = _typeEnvironment->lookupMember(name, context);
+    return AttachedTypeProcessor::attachedType(v);
+}
+
+void AttachedTypeEnvironment::processMembers(MemberProcessor *processor) const
+{
+    AttachedTypeProcessor wrappedProcessor(processor);
+    _typeEnvironment->processMembers(&wrappedProcessor);
+}
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 7c7a52a3446..c03c97a10aa 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -73,6 +73,7 @@ class Reference;
 class ColorValue;
 class AnchorLineValue;
 class TypeEnvironment;
+class AttachedTypeEnvironment;
 
 typedef QList<const Value *> ValueList;
 
@@ -304,6 +305,7 @@ public:
     QSharedPointer<const QmlComponentChain> qmlComponentScope;
     QList<const ObjectValue *> qmlScopeObjects;
     const TypeEnvironment *qmlTypes;
+    const AttachedTypeEnvironment *qmlAttachedTypes;
     QList<const ObjectValue *> jsScopes;
 
     // rebuilds the flat list of all scopes
@@ -461,6 +463,9 @@ public:
     using ObjectValue::prototype;
     const QmlObjectValue *prototype() const;
 
+    const QmlObjectValue *attachedType() const;
+    void setAttachedType(QmlObjectValue *value);
+
     LanguageUtils::FakeMetaObject::ConstPtr metaObject() const;
 
     QString packageName() const;
@@ -484,6 +489,7 @@ protected:
     bool isDerivedFrom(LanguageUtils::FakeMetaObject::ConstPtr base) const;
 
 private:
+    QmlObjectValue *_attachedType;
     LanguageUtils::FakeMetaObject::ConstPtr _metaObject;
     const QString _packageName;
     const LanguageUtils::ComponentVersion _componentVersion;
@@ -1032,6 +1038,19 @@ public:
     ImportInfo importInfo(const QString &name, const Context *context) const;
 };
 
+class QMLJS_EXPORT AttachedTypeEnvironment: public ObjectValue
+{
+    const TypeEnvironment *_typeEnvironment;
+
+public:
+    AttachedTypeEnvironment(const TypeEnvironment *typeEnv);
+
+    virtual const Value *lookupMember(const QString &name, const Context *context,
+                                      const ObjectValue **foundInObject = 0,
+                                      bool examinePrototypes = true) const;
+    virtual void processMembers(MemberProcessor *processor) const;
+};
+
 } } // namespace QmlJS::Interpreter
 
 #endif // QMLJS_INTERPRETER_H
diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp
index 166361bfecc..fc5e2a9bd61 100644
--- a/src/libs/qmljs/qmljsscopebuilder.cpp
+++ b/src/libs/qmljs/qmljsscopebuilder.cpp
@@ -129,8 +129,10 @@ void ScopeBuilder::initializeScopeChain()
         componentScopes.insert(_doc.data(), chain);
         makeComponentChain(_doc, chain, &componentScopes);
 
-        if (const TypeEnvironment *typeEnvironment = _context->typeEnvironment(_doc.data()))
+        if (const TypeEnvironment *typeEnvironment = _context->typeEnvironment(_doc.data())) {
             scopeChain.qmlTypes = typeEnvironment;
+            scopeChain.qmlAttachedTypes = new AttachedTypeEnvironment(typeEnvironment);
+        }
     } else {
         // add scope chains for all components that import this file
         foreach (Document::Ptr otherDoc, _snapshot) {
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index 39c3d9c33bb..f967c97da01 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -308,7 +308,7 @@ public:
         bool isAttachedProperty = false;
         if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
             isAttachedProperty = true;
-            if (const Interpreter::ObjectValue *qmlTypes = m_context->scopeChain().qmlTypes)
+            if (const Interpreter::ObjectValue *qmlTypes = m_context->scopeChain().qmlAttachedTypes)
                 scopeObjects += qmlTypes;
         }
 
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp
index 88a7c81206d..051debcb69c 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp
@@ -841,10 +841,12 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
         bool doGlobalCompletion = true;
         bool doQmlKeywordCompletion = true;
         bool doJsKeywordCompletion = true;
+        bool doQmlTypeCompletion = false;
 
         if (contextFinder.isInLhsOfBinding() && qmlScopeType) {
             doGlobalCompletion = false;
             doJsKeywordCompletion = false;
+            doQmlTypeCompletion = true;
 
             EnumerateProperties enumerateProperties(context);
             enumerateProperties.setGlobalCompletion(true);
@@ -858,8 +860,6 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
             m_completions.append(idPropertyCompletion);
 
             addCompletionsPropertyLhs(enumerateProperties(qmlScopeType), symbolIcon, PropertyOrder, contextFinder.isAfterOnInLhsOfBinding());
-            if (const Interpreter::ObjectValue *qmlTypes = context->scopeChain().qmlTypes)
-                addCompletions(enumerateProperties(qmlTypes), symbolIcon, TypeOrder);
 
             if (ScopeBuilder::isPropertyChangesObject(context, qmlScopeType)
                     && context->scopeChain().qmlScopeObjects.size() == 2) {
@@ -884,6 +884,16 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
             }
         }
 
+        if (!contextFinder.isInImport() && !contextFinder.isInQmlContext())
+            doQmlTypeCompletion = true;
+
+        if (doQmlTypeCompletion) {
+            if (const Interpreter::ObjectValue *qmlTypes = context->scopeChain().qmlTypes) {
+                EnumerateProperties enumerateProperties(context);
+                addCompletions(enumerateProperties(qmlTypes), symbolIcon, TypeOrder);
+            }
+        }
+
         if (doGlobalCompletion) {
             // It's a global completion.
             EnumerateProperties enumerateProperties(context);
@@ -934,9 +944,10 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 
             if (value && completionOperator == QLatin1Char('.')) { // member completion
                 EnumerateProperties enumerateProperties(context);
-                if (contextFinder.isInLhsOfBinding() && qmlScopeType && expressionUnderCursor.text().at(0).isLower())
+                if (contextFinder.isInLhsOfBinding() && qmlScopeType) {
+                    enumerateProperties.setEnumerateGeneratedSlots(true);
                     addCompletionsPropertyLhs(enumerateProperties(value), symbolIcon, PropertyOrder, contextFinder.isAfterOnInLhsOfBinding());
-                else
+                } else
                     addCompletions(enumerateProperties(value), symbolIcon, SymbolOrder);
             } else if (value && completionOperator == QLatin1Char('(') && m_startPosition == editor->position()) {
                 // function completion
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp
index a92bcbacdb6..779e3222d1e 100644
--- a/src/plugins/qmljseditor/qmljsfindreferences.cpp
+++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp
@@ -178,7 +178,7 @@ protected:
         const ScopeChain &chain = _context->scopeChain();
         if (chain.jsScopes.contains(scope)
                 || chain.qmlScopeObjects.contains(scope)
-                || chain.qmlTypes == scope
+                || chain.qmlAttachedTypes == scope
                 || chain.globalScope == scope)
             return false;
 
-- 
GitLab