QmlDesigner: Lookup the default property in prototypes too

The default property can be set too in any prototypes. Look them up
there too. Because it is expensive we cache them in the node meta info
to make hasDefaultProperty() followed by defaultProperty() cheaper.

Change-Id: I3b9ec90fc1bc5f0228dad3b580c335734f03821d
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
This commit is contained in:
Marco Bubke
2024-04-03 12:10:16 +02:00
parent db84bc43a9
commit afd1b3b6e9
10 changed files with 135 additions and 36 deletions

View File

@@ -265,12 +265,14 @@ public:
private: private:
const Storage::Info::Type &typeData() const; const Storage::Info::Type &typeData() const;
PropertyDeclarationId defaultPropertyDeclarationId() const;
bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const; bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const;
private: private:
TypeId m_typeId; TypeId m_typeId;
NotNullPointer<const ProjectStorageType> m_projectStorage = {}; NotNullPointer<const ProjectStorageType> m_projectStorage = {};
mutable std::optional<Storage::Info::Type> m_typeData; mutable std::optional<Storage::Info::Type> m_typeData;
mutable std::optional<PropertyDeclarationId> m_defaultPropertyId;
std::shared_ptr<NodeMetaInfoPrivate> m_privateData; std::shared_ptr<NodeMetaInfoPrivate> m_privateData;
}; };

View File

