QmlDsigner: Add entries to path watcher for initial run

If there are no cached data all importatnt files should be now being
watched.

Task-number: QDS-9178
Change-Id: I0c9093e41e6a2d4f3a5aa3d62aba1fd41e047da4
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Marco Bubke
2023-02-28 15:12:07 +01:00
parent 762261367d
commit 583bb87c6a
5 changed files with 245 additions and 39 deletions

View File

@@ -10,7 +10,7 @@
namespace QmlDesigner {
enum class SourceType : int { Qml, QmlUi, QmlTypes, QmlDir };
enum class SourceType : int { Qml, QmlUi, QmlTypes, QmlDir, Directory };
class ProjectChunkId
{

View File

@@ -6,6 +6,7 @@
#include "filestatuscache.h"
#include "filesysteminterface.h"
#include "projectstorage.h"
#include "projectstoragepathwatcherinterface.h"
#include "qmldocumentparserinterface.h"
#include "qmltypesparserinterface.h"
#include "sourcepath.h"
@@ -160,15 +161,38 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i
} // namespace
void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypesPaths)
void ProjectStorageUpdater::update(QStringList directories,
QStringList qmlTypesPaths,
ProjectPartId projectPartId)
{
Storage::Synchronization::SynchronizationPackage package;
SourceIds notUpdatedFileStatusSourceIds;
SourceIds notUpdatedSourceIds;
std::vector<IdPaths> idPaths;
idPaths.reserve(2);
SourceIds watchedDirectorySourceIds;
watchedDirectorySourceIds.reserve(directories.size());
SourceIds watchedQmldirSourceIds;
watchedQmldirSourceIds.reserve(directories.size());
SourceIds watchedQmlSourceIds;
watchedQmldirSourceIds.reserve(directories.size() * 100);
SourceIds watchedQmltypesSourceIds;
watchedQmltypesSourceIds.reserve(directories.size());
updateDirectories(directories, package, notUpdatedFileStatusSourceIds, notUpdatedSourceIds);
updateQmlTypes(qmlTypesPaths, package, notUpdatedFileStatusSourceIds, notUpdatedSourceIds);
updateDirectories(directories,
package,
notUpdatedFileStatusSourceIds,
notUpdatedSourceIds,
watchedDirectorySourceIds,
watchedQmldirSourceIds,
watchedQmlSourceIds,
watchedQmltypesSourceIds);
updateQmlTypes(qmlTypesPaths,
package,
notUpdatedFileStatusSourceIds,
notUpdatedSourceIds,
watchedQmltypesSourceIds);
package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds),
std::move(notUpdatedSourceIds));
@@ -176,12 +200,19 @@ void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypes
std::move(package.updatedFileStatusSourceIds), std::move(notUpdatedFileStatusSourceIds));
m_projectStorage.synchronize(std::move(package));
idPaths.push_back({projectPartId, SourceType::Directory, std::move(watchedDirectorySourceIds)});
idPaths.push_back({projectPartId, SourceType::QmlDir, std::move(watchedQmldirSourceIds)});
idPaths.push_back({projectPartId, SourceType::Qml, std::move(watchedQmlSourceIds)});
idPaths.push_back({projectPartId, SourceType::QmlTypes, std::move(watchedQmltypesSourceIds)});
m_pathWatcher.updateIdPaths(idPaths);
}
void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &notUpdatedSourceIds)
SourceIds &notUpdatedSourceIds,
SourceIds &watchedQmltypesSourceIds)
{
if (qmlTypesPaths.empty())
return;
@@ -190,6 +221,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
for (const QString &qmlTypesPath : qmlTypesPaths) {
SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath});
watchedQmltypesSourceIds.push_back(sourceId);
Storage::Synchronization::ProjectData projectData{sourceId,
sourceId,
@@ -224,8 +256,13 @@ ProjectStorageUpdater::FileState combineState(FileStates... fileStates)
void ProjectStorageUpdater::updateDirectories(const QStringList &directories,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &notUpdatedSourceIds)
SourceIds &notUpdatedSourceIds,
SourceIds &watchedDirectorySourceIds,
SourceIds &watchedQmldirSourceIds,
SourceIds &watchedQmlSourceIds,
SourceIds &watchedQmltypesSourceIds)
{
for (const QString &directory : directories) {
Utils::PathString directoryPath = directory;
SourcePath qmldirSourcePath{directory + "/qmldir"};
@@ -233,16 +270,19 @@ void ProjectStorageUpdater::updateDirectories(const QStringList &directories,
SourcePath directorySourcePath{directory + "/."};
auto directorySourceId = m_pathCache.sourceId(directorySourcePath);
auto directoryState = fileState(directorySourceId,
package.fileStatuses,
package.updatedFileStatusSourceIds,
notUpdatedFileStatusSourceIds);
if (directoryState != FileState::NotExists)
watchedDirectorySourceIds.push_back(directorySourceId);
auto qmldirState = fileState(qmlDirSourceId,
package.fileStatuses,
package.updatedFileStatusSourceIds,
notUpdatedFileStatusSourceIds);
if (qmldirState != FileState::NotExists)
watchedQmldirSourceIds.push_back(qmlDirSourceId);
switch (combineState(directoryState, qmldirState)) {
case FileState::Changed: {
@@ -280,14 +320,16 @@ void ProjectStorageUpdater::updateDirectories(const QStringList &directories,
cppModuleId,
package,
notUpdatedFileStatusSourceIds,
notUpdatedSourceIds);
notUpdatedSourceIds,
watchedQmltypesSourceIds);
}
parseQmlComponents(
createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directory),
qmlDirSourceId,
directoryId,
package,
notUpdatedFileStatusSourceIds);
notUpdatedFileStatusSourceIds,
watchedQmlSourceIds);
package.updatedProjectSourceIds.push_back(qmlDirSourceId);
break;
}
@@ -325,13 +367,16 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos,
ModuleId moduleId,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &notUpdatedSourceIds)
SourceIds &notUpdatedSourceIds,
SourceIds &watchedQmlSourceIds)
{
for (const QString &typeInfo : typeInfos) {
Utils::PathString qmltypesPath = Utils::PathString::join(
{directoryPath, "/", Utils::SmallString{typeInfo}});
SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath});
watchedQmlSourceIds.push_back(sourceId);
addDependencies(package.moduleDependencies,
sourceId,
joinImports(qmldirDependencies, qmldirImports),
@@ -404,6 +449,7 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec
break;
}
case FileState::NotExists:
throw CannotParseQmlTypesFile{};
break;
}
@@ -415,7 +461,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
Storage::Synchronization::ExportedTypes exportedTypes,
SourceId qmldirSourceId,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds)
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &watchedQmlSourceIds)
{
if (std::find(relativeFilePath.begin(), relativeFilePath.end(), '+') != relativeFilePath.end())
return;
@@ -424,11 +471,13 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmlFilePath});
Storage::Synchronization::Type type;
auto state = fileState(sourceId,
package.fileStatuses,
package.updatedFileStatusSourceIds,
notUpdatedFileStatusSourceIds);
watchedQmlSourceIds.push_back(sourceId);
switch (state) {
case FileState::NotChanged:
type.changeLevel = Storage::Synchronization::ChangeLevel::Minimal;
@@ -525,7 +574,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components,
SourceId qmldirSourceId,
SourceContextId directoryId,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds)
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &watchedQmlSourceIds)
{
std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) {
return first.fileName < second.fileName;
@@ -541,7 +591,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components,
createExportedTypes(componentsWithSameFileName),
qmldirSourceId,
package,
notUpdatedFileStatusSourceIds);
notUpdatedFileStatusSourceIds,
watchedQmlSourceIds);
};
rangeForTheSameFileName(components, callback);

