QmlDesigner: Add ProjectStorageUpdater

Adding a skeleton for the ProjectStorageUpdater.

Task-number: QDS-4819
Task-number: QDS-4793
Change-Id: I230d68f71480d360d71019883510ef22dd276802
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Marco Bubke
2021-09-16 17:19:56 +02:00
parent cb5eaf4a63
commit a570d53db2
24 changed files with 1350 additions and 526 deletions

View File

@@ -34,6 +34,7 @@ namespace QmlDesigner {
class FileStatus
{
public:
explicit FileStatus() = default;
explicit FileStatus(SourceId sourceId, long long size, long long lastModified)
: sourceId{sourceId}
, size{size}
@@ -49,7 +50,8 @@ public:
friend bool operator==(const FileStatus &first, const FileStatus &second)
{
return first.sourceId == second.sourceId && first.size == second.size
&& first.lastModified == second.lastModified;
&& first.lastModified == second.lastModified && first.size >= 0
&& first.lastModified >= 0;
}
friend bool operator!=(const FileStatus &first, const FileStatus &second)
@@ -72,10 +74,14 @@ public:
return first.sourceId < second;
}
bool isValid() const { return sourceId && size >= 0 && lastModified >= 0; }
explicit operator bool() const { return isValid(); }
public:
SourceId sourceId;
long long size;
long long lastModified;
long long size = -1;
long long lastModified = -1;
};
using FileStatuses = std::vector<FileStatus>;

View File

@@ -40,6 +40,7 @@ public:
virtual long long lastModified(SourceId sourceId) const = 0;
virtual FileStatus fileStatus(SourceId sourceId) const = 0;
virtual void remove(const SourceIds &sourceIds) = 0;
virtual QString contentAsQString(const QString &filePath) const = 0;
protected:
~FileSystemInterface() = default;

View File

@@ -0,0 +1,39 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
namespace QmlDesigner {
class ProjectManagerInterface
{
public:
virtual QStringList qtQmlDirs() const = 0;
protected:
~ProjectManagerInterface() = default;
};
} // namespace QmlDesigner

View File