@@ -1835,7 +1835,7 @@ PropertyName NodeMetaInfo::defaultPropertyName() const
{ {
if constexpr (useProjectStorage()) { if constexpr (useProjectStorage()) {
if (isValid()) { if (isValid()) {
if (auto name = m_projectStorage->propertyName(typeData().defaultPropertyId)) { if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) {
return name->toQByteArray(); return name->toQByteArray();
} }
} }
@@ -1851,7 +1851,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const
{ {
if constexpr (useProjectStorage()) { if constexpr (useProjectStorage()) {
if (isValid()) { if (isValid()) {
return PropertyMetaInfo(typeData().defaultPropertyId, m_projectStorage); return PropertyMetaInfo(defaultPropertyDeclarationId(), m_projectStorage);
} }
} else { } else {
return property(defaultPropertyName()); return property(defaultPropertyName());
@@ -1862,7 +1862,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const
bool NodeMetaInfo::hasDefaultProperty() const bool NodeMetaInfo::hasDefaultProperty() const
{ {
if constexpr (useProjectStorage()) if constexpr (useProjectStorage())
return isValid() && bool(typeData().defaultPropertyId); return isValid() && bool(defaultPropertyDeclarationId());
else else
return !defaultPropertyName().isEmpty(); return !defaultPropertyName().isEmpty();
} }
@@ -2089,6 +2089,14 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const
return *m_typeData; return *m_typeData;
} }
PropertyDeclarationId NodeMetaInfo::defaultPropertyDeclarationId() const
{
if (!m_defaultPropertyId)
m_defaultPropertyId = m_projectStorage->defaultPropertyDeclarationId(m_typeId);
return *m_defaultPropertyId;
}
bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const
{ {
if (!isValid()) { if (!isValid()) {

View File

@@ -408,6 +408,22 @@ public:
return propertyDeclarationId; return propertyDeclarationId;
} }
PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const override
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get default property declaration id"_t,
projectStorageCategory(),
keyValue("type id", typeId)};
auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] {
return fetchDefaultPropertyDeclarationId(typeId);
});
tracer.end(keyValue("property declaration id", propertyDeclarationId));
return propertyDeclarationId;
}
std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration( std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration(
PropertyDeclarationId propertyDeclarationId) const override PropertyDeclarationId propertyDeclarationId) const override
{ {
@@ -2379,17 +2395,42 @@ private:
return PropertyDeclarationId{}; return PropertyDeclarationId{};
} }
PropertyDeclarationId fetchPropertyDeclarationId(TypeId baseTypeId, PropertyDeclarationId fetchPropertyDeclarationId(TypeId typeId,
Utils::SmallStringView propertyName) const Utils::SmallStringView propertyName) const
{ {
auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement
.template value<PropertyDeclarationId>(baseTypeId, .template value<PropertyDeclarationId>(typeId, propertyName);
propertyName);
if (propertyDeclarationId) if (propertyDeclarationId)
return propertyDeclarationId; return propertyDeclarationId;
return fetchNextPropertyDeclarationId(baseTypeId, propertyName); return fetchNextPropertyDeclarationId(typeId, propertyName);
}
PropertyDeclarationId fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const
{
auto range = selectPrototypeAndExtensionIdsStatement.template range<TypeId>(baseTypeId);
for (TypeId prototype : range) {
auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement
.template value<PropertyDeclarationId>(prototype);
if (propertyDeclarationId)
return propertyDeclarationId;
}
return PropertyDeclarationId{};
}
PropertyDeclarationId fetchDefaultPropertyDeclarationId(TypeId typeId) const
{
auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement
.template value<PropertyDeclarationId>(typeId);
if (propertyDeclarationId)
return propertyDeclarationId;
return fetchNextDefaultPropertyDeclarationId(typeId);
} }
void synchronizePropertyDeclarationsInsertProperty( void synchronizePropertyDeclarationsInsertProperty(
@@ -4842,9 +4883,10 @@ public:
"UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database}; "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database};
WriteStatement<1> updateDefaultPropertyIdToNullStatement{ WriteStatement<1> updateDefaultPropertyIdToNullStatement{
"UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database};
mutable ReadStatement<4, 1> selectInfoTypeByTypeIdStatement{ mutable ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{
"SELECT defaultPropertyId, sourceId, traits, annotationTraits FROM types WHERE typeId=?", "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database};
database}; mutable ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{
"SELECT defaultPropertyId FROM types WHERE typeId=?", database};
mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{
"WITH RECURSIVE " "WITH RECURSIVE "
" all_prototype_and_extension(typeId, prototypeId) AS (" " all_prototype_and_extension(typeId, prototypeId) AS ("

View File

@@ -503,18 +503,13 @@ public:
class Type class Type
{ {
public: public:
Type(PropertyDeclarationId defaultPropertyId, Type(SourceId sourceId, long long typeTraits, long long typeAnnotationTraits)
SourceId sourceId, : sourceId{sourceId}
long long typeTraits,
long long typeAnnotationTraits)
: defaultPropertyId{defaultPropertyId}
, sourceId{sourceId}
, traits{typeTraits, typeAnnotationTraits} , traits{typeTraits, typeAnnotationTraits}
{} {}
Type(PropertyDeclarationId defaultPropertyId, SourceId sourceId, TypeTraits traits) Type(SourceId sourceId, TypeTraits traits)
: defaultPropertyId{defaultPropertyId} : sourceId{sourceId}
, sourceId{sourceId}
, traits{traits} , traits{traits}
{} {}
@@ -523,14 +518,11 @@ public:
{ {
using NanotraceHR::dictonary; using NanotraceHR::dictonary;
using NanotraceHR::keyValue; using NanotraceHR::keyValue;
auto dict = dictonary(keyValue("default property id", type.defaultPropertyId), auto dict = dictonary(keyValue("source id", type.sourceId), keyValue("traits", type.traits));
keyValue("source id", type.sourceId),
keyValue("traits", type.traits));
convertToString(string, dict); convertToString(string, dict);
} }
PropertyDeclarationId defaultPropertyId;
SourceId sourceId; SourceId sourceId;
TypeTraits traits; TypeTraits traits;
}; };

View File

@@ -55,6 +55,7 @@ public:
virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId, virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId,
::Utils::SmallStringView propertyName) const ::Utils::SmallStringView propertyName) const
= 0; = 0;
virtual PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const = 0;
virtual std::optional<Storage::Info::Type> type(TypeId typeId) const = 0; virtual std::optional<Storage::Info::Type> type(TypeId typeId) const = 0;
virtual Utils::PathString typeIconPath(TypeId typeId) const = 0; virtual Utils::PathString typeIconPath(TypeId typeId) const = 0;
virtual Storage::Info::TypeHints typeHints(TypeId typeId) const = 0; virtual Storage::Info::TypeHints typeHints(TypeId typeId) const = 0;

View File

@@ -293,8 +293,10 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId,
defaultPropertyTypeId); defaultPropertyTypeId);
} }
ON_CALL(*this, type(Eq(typeId))) ON_CALL(*this, type(Eq(typeId))).WillByDefault(Return(Storage::Info::Type{sourceId, typeTraits}));
.WillByDefault(Return(Storage::Info::Type{defaultPropertyDeclarationId, sourceId, typeTraits}));
ON_CALL(*this, defaultPropertyDeclarationId(Eq(typeId)))
.WillByDefault(Return(defaultPropertyDeclarationId));
ON_CALL(*this, isBasedOn(Eq(typeId), Eq(typeId))).WillByDefault(Return(true)); ON_CALL(*this, isBasedOn(Eq(typeId), Eq(typeId))).WillByDefault(Return(true));

View File

@@ -187,6 +187,10 @@ public:
propertyDeclarationId, propertyDeclarationId,
(QmlDesigner::TypeId typeId, ::Utils::SmallStringView propertyName), (QmlDesigner::TypeId typeId, ::Utils::SmallStringView propertyName),
(const, override)); (const, override));
MOCK_METHOD(QmlDesigner::PropertyDeclarationId,
defaultPropertyDeclarationId,
(QmlDesigner::TypeId typeId),
(const, override));
MOCK_METHOD(std::optional<QmlDesigner::Storage::Info::Type>, MOCK_METHOD(std::optional<QmlDesigner::Storage::Info::Type>,
type, type,
(QmlDesigner::TypeId typeId), (QmlDesigner::TypeId typeId),

View File

@@ -675,7 +675,7 @@ std::ostream &operator<<(std::ostream &out, const PropertyDeclaration &propertyD
std::ostream &operator<<(std::ostream &out, const Type &type) std::ostream &operator<<(std::ostream &out, const Type &type)
{ {
return out << "(" << type.defaultPropertyId << ")"; return out << "(" << type.sourceId << ")";
} }
std::ostream &operator<<(std::ostream &out, const ExportedTypeName &name) std::ostream &operator<<(std::ostream &out, const ExportedTypeName &name)

View File

@@ -61,8 +61,8 @@ protected:
++defaultPropertyIdNumber); ++defaultPropertyIdNumber);
ON_CALL(projectStorageMock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); ON_CALL(projectStorageMock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId));
ON_CALL(projectStorageMock, type(Eq(typeId))) ON_CALL(projectStorageMock, defaultPropertyDeclarationId(Eq(typeId)))
.WillByDefault(Return(Info::Type{defaultPropertyId, QmlDesigner::SourceId{}, {}})); .WillByDefault(Return(defaultPropertyId));
ON_CALL(projectStorageMock, propertyName(Eq(defaultPropertyId))) ON_CALL(projectStorageMock, propertyName(Eq(defaultPropertyId)))
.WillByDefault(Return(defaultPeopertyName)); .WillByDefault(Return(defaultPeopertyName));
} }

