From 490f2797f636ff49ce0a778610a2532d1277e4d4 Mon Sep 17 00:00:00 2001 From: Christian Kamm 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 +- src/plugins/qmljseditor/qmljscodecompletion.cpp | 19 ++++- src/plugins/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 8c3d0d1554..ce0d97f5cb 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 8eee26d03d..90416d3a6d 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(_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(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 7c7a52a344..c03c97a10a 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 ValueList; @@ -304,6 +305,7 @@ public: QSharedPointer qmlComponentScope; QList qmlScopeObjects; const TypeEnvironment *qmlTypes; + const AttachedTypeEnvironment *qmlAttachedTypes; QList 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 166361bfec..fc5e2a9bd6 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 39c3d9c33b..f967c97da0 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 88a7c81206..051debcb69 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 a92bcbacdb..779e3222d1 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; -- 2.11.0