diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index 00d90139bc03d55710745d911e94261dd4e91c13..b9dabcb3e853f3e5998dbc0f46d29fe21a290044 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -106,6 +106,11 @@ Interpreter::ObjectValue *Bind::findFunctionScope(AST::FunctionDeclaration *node return _functionScopes.value(node); } +bool Bind::isGroupedPropertyBinding(AST::Node *node) const +{ + return _groupedPropertyBindings.contains(node); +} + ObjectValue *Bind::switchObjectValue(ObjectValue *newObjectValue) { ObjectValue *oldObjectValue = _currentObjectValue; @@ -139,7 +144,6 @@ ExpressionNode *Bind::expression(UiScriptBinding *ast) const ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitializer *initializer) { ObjectValue *parentObjectValue = 0; - const QString typeName = toString(qualifiedTypeNameId); // normal component instance ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, _doc, &_engine); @@ -204,8 +208,20 @@ bool Bind::visit(UiPublicMember *) bool Bind::visit(UiObjectDefinition *ast) { - ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer); - _qmlObjects.insert(ast, value); + // an UiObjectDefinition may be used to group property bindings + // think anchors { ... } + bool isGroupedBinding = false; + for (UiQualifiedId *it = ast->qualifiedTypeNameId; it; it = it->next) { + if (!it->next) + isGroupedBinding = it->name->asString().at(0).isLower(); + } + + if (!isGroupedBinding) { + ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer); + _qmlObjects.insert(ast, value); + } else { + _groupedPropertyBindings.insert(ast); + } return false; } diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h index 9de6f2709cca661416425f05beb8e8b83ea0a0da..ed9264e24292fb4d21f9d11922e69e537d2f61be 100644 --- a/src/libs/qmljs/qmljsbind.h +++ b/src/libs/qmljs/qmljsbind.h @@ -63,6 +63,7 @@ public: Interpreter::Context *context) const; Interpreter::ObjectValue *findFunctionScope(AST::FunctionDeclaration *node) const; + bool isGroupedPropertyBinding(AST::Node *node) const; static QString toString(AST::UiQualifiedId *qualifiedId, QChar delimiter = QChar('.')); @@ -100,6 +101,7 @@ private: Interpreter::ObjectValue *_rootObjectValue; QHash<AST::Node *, Interpreter::ObjectValue *> _qmlObjects; + QSet<AST::Node *> _groupedPropertyBindings; QHash<AST::FunctionDeclaration *, Interpreter::ObjectValue *> _functionScopes; QStringList _includedScripts; diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp index a90b0b9efce1e8ca95f326664225f98930682231..7a3bcfd86a588d4465a35f4586944f539d734d80 100644 --- a/src/libs/qmljs/qmljsscopebuilder.cpp +++ b/src/libs/qmljs/qmljsscopebuilder.cpp @@ -66,10 +66,24 @@ void ScopeBuilder::setQmlScopeObject(Node *node) { ScopeChain &scopeChain = _context->scopeChain(); - scopeChain.qmlScopeObjects.clear(); + if (_doc->bind()->isGroupedPropertyBinding(node)) { + UiObjectDefinition *definition = cast<UiObjectDefinition *>(node); + if (!definition) + return; + const Value *v = scopeObjectLookup(definition->qualifiedTypeNameId); + if (!v) + return; + const ObjectValue *object = v->asObjectValue(); + if (!object) + return; + + scopeChain.qmlScopeObjects.clear(); + scopeChain.qmlScopeObjects += object; + } const ObjectValue *scopeObject = _doc->bind()->findQmlObject(node); if (scopeObject) { + scopeChain.qmlScopeObjects.clear(); scopeChain.qmlScopeObjects += scopeObject; } else { return; // Probably syntax errors, where we're working with a "recovered" AST. @@ -130,3 +144,28 @@ void ScopeBuilder::setQmlScopeObject(Node *node) } } } + +const Value *ScopeBuilder::scopeObjectLookup(AST::UiQualifiedId *id) +{ + // do a name lookup on the scope objects + const Value *result = 0; + foreach (const ObjectValue *scopeObject, _context->scopeChain().qmlScopeObjects) { + const ObjectValue *object = scopeObject; + for (UiQualifiedId *it = id; it; it = it->next) { + result = object->property(it->name->asString(), _context); + if (!result) + break; + if (it->next) { + object = result->asObjectValue(); + if (!object) { + result = 0; + break; + } + } + } + if (result) + break; + } + + return result; +} diff --git a/src/libs/qmljs/qmljsscopebuilder.h b/src/libs/qmljs/qmljsscopebuilder.h index 6b433c749ff073730d2a1604fbd9aebaa7500844..34485039bf2ebcc2879adb131aa4baf4d3023bc5 100644 --- a/src/libs/qmljs/qmljsscopebuilder.h +++ b/src/libs/qmljs/qmljsscopebuilder.h @@ -13,6 +13,7 @@ namespace AST { namespace Interpreter { class Context; + class Value; } class QMLJS_EXPORT ScopeBuilder @@ -27,6 +28,7 @@ public: private: void setQmlScopeObject(AST::Node *node); + const Interpreter::Value *scopeObjectLookup(AST::UiQualifiedId *id); Document::Ptr _doc; Interpreter::Context *_context; diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp index 9daad528c0d92cfcb2cf211d37c7bc0f7ba0cc5c..b9e7bbbfdc0a6bf8e89356a7db0a9099cf5d069c 100644 --- a/src/plugins/qmljseditor/qmljscodecompletion.cpp +++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp @@ -630,7 +630,7 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor) // Set up the current scope chain. QList<AST::Node *> astPath = semanticInfo.astPath(editor->position()); - context.build(astPath , document, snapshot, m_modelManager->importPaths()); + context.build(astPath, document, snapshot, m_modelManager->importPaths()); // Search for the operator that triggered the completion. QChar completionOperator;