@@ -25,10 +25,8 @@
#pragma once
#include "filestatus.h"
#include "projectstorageexceptions.h"
#include "projectstorageids.h"
#include "projectstoragetypes.h"
#include "projectstorageinterface.h"
#include "sourcepathcachetypes.h"
#include <sqlitealgorithms.h>
@@ -43,7 +41,7 @@
namespace QmlDesigner {
template<typename Database>
class ProjectStorage
class ProjectStorage final : public ProjectStorageInterface
{
public:
template<int ResultCount>
@@ -61,7 +59,7 @@ public:
Storage::Documents documents,
Storage::Types types,
SourceIds sourceIds,
FileStatuses fileStatuses)
FileStatuses fileStatuses) override
{
Sqlite::ImmediateTransaction transaction{database};
@@ -299,6 +297,14 @@ public:
return selectAllFileStatusesStatement.template rangeWithTransaction<FileStatus>();
}
FileStatus fetchFileStatus(SourceId sourceId) const override
{
return selectFileStatusesForSourceIdStatement.template valueWithTransaction<FileStatus>(
&sourceId);
}
SourceIds fetchSourceDependencieIds(SourceId sourceId) const override { return {}; }
private:
class AliasPropertyDeclaration
{
@@ -1240,6 +1246,11 @@ private:
TypeId declareType(Storage::Type &type)
{
if (type.import.name.isEmpty() && type.typeName.isEmpty()) {
type.typeId = selectTypeIdBySourceIdStatement.template value<TypeId>(&type.sourceId);
return type.typeId;
}
ImportId importId = fetchImportId(type.import);
if (!importId)
@@ -2186,11 +2197,16 @@ public:
"SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId IN carray(?1) ORDER "
"BY sourceId",
database};
mutable ReadStatement<3> selectFileStatusesForSourceIdStatement{
"SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId=?1 ORDER BY sourceId",
database};
WriteStatement insertFileStatusStatement{
"INSERT INTO fileStatuses(sourceId, size, lastModified) VALUES(?1, ?2, ?3)", database};
WriteStatement deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1", database};
WriteStatement updateFileStatusStatement{
"UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database};
ReadStatement<1> selectTypeIdBySourceIdStatement{"SELECT typeId FROM types WHERE sourceId=?",
database};
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,50 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "filestatus.h"
#include "projectstoragetypes.h"
namespace QmlDesigner {
class ProjectStorageInterface
{
public:
virtual void synchronize(Storage::ImportDependencies importDependencies,
Storage::Documents documents,
Storage::Types types,
SourceIds sourceIds,
FileStatuses fileStatuses)
= 0;
virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0;
virtual SourceIds fetchSourceDependencieIds(SourceId sourceId) const = 0;
protected:
~ProjectStorageInterface() = default;
};
} // namespace QmlDesigner

View File

@@ -145,6 +145,11 @@ public:
: name{name}
{}
friend bool operator==(const ExportedType &first, const ExportedType &second)
{
return first.name == second.name;
}
public:
Utils::SmallString name;
};
@@ -158,6 +163,11 @@ public:
, import{std::move(import)}
{}
friend bool operator==(const ExplicitExportedType &first, const ExplicitExportedType &second)
{
return first.name == second.name && first.import == second.import;
}
public:
Utils::SmallString name;
Import import;
@@ -173,6 +183,11 @@ public:
: name{name}
{}
friend bool operator==(const NativeType &first, const NativeType &second)
{
return first.name == second.name;
}
public:
Utils::SmallString name;
};
@@ -422,6 +437,13 @@ public:
, kind{PropertyKind::Alias}
{}
friend bool operator==(const PropertyDeclaration &first, const PropertyDeclaration &second)
{
return first.name == second.name && first.typeName == second.typeName
&& first.aliasPropertyName == second.aliasPropertyName
&& first.traits == second.traits && first.kind == second.kind;
}
public:
Utils::SmallString name;
TypeName typeName;
@@ -516,10 +538,20 @@ public:
, typeId{typeId}
{}
friend bool operator==(const Type &first, const Type &second) noexcept
{
return first.typeName == second.typeName && first.prototype == second.prototype
&& first.exportedTypes == second.exportedTypes
&& first.propertyDeclarations == second.propertyDeclarations
&& first.functionDeclarations == second.functionDeclarations
&& first.signalDeclarations == second.signalDeclarations
&& first.import == second.import && first.sourceId == second.sourceId
&& first.sourceId == second.sourceId;
}
public:
Utils::SmallString typeName;
TypeName prototype;
Utils::SmallString attachedType;
ExportedTypes exportedTypes;
PropertyDeclarations propertyDeclarations;
FunctionDeclarations functionDeclarations;
@@ -529,7 +561,6 @@ public:
TypeAccessSemantics accessSemantics = TypeAccessSemantics::Invalid;
SourceId sourceId;
TypeId typeId;
bool isCreatable = false;
};
using Types = std::vector<Type>;
@@ -591,8 +622,8 @@ public:
friend bool operator==(const ImportView &first, const ImportView &second)
{
return first.name == second.name
&& first.version == second.version && first.sourceId == second.sourceId;
return first.name == second.name && first.version == second.version
&& first.sourceId == second.sourceId;
}
public:

View File