View File

@@ -52,7 +52,9 @@ public:
, m_pathWatcher{pathWatcher}
{}
void update(QStringList directories, QStringList qmlTypesPaths);
void update(QStringList directories,
QStringList qmlTypesPaths,
ProjectPartId projectPartId = ProjectPartId{});
void pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) override;
void pathsChanged(const SourceIds &filePathIds) override;
@@ -94,15 +96,19 @@ public:
};
private:
void updateQmlTypes(const QStringList &qmlTypesPaths,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &notUpdatedSourceIds);
SourceIds &notUpdatedSourceIds,
SourceIds &watchedQmltypesSourceIds);
void updateDirectories(const QStringList &directories,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &notUpdatedSourceIds);
SourceIds &notUpdatedSourceIds,
SourceIds &watchedDirectorySourceIds,
SourceIds &watchedQmldirSourceIds,
SourceIds &watchedQmlSourceIds,
SourceIds &watchedQmltypesSourceIds);
void parseTypeInfos(const QStringList &typeInfos,
const QList<QmlDirParser::Import> &qmldirDependencies,
@@ -112,7 +118,8 @@ private:
ModuleId moduleId,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &notUpdatedSourceIds);
SourceIds &notUpdatedSourceIds,
SourceIds &watchedQmlSourceIds);
void parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds,
@@ -127,13 +134,15 @@ private:
SourceId qmldirSourceId,
SourceContextId directoryId,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds);
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &watchedQmlSourceIds);
void parseQmlComponent(Utils::SmallStringView fileName,
Utils::SmallStringView directory,
Storage::Synchronization::ExportedTypes exportedTypes,
SourceId qmldirSourceId,
Storage::Synchronization::SynchronizationPackage &package,
SourceIds &notUpdatedFileStatusSourceIds);
SourceIds &notUpdatedFileStatusSourceIds,
SourceIds &watchedQmlSourceIds);
void parseQmlComponent(Utils::SmallStringView fileName,
Utils::SmallStringView filePath,
Utils::SmallStringView directoryPath,

View File

@@ -454,6 +454,8 @@ const char *sourceTypeToText(SourceType sourceType)
return "QmlDir";
case SourceType::QmlTypes:
return "QmlTypes";
case SourceType::Directory:
return "Directory";
}
return "";

