diff options
author | Christian Kamm <[email protected]> | 2011-10-05 14:14:35 +0200 |
---|---|---|
committer | Christian Kamm <[email protected]> | 2011-10-12 10:29:27 +0200 |
commit | 010ce2d20dc8d776fad3062cf68ad2c3535c9d2b (patch) | |
tree | c68fff9977c4b23a413bf7b4e1b93b40f2cc27dc /src/libs | |
parent | 03689eeb5043dd8c49be87ec45b92a388232fdcf (diff) |
QmlJS: Set correct scope in signal handlers.
This means the code model will now offer correct completion and
highlighting for arguments of signals in their handlers, example:
MouseArea {
onClicked: {
mou<complete> // now also completes 'mouse'
}
}
Reviewed-by: Fawzi Mohamed
Change-Id: I01838ef00e391b13e6e5a832c9ec3cd983689c5b
Reviewed-on: http://codereview.qt-project.org/6147
Reviewed-by: Christian Kamm <[email protected]>
Sanity-Review: Christian Kamm <[email protected]>
Diffstat (limited to 'src/libs')
-rw-r--r-- | src/libs/qmljs/qmljsinterpreter.cpp | 81 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsinterpreter.h | 7 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsscopebuilder.cpp | 36 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsscopechain.cpp | 6 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsscopechain.h | 1 | ||||
-rw-r--r-- | src/libs/qmljs/qmljstypedescriptionreader.cpp | 4 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsvalueowner.cpp | 1 |
7 files changed, 108 insertions, 28 deletions
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index fab7b6ef6aa..e062078b1ab 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -180,7 +180,18 @@ QmlObjectValue::QmlObjectValue(FakeMetaObject::ConstPtr metaObject, const QStrin } QmlObjectValue::~QmlObjectValue() -{} +{ + delete _metaSignatures; + delete _signalScopes; +} + +static QString generatedSlotName(const QString &base) +{ + QString slotName = QLatin1String("on"); + slotName += base.at(0).toUpper(); + slotName += base.midRef(1); + return slotName; +} void QmlObjectValue::processMembers(MemberProcessor *processor) const { @@ -226,11 +237,8 @@ void QmlObjectValue::processMembers(MemberProcessor *processor) const processor->processSignal(methodName, signature); explicitSignals.insert(methodName); - QString slotName = QLatin1String("on"); - slotName += methodName.at(0).toUpper(); - slotName += methodName.midRef(1); - // process the generated slot + const QString &slotName = generatedSlotName(methodName); processor->processGeneratedSlot(slotName, signature); } } @@ -242,18 +250,15 @@ void QmlObjectValue::processMembers(MemberProcessor *processor) const continue; const QString propertyName = prop.name(); - processor->processProperty(propertyName, propertyValue(prop)); + processor->processProperty(propertyName, valueForCppName(prop.typeName())); // every property always has a onXyzChanged slot, even if the NOTIFY // signal has a different name QString signalName = propertyName; signalName += QLatin1String("Changed"); if (!explicitSignals.contains(signalName)) { - QString slotName = QLatin1String("on"); - slotName += signalName.at(0).toUpper(); - slotName += signalName.midRef(1); - // process the generated slot + const QString &slotName = generatedSlotName(signalName); processor->processGeneratedSlot(slotName, valueOwner()->undefinedValue()); } } @@ -264,9 +269,8 @@ void QmlObjectValue::processMembers(MemberProcessor *processor) const ObjectValue::processMembers(processor); } -const Value *QmlObjectValue::propertyValue(const FakeMetaProperty &prop) const +const Value *QmlObjectValue::valueForCppName(const QString &typeName) const { - QString typeName = prop.typeName(); const CppQmlTypes &cppTypes = valueOwner()->cppQmlTypes(); // check in the same package/version first @@ -322,10 +326,9 @@ const Value *QmlObjectValue::propertyValue(const FakeMetaProperty &prop) const const QStringList components = typeName.split(QLatin1String("::")); if (components.size() == 2) { base = valueOwner()->cppQmlTypes().objectByCppName(components.first()); - typeName = components.last(); } if (base) { - if (const QmlEnumValue *value = base->getEnumValue(typeName)) + if (const QmlEnumValue *value = base->getEnumValue(components.last())) return value; } @@ -419,6 +422,41 @@ const QmlEnumValue *QmlObjectValue::getEnumValue(const QString &typeName, const return 0; } +const ObjectValue *QmlObjectValue::signalScope(const QString &signalName) const +{ + QHash<QString, const ObjectValue *> *scopes = _signalScopes; + if (!scopes) { + scopes = new QHash<QString, const ObjectValue *>; + // usually not all methods are signals + scopes->reserve(_metaObject->methodCount() / 2); + for (int index = 0; index < _metaObject->methodCount(); ++index) { + const FakeMetaMethod &method = _metaObject->method(index); + if (method.methodType() != FakeMetaMethod::Signal || method.access() == FakeMetaMethod::Private) + continue; + + const QStringList ¶meterNames = method.parameterNames(); + const QStringList ¶meterTypes = method.parameterTypes(); + QTC_ASSERT(parameterNames.size() == parameterTypes.size(), continue); + + ObjectValue *scope = valueOwner()->newObject(/*prototype=*/0); + for (int i = 0; i < parameterNames.size(); ++i) { + const QString &name = parameterNames.at(i); + const QString &type = parameterTypes.at(i); + if (name.isEmpty()) + continue; + scope->setMember(name, valueForCppName(type)); + } + scopes->insert(generatedSlotName(method.methodName()), scope); + } + if (!_signalScopes.testAndSetOrdered(0, scopes)) { + delete _signalScopes; + scopes = _signalScopes; + } + } + + return scopes->value(signalName); +} + bool QmlObjectValue::isWritable(const QString &propertyName) const { for (const QmlObjectValue *it = this; it; it = it->prototype()) { @@ -1832,9 +1870,7 @@ ASTPropertyReference::ASTPropertyReference(UiPublicMember *ast, const Document * : Reference(valueOwner), _ast(ast), _doc(doc) { const QString &propertyName = ast->name.toString(); - _onChangedSlotName = QLatin1String("on"); - _onChangedSlotName += propertyName.at(0).toUpper(); - _onChangedSlotName += propertyName.midRef(1); + _onChangedSlotName = generatedSlotName(propertyName); _onChangedSlotName += QLatin1String("Changed"); } @@ -1882,9 +1918,14 @@ ASTSignal::ASTSignal(UiPublicMember *ast, const Document *doc, ValueOwner *value : FunctionValue(valueOwner), _ast(ast), _doc(doc) { const QString &signalName = ast->name.toString(); - _slotName = QLatin1String("on"); - _slotName += signalName.at(0).toUpper(); - _slotName += signalName.midRef(1); + _slotName = generatedSlotName(signalName); + + ObjectValue *v = valueOwner->newObject(/*prototype=*/0); + for (UiParameterList *it = ast->parameters; it; it = it->next) { + if (!it->name.isEmpty()) + v->setMember(it->name.toString(), valueOwner->defaultValueForBuiltinType(it->type.toString())); + } + _bodyScope = v; } ASTSignal::~ASTSignal() diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 6bc65a946e3..56268a127f2 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -424,7 +424,7 @@ public: virtual ~QmlObjectValue(); virtual void processMembers(MemberProcessor *processor) const; - const Value *propertyValue(const LanguageUtils::FakeMetaProperty &prop) const; + const Value *valueForCppName(const QString &typeName) const; using ObjectValue::prototype; const QmlObjectValue *prototype() const; @@ -448,6 +448,8 @@ public: LanguageUtils::FakeMetaEnum getEnum(const QString &typeName, const QmlObjectValue **foundInScope = 0) const; const QmlEnumValue *getEnumValue(const QString &typeName, const QmlObjectValue **foundInScope = 0) const; + + const ObjectValue *signalScope(const QString &signalName) const; protected: bool isDerivedFrom(LanguageUtils::FakeMetaObject::ConstPtr base) const; @@ -461,6 +463,7 @@ private: const LanguageUtils::ComponentVersion _componentVersion; const LanguageUtils::ComponentVersion _importVersion; mutable QAtomicPointer< QList<const Value *> > _metaSignatures; + mutable QAtomicPointer< QHash<QString, const ObjectValue *> > _signalScopes; QHash<QString, const QmlEnumValue * > _enums; int _metaObjectRevision; }; @@ -766,6 +769,7 @@ class QMLJS_EXPORT ASTSignal: public FunctionValue AST::UiPublicMember *_ast; const Document *_doc; QString _slotName; + const ObjectValue *_bodyScope; public: ASTSignal(AST::UiPublicMember *ast, const Document *doc, ValueOwner *valueOwner); @@ -773,6 +777,7 @@ public: AST::UiPublicMember *ast() const { return _ast; } QString slotName() const { return _slotName; } + const ObjectValue *bodyScope() const { return _bodyScope; } // FunctionValue interface virtual int argumentCount() const; diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp index ed52edb6a80..fd72b565338 100644 --- a/src/libs/qmljs/qmljsscopebuilder.cpp +++ b/src/libs/qmljs/qmljsscopebuilder.cpp @@ -67,6 +67,35 @@ void ScopeBuilder::push(AST::Node *node) setQmlScopeObject(qmlObject); } + // JS signal handler scope + if (UiScriptBinding *script = cast<UiScriptBinding *>(node)) { + QString name; + if (script->qualifiedId) + name = script->qualifiedId->name.toString(); + if (!_scopeChain->qmlScopeObjects().isEmpty() + && name.startsWith(QLatin1String("on")) + && !script->qualifiedId->next) { + const ObjectValue *owner = 0; + const Value *value = 0; + // try to find the name on the scope objects + foreach (const ObjectValue *scope, _scopeChain->qmlScopeObjects()) { + value = scope->lookupMember(name, _scopeChain->context(), &owner); + if (value) + break; + } + // signals defined in QML + if (const ASTSignal *astsig = dynamic_cast<const ASTSignal *>(value)) { + _scopeChain->appendJsScope(astsig->bodyScope()); + } + // signals defined in C++ + else if (const QmlObjectValue *qmlObject = dynamic_cast<const QmlObjectValue *>(owner)) { + if (const ObjectValue *scope = qmlObject->signalScope(name)) { + _scopeChain->appendJsScope(scope); + } + } + } + } + // JS scopes switch (node->kind) { case Node::Kind_UiScriptBinding: @@ -75,11 +104,8 @@ void ScopeBuilder::push(AST::Node *node) case Node::Kind_UiPublicMember: { ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(node); - if (scope) { - QList<const ObjectValue *> jsScopes = _scopeChain->jsScopes(); - jsScopes += scope; - _scopeChain->setJsScopes(jsScopes); - } + if (scope) + _scopeChain->appendJsScope(scope); break; } default: diff --git a/src/libs/qmljs/qmljsscopechain.cpp b/src/libs/qmljs/qmljsscopechain.cpp index 76016345b5e..8406be157d2 100644 --- a/src/libs/qmljs/qmljsscopechain.cpp +++ b/src/libs/qmljs/qmljsscopechain.cpp @@ -187,6 +187,12 @@ void ScopeChain::setJsScopes(const QList<const ObjectValue *> &jsScopes) m_jsScopes = jsScopes; } +void ScopeChain::appendJsScope(const ObjectValue *scope) +{ + m_modified = true; + m_jsScopes += scope; +} + QList<const ObjectValue *> ScopeChain::all() const { if (m_modified) diff --git a/src/libs/qmljs/qmljsscopechain.h b/src/libs/qmljs/qmljsscopechain.h index 6eb9e57f84e..1a16bb82a9c 100644 --- a/src/libs/qmljs/qmljsscopechain.h +++ b/src/libs/qmljs/qmljsscopechain.h @@ -100,6 +100,7 @@ public: QList<const ObjectValue *> jsScopes() const; void setJsScopes(const QList<const ObjectValue *> &jsScopes); + void appendJsScope(const ObjectValue *scope); QList<const ObjectValue *> all() const; diff --git a/src/libs/qmljs/qmljstypedescriptionreader.cpp b/src/libs/qmljs/qmljstypedescriptionreader.cpp index 0410fee3117..e5695bfcd2a 100644 --- a/src/libs/qmljs/qmljstypedescriptionreader.cpp +++ b/src/libs/qmljs/qmljstypedescriptionreader.cpp @@ -350,9 +350,9 @@ void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMetho continue; } - QString id = toString(script->qualifiedId); + const QString id = toString(script->qualifiedId); if (id == "name") { - id = readStringBinding(script); + name = readStringBinding(script); } else if (id == "type") { type = readStringBinding(script); } else if (id == "isPointer") { diff --git a/src/libs/qmljs/qmljsvalueowner.cpp b/src/libs/qmljs/qmljsvalueowner.cpp index c2c6974fbff..a4b96bd40d2 100644 --- a/src/libs/qmljs/qmljsvalueowner.cpp +++ b/src/libs/qmljs/qmljsvalueowner.cpp @@ -932,6 +932,7 @@ const ObjectValue *ValueOwner::qmlVector3DObject() const Value *ValueOwner::defaultValueForBuiltinType(const QString &name) const { + // this list is defined in ProcessAST::visit(UiPublicMember) in qdeclarativescript.cpp if (name == QLatin1String("int")) { return intValue(); } else if (name == QLatin1String("bool")) { |