@@ -0,0 +1,200 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "projectstorageupdater.h"
#include "filestatuscache.h"
#include "filesysteminterface.h"
#include "projectmanagerinterface.h"
#include "projectstorage.h"
#include "qmldocumentparserinterface.h"
#include "qmltypesparserinterface.h"
#include "sourcepathcache.h"
#include <sqlitedatabase.h>
#include <functional>
namespace QmlDesigner {
ComponentReferences createComponentReferences(const QMultiHash<QString, QmlDirParser::Component> &components)
{
ComponentReferences componentReferences;
componentReferences.reserve(static_cast<std::size_t>(components.size()));
for (const QmlDirParser::Component &component : components)
componentReferences.push_back(std::cref(component));
return componentReferences;
}
void ProjectUpdater::update()
{
Storage::ImportDependencies importDependencies;
Storage::Documents documents;
Storage::Types types;
SourceIds sourceIds;
FileStatuses fileStatuses;
for (const QString &qmldirPath : m_projectManager.qtQmlDirs()) {
SourcePath qmldirSourcePath{qmldirPath};
SourceId qmlDirSourceId = m_pathCache.sourceId(qmldirSourcePath);
switch (fileState(qmlDirSourceId)) {
case FileState::Changed: {
QmlDirParser parser;
parser.parse(m_fileSystem.contentAsQString(qmldirPath));
sourceIds.push_back(qmlDirSourceId);
Utils::SmallString moduleName{parser.typeNamespace()};
SourceContextId directoryId = m_pathCache.sourceContextId(qmlDirSourceId);
parseTypeInfos(parser.typeInfos(), directoryId, importDependencies, types, sourceIds);
parseQmlComponents(createComponentReferences(parser.components()),
directoryId,
moduleName,
importDependencies,
types,
sourceIds);
break;
}
case FileState::NotChanged: {
SourceIds qmltypesSourceIds = m_projectStorage.fetchSourceDependencieIds(qmlDirSourceId);
parseTypeInfos(qmltypesSourceIds, importDependencies, types, sourceIds);
break;
}
case FileState::NotExists: {
// sourceIds.push_back(qmlDirSourceId);
break;
}
}
}
m_projectStorage.synchronize(std::move(importDependencies),
std::move(documents),
std::move(types),
std::move(sourceIds),
std::move(fileStatuses));
}
void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos,
SourceContextId directoryId,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds)
{
QString directory{m_pathCache.sourceContextPath(directoryId)};
for (const QString &typeInfo : typeInfos) {
SourceId sourceId = m_pathCache.sourceId(directoryId, Utils::SmallString{typeInfo});
QString qmltypesPath = directory + "/" + typeInfo;
parseTypeInfo(sourceId, qmltypesPath, importDependencies, types, sourceIds);
}
}
void ProjectUpdater::parseTypeInfos(const SourceIds &qmltypesSourceIds,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds)
{
for (SourceId sourceId : qmltypesSourceIds) {
QString qmltypesPath = m_pathCache.sourcePath(sourceId).toQString();
parseTypeInfo(sourceId, qmltypesPath, importDependencies, types, sourceIds);
}
}
void ProjectUpdater::parseTypeInfo(SourceId sourceId,
const QString &qmltypesPath,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds)
{
if (fileState(sourceId) == FileState::Changed) {
sourceIds.push_back(sourceId);
const auto content = m_fileSystem.contentAsQString(qmltypesPath);
m_qmlTypesParser.parse(content, importDependencies, types, sourceIds);
}
}
void ProjectUpdater::parseQmlComponents(ComponentReferences components,
SourceContextId directoryId,
Utils::SmallStringView moduleName,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds)
{
std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) {
return std::tie(first.get().typeName, first.get().majorVersion, first.get().minorVersion)
> std::tie(second.get().typeName, second.get().majorVersion, second.get().minorVersion);
});
auto newEnd = std::unique(components.begin(), components.end(), [](auto &&first, auto &&second) {
return first.get().typeName == second.get().typeName
&& first.get().majorVersion == second.get().majorVersion;
});
components.erase(newEnd, components.end());
QString directory{m_pathCache.sourceContextPath(directoryId)};
for (const QmlDirParser::Component &component : components) {
Utils::SmallString fileName{component.fileName};
SourceId sourceId = m_pathCache.sourceId(directoryId, fileName);
sourceIds.push_back(sourceId);
const auto content = m_fileSystem.contentAsQString(directory + "/" + component.fileName);
auto type = m_qmlDocumentParser.parse(content);
type.typeName = fileName;
type.import.name = moduleName;
type.import.version.version = component.majorVersion;
type.accessSemantics = Storage::TypeAccessSemantics::Reference;
type.sourceId = sourceId;
type.exportedTypes.push_back(Storage::ExportedType{Utils::SmallString{component.typeName}});
types.push_back(std::move(type));
}
}
ProjectUpdater::FileState ProjectUpdater::fileState(SourceId sourceId) const
{
auto currentFileStatus = m_fileStatusCache.find(sourceId);
if (!currentFileStatus.isValid())
return FileState::NotExists;
auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId);
if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus)
return FileState::Changed;
return FileState::NotChanged;
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,119 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "nonlockingmutex.h"
#include "projectstorageids.h"
#include "projectstoragetypes.h"
#include <qmljs/parser/qmldirparser_p.h>
#include <QStringList>
#include <vector>
namespace Sqlite {
class Database;
}
namespace QmlDesigner {
class ProjectManagerInterface;
class FileSystemInterface;
class ProjectStorageInterface;
template<typename ProjectStorage, typename Mutex>
class SourcePathCache;
class FileStatusCache;
template<typename Database>
class ProjectStorage;
class QmlDocumentParserInterface;
class QmlTypesParserInterface;
using ComponentReferences = std::vector<std::reference_wrapper<const QmlDirParser::Component>>;
class ProjectUpdater
{
public:
using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>;
ProjectUpdater(ProjectManagerInterface &projectManager,
FileSystemInterface &fileSystem,
ProjectStorageInterface &projectStorage,
FileStatusCache &fileStatusCache,
PathCache &pathCache,
QmlDocumentParserInterface &qmlDocumentParser,
QmlTypesParserInterface &qmlTypesParser)
: m_projectManager{projectManager}
, m_fileSystem{fileSystem}
, m_projectStorage{projectStorage}
, m_fileStatusCache{fileStatusCache}
, m_pathCache{pathCache}
, m_qmlDocumentParser{qmlDocumentParser}
, m_qmlTypesParser{qmlTypesParser}
{}
void update();
private:
enum class FileState {
NotChanged,
Changed,
NotExists,
};
void parseTypeInfos(const QStringList &typeInfos,
SourceContextId directoryId,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds);
void parseTypeInfos(const SourceIds &qmltypesSourceIds,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds);
void parseTypeInfo(SourceId sourceId,
const QString &qmltypesPath,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds);
void parseQmlComponents(ComponentReferences components,
SourceContextId directoryId,
Utils::SmallStringView moduleName,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds);
FileState fileState(SourceId sourceId) const;
private:
ProjectManagerInterface &m_projectManager;
FileSystemInterface &m_fileSystem;
ProjectStorageInterface &m_projectStorage;
FileStatusCache &m_fileStatusCache;
PathCache &m_pathCache;
QmlDocumentParserInterface &m_qmlDocumentParser;
QmlTypesParserInterface &m_qmlTypesParser;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,42 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "projectstoragetypes.h"
#include <QString>
namespace QmlDesigner {
class QmlDocumentParserInterface
{
public:
virtual Storage::Type parse(const QString &sourceContent) = 0;
protected:
~QmlDocumentParserInterface() = default;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "projectstoragetypes.h"
#include <QString>
namespace QmlDesigner {
class QmlTypesParserInterface
{
public:
virtual void parse(const QString &sourceContent,
Storage::ImportDependencies &importDependencies,
Storage::Types &types,
SourceIds &sourceIds)
= 0;
protected:
~QmlTypesParserInterface() = default;
};
} // namespace QmlDesigner

View File

@@ -78,6 +78,11 @@ public:
return m_sourcePathCache.id({sourceName, sourceContextId});
}
SourceId sourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) const
{
return m_sourcePathCache.id({sourceName, sourceContextId});
}
SourceContextId sourceContextId(Utils::SmallStringView sourceContextPath) const
{
Utils::SmallStringView path = sourceContextPath.back() == '/'