QmlJS: Fix completion inside grouped propery bindings.

Task-number: QTCREATORBUG-3541
Change-Id: Ida8e59b65836c8515fbfbd2a9e4737d9ae04e76c
Reviewed-on: http://codereview.qt.nokia.com/639
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
This commit is contained in:
Christian Kamm
2011-06-23 10:25:43 +02:00
parent 87db498e72
commit 33a130e222
3 changed files with 35 additions and 12 deletions

View File

@@ -308,17 +308,18 @@ bool Bind::visit(UiObjectDefinition *ast)
{ {
// an UiObjectDefinition may be used to group property bindings // an UiObjectDefinition may be used to group property bindings
// think anchors { ... } // think anchors { ... }
bool isGroupedBinding = false; bool isGroupedBinding = ast->qualifiedTypeNameId
for (UiQualifiedId *it = ast->qualifiedTypeNameId; it; it = it->next) { && ast->qualifiedTypeNameId->name
if (!it->next && it->name) && ast->qualifiedTypeNameId->name->asString().at(0).isLower();
isGroupedBinding = it->name->asString().at(0).isLower();
}
if (!isGroupedBinding) { if (!isGroupedBinding) {
ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer); ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer);
_qmlObjects.insert(ast, value); _qmlObjects.insert(ast, value);
} else { } else {
_groupedPropertyBindings.insert(ast); _groupedPropertyBindings.insert(ast);
Interpreter::ObjectValue *oldObjectValue = switchObjectValue(0);
accept(ast->initializer);
switchObjectValue(oldObjectValue);
} }
return false; return false;
@@ -337,7 +338,7 @@ bool Bind::visit(UiObjectBinding *ast)
bool Bind::visit(UiScriptBinding *ast) bool Bind::visit(UiScriptBinding *ast)
{ {
if (toString(ast->qualifiedId) == QLatin1String("id")) { if (_currentObjectValue && toString(ast->qualifiedId) == QLatin1String("id")) {
if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement)) if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement))
if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression)) if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression))
if (i->name) if (i->name)
@@ -369,7 +370,8 @@ bool Bind::visit(VariableDeclaration *ast)
return false; return false;
ASTVariableReference *ref = new ASTVariableReference(ast, &_engine); ASTVariableReference *ref = new ASTVariableReference(ast, &_engine);
_currentObjectValue->setMember(ast->name->asString(), ref); if (_currentObjectValue)
_currentObjectValue->setMember(ast->name->asString(), ref);
return true; return true;
} }
@@ -380,7 +382,7 @@ bool Bind::visit(FunctionExpression *ast)
// return false; // return false;
ASTFunctionValue *function = new ASTFunctionValue(ast, _doc, &_engine); ASTFunctionValue *function = new ASTFunctionValue(ast, _doc, &_engine);
if (ast->name && cast<FunctionDeclaration *>(ast)) if (_currentObjectValue && ast->name && cast<FunctionDeclaration *>(ast))
_currentObjectValue->setMember(ast->name->asString(), function); _currentObjectValue->setMember(ast->name->asString(), function);
// build function scope // build function scope

View File

@@ -461,9 +461,9 @@ bool Check::visit(UiObjectBinding *ast)
void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId, void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
UiObjectInitializer *initializer) UiObjectInitializer *initializer)
{ {
// If the 'typeId' starts with a lower-case letter, it doesn't define // Don't do type checks if it's a grouped property binding.
// a new object instance. For instance: anchors { ... } // For instance: anchors { ... }
if (typeId->name->asString().at(0).isLower() && ! typeId->next) { if (_doc->bind()->isGroupedPropertyBinding(ast)) {
checkScopeObjectMember(typeId); checkScopeObjectMember(typeId);
// ### don't give up! // ### don't give up!
return; return;

View File

@@ -433,8 +433,10 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface
const Interpreter::ObjectValue *qmlScopeType = 0; const Interpreter::ObjectValue *qmlScopeType = 0;
if (contextFinder.isInQmlContext()) { if (contextFinder.isInQmlContext()) {
// find the enclosing qml object
// ### this should use semanticInfo.declaringMember instead, but that may also return functions // ### this should use semanticInfo.declaringMember instead, but that may also return functions
for (int i = path.size() - 1; i >= 0; --i) { int i;
for (i = path.size() - 1; i >= 0; --i) {
AST::Node *node = path[i]; AST::Node *node = path[i];
if (AST::cast<AST::UiObjectDefinition *>(node) || AST::cast<AST::UiObjectBinding *>(node)) { if (AST::cast<AST::UiObjectDefinition *>(node) || AST::cast<AST::UiObjectBinding *>(node)) {
qmlScopeType = document->bind()->findQmlObject(node); qmlScopeType = document->bind()->findQmlObject(node);
@@ -442,6 +444,25 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface
break; break;
} }
} }
// grouped property bindings change the scope type
for (i++; i < path.size(); ++i) {
AST::UiObjectDefinition *objDef = AST::cast<AST::UiObjectDefinition *>(path[i]);
if (!objDef || !document->bind()->isGroupedPropertyBinding(objDef))
break;
const Interpreter::ObjectValue *newScopeType = qmlScopeType;
for (AST::UiQualifiedId *it = objDef->qualifiedTypeNameId; it; it = it->next) {
if (!newScopeType || !it->name) {
newScopeType = 0;
break;
}
const Interpreter::Value *v = newScopeType->lookupMember(it->name->asString(), context);
v = context->lookupReference(v);
newScopeType = Interpreter::value_cast<const Interpreter::ObjectValue *>(v);
}
if (!newScopeType)
break;
qmlScopeType = newScopeType;
}
// fallback to getting the base type object // fallback to getting the base type object
if (!qmlScopeType) if (!qmlScopeType)
qmlScopeType = context->lookupType(document.data(), contextFinder.qmlObjectTypeName()); qmlScopeType = context->lookupType(document.data(), contextFinder.qmlObjectTypeName());