View File

@@ -253,17 +253,15 @@ MATCHER(StringsAreSorted, std::string(negation ? "isn't sorted" : "is sorted"))
}); });
} }
MATCHER_P3(IsInfoType, MATCHER_P2(IsInfoType,
defaultPropertyId,
sourceId, sourceId,
traits, traits,
std::string(negation ? "isn't " : "is ") std::string(negation ? "isn't " : "is ")
+ PrintToString(Storage::Info::Type{defaultPropertyId, sourceId, traits})) + PrintToString(Storage::Info::Type{sourceId, traits}))
{ {
const Storage::Info::Type &type = arg; const Storage::Info::Type &type = arg;
return type.defaultPropertyId == defaultPropertyId && type.sourceId == sourceId return type.sourceId == sourceId && type.traits == traits;
&& type.traits == traits;
} }
class ProjectStorage : public testing::Test class ProjectStorage : public testing::Test
@@ -6683,12 +6681,10 @@ TEST_F(ProjectStorage, get_type)
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
storage.synchronize(package); storage.synchronize(package);
auto typeId = fetchTypeId(sourceId1, "QQuickItem"); auto typeId = fetchTypeId(sourceId1, "QQuickItem");
auto defaultPropertyName = storage.fetchTypeByTypeId(typeId).defaultPropertyName;
auto defaultPropertyId = storage.propertyDeclarationId(typeId, defaultPropertyName);
auto type = storage.type(typeId); auto type = storage.type(typeId);
ASSERT_THAT(type, Optional(IsInfoType(defaultPropertyId, sourceId1, TypeTraitsKind::Reference))); ASSERT_THAT(type, Optional(IsInfoType(sourceId1, TypeTraitsKind::Reference)));
} }
TEST_F(ProjectStorage, dont_get_type_for_invalid_id) TEST_F(ProjectStorage, dont_get_type_for_invalid_id)
@@ -6701,6 +6697,58 @@ TEST_F(ProjectStorage, dont_get_type_for_invalid_id)
ASSERT_THAT(type, Eq(std::nullopt)); ASSERT_THAT(type, Eq(std::nullopt));
} }
TEST_F(ProjectStorage, get_default_property_declarartion_id)
{
auto package{createSimpleSynchronizationPackage()};
storage.synchronize(package);
auto typeId = fetchTypeId(sourceId1, "QQuickItem");
auto defaultPropertyName = storage.fetchTypeByTypeId(typeId).defaultPropertyName;
auto defaultPropertyId = storage.propertyDeclarationId(typeId, defaultPropertyName);
auto propertyId = storage.defaultPropertyDeclarationId(typeId);
ASSERT_THAT(propertyId, defaultPropertyId);
}
TEST_F(ProjectStorage, get_default_property_declarartion_id_in_base_type)
{
auto package{createSynchronizationPackageWithAliases()};
storage.synchronize(package);
auto baseTypeId = fetchTypeId(sourceId1, "QQuickItem");
auto defaultPropertyName = storage.fetchTypeByTypeId(baseTypeId).defaultPropertyName;
auto defaultPropertyId = storage.propertyDeclarationId(baseTypeId, defaultPropertyName);
auto typeId = fetchTypeId(sourceId3, "QAliasItem");
auto propertyId = storage.defaultPropertyDeclarationId(typeId);
ASSERT_THAT(propertyId, defaultPropertyId);
}
TEST_F(ProjectStorage, do_not_get_default_property_declarartion_id_wrong_type_in_property_chain)
{
auto package{createSynchronizationPackageWithAliases()};
package.types[1].defaultPropertyName = "objects";
storage.synchronize(package);
auto baseTypeId = fetchTypeId(sourceId1, "QQuickItem");
auto defaultPropertyName = storage.fetchTypeByTypeId(baseTypeId).defaultPropertyName;
auto defaultPropertyId = storage.propertyDeclarationId(baseTypeId, defaultPropertyName);
auto typeId = fetchTypeId(sourceId3, "QAliasItem");
auto propertyId = storage.defaultPropertyDeclarationId(typeId);
ASSERT_THAT(propertyId, defaultPropertyId);
}
TEST_F(ProjectStorage, get_invalid_default_property_declarartion_id_for_invalid_type)
{
auto package{createSimpleSynchronizationPackage()};
storage.synchronize(package);
auto propertyId = storage.defaultPropertyDeclarationId(TypeId());
ASSERT_FALSE(propertyId);
}
TEST_F(ProjectStorage, get_common_type) TEST_F(ProjectStorage, get_common_type)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};