forked from qt-creator/qt-creator
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 <christian.d.kamm@nokia.com>
Sanity-Review: Christian Kamm <christian.d.kamm@nokia.com>
This commit is contained in:
@@ -180,7 +180,18 @@ QmlObjectValue::QmlObjectValue(FakeMetaObject::ConstPtr metaObject, const QStrin
|
|||||||
}
|
}
|
||||||
|
|
||||||
QmlObjectValue::~QmlObjectValue()
|
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
|
void QmlObjectValue::processMembers(MemberProcessor *processor) const
|
||||||
{
|
{
|
||||||
@@ -226,11 +237,8 @@ void QmlObjectValue::processMembers(MemberProcessor *processor) const
|
|||||||
processor->processSignal(methodName, signature);
|
processor->processSignal(methodName, signature);
|
||||||
explicitSignals.insert(methodName);
|
explicitSignals.insert(methodName);
|
||||||
|
|
||||||
QString slotName = QLatin1String("on");
|
|
||||||
slotName += methodName.at(0).toUpper();
|
|
||||||
slotName += methodName.midRef(1);
|
|
||||||
|
|
||||||
// process the generated slot
|
// process the generated slot
|
||||||
|
const QString &slotName = generatedSlotName(methodName);
|
||||||
processor->processGeneratedSlot(slotName, signature);
|
processor->processGeneratedSlot(slotName, signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,18 +250,15 @@ void QmlObjectValue::processMembers(MemberProcessor *processor) const
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const QString propertyName = prop.name();
|
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
|
// every property always has a onXyzChanged slot, even if the NOTIFY
|
||||||
// signal has a different name
|
// signal has a different name
|
||||||
QString signalName = propertyName;
|
QString signalName = propertyName;
|
||||||
signalName += QLatin1String("Changed");
|
signalName += QLatin1String("Changed");
|
||||||
if (!explicitSignals.contains(signalName)) {
|
if (!explicitSignals.contains(signalName)) {
|
||||||
QString slotName = QLatin1String("on");
|
|
||||||
slotName += signalName.at(0).toUpper();
|
|
||||||
slotName += signalName.midRef(1);
|
|
||||||
|
|
||||||
// process the generated slot
|
// process the generated slot
|
||||||
|
const QString &slotName = generatedSlotName(signalName);
|
||||||
processor->processGeneratedSlot(slotName, valueOwner()->undefinedValue());
|
processor->processGeneratedSlot(slotName, valueOwner()->undefinedValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,9 +269,8 @@ void QmlObjectValue::processMembers(MemberProcessor *processor) const
|
|||||||
ObjectValue::processMembers(processor);
|
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();
|
const CppQmlTypes &cppTypes = valueOwner()->cppQmlTypes();
|
||||||
|
|
||||||
// check in the same package/version first
|
// 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("::"));
|
const QStringList components = typeName.split(QLatin1String("::"));
|
||||||
if (components.size() == 2) {
|
if (components.size() == 2) {
|
||||||
base = valueOwner()->cppQmlTypes().objectByCppName(components.first());
|
base = valueOwner()->cppQmlTypes().objectByCppName(components.first());
|
||||||
typeName = components.last();
|
|
||||||
}
|
}
|
||||||
if (base) {
|
if (base) {
|
||||||
if (const QmlEnumValue *value = base->getEnumValue(typeName))
|
if (const QmlEnumValue *value = base->getEnumValue(components.last()))
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,6 +422,41 @@ const QmlEnumValue *QmlObjectValue::getEnumValue(const QString &typeName, const
|
|||||||
return 0;
|
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
|
bool QmlObjectValue::isWritable(const QString &propertyName) const
|
||||||
{
|
{
|
||||||
for (const QmlObjectValue *it = this; it; it = it->prototype()) {
|
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)
|
: Reference(valueOwner), _ast(ast), _doc(doc)
|
||||||
{
|
{
|
||||||
const QString &propertyName = ast->name.toString();
|
const QString &propertyName = ast->name.toString();
|
||||||
_onChangedSlotName = QLatin1String("on");
|
_onChangedSlotName = generatedSlotName(propertyName);
|
||||||
_onChangedSlotName += propertyName.at(0).toUpper();
|
|
||||||
_onChangedSlotName += propertyName.midRef(1);
|
|
||||||
_onChangedSlotName += QLatin1String("Changed");
|
_onChangedSlotName += QLatin1String("Changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1882,9 +1918,14 @@ ASTSignal::ASTSignal(UiPublicMember *ast, const Document *doc, ValueOwner *value
|
|||||||
: FunctionValue(valueOwner), _ast(ast), _doc(doc)
|
: FunctionValue(valueOwner), _ast(ast), _doc(doc)
|
||||||
{
|
{
|
||||||
const QString &signalName = ast->name.toString();
|
const QString &signalName = ast->name.toString();
|
||||||
_slotName = QLatin1String("on");
|
_slotName = generatedSlotName(signalName);
|
||||||
_slotName += signalName.at(0).toUpper();
|
|
||||||
_slotName += signalName.midRef(1);
|
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()
|
ASTSignal::~ASTSignal()
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ public:
|
|||||||
virtual ~QmlObjectValue();
|
virtual ~QmlObjectValue();
|
||||||
|
|
||||||
virtual void processMembers(MemberProcessor *processor) const;
|
virtual void processMembers(MemberProcessor *processor) const;
|
||||||
const Value *propertyValue(const LanguageUtils::FakeMetaProperty &prop) const;
|
const Value *valueForCppName(const QString &typeName) const;
|
||||||
|
|
||||||
using ObjectValue::prototype;
|
using ObjectValue::prototype;
|
||||||
const QmlObjectValue *prototype() const;
|
const QmlObjectValue *prototype() const;
|
||||||
@@ -448,6 +448,8 @@ public:
|
|||||||
|
|
||||||
LanguageUtils::FakeMetaEnum getEnum(const QString &typeName, const QmlObjectValue **foundInScope = 0) const;
|
LanguageUtils::FakeMetaEnum getEnum(const QString &typeName, const QmlObjectValue **foundInScope = 0) const;
|
||||||
const QmlEnumValue *getEnumValue(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:
|
protected:
|
||||||
bool isDerivedFrom(LanguageUtils::FakeMetaObject::ConstPtr base) const;
|
bool isDerivedFrom(LanguageUtils::FakeMetaObject::ConstPtr base) const;
|
||||||
|
|
||||||
@@ -461,6 +463,7 @@ private:
|
|||||||
const LanguageUtils::ComponentVersion _componentVersion;
|
const LanguageUtils::ComponentVersion _componentVersion;
|
||||||
const LanguageUtils::ComponentVersion _importVersion;
|
const LanguageUtils::ComponentVersion _importVersion;
|
||||||
mutable QAtomicPointer< QList<const Value *> > _metaSignatures;
|
mutable QAtomicPointer< QList<const Value *> > _metaSignatures;
|
||||||
|
mutable QAtomicPointer< QHash<QString, const ObjectValue *> > _signalScopes;
|
||||||
QHash<QString, const QmlEnumValue * > _enums;
|
QHash<QString, const QmlEnumValue * > _enums;
|
||||||
int _metaObjectRevision;
|
int _metaObjectRevision;
|
||||||
};
|
};
|
||||||
@@ -766,6 +769,7 @@ class QMLJS_EXPORT ASTSignal: public FunctionValue
|
|||||||
AST::UiPublicMember *_ast;
|
AST::UiPublicMember *_ast;
|
||||||
const Document *_doc;
|
const Document *_doc;
|
||||||
QString _slotName;
|
QString _slotName;
|
||||||
|
const ObjectValue *_bodyScope;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ASTSignal(AST::UiPublicMember *ast, const Document *doc, ValueOwner *valueOwner);
|
ASTSignal(AST::UiPublicMember *ast, const Document *doc, ValueOwner *valueOwner);
|
||||||
@@ -773,6 +777,7 @@ public:
|
|||||||
|
|
||||||
AST::UiPublicMember *ast() const { return _ast; }
|
AST::UiPublicMember *ast() const { return _ast; }
|
||||||
QString slotName() const { return _slotName; }
|
QString slotName() const { return _slotName; }
|
||||||
|
const ObjectValue *bodyScope() const { return _bodyScope; }
|
||||||
|
|
||||||
// FunctionValue interface
|
// FunctionValue interface
|
||||||
virtual int argumentCount() const;
|
virtual int argumentCount() const;
|
||||||
|
|||||||
@@ -67,6 +67,35 @@ void ScopeBuilder::push(AST::Node *node)
|
|||||||
setQmlScopeObject(qmlObject);
|
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
|
// JS scopes
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
case Node::Kind_UiScriptBinding:
|
case Node::Kind_UiScriptBinding:
|
||||||
@@ -75,11 +104,8 @@ void ScopeBuilder::push(AST::Node *node)
|
|||||||
case Node::Kind_UiPublicMember:
|
case Node::Kind_UiPublicMember:
|
||||||
{
|
{
|
||||||
ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(node);
|
ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(node);
|
||||||
if (scope) {
|
if (scope)
|
||||||
QList<const ObjectValue *> jsScopes = _scopeChain->jsScopes();
|
_scopeChain->appendJsScope(scope);
|
||||||
jsScopes += scope;
|
|
||||||
_scopeChain->setJsScopes(jsScopes);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -187,6 +187,12 @@ void ScopeChain::setJsScopes(const QList<const ObjectValue *> &jsScopes)
|
|||||||
m_jsScopes = jsScopes;
|
m_jsScopes = jsScopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScopeChain::appendJsScope(const ObjectValue *scope)
|
||||||
|
{
|
||||||
|
m_modified = true;
|
||||||
|
m_jsScopes += scope;
|
||||||
|
}
|
||||||
|
|
||||||
QList<const ObjectValue *> ScopeChain::all() const
|
QList<const ObjectValue *> ScopeChain::all() const
|
||||||
{
|
{
|
||||||
if (m_modified)
|
if (m_modified)
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public:
|
|||||||
|
|
||||||
QList<const ObjectValue *> jsScopes() const;
|
QList<const ObjectValue *> jsScopes() const;
|
||||||
void setJsScopes(const QList<const ObjectValue *> &jsScopes);
|
void setJsScopes(const QList<const ObjectValue *> &jsScopes);
|
||||||
|
void appendJsScope(const ObjectValue *scope);
|
||||||
|
|
||||||
QList<const ObjectValue *> all() const;
|
QList<const ObjectValue *> all() const;
|
||||||
|
|
||||||
|
|||||||
@@ -350,9 +350,9 @@ void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMetho
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString id = toString(script->qualifiedId);
|
const QString id = toString(script->qualifiedId);
|
||||||
if (id == "name") {
|
if (id == "name") {
|
||||||
id = readStringBinding(script);
|
name = readStringBinding(script);
|
||||||
} else if (id == "type") {
|
} else if (id == "type") {
|
||||||
type = readStringBinding(script);
|
type = readStringBinding(script);
|
||||||
} else if (id == "isPointer") {
|
} else if (id == "isPointer") {
|
||||||
|
|||||||
@@ -932,6 +932,7 @@ const ObjectValue *ValueOwner::qmlVector3DObject()
|
|||||||
|
|
||||||
const Value *ValueOwner::defaultValueForBuiltinType(const QString &name) const
|
const Value *ValueOwner::defaultValueForBuiltinType(const QString &name) const
|
||||||
{
|
{
|
||||||
|
// this list is defined in ProcessAST::visit(UiPublicMember) in qdeclarativescript.cpp
|
||||||
if (name == QLatin1String("int")) {
|
if (name == QLatin1String("int")) {
|
||||||
return intValue();
|
return intValue();
|
||||||
} else if (name == QLatin1String("bool")) {
|
} else if (name == QLatin1String("bool")) {
|
||||||
|
|||||||
Reference in New Issue
Block a user