Commit 490f2797 authored by Christian Kamm's avatar Christian Kamm

QmlJS: Completion for attached properties.

parent 187ae1e9
......@@ -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;
}
......
......@@ -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<const QmlObjectValue *>(_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<const QmlObjectValue *>(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);
}
......@@ -73,6 +73,7 @@ class Reference;
class ColorValue;
class AnchorLineValue;
class TypeEnvironment;
class AttachedTypeEnvironment;
typedef QList<const Value *> ValueList;
......@@ -304,6 +305,7 @@ public:
QSharedPointer<const QmlComponentChain> qmlComponentScope;
QList<const ObjectValue *> qmlScopeObjects;
const TypeEnvironment *qmlTypes;
const AttachedTypeEnvironment *qmlAttachedTypes;
QList<const ObjectValue *> 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
......@@ -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) {
......
......@@ -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;
}
......
......@@ -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
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment