diff options
author | Marco Bubke <marco.bubke@qt.io> | 2021-10-21 17:23:18 +0200 |
---|---|---|
committer | Marco Bubke <marco.bubke@qt.io> | 2021-12-08 15:00:44 +0000 |
commit | 56af8b905e17254b86bad73a02c023b043039a31 (patch) | |
tree | 179f1083022ed13a39c8882cc8a48718998eb928 | |
parent | 1fdc358eef46bdc195bb935fd95a17a3828c0818 (diff) |
QmlDesigner: Improve project storage update
Task-number: QDS-4920
Change-Id: If5a839a6862bab50fe3a3e049cb1214f04eeca1a
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
10 files changed, 700 insertions, 131 deletions
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 5df23b7385..83131404de 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -77,14 +77,15 @@ public: TypeIds typeIdsToBeDeleted; - auto sourceIdValues = Utils::transform<std::vector>(package.sourceIds, [](SourceId sourceId) { - return &sourceId; - }); + auto updatedSourceIdValues = Utils::transform<std::vector>(package.updatedSourceIds, + [](SourceId sourceId) { + return &sourceId; + }); - std::sort(sourceIdValues.begin(), sourceIdValues.end()); + std::sort(updatedSourceIdValues.begin(), updatedSourceIdValues.end()); - synchronizeFileStatuses(package.fileStatuses, sourceIdValues); - synchronizeImports(package.imports, sourceIdValues); + synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds); + synchronizeImports(package.imports, updatedSourceIdValues); synchronizeTypes(package.types, updatedTypeIds, insertedAliasPropertyDeclarations, @@ -92,10 +93,10 @@ public: relinkableAliasPropertyDeclarations, relinkablePropertyDeclarations, relinkablePrototypes, - sourceIdValues); + updatedSourceIdValues); deleteNotUpdatedTypes(updatedTypeIds, - sourceIdValues, + updatedSourceIdValues, typeIdsToBeDeleted, relinkableAliasPropertyDeclarations, relinkablePropertyDeclarations, @@ -503,7 +504,7 @@ private: AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, - const std::vector<int> &sourceIdValues) + const std::vector<int> &updatedSourceIdValues) { Storage::ExportedTypes exportedTypes; exportedTypes.reserve(types.size() * 3); @@ -517,7 +518,7 @@ private: extractExportedTypes(typeId, type, exportedTypes); } - synchronizeExportedTypes(sourceIdValues, + synchronizeExportedTypes(updatedSourceIdValues, updatedTypeIds, exportedTypes, relinkableAliasPropertyDeclarations, @@ -532,8 +533,13 @@ private: relinkablePropertyDeclarations); } - void synchronizeFileStatuses(FileStatuses &fileStatuses, const std::vector<int> &sourceIdValues) + void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds) { + auto updatedSourceIdValues = Utils::transform<std::vector>(updatedSourceIds, + [](SourceId sourceId) { + return &sourceId; + }); + auto compareKey = [](auto &&first, auto &&second) { return first.sourceId.id - second.sourceId.id; }; @@ -543,7 +549,7 @@ private: }); auto range = selectFileStatusesForSourceIdsStatement.template range<FileStatus>( - Utils::span(sourceIdValues)); + Utils::span(updatedSourceIdValues)); auto insert = [&](const FileStatus &fileStatus) { insertFileStatusStatement.write(&fileStatus.sourceId, @@ -567,15 +573,15 @@ private: Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove); } - void synchronizeImports(Storage::Imports &imports, std::vector<int> &sourceIdValues) + void synchronizeImports(Storage::Imports &imports, std::vector<int> &updatedSourceIdValues) { - deleteDocumentImportsForDeletedDocuments(imports, sourceIdValues); + deleteDocumentImportsForDeletedDocuments(imports, updatedSourceIdValues); - synchronizeDocumentImports(imports, sourceIdValues); + synchronizeDocumentImports(imports, updatedSourceIdValues); } void deleteDocumentImportsForDeletedDocuments(Storage::Imports &imports, - const std::vector<int> &sourceIdValues) + const std::vector<int> &updatedSourceIdValues) { std::vector<int> importSourceIds = Utils::transform<std::vector<int>>( imports, [](const Storage::Import &import) { return &import.sourceId; }); @@ -584,8 +590,8 @@ private: std::vector<int> documentSourceIdsToBeDeleted; - std::set_difference(sourceIdValues.begin(), - sourceIdValues.end(), + std::set_difference(updatedSourceIdValues.begin(), + updatedSourceIdValues.end(), importSourceIds.begin(), importSourceIds.end(), std::back_inserter(documentSourceIdsToBeDeleted)); @@ -767,7 +773,7 @@ private: } void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, - const std::vector<int> &sourceIdValues, + const std::vector<int> &updatedSourceIdValues, const TypeIds &typeIdsToBeDeleted, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, @@ -788,7 +794,7 @@ private: }; selectNotUpdatedTypesInSourcesStatement.readCallback(callback, - Utils::span(sourceIdValues), + Utils::span(updatedSourceIdValues), Utils::span(updatedTypeIdValues)); for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted) callback(&typeIdToBeDeleted); @@ -853,7 +859,7 @@ private: updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); } - void synchronizeExportedTypes(const std::vector<int> &sourceIdValues, + void synchronizeExportedTypes(const std::vector<int> &updatedSourceIdValues, const TypeIds &updatedTypeIds, Storage::ExportedTypes &exportedTypes, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, @@ -869,9 +875,8 @@ private: &updatedTypeIds.data()->id), updatedTypeIds.size()}; - auto range = selectExportedTypesForSourceIdsStatement - .template range<Storage::ExportedTypeView>(Utils::span{sourceIdValues}, - typeIdValues); + auto range = selectExportedTypesForSourceIdsStatement.template range<Storage::ExportedTypeView>( + Utils::span{updatedSourceIdValues}, typeIdValues); auto compareKey = [](const Storage::ExportedTypeView &view, const Storage::ExportedType &type) -> long long { @@ -1151,7 +1156,8 @@ private: PropertyCompare<AliasPropertyDeclaration>{}); } - void synchronizeDocumentImports(Storage::Imports &imports, const std::vector<int> &sourceIdValues) + void synchronizeDocumentImports(Storage::Imports &imports, + const std::vector<int> &updatedSourceIdValues) { std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { return std::tie(first.sourceId, first.moduleId, first.version) @@ -1159,7 +1165,7 @@ private: }); auto range = selectDocumentImportForSourceIdStatement.template range<Storage::ImportView>( - Utils::span{sourceIdValues}); + Utils::span{updatedSourceIdValues}); auto compareKey = [](const Storage::ImportView &view, const Storage::Import &import) -> long long { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index 42790bded6..c95d3e00e2 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -107,4 +107,10 @@ public: const char *what() const noexcept override { return "Cannot parse qml types file!"; } }; +class CannotParseQmlDocumentFile : std::exception +{ +public: + const char *what() const noexcept override { return "Cannot parse qml types file!"; } +}; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 366da00407..b564372a86 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -47,6 +47,8 @@ enum class PropertyDeclarationTraits : unsigned int { enum class TypeNameKind { Native = 0, Exported = 1, QualifiedExported = 2 }; +enum class FileType : char { QmlTypes, QmlDocument }; + constexpr PropertyDeclarationTraits operator|(PropertyDeclarationTraits first, PropertyDeclarationTraits second) { @@ -630,7 +632,7 @@ public: PropertyDeclarationId aliasId; }; -enum class ChangeLevel { Full, Minimal }; +enum class ChangeLevel : char { Full, Minimal, ExcludeExportedTypes }; class Type { @@ -669,6 +671,18 @@ public: {} explicit Type(Utils::SmallStringView typeName, + ImportedTypeName prototype, + TypeAccessSemantics accessSemantics, + SourceId sourceId, + ChangeLevel changeLevel) + : typeName{typeName} + , prototype{std::move(prototype)} + , accessSemantics{accessSemantics} + , sourceId{sourceId} + , changeLevel{changeLevel} + {} + + explicit Type(Utils::SmallStringView typeName, Utils::SmallStringView prototype, int accessSemantics, int sourceId) @@ -721,8 +735,16 @@ using Types = std::vector<Type>; class ProjectData { public: + ProjectData(ModuleId extraModuleId, SourceId sourceId, FileType fileType) + : extraModuleId{extraModuleId} + , sourceId{sourceId} + , fileType{fileType} + {} + +public: ModuleId extraModuleId; SourceId sourceId; + FileType fileType; }; using ProjectDatas = std::vector<ProjectData>; @@ -731,30 +753,33 @@ class SynchronizationPackage { public: SynchronizationPackage() = default; - SynchronizationPackage(Imports imports, Types types, SourceIds sourceIds) + SynchronizationPackage(Imports imports, Types types, SourceIds updatedSourceIds) : imports{std::move(imports)} , types{std::move(types)} - , sourceIds(std::move(sourceIds)) + , updatedSourceIds(std::move(updatedSourceIds)) {} SynchronizationPackage(Types types) : types{std::move(types)} {} - SynchronizationPackage(SourceIds sourceIds) - : sourceIds(std::move(sourceIds)) + SynchronizationPackage(SourceIds updatedSourceIds) + : updatedSourceIds(std::move(updatedSourceIds)) {} - SynchronizationPackage(SourceIds sourceIds, FileStatuses fileStatuses) - : sourceIds(std::move(sourceIds)) + SynchronizationPackage(SourceIds updatedSourceIds, FileStatuses fileStatuses) + : updatedSourceIds(std::move(updatedSourceIds)) , fileStatuses(std::move(fileStatuses)) {} public: Imports imports; Types types; - SourceIds sourceIds; + SourceIds updatedSourceIds; FileStatuses fileStatuses; + ProjectDatas projectDatas; + ModuleIds updatedProjectDataModuleIds; + SourceIds updatedFileStatusSourceIds; }; } // namespace QmlDesigner::Storage diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index cc91a06fae..3f3ac32166 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -38,6 +38,7 @@ #include <functional> namespace QmlDesigner { +namespace { ComponentReferences createComponentReferences(const QMultiHash<QString, QmlDirParser::Component> &components) { @@ -50,45 +51,103 @@ ComponentReferences createComponentReferences(const QMultiHash<QString, QmlDirPa return componentReferences; } +SourceIds filterNotUpdatedSourceIds(SourceIds updatedSourceIds, SourceIds notUpdatedSourceIds) +{ + std::sort(updatedSourceIds.begin(), updatedSourceIds.end()); + std::sort(notUpdatedSourceIds.begin(), notUpdatedSourceIds.end()); + + SourceIds filteredUpdatedSourceIds; + filteredUpdatedSourceIds.reserve(updatedSourceIds.size()); + + std::set_difference(updatedSourceIds.cbegin(), + updatedSourceIds.cend(), + notUpdatedSourceIds.cbegin(), + notUpdatedSourceIds.cend(), + std::back_inserter(filteredUpdatedSourceIds)); + + filteredUpdatedSourceIds.erase(std::unique(filteredUpdatedSourceIds.begin(), + filteredUpdatedSourceIds.end()), + filteredUpdatedSourceIds.end()); + + return filteredUpdatedSourceIds; +} + +void addSourceIds(SourceIds &sourceIds, const Storage::ProjectDatas &projectDatas) +{ + for (const auto &projectData : projectDatas) + sourceIds.push_back(projectData.sourceId); +} + +} // namespace + void ProjectUpdater::update() { Storage::SynchronizationPackage package; + SourceIds notUpdatedFileStatusSourceIds; + SourceIds notUpdatedSourceIds; + for (const QString &qmldirPath : m_projectManager.qtQmlDirs()) { SourcePath qmldirSourcePath{qmldirPath}; SourceId qmlDirSourceId = m_pathCache.sourceId(qmldirSourcePath); - switch (fileState(qmlDirSourceId, package.fileStatuses)) { + auto state = fileState(qmlDirSourceId, + package.fileStatuses, + package.updatedFileStatusSourceIds, + notUpdatedFileStatusSourceIds); + switch (state) { case FileState::Changed: { QmlDirParser parser; parser.parse(m_fileSystem.contentAsQString(qmldirPath)); - package.sourceIds.push_back(qmlDirSourceId); + package.updatedSourceIds.push_back(qmlDirSourceId); SourceContextId directoryId = m_pathCache.sourceContextId(qmlDirSourceId); Utils::PathString moduleName{parser.typeNamespace()}; ModuleId moduleId = m_projectStorage.moduleId(moduleName); - parseTypeInfos(parser.typeInfos(), directoryId, package); + const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(qmlDirSourceId); + addSourceIds(package.updatedSourceIds, qmlProjectDatas); + addSourceIds(package.updatedFileStatusSourceIds, qmlProjectDatas); + + parseTypeInfos(parser.typeInfos(), + directoryId, + moduleId, + package, + notUpdatedFileStatusSourceIds, + notUpdatedSourceIds); parseQmlComponents(createComponentReferences(parser.components()), directoryId, moduleId, - package); + package, + notUpdatedFileStatusSourceIds); + package.updatedProjectDataModuleIds.push_back(moduleId); break; } case FileState::NotChanged: { - auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(qmlDirSourceId); - parseTypeInfos(qmlProjectDatas, package); + const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(qmlDirSourceId); + parseTypeInfos(qmlProjectDatas, package, notUpdatedFileStatusSourceIds, notUpdatedSourceIds); + parseQmlComponents(qmlProjectDatas, package, notUpdatedFileStatusSourceIds); break; } case FileState::NotExists: { - // sourceIds.push_back(qmlDirSourceId); + package.updatedSourceIds.push_back(qmlDirSourceId); + auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(qmlDirSourceId); + for (const Storage::ProjectData &projectData : qmlProjectDatas) { + package.updatedSourceIds.push_back(projectData.sourceId); + } + break; } } } + package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds), + std::move(notUpdatedSourceIds)); + package.updatedFileStatusSourceIds = filterNotUpdatedSourceIds( + std::move(package.updatedFileStatusSourceIds), std::move(notUpdatedFileStatusSourceIds)); + m_projectStorage.synchronize(std::move(package)); } @@ -96,7 +155,10 @@ void ProjectUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) {} void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos, SourceContextId directoryId, - Storage::SynchronizationPackage &package) + ModuleId moduleId, + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds, + SourceIds ¬UpdatedSourceIds) { QString directory{m_pathCache.sourceContextPath(directoryId)}; @@ -104,37 +166,137 @@ void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos, SourceId sourceId = m_pathCache.sourceId(directoryId, Utils::SmallString{typeInfo}); QString qmltypesPath = directory + "/" + typeInfo; - Storage::ProjectData projectData{ModuleId{}, sourceId}; + auto projectData = package.projectDatas.emplace_back(moduleId, + sourceId, + Storage::FileType::QmlTypes); - parseTypeInfo(projectData, qmltypesPath, package); + parseTypeInfo(projectData, + qmltypesPath, + package, + notUpdatedFileStatusSourceIds, + notUpdatedSourceIds); } } void ProjectUpdater::parseTypeInfos(const Storage::ProjectDatas &projectDatas, - Storage::SynchronizationPackage &package) + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds, + SourceIds ¬UpdatedSourceIds) { for (const Storage::ProjectData &projectData : projectDatas) { + if (projectData.fileType != Storage::FileType::QmlTypes) + continue; + QString qmltypesPath = m_pathCache.sourcePath(projectData.sourceId).toQString(); - parseTypeInfo(projectData, qmltypesPath, package); + parseTypeInfo(projectData, + qmltypesPath, + package, + notUpdatedFileStatusSourceIds, + notUpdatedSourceIds); } } void ProjectUpdater::parseTypeInfo(const Storage::ProjectData &projectData, const QString &qmltypesPath, - Storage::SynchronizationPackage &package) + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds, + SourceIds ¬UpdatedSourceIds) { - if (fileState(projectData.sourceId, package.fileStatuses) == FileState::Changed) { - package.sourceIds.push_back(projectData.sourceId); + auto state = fileState(projectData.sourceId, + package.fileStatuses, + package.updatedFileStatusSourceIds, + notUpdatedFileStatusSourceIds); + switch (state) { + case FileState::Changed: { + package.updatedSourceIds.push_back(projectData.sourceId); + const auto content = m_fileSystem.contentAsQString(qmltypesPath); m_qmlTypesParser.parse(content, package.imports, package.types, projectData); + break; + } + case FileState::NotChanged: { + notUpdatedSourceIds.push_back(projectData.sourceId); + break; + } + case FileState::NotExists: + break; } } +void ProjectUpdater::parseQmlComponent(Utils::SmallStringView fileName, + Utils::SmallStringView directory, + Utils::SmallStringView typeName, + Storage::Version version, + ModuleId moduleId, + SourceContextId directoryId, + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds) +{ + SourceId sourceId = m_pathCache.sourceId(directoryId, fileName); + + Storage::Type type; + + auto state = fileState(sourceId, + package.fileStatuses, + package.updatedFileStatusSourceIds, + notUpdatedFileStatusSourceIds); + switch (state) { + case FileState::NotChanged: + type.changeLevel = Storage::ChangeLevel::Minimal; + break; + case FileState::NotExists: + throw CannotParseQmlDocumentFile{}; + case FileState::Changed: + const auto content = m_fileSystem.contentAsQString( + QString{Utils::PathString{directory} + "/" + fileName}); + type = m_qmlDocumentParser.parse(content, package.imports); + break; + } + + package.projectDatas.emplace_back(moduleId, sourceId, Storage::FileType::QmlDocument); + + package.updatedSourceIds.push_back(sourceId); + + type.typeName = fileName; + type.accessSemantics = Storage::TypeAccessSemantics::Reference; + type.sourceId = sourceId; + type.exportedTypes.push_back(Storage::ExportedType{moduleId, typeName, version}); + + package.types.push_back(std::move(type)); +} + +void ProjectUpdater::parseQmlComponent(Utils::SmallStringView fileName, + Utils::SmallStringView filePath, + SourceId sourceId, + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds) +{ + auto state = fileState(sourceId, + package.fileStatuses, + package.updatedFileStatusSourceIds, + notUpdatedFileStatusSourceIds); + if (state != FileState::Changed) + return; + + package.updatedSourceIds.push_back(sourceId); + + const auto content = m_fileSystem.contentAsQString(QString{filePath}); + auto type = m_qmlDocumentParser.parse(content, package.imports); + + type.typeName = fileName; + type.accessSemantics = Storage::TypeAccessSemantics::Reference; + type.sourceId = sourceId; + type.changeLevel = Storage::ChangeLevel::ExcludeExportedTypes; + + package.types.push_back(std::move(type)); +} + void ProjectUpdater::parseQmlComponents(ComponentReferences components, SourceContextId directoryId, ModuleId moduleId, - Storage::SynchronizationPackage &package) + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds) { std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) { return std::tie(first.get().typeName, first.get().majorVersion, first.get().minorVersion) @@ -148,33 +310,42 @@ void ProjectUpdater::parseQmlComponents(ComponentReferences components, components.erase(newEnd, components.end()); - QString directory{m_pathCache.sourceContextPath(directoryId)}; + auto directory = m_pathCache.sourceContextPath(directoryId); for (const QmlDirParser::Component &component : components) { - Utils::SmallString fileName{component.fileName}; - SourceId sourceId = m_pathCache.sourceId(directoryId, fileName); + parseQmlComponent(Utils::SmallString{component.fileName}, + directory, + Utils::SmallString{component.typeName}, + Storage::Version{component.majorVersion, component.minorVersion}, + moduleId, + directoryId, + package, + notUpdatedFileStatusSourceIds); + } +} - if (fileState(sourceId, package.fileStatuses) != FileState::Changed) +void ProjectUpdater::parseQmlComponents(const Storage::ProjectDatas &projectDatas, + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds) +{ + for (const Storage::ProjectData &projectData : projectDatas) { + if (projectData.fileType != Storage::FileType::QmlDocument) continue; - package.sourceIds.push_back(sourceId); - - const auto content = m_fileSystem.contentAsQString(directory + "/" + component.fileName); - auto type = m_qmlDocumentParser.parse(content, package.imports); - - type.typeName = fileName; - type.accessSemantics = Storage::TypeAccessSemantics::Reference; - type.sourceId = sourceId; - type.exportedTypes.push_back( - Storage::ExportedType{moduleId, - Utils::SmallString{component.typeName}, - Storage::Version{component.majorVersion, component.minorVersion}}); + SourcePath qmlDocumentPath = m_pathCache.sourcePath(projectData.sourceId); - package.types.push_back(std::move(type)); + parseQmlComponent(qmlDocumentPath.name(), + qmlDocumentPath, + projectData.sourceId, + package, + notUpdatedFileStatusSourceIds); } } -ProjectUpdater::FileState ProjectUpdater::fileState(SourceId sourceId, FileStatuses &fileStatuses) const +ProjectUpdater::FileState ProjectUpdater::fileState(SourceId sourceId, + FileStatuses &fileStatuses, + SourceIds &updatedSourceIds, + SourceIds ¬UpdatedSourceIds) const { auto currentFileStatus = m_fileStatusCache.find(sourceId); @@ -185,9 +356,11 @@ ProjectUpdater::FileState ProjectUpdater::fileState(SourceId sourceId, FileStatu if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus) { fileStatuses.push_back(currentFileStatus); + updatedSourceIds.push_back(currentFileStatus.sourceId); return FileState::Changed; } + notUpdatedSourceIds.push_back(currentFileStatus.sourceId); return FileState::NotChanged; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index e3323d7609..237c75e5da 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -88,18 +88,45 @@ private: void parseTypeInfos(const QStringList &typeInfos, SourceContextId directoryId, - Storage::SynchronizationPackage &package); + ModuleId moduleId, + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds, + SourceIds ¬UpdatedSourceIds); void parseTypeInfos(const Storage::ProjectDatas &projectDatas, - Storage::SynchronizationPackage &package); + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds, + SourceIds ¬UpdatedSourceIds); void parseTypeInfo(const Storage::ProjectData &projectData, const QString &qmltypesPath, - Storage::SynchronizationPackage &package); + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds, + SourceIds ¬UpdatedSourceIds); void parseQmlComponents(ComponentReferences components, SourceContextId directoryId, ModuleId moduleId, - Storage::SynchronizationPackage &package); - - FileState fileState(SourceId sourceId, FileStatuses &fileStatuses) const; + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds); + void parseQmlComponents(const Storage::ProjectDatas &projectDatas, + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds); + void parseQmlComponent(Utils::SmallStringView fileName, + Utils::SmallStringView directory, + Utils::SmallStringView typeName, + Storage::Version version, + ModuleId moduleId, + SourceContextId directoryId, + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds); + void parseQmlComponent(Utils::SmallStringView fileName, + Utils::SmallStringView filePath, + SourceId sourceId, + Storage::SynchronizationPackage &package, + SourceIds ¬UpdatedFileStatusSourceIds); + + FileState fileState(SourceId sourceId, + FileStatuses &fileStatuses, + SourceIds &updatedSourceIds, + SourceIds ¬UpdatedSourceIds) const; private: ProjectManagerInterface &m_projectManager; diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 3123ffcf1d..49831bab17 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -1070,8 +1070,56 @@ const char *importKindToText(ImportKind kind) return ""; } +const char *fileTypeToText(FileType fileType) +{ + switch (fileType) { + case FileType::QmlDocument: + return "QmlDocument"; + case FileType::QmlTypes: + return "QmlTypes"; + } + + return ""; +} + +const char *changeLevelToText(ChangeLevel changeLevel) +{ + switch (changeLevel) { + case ChangeLevel::Full: + return "Full"; + case ChangeLevel::Minimal: + return "Minimal"; + case ChangeLevel::ExcludeExportedTypes: + return "ExcludeExportedTypes"; + } + + return ""; +} + } // namespace +std::ostream &operator<<(std::ostream &out, FileType fileType) +{ + return out << fileTypeToText(fileType); +} + +std::ostream &operator<<(std::ostream &out, ChangeLevel changeLevel) +{ + return out << changeLevelToText(changeLevel); +} + +std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &package) +{ + return out << "(" << package.imports << ", " << package.types << ", " + << package.updatedSourceIds << ", " << package.fileStatuses << ", " + << package.updatedFileStatusSourceIds << ", " << package.projectDatas << ")"; +} + +std::ostream &operator<<(std::ostream &out, const ProjectData &data) +{ + return out << "(" << data.extraModuleId << ", " << data.sourceId << ", " << data.fileType << ")"; +} + std::ostream &operator<<(std::ostream &out, TypeAccessSemantics accessSemantics) { return out << typeAccessSemanticsToString(accessSemantics) @@ -1106,7 +1154,7 @@ std::ostream &operator<<(std::ostream &out, const NativeType &nativeType) std::ostream &operator<<(std::ostream &out, const ImportedType &importedType) { - return out << "(\"" << importedType.name << ")"; + return out << "(\"" << importedType.name << "\")"; } std::ostream &operator<<(std::ostream &out, const QualifiedImportedType &importedType) { @@ -1120,7 +1168,8 @@ std::ostream &operator<<(std::ostream &out, const Type &type) << type.prototypeId << ", " << type.accessSemantics << ", source: " << type.sourceId << ", exports: " << type.exportedTypes << ", properties: " << type.propertyDeclarations << ", functions: " << type.functionDeclarations - << ", signals: " << type.signalDeclarations << ")"; + << ", signals: " << type.signalDeclarations << ", changeLevel: " << type.changeLevel + << ")"; } std::ostream &operator<<(std::ostream &out, const PropertyDeclaration &propertyDeclaration) diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index 074fada6d9..d84534b9b0 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -263,6 +263,10 @@ class EnumeratorDeclaration; enum class ImportKind : char; class Import; enum class IsQualified : int; +class ProjectData; +class SynchronizationPackage; +enum class FileType : char; +enum class ChangeLevel : char; std::ostream &operator<<(std::ostream &out, TypeAccessSemantics accessSemantics); std::ostream &operator<<(std::ostream &out, VersionNumber versionNumber); @@ -282,6 +286,10 @@ std::ostream &operator<<(std::ostream &out, const EnumeratorDeclaration &enumera std::ostream &operator<<(std::ostream &out, const ImportKind &importKind); std::ostream &operator<<(std::ostream &out, const Import &import); std::ostream &operator<<(std::ostream &out, IsQualified isQualified); +std::ostream &operator<<(std::ostream &out, const ProjectData &data); +std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &package); +std::ostream &operator<<(std::ostream &out, FileType fileType); +std::ostream &operator<<(std::ostream &out, ChangeLevel changeLevel); } // namespace Storage diff --git a/tests/unit/unittest/projectstorage-test.cpp b/tests/unit/unittest/projectstorage-test.cpp index 43ac728fd9..dd46b0ae59 100644 --- a/tests/unit/unittest/projectstorage-test.cpp +++ b/tests/unit/unittest/projectstorage-test.cpp @@ -266,7 +266,7 @@ protected: Storage::ExportedType{qmlModuleId, "Obj", Storage::Version{2}}, Storage::ExportedType{qmlNativeModuleId, "QObject"}}}); - package.sourceIds = {sourceId1, sourceId2}; + package.updatedSourceIds = {sourceId1, sourceId2}; return package; } @@ -324,8 +324,8 @@ protected: Storage::ImportedType{"QObject"}, Storage::PropertyDeclarationTraits::IsList}); - package.sourceIds.push_back(sourceId3); - package.sourceIds.push_back(sourceId4); + package.updatedSourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId4); return package; } @@ -359,7 +359,7 @@ protected: package.types.back().propertyDeclarations.push_back( Storage::PropertyDeclaration{"objects", Storage::ImportedType{"AliasItem"}, "objects"}); - package.sourceIds.push_back(sourceId5); + package.updatedSourceIds.push_back(sourceId5); return package; } @@ -403,7 +403,7 @@ protected: Storage::ExportedType{qmlModuleId, "BuiltInObj", Storage::Version{3, 4}}, Storage::ExportedType{qmlNativeModuleId, "QObject4"}}}); - package.sourceIds.push_back(sourceId1); + package.updatedSourceIds.push_back(sourceId1); return package; } @@ -2400,7 +2400,7 @@ TEST_F(ProjectStorage, sourceId5, {Storage::ExportedType{qtQuickModuleId, "Object2"}, Storage::ExportedType{qtQuickModuleId, "Obj2"}}}); - package.sourceIds.push_back(sourceId5); + package.updatedSourceIds.push_back(sourceId5); storage.synchronize(package); @@ -2849,7 +2849,7 @@ TEST_F(ProjectStorage, QualifiedPrototype) sourceId3, {Storage::ExportedType{qtQuickModuleId, "Object"}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.sourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -2884,7 +2884,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeUpperInTheModuleChain) sourceId3, {Storage::ExportedType{qtQuickModuleId, "Object"}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.sourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -2908,7 +2908,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeWithWrongVersionThrows) sourceId3, {Storage::ExportedType{qtQuickModuleId, "Object"}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.sourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId3); ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); } @@ -2924,7 +2924,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeWithVersion) sourceId3, {Storage::ExportedType{qtQuickModuleId, "Object"}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.sourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -2948,7 +2948,7 @@ TEST_F(ProjectStorage, QualifiedPrototypeWithVersionInTheProtoTypeChain) sourceId3, {Storage::ExportedType{qtQuickModuleId, "Object", Storage::Version{2}}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.sourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -2981,7 +2981,7 @@ TEST_F(ProjectStorage, QualifiedPropertyDeclarationTypeName) sourceId3, {Storage::ExportedType{qtQuickModuleId, "Object"}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.sourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); @@ -3013,7 +3013,7 @@ TEST_F(ProjectStorage, QualifiedPropertyDeclarationTypeNameInTheModuleChain) sourceId3, {Storage::ExportedType{qtQuickModuleId, "Object"}}}); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); - package.sourceIds.push_back(sourceId3); + package.updatedSourceIds.push_back(sourceId3); storage.synchronize(package); diff --git a/tests/unit/unittest/projectstorageupdater-test.cpp b/tests/unit/unittest/projectstorageupdater-test.cpp index c26aaa7b70..ef3109be2a 100644 --- a/tests/unit/unittest/projectstorageupdater-test.cpp +++ b/tests/unit/unittest/projectstorageupdater-test.cpp @@ -47,21 +47,24 @@ using QmlDesigner::SourceId; using QmlDesigner::Storage::TypeAccessSemantics; namespace Storage = QmlDesigner::Storage; using QmlDesigner::IdPaths; +using QmlDesigner::Storage::FileType; using QmlDesigner::Storage::SynchronizationPackage; using QmlDesigner::Storage::Version; -MATCHER_P4(IsStorageType, +MATCHER_P5(IsStorageType, typeName, prototype, accessSemantics, sourceId, + changeLevel, std::string(negation ? "isn't " : "is ") - + PrintToString(Storage::Type{typeName, prototype, accessSemantics, sourceId})) + + PrintToString(Storage::Type(typeName, prototype, accessSemantics, sourceId, changeLevel))) { const Storage::Type &type = arg; return type.typeName == typeName && type.accessSemantics == accessSemantics - && type.sourceId == sourceId && Storage::ImportedTypeName{prototype} == type.prototype; + && type.sourceId == sourceId && Storage::ImportedTypeName{prototype} == type.prototype + && type.changeLevel == changeLevel; } MATCHER_P3(IsPropertyDeclaration, @@ -107,12 +110,27 @@ MATCHER_P3(IsFileStatus, && fileStatus.lastModified == lastModified; } +MATCHER_P3(IsProjectData, + moduleId, + sourceId, + fileType, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::ProjectData{moduleId, sourceId, fileType})) +{ + const Storage::ProjectData &projectData = arg; + + return projectData.sourceId == sourceId && projectData.extraModuleId == moduleId + && projectData.fileType == fileType; +} + MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) { const Storage::SynchronizationPackage &package = arg; return package.imports.empty() && package.types.empty() && package.fileStatuses.empty() - && package.sourceIds.empty(); + && package.updatedSourceIds.empty() && package.projectDatas.empty() + && package.updatedFileStatusSourceIds.empty() + && package.updatedProjectDataModuleIds.empty(); } class ProjectStorageUpdater : public testing::Test @@ -135,15 +153,9 @@ public: ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId))) .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 2, 421})); - ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId))) - .WillByDefault( - Return(QmlDesigner::Storage::ProjectDatas{{ModuleId{}, qmltypesPathSourceId}, - {ModuleId{}, qmltypes2PathSourceId}})); - - QString qmldir{"module Example\ntypeinfo example.qmltypes\n"}; ON_CALL(projectManagerMock, qtQmlDirs()).WillByDefault(Return(QStringList{"/path/qmldir"})); ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))) - .WillByDefault(Return(qmldir)); + .WillByDefault(Return(qmldirContent)); ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId1))) .WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 12})); @@ -170,6 +182,10 @@ public: .WillByDefault(Return(qmlDocument2)); ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml")))) .WillByDefault(Return(qmlDocument3)); + ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))) + .WillByDefault(Return(qmltypes1)); + ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example2.qmltypes")))) + .WillByDefault(Return(qmltypes2)); ON_CALL(qmlDocumentParserMock, parse(qmlDocument1, _)).WillByDefault([&](auto, auto &imports) { imports.push_back(import1); @@ -183,6 +199,16 @@ public: imports.push_back(import3); return thirdType; }); + ON_CALL(qmlTypesParserMock, parse(Eq(qmltypes1), _, _, _)) + .WillByDefault([&](auto, auto &imports, auto &types, auto) { + types.push_back(objectType); + imports.push_back(import4); + }); + ON_CALL(qmlTypesParserMock, parse(Eq(qmltypes2), _, _, _)) + .WillByDefault([&](auto, auto &imports, auto &types, auto) { + types.push_back(itemType); + imports.push_back(import5); + }); } protected: @@ -203,8 +229,6 @@ protected: sourcePathCache, qmlDocumentParserMock, qmlTypesParserMock}; - SourceId objectTypeSourceId{sourcePathCache.sourceId("/path/Object")}; - SourceId qmltypesPathSourceId = sourcePathCache.sourceId("/path/example.qmltypes"); SourceId qmltypes2PathSourceId = sourcePathCache.sourceId("/path/example2.qmltypes"); SourceId qmlDirPathSourceId = sourcePathCache.sourceId("/path/qmldir"); @@ -216,9 +240,14 @@ protected: Storage::Type objectType{"QObject", Storage::NativeType{}, Storage::TypeAccessSemantics::Reference, - objectTypeSourceId, + qmltypesPathSourceId, {Storage::ExportedType{exampleModuleId, "Object"}, Storage::ExportedType{exampleModuleId, "Obj"}}}; + Storage::Type itemType{"QItem", + Storage::NativeType{}, + Storage::TypeAccessSemantics::Reference, + qmltypes2PathSourceId, + {Storage::ExportedType{exampleModuleId, "Item"}}}; QString qmlDocument1{"First{}"}; QString qmlDocument2{"Second{}"}; QString qmlDocument3{"Third{}"}; @@ -228,6 +257,11 @@ protected: Storage::Import import1{qmlModuleId, Storage::Version{2, 3}, qmlDocumentSourceId1}; Storage::Import import2{qmlModuleId, Storage::Version{}, qmlDocumentSourceId2}; Storage::Import import3{qmlModuleId, Storage::Version{2}, qmlDocumentSourceId3}; + Storage::Import import4{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId}; + Storage::Import import5{qmlModuleId, Storage::Version{2, 3}, qmltypes2PathSourceId}; + QString qmldirContent{"module Example\ntypeinfo example.qmltypes\n"}; + QString qmltypes1{"Module {\ndependencies: [module1]}"}; + QString qmltypes2{"Module {\ndependencies: [module2]}"}; }; TEST_F(ProjectStorageUpdater, GetContentForQmlDirPathsIfFileStatusIsDifferent) @@ -266,7 +300,8 @@ TEST_F(ProjectStorageUpdater, RequestFileStatusFromFileSystem) TEST_F(ProjectStorageUpdater, GetContentForQmlTypes) { - QString qmldir{"module Example\ntypeinfo example.qmltypes\n"}; + QString qmldir{R"(module Example + typeinfo example.qmltypes)"}; EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))) .WillRepeatedly(Return(qmldir)); @@ -277,7 +312,8 @@ TEST_F(ProjectStorageUpdater, GetContentForQmlTypes) TEST_F(ProjectStorageUpdater, GetContentForQmlTypesIfProjectStorageFileStatusIsInvalid) { - QString qmldir{"module Example\ntypeinfo example.qmltypes\n"}; + QString qmldir{R"(module Example + typeinfo example.qmltypes)"}; EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))) .WillRepeatedly(Return(qmldir)); ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypesPathSourceId))) @@ -290,7 +326,8 @@ TEST_F(ProjectStorageUpdater, GetContentForQmlTypesIfProjectStorageFileStatusIsI TEST_F(ProjectStorageUpdater, DontGetContentForQmlTypesIfFileSystemFileStatusIsInvalid) { - QString qmldir{"module Example\ntypeinfo example.qmltypes\n"}; + QString qmldir{R"(module Example + typeinfo example.qmltypes)"}; EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))) .WillRepeatedly(Return(qmldir)); ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId))).WillByDefault(Return(FileStatus{})); @@ -302,7 +339,9 @@ TEST_F(ProjectStorageUpdater, DontGetContentForQmlTypesIfFileSystemFileStatusIsI TEST_F(ProjectStorageUpdater, ParseQmlTypes) { - QString qmldir{"module Example\ntypeinfo example.qmltypes\ntypeinfo example2.qmltypes\n"}; + QString qmldir{R"(module Example + typeinfo example.qmltypes + typeinfo example2.qmltypes)"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); QString qmltypes{"Module {\ndependencies: []}"}; QString qmltypes2{"Module {\ndependencies: [foo]}"}; @@ -333,8 +372,6 @@ TEST_F(ProjectStorageUpdater, SynchronizeIsEmptyForNoChange) TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes) { - auto qmlDirPathSourceId = sourcePathCache.sourceId("/path/qmldir"); - auto qmltypesPathSourceId = sourcePathCache.sourceId("/path/example.qmltypes"); Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId}; QString qmltypes{"Module {\ndependencies: []}"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))) @@ -350,11 +387,17 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes) synchronize( AllOf(Field(&SynchronizationPackage::imports, ElementsAre(import)), Field(&SynchronizationPackage::types, ElementsAre(Eq(objectType))), - Field(&SynchronizationPackage::sourceIds, + Field(&SynchronizationPackage::updatedSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421), - IsFileStatus(qmltypesPathSourceId, 21, 421)))))); + IsFileStatus(qmltypesPathSourceId, 21, 421))), + Field(&SynchronizationPackage::projectDatas, + UnorderedElementsAre(IsProjectData(exampleModuleId, + qmltypesPathSourceId, + FileType::QmlTypes))), + Field(&SynchronizationPackage::updatedProjectDataModuleIds, + UnorderedElementsAre(exampleModuleId))))); updater.update(); } @@ -394,8 +437,10 @@ TEST_F(ProjectStorageUpdater, GetContentForQmlDocuments) TEST_F(ProjectStorageUpdater, ParseQmlDocuments) { - QString qmldir{"module Example\nFirstType 1.0 First.qml\nFirstTypeV2 2.2 " - "First.2.qml\nSecondType 2.1 OldSecond.qml\nSecondType 2.2 Second.qml\n"}; + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstTypeV2 2.2 First.2.qml + SecondType 2.2 Second.qml)"}; QString qmlDocument1{"First{}"}; QString qmlDocument2{"Second{}"}; QString qmlDocument3{"Third{}"}; @@ -414,10 +459,21 @@ TEST_F(ProjectStorageUpdater, ParseQmlDocuments) updater.update(); } +TEST_F(ProjectStorageUpdater, ParseQmlDocumentsWithNonExistingQmlDocumentThrows) +{ + QString qmldir{R"(module Example + NonexitingType 1.0 NonexitingType.qml)"}; + ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); + + ASSERT_THROW(updater.update(), QmlDesigner::CannotParseQmlDocumentFile); +} + TEST_F(ProjectStorageUpdater, SynchronizeQmlDocuments) { - QString qmldir{"module Example\nFirstType 1.0 First.qml\nFirstType 2.2 " - "First.2.qml\nSecondType 2.1 OldSecond.qml\nSecondType 2.2 Second.qml\n"}; + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First.2.qml + SecondType 2.2 Second.qml)"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); EXPECT_CALL( @@ -429,22 +485,30 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocuments) AllOf(IsStorageType("First.qml", Storage::ImportedType{"Object"}, TypeAccessSemantics::Reference, - qmlDocumentSourceId1), + qmlDocumentSourceId1, + Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))), AllOf(IsStorageType("First.2.qml", Storage::ImportedType{"Object2"}, TypeAccessSemantics::Reference, - qmlDocumentSourceId2), + qmlDocumentSourceId2, + Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))), AllOf(IsStorageType("Second.qml", Storage::ImportedType{"Object3"}, TypeAccessSemantics::Reference, - qmlDocumentSourceId3), + qmlDocumentSourceId3, + Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, ElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2)))))), - Field(&SynchronizationPackage::sourceIds, + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2, @@ -453,15 +517,93 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocuments) UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421), IsFileStatus(qmlDocumentSourceId1, 22, 12), IsFileStatus(qmlDocumentSourceId2, 22, 13), - IsFileStatus(qmlDocumentSourceId3, 22, 14)))))); + IsFileStatus(qmlDocumentSourceId3, 22, 14))), + Field(&SynchronizationPackage::updatedProjectDataModuleIds, + UnorderedElementsAre(exampleModuleId)), + Field(&SynchronizationPackage::projectDatas, + UnorderedElementsAre( + IsProjectData(exampleModuleId, qmlDocumentSourceId1, FileType::QmlDocument), + IsProjectData(exampleModuleId, qmlDocumentSourceId2, FileType::QmlDocument), + IsProjectData(exampleModuleId, qmlDocumentSourceId3, FileType::QmlDocument)))))); + + updater.update(); +} + +TEST_F(ProjectStorageUpdater, SynchronizeRemoved) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First.2.qml + typeinfo example.qmltypes + typeinfo example2.qmltypes + )"}; + ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); + ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId2))) + .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 13})); + ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId))) + .WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421})); + ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId))) + .WillByDefault(Return(QmlDesigner::Storage::ProjectDatas{ + {exampleModuleId, qmltypesPathSourceId, FileType::QmlTypes}, + {exampleModuleId, qmltypes2PathSourceId, FileType::QmlTypes}, + {exampleModuleId, qmlDocumentSourceId1, FileType::QmlDocument}, + {exampleModuleId, qmlDocumentSourceId2, FileType::QmlDocument}, + {exampleModuleId, qmlDocumentSourceId3, FileType::QmlDocument}})); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import4)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + AllOf(IsStorageType("First.qml", + Storage::ImportedType{"Object"}, + TypeAccessSemantics::Reference, + qmlDocumentSourceId1, + Storage::ChangeLevel::Full), + Field(&Storage::Type::exportedTypes, + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))), + AllOf(IsStorageType("First.2.qml", + Storage::NativeType{}, + TypeAccessSemantics::Reference, + qmlDocumentSourceId2, + Storage::ChangeLevel::Minimal), + Field(&Storage::Type::exportedTypes, + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmltypesPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmltypesPathSourceId, + qmlDocumentSourceId3)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421), + IsFileStatus(qmlDocumentSourceId1, 22, 12), + IsFileStatus(qmltypesPathSourceId, 21, 421))), + Field(&SynchronizationPackage::updatedProjectDataModuleIds, + UnorderedElementsAre(exampleModuleId)), + Field(&SynchronizationPackage::projectDatas, + UnorderedElementsAre( + IsProjectData(exampleModuleId, qmlDocumentSourceId1, FileType::QmlDocument), + IsProjectData(exampleModuleId, qmlDocumentSourceId2, FileType::QmlDocument), + IsProjectData(exampleModuleId, qmltypesPathSourceId, FileType::QmlTypes), + IsProjectData(exampleModuleId, qmltypes2PathSourceId, FileType::QmlTypes)))))); updater.update(); } TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsDontUpdateIfUpToDate) { - QString qmldir{"module Example\nFirstType 1.0 First.qml\nFirstType 2.2 " - "First.2.qml\nSecondType 2.1 OldSecond.qml\nSecondType 2.2 Second.qml\n"}; + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First.2.qml + SecondType 2.2 Second.qml)"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3))) .WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 14})); @@ -475,29 +617,50 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsDontUpdateIfUpToDate) AllOf(IsStorageType("First.qml", Storage::ImportedType{"Object"}, TypeAccessSemantics::Reference, - qmlDocumentSourceId1), + qmlDocumentSourceId1, + Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, ElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0)))), AllOf(IsStorageType("First.2.qml", Storage::ImportedType{"Object2"}, TypeAccessSemantics::Reference, - qmlDocumentSourceId2), + qmlDocumentSourceId2, + Storage::ChangeLevel::Full), Field(&Storage::Type::exportedTypes, - ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))))), - Field(&SynchronizationPackage::sourceIds, - UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), + ElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2)))), + AllOf(IsStorageType("Second.qml", + Storage::NativeType{}, + TypeAccessSemantics::Reference, + qmlDocumentSourceId3, + Storage::ChangeLevel::Minimal), + Field(&Storage::Type::exportedTypes, + ElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2, + qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421), IsFileStatus(qmlDocumentSourceId1, 22, 12), - IsFileStatus(qmlDocumentSourceId2, 22, 13)))))); + IsFileStatus(qmlDocumentSourceId2, 22, 13))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), + Field(&SynchronizationPackage::projectDatas, + UnorderedElementsAre( + IsProjectData(exampleModuleId, qmlDocumentSourceId1, FileType::QmlDocument), + IsProjectData(exampleModuleId, qmlDocumentSourceId2, FileType::QmlDocument), + IsProjectData(exampleModuleId, qmlDocumentSourceId3, FileType::QmlDocument)))))); updater.update(); } TEST_F(ProjectStorageUpdater, UpdateQmldirDocuments) { - QString qmldir{"module Example\nFirstType 1.1 First.qml\nFirstType 2.2 " - "First.2.qml\nSecondType 2.1 OldSecond.qml\nSecondType 2.2 Second.qml\n"}; + QString qmldir{R"(module Example + FirstType 1.1 First.qml + FirstType 2.2 First.2.qml + SecondType 2.2 Second.qml)"}; ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir)); ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3))) .WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 14})); @@ -505,4 +668,115 @@ TEST_F(ProjectStorageUpdater, UpdateQmldirDocuments) updater.pathsWithIdsChanged({}); } +TEST_F(ProjectStorageUpdater, AddSourceIdForForInvalidQmldirFileStatus) +{ + ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId))) + .WillByDefault(Return(QmlDesigner::Storage::ProjectDatas{ + {exampleModuleId, qmltypesPathSourceId, FileType::QmlTypes}, + {exampleModuleId, qmltypes2PathSourceId, FileType::QmlTypes}})); + ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId))).WillByDefault(Return(FileStatus{})); + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()), + Field(&SynchronizationPackage::types, IsEmpty()), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDirPathSourceId, + qmltypesPathSourceId, + qmltypes2PathSourceId)), + Field(&SynchronizationPackage::fileStatuses, IsEmpty()), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, IsEmpty()), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + updater.update(); +} + +TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChanged) +{ + ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId))) + .WillByDefault(Return(QmlDesigner::Storage::ProjectDatas{ + {exampleModuleId, qmltypesPathSourceId, FileType::QmlTypes}, + {exampleModuleId, qmltypes2PathSourceId, FileType::QmlTypes}, + {exampleModuleId, qmlDocumentSourceId1, FileType::QmlDocument}, + {exampleModuleId, qmlDocumentSourceId2, FileType::QmlDocument}})); + ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId))) + .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421})); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::imports, + UnorderedElementsAre(import1, import2, import4, import5)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre( + Eq(objectType), + Eq(itemType), + AllOf(IsStorageType("First.qml", + Storage::ImportedType{"Object"}, + TypeAccessSemantics::Reference, + qmlDocumentSourceId1, + Storage::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Type::exportedTypes, IsEmpty())), + AllOf(IsStorageType("First.2.qml", + Storage::ImportedType{"Object2"}, + TypeAccessSemantics::Reference, + qmlDocumentSourceId2, + Storage::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 21, 421), + IsFileStatus(qmltypes2PathSourceId, 21, 421), + IsFileStatus(qmlDocumentSourceId1, 22, 12), + IsFileStatus(qmlDocumentSourceId2, 22, 13))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDocumentSourceId1, + qmlDocumentSourceId2)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.update(); +} + +TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChangedAndSomeUpdatedFiles) +{ + ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId))) + .WillByDefault(Return(QmlDesigner::Storage::ProjectDatas{ + {exampleModuleId, qmltypesPathSourceId, FileType::QmlTypes}, + {exampleModuleId, qmltypes2PathSourceId, FileType::QmlTypes}, + {exampleModuleId, qmlDocumentSourceId1, FileType::QmlDocument}, + {exampleModuleId, qmlDocumentSourceId2, FileType::QmlDocument}})); + ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId))) + .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421})); + ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId))) + .WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421})); + ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId2))) + .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 13})); + + EXPECT_CALL( + projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import4)), + Field(&SynchronizationPackage::types, + UnorderedElementsAre(Eq(objectType), + AllOf(IsStorageType("First.qml", + Storage::ImportedType{"Object"}, + TypeAccessSemantics::Reference, + qmlDocumentSourceId1, + Storage::ChangeLevel::ExcludeExportedTypes), + Field(&Storage::Type::exportedTypes, IsEmpty())))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)), + Field(&SynchronizationPackage::fileStatuses, + UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 21, 421), + IsFileStatus(qmlDocumentSourceId1, 22, 12))), + Field(&SynchronizationPackage::updatedFileStatusSourceIds, + UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)), + Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + + updater.update(); +} + } // namespace diff --git a/tests/unit/unittest/qmltypesparser-test.cpp b/tests/unit/unittest/qmltypesparser-test.cpp index 9de60a1807..0c2c3c26bb 100644 --- a/tests/unit/unittest/qmltypesparser-test.cpp +++ b/tests/unit/unittest/qmltypesparser-test.cpp @@ -174,7 +174,8 @@ protected: Storage::Types types; SourceId qmltypesFileSourceId{sourcePathCache.sourceId("path/to/types.qmltypes")}; QmlDesigner::Storage::ProjectData projectData{storage.moduleId("QtQml-cppnative"), - qmltypesFileSourceId}; + qmltypesFileSourceId, + Storage::FileType::QmlTypes}; SourceContextId qmltypesFileSourceContextId{sourcePathCache.sourceContextId(qmltypesFileSourceId)}; ModuleId directoryModuleId{storage.moduleId("path/to/")}; }; |