View File

@@ -140,10 +140,31 @@ public:
ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
.WillByDefault(Return(FileStatus{qmlDirPathSourceId, 2, 421}));
ON_CALL(fileSystemMock, fileStatus(Eq(directoryPathSourceId)))
.WillByDefault(Return(FileStatus{directoryPathSourceId, 2, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(directoryPathSourceId)))
.WillByDefault(Return(FileStatus{directoryPathSourceId, 2, 421}));
{
auto sourceIds = {directoryPathSourceId,
path1SourceId,
path2SourceId,
path3SourceId,
firstSourceId,
secondSourceId,
thirdSourceId,
qmltypes1SourceId,
qmltypes2SourceId};
for (auto sourceId : sourceIds) {
ON_CALL(fileSystemMock, fileStatus(Eq(sourceId)))
.WillByDefault(Return(FileStatus{sourceId, 2, 421}));
ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId)))
.WillByDefault(Return(FileStatus{sourceId, 2, 421}));
}
}
ON_CALL(fileSystemMock, fileStatus(Eq(qmldir1SourceId)))
.WillByDefault(Return(FileStatus{qmldir1SourceId, 2, 421}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmldir2SourceId)))
.WillByDefault(Return(FileStatus{qmldir2SourceId, 2, 421}));
ON_CALL(fileSystemMock, fileStatus(Eq(qmldir3SourceId)))
.WillByDefault(Return(FileStatus{qmldir3SourceId, 2, 421}));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
.WillByDefault(Return(qmldirContent));
@@ -284,6 +305,20 @@ protected:
QString qmltypes1{"Module {\ndependencies: [module1]}"};
QString qmltypes2{"Module {\ndependencies: [module2]}"};
QStringList directories = {"/path"};
QStringList directories2 = {"/path/one", "/path/two"};
QStringList directories3 = {"/path/one", "/path/two", "/path/three"};
QmlDesigner::ProjectPartId projectPartId = QmlDesigner::ProjectPartId::create(1);
SourceId path1SourceId = sourcePathCache.sourceId("/path/one/.");
SourceId path2SourceId = sourcePathCache.sourceId("/path/two/.");
SourceId path3SourceId = sourcePathCache.sourceId("/path/three/.");
SourceId qmldir1SourceId = sourcePathCache.sourceId("/path/one/qmldir");
SourceId qmldir2SourceId = sourcePathCache.sourceId("/path/two/qmldir");
SourceId qmldir3SourceId = sourcePathCache.sourceId("/path/three/qmldir");
SourceId firstSourceId = sourcePathCache.sourceId("/path/one/First.qml");
SourceId secondSourceId = sourcePathCache.sourceId("/path/one/Second.qml");
SourceId thirdSourceId = sourcePathCache.sourceId("/path/two/Third.qml");
SourceId qmltypes1SourceId = sourcePathCache.sourceId("/path/one/example.qmltypes");
SourceId qmltypes2SourceId = sourcePathCache.sourceId("/path/two/example2.qmltypes");
};
TEST_F(ProjectStorageUpdater, GetContentForQmlDirPathsIfFileStatusIsDifferent)
@@ -349,20 +384,6 @@ TEST_F(ProjectStorageUpdater, GetContentForQmlTypesIfProjectStorageFileStatusIsI
updater.update(directories, {});
}
TEST_F(ProjectStorageUpdater, DontGetContentForQmlTypesIfFileSystemFileStatusIsInvalid)
{
QString qmldir{R"(module Example
typeinfo example.qmltypes)"};
ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path")))).WillByDefault(Return(QStringList{}));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
.WillRepeatedly(Return(qmldir));
ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId))).WillByDefault(Return(FileStatus{}));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))).Times(0);
updater.update(directories, {});
}
TEST_F(ProjectStorageUpdater, ParseQmlTypes)
{
QString qmldir{R"(module Example
@@ -436,6 +457,16 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes)
updater.update(directories, {});
}
TEST_F(ProjectStorageUpdater, SynchronizeQmlTypesThrowsIfQmltpesDoesNotExists)
{
Storage::Synchronization::Import import{qmlModuleId,
Storage::Synchronization::Version{2, 3},
qmltypesPathSourceId};
ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId))).WillByDefault(Return(FileStatus{}));
ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlTypesFile);
}
TEST_F(ProjectStorageUpdater, SynchronizeQmlTypesAreEmptyIfFileDoesNotChanged)
{
QString qmltypes{"Module {\ndependencies: []}"};
@@ -1506,4 +1537,117 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirOptionalImports)
updater.update(directories, {});
}
TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectories)
{
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
QmlDesigner::SourceType::Directory,
{path1SourceId, path2SourceId, path3SourceId}})));
updater.update(directories3, {}, projectPartId);
}
TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryDoesNotExists)
{
ON_CALL(fileSystemMock, fileStatus(Eq(path2SourceId))).WillByDefault(Return(FileStatus{}));
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
QmlDesigner::SourceType::Directory,
{path1SourceId, path3SourceId}})));
updater.update(directories3, {}, projectPartId);
}
TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirs)
{
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
QmlDesigner::SourceType::QmlDir,
{qmldir1SourceId, qmldir2SourceId, qmldir3SourceId}})));
updater.update(directories3, {}, projectPartId);
}
TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirDoesNotExists)
{
ON_CALL(fileSystemMock, fileStatus(Eq(qmldir2SourceId))).WillByDefault(Return(FileStatus{}));
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
QmlDesigner::SourceType::QmlDir,
{qmldir1SourceId, qmldir3SourceId}})));
updater.update(directories3, {}, projectPartId);
}
TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmlFiles)
{
QString qmldir1{R"(module Example
FirstType 1.0 First.qml
Second 1.0 Second.qml)"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir"))))
.WillByDefault(Return(qmldir1));
ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path/one"))))
.WillByDefault(Return(QStringList{"First.qml", "Second.qml"}));
ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path/two"))))
.WillByDefault(Return(QStringList{"Third.qml"}));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/First.qml"))))
.WillByDefault(Return(qmlDocument1));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/Second.qml"))))
.WillByDefault(Return(qmlDocument2));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/Third.qml"))))
.WillByDefault(Return(qmlDocument3));
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
QmlDesigner::SourceType::Qml,
{firstSourceId, secondSourceId, thirdSourceId}})));
updater.update(directories2, {}, projectPartId);
}
TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmltypesFilesInQmldirFiles)
{
QString qmldir1{R"(module Example
typeinfo example.qmltypes)"};
QString qmldir2{R"(module Example2
typeinfo example2.qmltypes)"};
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir"))))
.WillByDefault(Return(qmldir1));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/qmldir"))))
.WillByDefault(Return(qmldir2));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/example.qmltypes"))))
.WillByDefault(Return(qmltypes1));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/example2.qmltypes"))))
.WillByDefault(Return(qmltypes2));
SourceId qmltypes1SourceId = sourcePathCache.sourceId("/path/one/example.qmltypes");
SourceId qmltypes2SourceId = sourcePathCache.sourceId("/path/two/example2.qmltypes");
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
QmlDesigner::SourceType::QmlTypes,
{qmltypes1SourceId, qmltypes2SourceId}})));
updater.update(directories2, {}, projectPartId);
}
TEST_F(ProjectStorageUpdater, UpdatePathWatcherBuiltinQmltypesFiles)
{
QString builtinQmltyplesPath1{"/path/one/example.qmltypes"};
QString builtinQmltyplesPath2{"/path/two/example2.qmltypes"};
ON_CALL(fileSystemMock, contentAsQString(Eq(builtinQmltyplesPath1))).WillByDefault(Return(qmltypes1));
ON_CALL(fileSystemMock, contentAsQString(Eq(QString(builtinQmltyplesPath2))))
.WillByDefault(Return(qmltypes2));
SourceId qmltypes1SourceId = sourcePathCache.sourceId("/path/one/example.qmltypes");
SourceId qmltypes2SourceId = sourcePathCache.sourceId("/path/two/example2.qmltypes");
EXPECT_CALL(patchWatcherMock,
updateIdPaths(Contains(IdPaths{projectPartId,
QmlDesigner::SourceType::QmlTypes,
{qmltypes1SourceId, qmltypes2SourceId}})));
updater.update({}, {builtinQmltyplesPath1, builtinQmltyplesPath2}, projectPartId);
}
} // namespace