diff options
Diffstat (limited to 'src/plugins/qmldesigner')
145 files changed, 9244 insertions, 4442 deletions
diff --git a/src/plugins/qmldesigner/.clang-format b/src/plugins/qmldesigner/.clang-format index d3695ac298..366f82f76f 100644 --- a/src/plugins/qmldesigner/.clang-format +++ b/src/plugins/qmldesigner/.clang-format @@ -2,6 +2,7 @@ Language: Cpp AccessModifierOffset: -4 AlignEscapedNewlines: DontAlign AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: true # use with clang 19 BinPackArguments: false BinPackParameters: false BraceWrapping: @@ -15,6 +16,7 @@ BreakBeforeBinaryOperators: All BreakBeforeBraces: Custom BreakConstructorInitializers: BeforeComma BreakInheritanceList: AfterComma +# BreakTemplateDeclarations: Yes # use with clang 19 ColumnLimit: 100 IncludeCategories: - Regex: 'Q.*' diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index b5b64bebbc..520c4ebc79 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -8,7 +8,6 @@ if (APPLE) set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner") endif() -add_compile_options("$<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>") set(BUILD_NOT_DESIGNSTUDIO NOT ${BUILD_NOT_DESIGNSTUDIO}) option(QTC_USE_QML_DESIGNER_LITE "Use Qml Designer Lite" ${BUILD_NOT_DESIGNSTUDIO}) @@ -46,6 +45,10 @@ add_qtc_library(QmlDesignerUtils STATIC qmldesignerutils_global.h ) + +target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>) +target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-Wno-unneeded-internal-declaration>) + extend_qtc_library(QmlDesignerUtils CONDITION ENABLE_COMPILE_WARNING_AS_ERROR PROPERTIES COMPILE_WARNING_AS_ERROR ON @@ -91,6 +94,8 @@ add_qtc_library(QmlDesignerCore STATIC SOURCES rewritertransaction.cpp rewritertransaction.h + generatedcomponentutils.cpp + generatedcomponentutils.h ) extend_qtc_library(QmlDesignerCore @@ -631,6 +636,7 @@ extend_qtc_plugin(QmlDesigner svgpasteaction.cpp svgpasteaction.h viewmanager.cpp viewmanager.h utils3d.cpp utils3d.h + dialogutils.cpp dialogutils.h ) extend_qtc_plugin(QmlDesigner @@ -822,6 +828,7 @@ extend_qtc_plugin(QmlDesigner contentlibraryeffect.cpp contentlibraryeffect.h contentlibraryeffectscategory.cpp contentlibraryeffectscategory.h contentlibraryeffectsmodel.cpp contentlibraryeffectsmodel.h + contentlibraryusermodel.cpp contentlibraryusermodel.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index dc5a1c9741..b821cc6595 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp @@ -2,9 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "assetslibraryiconprovider.h" -#include "asset.h" -#include "modelnodeoperations.h" +#include <modelnodeoperations.h> #include <theme.h> #include <utils/hdrimage.h> #include <utils/ktximage.h> diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h index fb38605ea6..d52779232f 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h @@ -3,12 +3,11 @@ #pragma once +#include <asset.h> #include <synchronousimagecache.h> #include <QQuickImageProvider> -#include "asset.h" - namespace QmlDesigner { struct Thumbnail diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index c2359409eb..9d09f52d8f 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -1,21 +1,19 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <QCheckBox> -#include <QFileInfo> -#include <QFileSystemModel> -#include <QMessageBox> -#include <QSortFilterProxyModel> - -#include "asset.h" #include "assetslibrarymodel.h" +#include <asset.h> #include <modelnodeoperations.h> #include <qmldesignerplugin.h> #include <coreplugin/icore.h> #include <utils/algorithm.h> -#include <utils/qtcassert.h> +#include <utils/filesystemwatcher.h> + +#include <QFileInfo> +#include <QFileSystemModel> +#include <QMessageBox> namespace QmlDesigner { @@ -38,7 +36,7 @@ void AssetsLibraryModel::createBackendModel() QObject::connect(m_sourceFsModel, &QFileSystemModel::directoryLoaded, this, [this]([[maybe_unused]] const QString &dir) { - syncHaveFiles(); + syncHasFiles(); }); m_fileWatcher = new Utils::FileSystemWatcher(parent()); @@ -207,7 +205,7 @@ bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour } } -bool AssetsLibraryModel::checkHaveFiles(const QModelIndex &parentIdx) const +bool AssetsLibraryModel::checkHasFiles(const QModelIndex &parentIdx) const { if (!parentIdx.isValid()) return false; @@ -218,30 +216,30 @@ bool AssetsLibraryModel::checkHaveFiles(const QModelIndex &parentIdx) const if (!isDirectory(newIdx)) return true; - if (checkHaveFiles(newIdx)) + if (checkHasFiles(newIdx)) return true; } return false; } -void AssetsLibraryModel::setHaveFiles(bool value) +void AssetsLibraryModel::setHasFiles(bool value) { - if (m_haveFiles != value) { - m_haveFiles = value; - emit haveFilesChanged(); + if (m_hasFiles != value) { + m_hasFiles = value; + emit hasFilesChanged(); } } -bool AssetsLibraryModel::checkHaveFiles() const +bool AssetsLibraryModel::checkHasFiles() const { auto rootIdx = indexForPath(m_rootPath); - return checkHaveFiles(rootIdx); + return checkHasFiles(rootIdx); } -void AssetsLibraryModel::syncHaveFiles() +void AssetsLibraryModel::syncHasFiles() { - setHaveFiles(checkHaveFiles()); + setHasFiles(checkHasFiles()); } QString AssetsLibraryModel::getUniqueName(const QString &oldName) { diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 9334e86e9b..2516be787f 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -3,12 +3,13 @@ #pragma once -#include <QFileInfo> -#include <QFileSystemModel> #include <QSortFilterProxyModel> -#include <utils/filesystemwatcher.h> -#include <utils/qtcassert.h> +namespace Utils { +class FileSystemWatcher; +} + +QT_FORWARD_DECLARE_CLASS(QFileSystemModel) namespace QmlDesigner { @@ -22,7 +23,7 @@ public: void setRootPath(const QString &newPath); void setSearchText(const QString &searchText); - Q_PROPERTY(bool haveFiles READ haveFiles NOTIFY haveFilesChanged); + Q_PROPERTY(bool hasFiles READ hasFiles NOTIFY hasFilesChanged) Q_INVOKABLE QString rootPath() const; Q_INVOKABLE QString filePath(const QModelIndex &index) const; @@ -35,7 +36,7 @@ public: Q_INVOKABLE QModelIndex parentDirIndex(const QString &path) const; Q_INVOKABLE QModelIndex parentDirIndex(const QModelIndex &index) const; Q_INVOKABLE QString parentDirPath(const QString &path) const; - Q_INVOKABLE void syncHaveFiles(); + Q_INVOKABLE void syncHasFiles(); Q_INVOKABLE QList<QModelIndex> parentIndices(const QModelIndex &index) const; Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const; @@ -55,30 +56,30 @@ public: return std::min(result, 1); } - bool haveFiles() const { return m_haveFiles; } + bool hasFiles() const { return m_hasFiles; } QString getUniqueName(const QString &oldName); signals: void directoryLoaded(const QString &path); void rootPathChanged(); - void haveFilesChanged(); + void hasFilesChanged(); void fileChanged(const QString &path); void effectsDeleted(const QStringList &effectNames); private: - void setHaveFiles(bool value); + void setHasFiles(bool value); bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; void resetModel(); void createBackendModel(); void destroyBackendModel(); - bool checkHaveFiles(const QModelIndex &parentIdx) const; - bool checkHaveFiles() const; + bool checkHasFiles(const QModelIndex &parentIdx) const; + bool checkHasFiles() const; QString m_searchText; QString m_rootPath; QFileSystemModel *m_sourceFsModel = nullptr; - bool m_haveFiles = false; + bool m_hasFiles = false; Utils::FileSystemWatcher *m_fileWatcher = nullptr; }; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 3b98eb6baf..4b270c8902 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -3,20 +3,22 @@ #include "assetslibrarywidget.h" -#include "asset.h" #include "assetslibraryiconprovider.h" #include "assetslibrarymodel.h" #include "assetslibraryview.h" -#include "designeractionmanager.h" -#include "import.h" -#include "modelnodeoperations.h" -#include "nodemetainfo.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "theme.h" -#include <utils3d.h> +#include <asset.h> +#include <designeractionmanager.h> +#include <designerpaths.h> +#include <hdrimage.h> +#include <import.h> +#include <modelnodeoperations.h> +#include <nodemetainfo.h> +#include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> #include <studioquickwidget.h> +#include <theme.h> +#include <utils3d.h> #include <coreplugin/fileutils.h> #include <coreplugin/icore.h> @@ -287,14 +289,16 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList // Remove usages of deleted effects from the current document m_assetsView->executeInTransaction(__FUNCTION__, [&]() { QList<ModelNode> allNodes = m_assetsView->allModelNodes(); - const QString typeTemplate = "Effects.%1.%1"; - const QString importUrlTemplate = "Effects.%1"; + const QString typeTemplate = "%1.%2.%2"; + const QString importUrlTemplate = "%1.%2"; const Imports imports = m_assetsView->model()->imports(); Imports removedImports; + const QString typePrefix = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().composedEffectsTypePrefix(); for (const QString &effectName : effectNames) { if (effectName.isEmpty()) continue; - const TypeName type = typeTemplate.arg(effectName).toUtf8(); + const TypeName type = typeTemplate.arg(typePrefix, effectName).toUtf8(); for (ModelNode &node : allNodes) { if (node.metaInfo().typeName() == type) { clearStacks = true; @@ -302,7 +306,7 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList } } - const QString importPath = importUrlTemplate.arg(effectName); + const QString importPath = importUrlTemplate.arg(typePrefix, effectName); Import removedImport = Utils::findOrDefault(imports, [&importPath](const Import &import) { return import.url() == importPath; }); @@ -374,7 +378,7 @@ QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets() void AssetsLibraryWidget::handleSearchFilterChanged(const QString &filterText) { - if (filterText == m_filterText || (!m_assetsModel->haveFiles() + if (filterText == m_filterText || (!m_assetsModel->hasFiles() && filterText.contains(m_filterText, Qt::CaseInsensitive))) return; @@ -643,4 +647,15 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog } } +bool AssetsLibraryWidget::userBundleEnabled() const +{ + // TODO: this method is to be removed after user bundle implementation is complete + return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); +} + +void AssetsLibraryWidget::addAssetsToContentLibrary(const QStringList &assetPaths) +{ + m_assetsView->emitCustomNotification("add_assets_to_content_lib", {}, {assetPaths}); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index ed987d14de..8b59ae0785 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -98,6 +98,8 @@ public: Q_INVOKABLE void showInGraphicalShell(const QString &path); Q_INVOKABLE QString showInGraphicalShellMsg() const; + Q_INVOKABLE bool userBundleEnabled() const; + Q_INVOKABLE void addAssetsToContentLibrary(const QStringList &assetPaths); signals: void itemActivated(const QString &itemName); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index ddfb82746c..8b506affc4 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -60,28 +60,21 @@ inline static bool isValidColorName(const QString &colorName) /** * @brief getCustomUrl - * MimeType = <MainType/SubType> * Address = <Url|LocalFile> * * @param value The input value to be evaluated - * @param dataType if the value is a valid url or image, the data type + * @param dataType if the value is a valid url, the data type * will be stored to this parameter, otherwise, it will be Unknown - * @param urlResult if the value is a valid url or image, the address + * @param urlResult if the value is a valid url, the address * will be stored in this parameter, otherwise it will be empty. - * @param subType if the value is a valid image, the image subtype - * will be stored in this parameter, otherwise it will be empty. - * @return true if the result is either url or image + * @return true if the result is url */ static bool getCustomUrl(const QString &value, CollectionDetails::DataType &dataType, - QUrl *urlResult = nullptr, - QString *subType = nullptr) + QUrl *urlResult = nullptr) { static const QRegularExpression urlRegex{ - "^(?<MimeType>" - "(?<MainType>image)\\/" - "(?<SubType>apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType - "(?<Address>" + "^(?<Address>" "(?<Url>https?:\\/\\/" "(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+" "[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/" @@ -92,29 +85,18 @@ static bool getCustomUrl(const QString &value, }; const QRegularExpressionMatch match = urlRegex.match(value.trimmed()); - if (match.hasMatch()) { - if (match.hasCaptured("Address")) { - if (match.hasCaptured("MimeType") && match.captured("MainType") == "image") - dataType = CollectionDetails::DataType::Image; - else - dataType = CollectionDetails::DataType::Url; + if (match.hasCaptured("Address")) { + dataType = CollectionDetails::DataType::Url; - if (urlResult) - urlResult->setUrl(match.captured("Address")); + if (urlResult) + urlResult->setUrl(match.captured("Address")); - if (subType) - *subType = match.captured("SubType"); - - return true; - } + return true; } if (urlResult) urlResult->clear(); - if (subType) - subType->clear(); - dataType = CollectionDetails::DataType::Unknown; return false; } @@ -248,14 +230,8 @@ static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataT return variantValue.toBool(); case DataType::Color: return variantValue.value<QColor>(); - case DataType::Image: { - DataType type; - QUrl url; - if (getCustomUrl(variantValue.toString(), type, &url)) - return url; - return variantValue.toString(); - } case DataType::Url: + case DataType::Image: return variantValue.value<QUrl>(); default: return variantValue; @@ -285,12 +261,7 @@ static QJsonValue variantToJsonValue( return variant.toDouble(); case DataType::Integer: return variant.toInt(); - case DataType::Image: { - const QUrl url(variant.toUrl()); - if (url.isValid()) - return QString("image/xyz:%1").arg(url.toString()); - return {}; - } + case DataType::Image: case DataType::String: case DataType::Color: case DataType::Url: @@ -569,13 +540,6 @@ QVariant CollectionDetails::data(int row, int column) const const QJsonValue cellValue = d->dataRecords.at(row).at(column); - if (typeAt(column) == DataType::Image) { - const QUrl imageUrl = valueToVariant(cellValue, DataType::Image).toUrl(); - - if (imageUrl.isValid()) - return imageUrl; - } - return cellValue.toVariant(); } @@ -614,7 +578,10 @@ DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column if (columnType == DataType::Unknown || isEmptyJsonValue(cellValue)) return DataTypeWarning::Warning::None; - if (columnType == DataType::Real && cellType == DataType::Integer) + if ((columnType == DataType::String || columnType == DataType::Real) && cellType == DataType::Integer) + return DataTypeWarning::Warning::None; + + if ((columnType == DataType::Url || columnType == DataType::Image) && cellType == DataType::String) return DataTypeWarning::Warning::None; if (columnType != cellType) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index b26b1a845e..d2917ec302 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -93,6 +93,7 @@ bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &v if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column())) roles << DataTypeWarningRole; + setHasUnsavedChanges(true); emit dataChanged(index, index, roles); } @@ -128,11 +129,11 @@ bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] con row = qBound(0, row, rowCount()); - beginResetModel(); + beginInsertRows({}, row, row + count - 1); m_currentCollection.insertEmptyRows(row, count); - endResetModel(); + endInsertRows(); + setHasUnsavedChanges(true); - selectRow(row); return true; } @@ -151,12 +152,6 @@ bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIn if (!columnCount(parent)) removeRows(0, rowCount(parent), parent); - int nextColumn = column - 1; - if (nextColumn < 0 && columnCount(parent) > 0) - nextColumn = 0; - - selectColumn(nextColumn); - ensureSingleCell(); return columnsRemoved; } @@ -254,6 +249,7 @@ bool CollectionDetailsModel::addColumn(int column, const QString &name, const QS {}, CollectionDataTypeModel::dataTypeFromString(propertyType)); endInsertColumns(); + setHasUnsavedChanges(true); return m_currentCollection.containsPropertyName(name); } @@ -309,6 +305,7 @@ bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); } + setHasUnsavedChanges(true); return changed; } @@ -441,6 +438,7 @@ bool CollectionDetailsModel::saveDataStoreCollections() if (reference != currentReference) closeCollectionIfSaved(reference); } + setHasUnsavedChanges(false); return true; } } @@ -618,4 +616,12 @@ QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning return DataTypeWarning::getDataTypeWarningString(warning); } +void CollectionDetailsModel::setHasUnsavedChanges(bool val) +{ + if (m_hasUnsavedChanges == val) + return; + m_hasUnsavedChanges = val; + emit hasUnsavedChangesChanged(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 24a040cce6..8844ff4a3e 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -20,6 +20,7 @@ class CollectionDetailsModel : public QAbstractTableModel Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged) public: enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole }; @@ -70,12 +71,14 @@ public: const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const; bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const; QString getFirstColumnName(const CollectionReference &reference) const; + void setHasUnsavedChanges(bool val); signals: void collectionNameChanged(const QString &collectionName); void selectedColumnChanged(int); void selectedRowChanged(int); void isEmptyChanged(bool); + void hasUnsavedChangesChanged(); void warning(const QString &title, const QString &body); private slots: @@ -93,6 +96,7 @@ private: QHash<CollectionReference, CollectionDetails> m_openedCollections; CollectionDetails m_currentCollection; bool m_isEmpty = true; + bool m_hasUnsavedChanges = false; int m_selectedColumn = -1; int m_selectedRow = -1; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp index f56bb36e88..2cc6ac05a6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp @@ -62,6 +62,12 @@ bool CollectionDetailsSortFilterModel::selectColumn(int column) return m_source->selectColumn(mapToSource(index(0, column)).column()); } +void CollectionDetailsSortFilterModel::deselectAll() +{ + QTC_ASSERT(m_source, return); + m_source->deselectAll(); +} + CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h index 93305f3ca2..10f6e09b05 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h @@ -31,6 +31,7 @@ public: Q_INVOKABLE bool selectRow(int row); Q_INVOKABLE bool selectColumn(int column); + Q_INVOKABLE void deselectAll(); signals: void selectedColumnChanged(int); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 4725987f12..29b833cc2c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -3,6 +3,7 @@ #include "collectioneditorutils.h" +#include "collectiondatatypemodel.h" #include "model.h" #include "nodemetainfo.h" #include "propertymetainfo.h" @@ -95,13 +96,17 @@ Utils::FilePath dataStoreDir() if (!currentProject) return {}; - return currentProject->projectDirectory().pathAppended("/imports/" - + currentProject->displayName()); + FilePath oldImportDirectory = currentProject->projectDirectory().pathAppended( + "imports/" + currentProject->displayName()); + if (oldImportDirectory.exists()) + return oldImportDirectory; + + return currentProject->projectDirectory().pathAppended(currentProject->displayName()); } inline Utils::FilePath collectionPath(const QString &filePath) { - return dataStoreDir().pathAppended("/" + filePath); + return dataStoreDir().pathAppended(filePath); } inline Utils::FilePath qmlDirFilePath() @@ -288,7 +293,7 @@ QJsonObject defaultCollection() QJsonArray columns; QJsonObject defaultColumn; defaultColumn.insert("name", "Column 1"); - defaultColumn.insert("type", "string"); + defaultColumn.insert("type", CollectionDataTypeModel::dataTypeToString(DataType::String)); columns.append(defaultColumn); QJsonArray collectionData; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index f6ec821fde..0c9a2eed94 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -35,6 +35,12 @@ bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) return node.metaInfo().isQtQuickStudioUtilsJsonListModel(); } +inline bool isProjectImport(const QmlDesigner::Import &import) +{ + ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); + return currentProject && import.toString() == currentProject->displayName(); +} + inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, const QmlDesigner::PropertyName &propertyName, const QVariant &value) @@ -60,14 +66,10 @@ CollectionView::CollectionView(ExternalDependenciesInterface &externalDependenci , m_dataStore(std::make_unique<DataStoreModelNode>()) { - connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, this, [this] { - resetDataStoreNode(); - if (m_widget.get()) - m_widget->collectionDetailsModel()->removeAllCollections(); - }); } +CollectionView::~CollectionView() = default; + bool CollectionView::hasWidget() const { return true; @@ -75,11 +77,16 @@ bool CollectionView::hasWidget() const QmlDesigner::WidgetInfo CollectionView::widgetInfo() { - if (m_widget.isNull()) { - m_widget = new CollectionWidget(this); + if (!m_widget) { + m_widget = Utils::makeUniqueObjectPtr<CollectionWidget>(this); m_widget->setMinimumSize(m_widget->minimumSizeHint()); + connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::startupProjectChanged, m_widget.get(), [&] { + resetDataStoreNode(); + m_widget->collectionDetailsModel()->removeAllCollections(); + }); - auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data()); + auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.get()); Core::ICore::addContextObject(collectionEditorContext); CollectionListModel *listModel = m_widget->listModel().data(); @@ -97,7 +104,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() connect(listModel, &CollectionListModel::modelReset, this, [this] { CollectionListModel *listModel = m_widget->listModel().data(); - if (listModel->sourceNode() == m_dataStore->modelNode()) + if (listModel->sourceNode() == dataStoreNode()) m_dataStore->setCollectionNames(listModel->collections()); }); @@ -128,7 +135,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() }); } - return createWidgetInfo(m_widget.data(), + return createWidgetInfo(m_widget.get(), "CollectionEditor", WidgetInfo::LeftPane, 0, @@ -139,23 +146,22 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() void CollectionView::modelAttached(Model *model) { AbstractView::modelAttached(model); + m_widget->setProjectImportExists(Utils::anyOf(model->imports(), isProjectImport)); resetDataStoreNode(); } void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) { - m_libraryInfoIsUpdated = false; - m_reloadCounter = 0; - m_rewriterAmended = false; - m_dataStoreTypeFound = false; - disconnect(m_documentUpdateConnection); - QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); - m_widget->listModel()->setDataStoreNode(); + unloadDataStore(); + m_widget->setProjectImportExists(false); } void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, [[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList) { + if (!m_widget) + return; + QList<ModelNode> selectedCollectionNodes = Utils::filtered(selectedNodeList, &isStudioCollectionModel); @@ -170,10 +176,17 @@ void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeLi } m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); +} - // More than one model is selected. So ignore them - if (selectedCollectionNodes.size() > 1) - return; +void CollectionView::importsChanged(const Imports &addedImports, const Imports &removedImports) +{ + if (Utils::anyOf(addedImports, isProjectImport)) { + m_widget->setProjectImportExists(true); + resetDataStoreNode(); + } else if (Utils::anyOf(removedImports, isProjectImport)) { + m_widget->setProjectImportExists(false); + unloadDataStore(); + } } void CollectionView::customNotification(const AbstractView *, @@ -181,6 +194,9 @@ void CollectionView::customNotification(const AbstractView *, const QList<ModelNode> &nodeList, const QList<QVariant> &data) { + if (!m_widget) + return; + if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty()) onItemLibraryNodeCreated(nodeList.first()); else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty()) @@ -219,8 +235,27 @@ void CollectionView::addResource(const QUrl &url, const QString &name) }); } +void CollectionView::addProjectImport() +{ + if (!m_widget) + return; + + ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); + if (!currentProject) + return; + + executeInTransaction(__FUNCTION__, [&] { + Import import = Import::createLibraryImport(currentProject->displayName()); + if (!model()->hasImport(import, true, true)) + model()->changeImports({import}, {}); + }); +} + void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) { + if (!m_widget) + return; + using DataType = CollectionDetails::DataType; executeInTransaction("CollectionView::assignCollectionToNode", [&]() { m_dataStore->assignCollectionToNode( @@ -279,12 +314,18 @@ void CollectionView::assignCollectionToSelectedNode(const QString &collectionNam void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection) { + if (!m_widget) + return; + addTask(QSharedPointer<CollectionTask>( new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName))); } void CollectionView::openCollection(const QString &collectionName) { + if (!m_widget) + return; + m_widget->openCollection(collectionName); } @@ -296,9 +337,13 @@ void CollectionView::registerDeclarativeType() void CollectionView::resetDataStoreNode() { + if (!m_widget) + return; + m_dataStore->reloadModel(); - ModelNode dataStore = m_dataStore->modelNode(); + ModelNode dataStore = dataStoreNode(); + m_widget->setDataStoreExists(dataStore.isValid()); if (!dataStore || m_widget->listModel()->sourceNode() == dataStore) return; @@ -339,28 +384,11 @@ void CollectionView::ensureDataStoreExists() { bool filesJustCreated = false; bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); - if (filesExist) { - if (filesJustCreated) { - // Force code model reset to notice changes to existing module - auto modelManager = QmlJS::ModelManagerInterface::instance(); - if (modelManager) { - m_libraryInfoIsUpdated = false; - - m_expectedDocumentUpdates.clear(); - m_expectedDocumentUpdates << CollectionEditorUtils::dataStoreQmlFilePath() - << CollectionEditorUtils::dataStoreJsonFilePath(); - - m_documentUpdateConnection = connect(modelManager, - &QmlJS::ModelManagerInterface::documentUpdated, - this, - &CollectionView::onDocumentUpdated); - - modelManager->resetCodeModel(); - } - resetDataStoreNode(); - } else { - m_libraryInfoIsUpdated = true; - } + if (filesExist && filesJustCreated) { + // Force code model reset to notice changes to existing module + if (auto modelManager = QmlJS::ModelManagerInterface::instance()) + modelManager->resetCodeModel(); + resetDataStoreNode(); } } @@ -380,6 +408,18 @@ NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME); } +void CollectionView::unloadDataStore() +{ + m_reloadCounter = 0; + m_rewriterAmended = false; + m_dataStoreTypeFound = false; + QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); + if (m_widget) { + m_widget->setDataStoreExists(dataStoreNode().isValid()); + m_widget->listModel()->setDataStoreNode(); + } +} + void CollectionView::ensureStudioModelImport() { executeInTransaction(__FUNCTION__, [&] { @@ -395,29 +435,21 @@ void CollectionView::ensureStudioModelImport() void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) { + if (!m_widget) + return; + if (node.metaInfo().isQtQuickListView()) { addTask(QSharedPointer<CollectionTask>( new DropListViewTask(this, m_widget->listModel(), node))); } } -void CollectionView::onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc) -{ - if (m_expectedDocumentUpdates.contains(doc->fileName())) - m_expectedDocumentUpdates.remove(doc->fileName()); - - if (m_expectedDocumentUpdates.isEmpty()) { - disconnect(m_documentUpdateConnection); - m_libraryInfoIsUpdated = true; - } -} - void CollectionView::addTask(QSharedPointer<CollectionTask> task) { ensureDataStoreExists(); if (m_dataStoreTypeFound) task->process(); - else if (m_dataStore->modelNode()) + else if (dataStoreNode()) m_delayedTasks << task; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index a4b16c4c27..3de3bd7ae6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -3,9 +3,12 @@ #pragma once -#include "abstractview.h" #include "datastoremodelnode.h" -#include "modelnode.h" + +#include <abstractview.h> +#include <modelnode.h> + +#include <utils/uniqueobjectptr.h> #include <QJsonObject> @@ -27,6 +30,7 @@ class CollectionView : public AbstractView public: explicit CollectionView(ExternalDependenciesInterface &externalDependencies); + ~CollectionView(); bool hasWidget() const override; WidgetInfo widgetInfo() override; @@ -37,6 +41,8 @@ public: void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, @@ -44,6 +50,7 @@ public: void addResource(const QUrl &url, const QString &name); + void addProjectImport(); void assignCollectionToNode(const QString &collectionName, const ModelNode &node); void assignCollectionToSelectedNode(const QString &collectionName); void addNewCollection(const QString &collectionName, const QJsonObject &localCollection); @@ -61,17 +68,14 @@ private: friend class CollectionTask; NodeMetaInfo jsonCollectionMetaInfo() const; + void unloadDataStore(); void ensureStudioModelImport(); void onItemLibraryNodeCreated(const ModelNode &node); - void onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc); void addTask(QSharedPointer<CollectionTask> task); - QPointer<CollectionWidget> m_widget; std::unique_ptr<DataStoreModelNode> m_dataStore; - QSet<Utils::FilePath> m_expectedDocumentUpdates; + Utils::UniqueObjectPtr<CollectionWidget> m_widget; QList<QSharedPointer<CollectionTask>> m_delayedTasks; - QMetaObject::Connection m_documentUpdateConnection; - bool m_libraryInfoIsUpdated = false; bool m_dataStoreTypeFound = false; bool m_rewriterAmended = false; int m_reloadCounter = 0; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 093729dc67..dd706145cf 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -54,8 +54,7 @@ QString getPreferredCollectionName(const QUrl &url, const QString &collectionNam namespace QmlDesigner { CollectionWidget::CollectionWidget(CollectionView *view) - : QFrame() - , m_view(view) + : m_view(view) , m_listModel(new CollectionListModel) , m_collectionDetailsModel(new CollectionDetailsModel) , m_collectionDetailsSortFilterModel(std::make_unique<CollectionDetailsSortFilterModel>()) @@ -104,6 +103,8 @@ CollectionWidget::CollectionWidget(CollectionView *view) QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME); } +CollectionWidget::~CollectionWidget() = default; + void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const { if (m_view) @@ -250,6 +251,11 @@ bool CollectionWidget::importFile(const QString &collectionName, return false; } +void CollectionWidget::addProjectImport() +{ + m_view->addProjectImport(); +} + void CollectionWidget::addCollectionToDataStore(const QString &collectionName) { m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection()); @@ -288,6 +294,24 @@ void CollectionWidget::setTargetNodeSelected(bool selected) emit targetNodeSelectedChanged(m_targetNodeSelected); } +void CollectionWidget::setProjectImportExists(bool exists) +{ + if (m_projectImportExists == exists) + return; + + m_projectImportExists = exists; + emit projectImportExistsChanged(m_projectImportExists); +} + +void CollectionWidget::setDataStoreExists(bool exists) +{ + if (m_dataStoreExists == exists) + return; + + m_dataStoreExists = exists; + emit dataStoreExistsChanged(m_dataStoreExists); +} + void CollectionWidget::deleteSelectedCollection() { QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection"); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 0957bd81e0..13c3566c78 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -22,9 +22,12 @@ class CollectionWidget : public QFrame Q_OBJECT Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged) + Q_PROPERTY(bool projectImportExists MEMBER m_projectImportExists NOTIFY projectImportExistsChanged) + Q_PROPERTY(bool dataStoreExists MEMBER m_dataStoreExists NOTIFY dataStoreExistsChanged) public: CollectionWidget(CollectionView *view); + ~CollectionWidget(); void contextHelp(const Core::IContext::HelpCallback &callback) const; QPointer<CollectionListModel> listModel() const; @@ -32,7 +35,7 @@ public: void reloadQmlSource(); - virtual QSize minimumSizeHint() const; + QSize minimumSizeHint() const override; Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {}); Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {}); @@ -44,6 +47,7 @@ public: const QUrl &url, const bool &firstRowIsHeader = true); + Q_INVOKABLE void addProjectImport(); Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName); Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName); Q_INVOKABLE void openCollection(const QString &collectionName); @@ -51,11 +55,15 @@ public: void warn(const QString &title, const QString &body); void setTargetNodeSelected(bool selected); + void setProjectImportExists(bool exists); + void setDataStoreExists(bool exists); void deleteSelectedCollection(); signals: void targetNodeSelectedChanged(bool); + void projectImportExistsChanged(bool); + void dataStoreExistsChanged(bool); private: QString generateUniqueCollectionName(const ModelNode &node, const QString &name); @@ -66,6 +74,8 @@ private: std::unique_ptr<CollectionDetailsSortFilterModel> m_collectionDetailsSortFilterModel; QScopedPointer<StudioQuickWidget> m_quickWidget; bool m_targetNodeSelected = false; + bool m_projectImportExists = false; + bool m_dataStoreExists = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp new file mode 100644 index 0000000000..f882ae528d --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp @@ -0,0 +1,32 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <model.h> + +#include <coreplugin/messagebox.h> + +namespace QmlDesigner { + +namespace DialogUtils { + +void showWarningForInvalidId(const QString &id) +{ + constexpr char text[] = R"( +The ID <b>'%1'</b> is invalid. + +Make sure the ID is: +<ul> +<li>Unique within the QML file.</li> +<li>Beginning with a lowercase letter.</li> +<li>Without any blank space or symbol.</li> +<li>Not a reserved QML keyword. </li> +</ul> +)"; + + Core::AsynchronousMessageBox::warning(Model::tr("Invalid Id"), + Model::tr(text).arg(id)); +} + +} // namespace DialogUtils + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.h b/src/plugins/qmldesigner/components/componentcore/dialogutils.h new file mode 100644 index 0000000000..3ca98016dd --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.h @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <qmldesignercomponents_global.h> + +#include <QString> + +namespace QmlDesigner { + +namespace DialogUtils { + +QMLDESIGNERCOMPONENTS_EXPORT void showWarningForInvalidId(const QString &id); + +} // namespace DialogUtils +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp index 8d3412e0e8..89b50c4d1a 100644 --- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp +++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp @@ -452,7 +452,9 @@ void LayoutInGridLayout::removeSpacersBySpanning(QList<ModelNode> &nodes) { for (const ModelNode &node : std::as_const(m_spacerNodes)) { if (int index = nodes.indexOf(node)) { - ModelNode before = nodes.at(index -1); + ModelNode before; + if (index > 0) + before = nodes.at(index - 1); if (m_spacerNodes.contains(before)) { m_spacerNodes.removeAll(node); m_layoutedNodes.removeAll(node); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp index f6e18458b2..4cbebd738d 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp @@ -105,8 +105,10 @@ bool selectionIsImported3DAsset(const SelectionContext &selectionState) // Node is not a file component, so we have to check if the current doc itself is fileName = node.model()->fileUrl().toLocalFile(); } - if (fileName.contains(Constants::QUICK_3D_ASSETS_FOLDER)) + if (QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().isImport3dPath(fileName)) { return true; + } } return false; } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index cebe7d7c53..a5274c70e2 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1138,18 +1138,12 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString & { QString adjustedDefaultDirectory = defaultDirectory; - Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); - - if (contentPath.pathAppended("content").exists()) - contentPath = contentPath.pathAppended("content"); + Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentResourcePath(); Utils::FilePath assetPath = contentPath.pathAppended(assetDir); - if (!assetPath.exists()) { - // Create the default asset type directory if it doesn't exist - QDir dir(contentPath.toString()); - dir.mkpath(assetDir); - } + if (!assetPath.exists()) + assetPath.createDir(); if (assetPath.exists() && assetPath.isDir()) adjustedDefaultDirectory = assetPath.toString(); @@ -1694,7 +1688,14 @@ void editIn3dView(const SelectionContext &selectionContext) if (selectionContext.view() && selectionContext.hasSingleSelectedModelNode() && selectionContext.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) { QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true); - selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); + const QPointF scenePos = selectionContext.scenePosition(); + if (scenePos.isNull()) { + selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); + } else { + selectionContext.view()->emitCustomNotification("pick_3d_node_from_2d_scene", + {selectionContext.currentSingleSelectedNode()}, + {scenePos}); + } } } @@ -1727,13 +1728,12 @@ void openOldEffectMaker(const QString &filePath) return; } - Utils::FilePath projectPath = target->project()->projectDirectory(); - QString effectName = QFileInfo(filePath).baseName(); - QString effectResDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER) - + "/" + effectName; - Utils::FilePath effectResPath = projectPath.pathAppended(effectResDir); + Utils::FilePath effectResPath = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().composedEffectsBasePath() + .pathAppended(QFileInfo(filePath).baseName()); + if (!effectResPath.exists()) - QDir().mkpath(effectResPath.toString()); + effectResPath.createDir(); const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); if (baseQtVersion) { @@ -1769,14 +1769,11 @@ void openOldEffectMaker(const QString &filePath) Utils::FilePath getEffectsImportDirectory() { - QString defaultDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER); - Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); - Utils::FilePath effectsPath = projectPath.pathAppended(defaultDir); + Utils::FilePath effectsPath = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().composedEffectsBasePath(); - if (!effectsPath.exists()) { - QDir dir(projectPath.toString()); - dir.mkpath(effectsPath.toString()); - } + if (!effectsPath.exists()) + effectsPath.createDir(); return effectsPath; } @@ -1794,12 +1791,9 @@ QString getEffectsDefaultDirectory(const QString &defaultDir) QString getEffectIcon(const QString &effectPath) { - Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); - QString effectName = QFileInfo(effectPath).baseName(); - QString effectResDir = "asset_imports/Effects/" + effectName; - Utils::FilePath effectResPath = projectPath.resolvePath(effectResDir + "/" + effectName + ".qml"); - - return effectResPath.exists() ? QString("effectExported") : QString("effectClass"); + Utils::FilePath effectFile = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().composedEffectPath(effectPath); + return effectFile.exists() ? QString("effectExported") : QString("effectClass"); } bool useLayerEffect() diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp index 4a229564c6..24047f650f 100644 --- a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp @@ -222,6 +222,10 @@ bool createQmlrcFile(const FilePath &qmlrcFilePath) rccProcess.setWorkingDirectory(project->projectDirectory()); const QStringList arguments = {"--binary", + "--compress", + "9", + "--threshold", + "30", "--output", qmlrcFilePath.toString(), tempQrcFile.toString()}; diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 73184d391c..392f6c94f6 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -83,6 +83,7 @@ public: binding_medium, bounds_small, branch_medium, + cameraSpeed_medium, camera_medium, camera_small, centerHorizontal, diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 05d6f5fdf0..b011d9fbbf 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -67,16 +67,20 @@ public: , collectionView{externalDependencies} , contentLibraryView{externalDependencies} , componentView{externalDependencies} +#ifndef QTC_USE_QML_DESIGNER_LITE , edit3DView{externalDependencies} +#endif , formEditorView{externalDependencies} , textEditorView{externalDependencies} , assetsLibraryView{externalDependencies} , itemLibraryView(imageCache, externalDependencies) , navigatorView{externalDependencies} , propertyEditorView(imageCache, externalDependencies) +#ifndef QTC_USE_QML_DESIGNER_LITE , materialEditorView{externalDependencies} , materialBrowserView{imageCache, externalDependencies} , textureEditorView{imageCache, externalDependencies} +#endif , statesEditorView{externalDependencies} {} @@ -89,16 +93,20 @@ public: CollectionView collectionView; ContentLibraryView contentLibraryView; ComponentView componentView; +#ifndef QTC_USE_QML_DESIGNER_LITE Edit3DView edit3DView; +#endif FormEditorView formEditorView; TextEditorView textEditorView; AssetsLibraryView assetsLibraryView; ItemLibraryView itemLibraryView; NavigatorView navigatorView; PropertyEditorView propertyEditorView; +#ifndef QTC_USE_QML_DESIGNER_LITE MaterialEditorView materialEditorView; MaterialBrowserView materialBrowserView; TextureEditorView textureEditorView; +#endif StatesEditorView statesEditorView; std::vector<std::unique_ptr<AbstractView>> additionalViews; @@ -203,6 +211,7 @@ QList<AbstractView *> ViewManager::views() const QList<AbstractView *> ViewManager::standardViews() const { +#ifndef QTC_USE_QML_DESIGNER_LITE QList<AbstractView *> list = {&d->edit3DView, &d->formEditorView, &d->textEditorView, @@ -215,6 +224,16 @@ QList<AbstractView *> ViewManager::standardViews() const &d->textureEditorView, &d->statesEditorView, &d->designerActionManagerView}; +#else + QList<AbstractView *> list = {&d->formEditorView, + &d->textEditorView, + &d->assetsLibraryView, + &d->itemLibraryView, + &d->navigatorView, + &d->propertyEditorView, + &d->statesEditorView, + &d->designerActionManagerView}; +#endif if (enableModelEditor()) list.append(&d->collectionView); @@ -384,16 +403,20 @@ QList<WidgetInfo> ViewManager::widgetInfos() const { QList<WidgetInfo> widgetInfoList; +#ifndef QTC_USE_QML_DESIGNER_LITE widgetInfoList.append(d->edit3DView.widgetInfo()); +#endif widgetInfoList.append(d->formEditorView.widgetInfo()); widgetInfoList.append(d->textEditorView.widgetInfo()); widgetInfoList.append(d->assetsLibraryView.widgetInfo()); widgetInfoList.append(d->itemLibraryView.widgetInfo()); widgetInfoList.append(d->navigatorView.widgetInfo()); widgetInfoList.append(d->propertyEditorView.widgetInfo()); +#ifndef QTC_USE_QML_DESIGNER_LITE widgetInfoList.append(d->materialEditorView.widgetInfo()); widgetInfoList.append(d->materialBrowserView.widgetInfo()); widgetInfoList.append(d->textureEditorView.widgetInfo()); +#endif widgetInfoList.append(d->statesEditorView.widgetInfo()); if (enableModelEditor()) widgetInfoList.append(d->collectionView.widgetInfo()); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index 9e6bdd03b9..5c8d42a306 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -32,9 +32,6 @@ ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundle { m_importTimer.setInterval(200); connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer); - m_moduleName = QStringLiteral("%1.%2").arg( - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER), - m_bundleId).mid(1); // Chop leading slash } // Returns empty string on success or an error message on failure. @@ -69,7 +66,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { qmldirContent.append("module "); - qmldirContent.append(m_moduleName); + qmldirContent.append(moduleName()); qmldirContent.append('\n'); } @@ -77,7 +74,9 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, const bool qmlFileExists = qmlSourceFile.exists(); const QString qmlType = qmlSourceFile.baseName(); const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType); + .arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + m_bundleId, qmlType); if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName]) return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName); if (!qmldirContent.contains(qmlFile)) { @@ -126,7 +125,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, if (!model) return "Model not available, cannot add import statement or update code model"; - Import import = Import::createLibraryImport(m_moduleName, "1.0"); + Import import = Import::createLibraryImport(moduleName(), "1.0"); if (!model->hasImport(import)) { if (model->possibleImports().contains(import)) { m_importAddPending = false; @@ -134,7 +133,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, model->changeImports({import}, {}); } catch (const RewritingException &) { // No point in trying to add import asynchronously either, so just fail out - return QStringLiteral("Failed to add import statement for: '%1'").arg(m_moduleName); + return QStringLiteral("Failed to add import statement for: '%1'").arg(moduleName()); } } else { // If import is not yet possible, import statement needs to be added asynchronously to @@ -188,7 +187,7 @@ void ContentLibraryBundleImporter::handleImportTimer() if (m_importAddPending) { try { - Import import = Import::createLibraryImport(m_moduleName, "1.0"); + Import import = Import::createLibraryImport(moduleName(), "1.0"); if (model->possibleImports().contains(import)) { model->changeImports({import}, {}); m_importAddPending = false; @@ -253,6 +252,13 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl } } +QString ContentLibraryBundleImporter::moduleName() +{ + return QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + m_bundleId); +} + QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) { FilePath bundleImportPath = resolveBundleImportPath(); @@ -275,7 +281,9 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) QString qmlType = qmlFilePath.baseName(); const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType); + .arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + m_bundleId, qmlType); if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName]) return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName); @@ -327,7 +335,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); Model *model = doc ? doc->currentModel() : nullptr; if (model) { - Import import = Import::createLibraryImport(m_moduleName, "1.0"); + Import import = Import::createLibraryImport(moduleName(), "1.0"); if (model->imports().contains(import)) model->changeImports({}, {import}); } @@ -342,16 +350,12 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) FilePath ContentLibraryBundleImporter::resolveBundleImportPath() { - FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); + FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesBasePath(); if (bundleImportPath.isEmpty()) return bundleImportPath; - const QString projectBundlePath = QStringLiteral("%1%2/%3").arg( - QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER), - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER), - m_bundleId).mid(1); // Chop leading slash - - return bundleImportPath.resolvePath(projectBundlePath); + return bundleImportPath.resolvePath(m_bundleId); } } // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h index 3aff09fe34..7fb2a48886 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h @@ -46,10 +46,10 @@ private: void handleImportTimer(); QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath); void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap); + QString moduleName(); Utils::FilePath m_bundleDir; QString m_bundleId; - QString m_moduleName; QStringList m_sharedFiles; QTimer m_importTimer; int m_importTimerCount = 0; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 6b1de2d2a7..334c017116 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -7,8 +7,8 @@ #include "contentlibraryeffect.h" #include "contentlibraryeffectscategory.h" #include "contentlibrarywidget.h" -#include "qmldesignerconstants.h" +#include <qmldesignerplugin.h> #include <utils/algorithm.h> #include <utils/qtcassert.h> #include <utils/hostosinfo.h> @@ -187,10 +187,11 @@ void ContentLibraryEffectsModel::loadBundle() QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString())); QString qml = itemObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3").arg( - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = QLatin1String("%1.%2.%3") + .arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + bundleId, + qml.chopped(4)).toLatin1(); // chopped(4): remove .qml auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index f546ea98cd..55af2accbd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h @@ -3,9 +3,8 @@ #pragma once -#include "qmldesignercorelib_global.h" +#include "nodeinstanceglobal.h" -#include <QDataStream> #include <QObject> #include <QUrl> @@ -22,6 +21,7 @@ class ContentLibraryMaterial : public QObject Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT) Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT) Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT) + Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: ContentLibraryMaterial(QObject *parent, @@ -31,7 +31,7 @@ public: const QUrl &icon, const QStringList &files, const QString &downloadPath, - const QString &baseWebUrl); + const QString &baseWebUrl = {}); bool filter(const QString &searchText); @@ -66,6 +66,7 @@ private: QString m_downloadPath; QString m_baseWebUrl; QStringList m_allFiles; + const QString m_itemType = "material"; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 7594c691b5..26747d359c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -8,12 +8,12 @@ #include "contentlibrarymaterialscategory.h" #include "contentlibrarywidget.h" -#include <designerpaths.h> +#include "designerpaths.h" #include "filedownloader.h" #include "fileextractor.h" #include "multifiledownloader.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" + +#include <qmldesignerplugin.h> #include <utils/algorithm.h> #include <utils/hostosinfo.h> @@ -275,9 +275,9 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) auto category = new ContentLibraryMaterialsCategory(this, cat); const QJsonObject matsObj = catsObj.value(cat).toObject(); - const QStringList mats = matsObj.keys(); - for (const QString &mat : mats) { - const QJsonObject matObj = matsObj.value(mat).toObject(); + const QStringList matsNames = matsObj.keys(); + for (const QString &matName : matsNames) { + const QJsonObject matObj = matsObj.value(matName).toObject(); QStringList files; const QJsonArray assetsArr = matObj.value("files").toArray(); @@ -286,12 +286,13 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())); QString qml = matObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3").arg( - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = QLatin1String("%1.%2.%3") + .arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + bundleId, + qml.chopped(4)).toLatin1(); // chopped(4): remove .qml - auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files, + auto bundleMat = new ContentLibraryMaterial(category, matName, qml, type, icon, files, m_downloadPath, m_baseUrl); category->addBundleMaterial(bundleMat); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp index 7ab239aab4..80dd7e816f 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp @@ -12,20 +12,19 @@ namespace QmlDesigner { ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, - const QString &downloadPath, const QUrl &icon, - const QString &key, const QString &webTextureUrl, - const QString &webIconUrl, const QString &fileExt, + const QString &dirPath, const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, - bool hasUpdate, bool isNew) + const QString &key, const QString &textureUrl, + const QString &iconUrl, bool hasUpdate, bool isNew) : QObject(parent) , m_iconPath(iconFileInfo.filePath()) - , m_downloadPath(downloadPath) - , m_webTextureUrl(webTextureUrl) - , m_webIconUrl(webIconUrl) + , m_dirPath(dirPath) + , m_textureUrl(textureUrl) + , m_iconUrl(iconUrl) , m_baseName{iconFileInfo.baseName()} - , m_fileExt(fileExt) + , m_suffix(suffix) , m_textureKey(key) - , m_icon(icon) + , m_icon(QUrl::fromLocalFile(iconFileInfo.absoluteFilePath())) , m_dimensions(dimensions) , m_sizeInBytes(sizeInBytes) , m_hasUpdate(hasUpdate) @@ -54,9 +53,9 @@ QString ContentLibraryTexture::iconPath() const return m_iconPath; } -QString ContentLibraryTexture::resolveFileExt() +QString ContentLibraryTexture::resolveSuffix() { - const QFileInfoList files = QDir(m_downloadPath).entryInfoList(QDir::Files); + const QFileInfoList files = QDir(m_dirPath).entryInfoList(QDir::Files); const QFileInfoList textureFiles = Utils::filtered(files, [this](const QFileInfo &fi) { return fi.baseName() == m_baseName; }); @@ -76,22 +75,20 @@ QString ContentLibraryTexture::resolveFileExt() QString ContentLibraryTexture::resolveToolTipText() { - if (m_fileExt.isEmpty()) { - // No supplied or resolved extension means we have just the icon and no other data - return m_baseName; - } + if (m_suffix.isEmpty()) + return m_baseName; // empty suffix means we have just the icon and no other data - QString fileName = m_baseName + m_fileExt; + QString fileName = m_baseName + m_suffix; QString imageInfo; if (!m_isDownloaded && m_sizeInBytes > 0 && !m_dimensions.isNull()) { - imageInfo = ImageUtils::imageInfo(m_dimensions, m_sizeInBytes); + imageInfo = ImageUtils::imageInfoString(m_dimensions, m_sizeInBytes); } else { - QString fullDownloadPath = m_downloadPath + '/' + fileName; - imageInfo = ImageUtils::imageInfo(fullDownloadPath); + QString fullDownloadPath = m_dirPath + '/' + fileName; + imageInfo = ImageUtils::imageInfoString(fullDownloadPath); } - return QStringLiteral("%1\n%2").arg(fileName, imageInfo); + return QString("%1\n%2").arg(fileName, imageInfo); } bool ContentLibraryTexture::isDownloaded() const @@ -99,9 +96,9 @@ bool ContentLibraryTexture::isDownloaded() const return m_isDownloaded; } -QString ContentLibraryTexture::downloadedTexturePath() const +QString ContentLibraryTexture::texturePath() const { - return m_downloadPath + '/' + m_baseName + m_fileExt; + return m_dirPath + '/' + m_baseName + m_suffix; } void ContentLibraryTexture::setDownloaded() @@ -116,16 +113,16 @@ void ContentLibraryTexture::setDownloaded() void ContentLibraryTexture::doSetDownloaded() { - if (m_fileExt.isEmpty()) - m_fileExt = resolveFileExt(); + if (m_suffix.isEmpty()) + m_suffix = resolveSuffix(); - m_isDownloaded = QFileInfo::exists(downloadedTexturePath()); + m_isDownloaded = QFileInfo::exists(texturePath()); m_toolTip = resolveToolTipText(); } QString ContentLibraryTexture::parentDirPath() const { - return m_downloadPath; + return m_dirPath; } QString ContentLibraryTexture::textureKey() const diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h index 9f5b46630f..8f7197bc72 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h @@ -19,17 +19,18 @@ class ContentLibraryTexture : public QObject Q_PROPERTY(QString textureToolTip MEMBER m_toolTip NOTIFY textureToolTipChanged) Q_PROPERTY(QUrl textureIcon MEMBER m_icon CONSTANT) Q_PROPERTY(bool textureVisible MEMBER m_visible NOTIFY textureVisibleChanged) - Q_PROPERTY(QString textureWebUrl MEMBER m_webTextureUrl CONSTANT) - Q_PROPERTY(QString textureWebIconUrl MEMBER m_webIconUrl CONSTANT) + Q_PROPERTY(QString textureUrl MEMBER m_textureUrl CONSTANT) + Q_PROPERTY(QString textureIconUrl MEMBER m_iconUrl CONSTANT) Q_PROPERTY(bool textureHasUpdate WRITE setHasUpdate READ hasUpdate NOTIFY hasUpdateChanged) Q_PROPERTY(bool textureIsNew MEMBER m_isNew CONSTANT) Q_PROPERTY(QString textureKey MEMBER m_textureKey CONSTANT) + Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: - ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &downloadPath, - const QUrl &icon, const QString &key, const QString &webTextureUrl, - const QString &webIconUrl, const QString &fileExt, const QSize &dimensions, - const qint64 sizeInBytes, bool hasUpdate, bool isNew); + ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &dirPath, + const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, + const QString &key = {}, const QString &textureUrl = {}, + const QString &iconUrl = {}, bool hasUpdate = false, bool isNew = false); Q_INVOKABLE bool isDownloaded() const; Q_INVOKABLE void setDownloaded(); @@ -38,7 +39,7 @@ public: QUrl icon() const; QString iconPath() const; - QString downloadedTexturePath() const; + QString texturePath() const; QString parentDirPath() const; QString textureKey() const; @@ -51,17 +52,17 @@ signals: void hasUpdateChanged(); private: - QString resolveFileExt(); + QString resolveSuffix(); QString resolveToolTipText(); void doSetDownloaded(); QString m_iconPath; - QString m_downloadPath; - QString m_webTextureUrl; - QString m_webIconUrl; + QString m_dirPath; + QString m_textureUrl; + QString m_iconUrl; QString m_toolTip; QString m_baseName; - QString m_fileExt; + QString m_suffix; QString m_textureKey; QUrl m_icon; QSize m_dimensions; @@ -71,6 +72,7 @@ private: bool m_visible = true; bool m_hasUpdate = false; bool m_isNew = false; + const QString m_itemType = "texture"; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp index 77519ad88f..0cafe8d138 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp @@ -14,17 +14,15 @@ namespace QmlDesigner { ContentLibraryTexturesCategory::ContentLibraryTexturesCategory(QObject *parent, const QString &name) : QObject(parent), m_name(name) {} -void ContentLibraryTexturesCategory::addTexture(const QFileInfo &tex, const QString &downloadPath, +void ContentLibraryTexturesCategory::addTexture(const QFileInfo &texIcon, const QString &downloadPath, const QString &key, const QString &webTextureUrl, - const QString &webIconUrl, const QString &fileExt, + const QString &iconUrl, const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, bool hasUpdate, bool isNew) { - QUrl icon = QUrl::fromLocalFile(tex.absoluteFilePath()); - m_categoryTextures.append(new ContentLibraryTexture( - this, tex, downloadPath, icon, key, webTextureUrl, webIconUrl, - fileExt, dimensions, sizeInBytes, hasUpdate, isNew)); + this, texIcon, downloadPath, suffix, dimensions, sizeInBytes, + key, webTextureUrl, iconUrl, hasUpdate, isNew)); } bool ContentLibraryTexturesCategory::filter(const QString &searchText) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h index 166528f05a..857346df06 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h @@ -28,7 +28,7 @@ public: ContentLibraryTexturesCategory(QObject *parent, const QString &name); void addTexture(const QFileInfo &tex, const QString &subPath, const QString &key, - const QString &webTextureUrl, const QString &webIconUrl, const QString &fileExt, + const QString &webTextureUrl, const QString &iconUrl, const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, bool hasUpdate, bool isNew); bool filter(const QString &searchText); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp index 319ca2686f..b575b6b9b2 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp @@ -95,37 +95,37 @@ QHash<int, QByteArray> ContentLibraryTexturesModel::roleNames() const /** * @brief Load the bundle categorized icons. Actual textures are downloaded on demand * - * @param bundlePath local path to the bundle folder and icons - * @param metaData bundle textures metadata + * @param textureBundleUrl remote url to the texture bundle + * @param bundleIconPath local path to the texture bundle icons folder + * @param jsonData bundle textures information from the bundle json */ -void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, const QString &iconsUrl, +void ContentLibraryTexturesModel::loadTextureBundle(const QString &textureBundleUrl, const QString &bundleIconPath, - const QVariantMap &metaData) + const QVariantMap &jsonData) { if (!m_bundleCategories.isEmpty()) return; QDir bundleDir = QString("%1/%2").arg(bundleIconPath, m_category); - if (!bundleDir.exists()) { - qWarning() << __FUNCTION__ << "textures bundle folder doesn't exist." << bundleDir.absolutePath(); - return; - } + QTC_ASSERT(bundleDir.exists(), return); - const QVariantMap imageItems = metaData.value("image_items").toMap(); + const QVariantMap imageItems = jsonData.value("image_items").toMap(); const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo &dir : dirs) { auto category = new ContentLibraryTexturesCategory(this, dir.fileName()); - const QFileInfoList texFiles = QDir(dir.filePath()).entryInfoList(QDir::Files); - for (const QFileInfo &tex : texFiles) { - QString textureUrl = QString("%1/%2/%3.zip").arg(remoteUrl, dir.fileName(), tex.baseName()); - QString iconUrl = QString("%1/%2/%3.png").arg(iconsUrl, dir.fileName(), tex.baseName()); - - QString localDownloadPath = QString("%1/%2/%3") + const QFileInfoList texIconFiles = QDir(dir.filePath()).entryInfoList(QDir::Files); + for (const QFileInfo &texIcon : texIconFiles) { + QString textureUrl = QString("%1/%2/%3/%4.zip").arg(textureBundleUrl, m_category, + dir.fileName(), texIcon.baseName()); + QString iconUrl = QString("%1/icons/%2/%3/%4.png").arg(textureBundleUrl, m_category, + dir.fileName(), texIcon.baseName()); + + QString texturePath = QString("%1/%2/%3") .arg(Paths::bundlesPathSetting(), m_category, dir.fileName()); - QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), tex.baseName()); + QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), texIcon.baseName()); QString fileExt; QSize dimensions; qint64 sizeInBytes = -1; @@ -141,7 +141,7 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, co isNew = m_newFiles.contains(key); } - category->addTexture(tex, localDownloadPath, key, textureUrl, iconUrl, fileExt, + category->addTexture(texIcon, texturePath, key, textureUrl, iconUrl, fileExt, dimensions, sizeInBytes, hasUpdate, isNew); } m_bundleCategories.append(category); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h index 92db4151a8..94e223a251 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h @@ -37,8 +37,8 @@ public: void setHasSceneEnv(bool b); void resetModel(); - void loadTextureBundle(const QString &remoteUrl, const QString &iconsUrl, - const QString &bundlePath, const QVariantMap &metaData); + void loadTextureBundle(const QString &textureBundleUrl, const QString &bundlePath, + const QVariantMap &metaData); signals: void isEmptyChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp new file mode 100644 index 0000000000..18d6e45fa8 --- /dev/null +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -0,0 +1,423 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "contentlibraryusermodel.h" + +#include "contentlibrarybundleimporter.h" +#include "contentlibrarymaterial.h" +#include "contentlibrarymaterialscategory.h" +#include "contentlibrarytexture.h" +#include "contentlibrarywidget.h" + +#include <designerpaths.h> +#include <imageutils.h> +#include <qmldesignerplugin.h> + +#include <utils/algorithm.h> +#include <utils/hostosinfo.h> +#include <utils/qtcassert.h> + +#include <QCoreApplication> +#include <QFileInfo> +#include <QJsonArray> +#include <QJsonDocument> +#include <QQmlEngine> +#include <QStandardPaths> +#include <QUrl> + +namespace QmlDesigner { + +ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent) + : QAbstractListModel(parent) + , m_widget(parent) +{ + m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO + + loadMaterialBundle(); + loadTextureBundle(); +} + +int ContentLibraryUserModel::rowCount(const QModelIndex &) const +{ + return m_userCategories.size(); +} + +QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.row() < m_userCategories.size(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); + + if (role == NameRole) + return m_userCategories.at(index.row()); + + if (role == ItemsRole) { + if (index.row() == 0) + return QVariant::fromValue(m_userMaterials); + if (index.row() == 1) + return QVariant::fromValue(m_userTextures); + if (index.row() == 2) + return QVariant::fromValue(m_user3DItems); + if (index.row() == 3) + return QVariant::fromValue(m_userEffects); + } + + if (role == VisibleRole) + return true; // TODO + + return {}; +} + +bool ContentLibraryUserModel::isValidIndex(int idx) const +{ + return idx > -1 && idx < rowCount(); +} + +void ContentLibraryUserModel::updateIsEmpty() +{ + bool anyMatVisible = Utils::anyOf(m_userMaterials, [&](ContentLibraryMaterial *mat) { + return mat->visible(); + }); + + bool newEmpty = !anyMatVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport(); + + if (newEmpty != m_isEmpty) { + m_isEmpty = newEmpty; + emit isEmptyChanged(); + } +} + +void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml, + const QUrl &icon, const QStringList &files) +{ + auto libMat = new ContentLibraryMaterial(this, name, qml, qmlToModule(qml), icon, files, + Paths::bundlesPathSetting().append("/User/materials")); + + m_userMaterials.append(libMat); + int matSectionIdx = 0; + emit dataChanged(index(matSectionIdx), index(matSectionIdx)); +} + +void ContentLibraryUserModel::addTextures(const QStringList &paths) +{ + QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"}; + bundleDir.mkpath("."); + bundleDir.mkdir("icons"); + + for (const QString &path : paths) { + QFileInfo fileInfo(path); + QString suffix = '.' + fileInfo.suffix(); + auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png")); + QPair<QSize, qint64> info = ImageUtils::imageInfo(path); + QString dirPath = fileInfo.path(); + QSize imgDims = info.first; + qint64 imgFileSize = info.second; + + auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize); + m_userTextures.append(tex); + } + + int texSectionIdx = 1; + emit dataChanged(index(texSectionIdx), index(texSectionIdx)); +} + +// returns unique library material's name and qml component +QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const +{ + QTC_ASSERT(!m_bundleObj.isEmpty(), return {}); + + const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); + const QStringList matNames = matsObj.keys(); + + QStringList matQmls; + for (const QString &matName : matNames) + matQmls.append(matsObj.value(matName).toObject().value("qml").toString().chopped(4)); // remove .qml + + QString retName = matName.isEmpty() ? "Material" : matName; + retName = retName.trimmed(); + + QString retQml = retName; + retQml.remove(' '); + if (retQml.at(0).isLower()) + retQml[0] = retQml.at(0).toUpper(); + retQml.prepend("My"); + + int num = 1; + if (matNames.contains(retName) || matQmls.contains(retQml)) { + while (matNames.contains(retName + QString::number(num)) + || matQmls.contains(retQml + QString::number(num))) { + ++num; + } + + retName += QString::number(num); + retQml += QString::number(num); + } + + return {retName, retQml + ".qml"}; +} + +TypeName ContentLibraryUserModel::qmlToModule(const QString &qmlName) const +{ + return QLatin1String("%1.%2.%3").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + m_bundleId, + qmlName.chopped(4)).toLatin1(); // chopped(4): remove .qml +} + +QHash<int, QByteArray> ContentLibraryUserModel::roleNames() const +{ + static const QHash<int, QByteArray> roles { + {NameRole, "categoryName"}, + {VisibleRole, "categoryVisible"}, + {ItemsRole, "categoryItems"} + }; + return roles; +} + +void ContentLibraryUserModel::createImporter(const QString &bundlePath, const QString &bundleId, + const QStringList &sharedFiles) +{ + m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); +#ifdef QDS_USE_PROJECTSTORAGE + connect(m_importer, + &Internal::ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName) { + m_importerRunning = false; + emit importerRunningChanged(); + if (typeName.size()) + emit bundleMaterialImported(typeName); + }); +#else + connect(m_importer, + &Internal::ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + m_importerRunning = false; + emit importerRunningChanged(); + if (metaInfo.isValid()) + emit bundleMaterialImported(metaInfo); + }); +#endif + + connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + Q_UNUSED(metaInfo) + m_importerRunning = false; + emit importerRunningChanged(); + emit bundleMaterialUnimported(metaInfo); + }); + + resetModel(); + updateIsEmpty(); +} + +QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef() +{ + return m_bundleObj; +} + +void ContentLibraryUserModel::loadMaterialBundle() +{ + if (m_matBundleExists) + return; + + QDir bundleDir{Paths::bundlesPathSetting() + "/User/materials"}; + bundleDir.mkpath("."); + + if (m_bundleObj.isEmpty()) { + auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json")); + if (!jsonFilePath.exists()) { + QString jsonContent = "{\n"; + jsonContent += " \"id\": \"UserMaterialBundle\",\n"; + jsonContent += " \"materials\": {\n"; + jsonContent += " }\n"; + jsonContent += "}"; + jsonFilePath.writeFileContents(jsonContent.toLatin1()); + } + + QFile jsonFile(jsonFilePath.path()); + if (!jsonFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open user_materials_bundle.json"); + return; + } + + QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll()); + if (matBundleJsonDoc.isNull()) { + qWarning("Invalid user_materials_bundle.json file"); + return; + } else { + m_bundleObj = matBundleJsonDoc.object(); + } + } + + m_bundleId = m_bundleObj.value("id").toString(); + + // parse materials + const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); + const QStringList materialNames = matsObj.keys(); + for (const QString &matName : materialNames) { + const QJsonObject matObj = matsObj.value(matName).toObject(); + + QStringList files; + const QJsonArray assetsArr = matObj.value("files").toArray(); + for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + files.append(asset.toString()); + + QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); + QString qml = matObj.value("qml").toString(); + + TypeName type = qmlToModule(qml); + + auto userMat = new ContentLibraryMaterial(this, matName, qml, type, icon, files, + bundleDir.path(), ""); + + m_userMaterials.append(userMat); + } + + QStringList sharedFiles; + const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); + for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) + sharedFiles.append(file.toString()); + + createImporter(bundleDir.path(), m_bundleId, sharedFiles); + + m_matBundleExists = true; + emit matBundleExistsChanged(); +} + +void ContentLibraryUserModel::loadTextureBundle() +{ + if (!m_userTextures.isEmpty()) + return; + + QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"}; + bundleDir.mkpath("."); + bundleDir.mkdir("icons"); + + const QFileInfoList fileInfos = bundleDir.entryInfoList(QDir::Files); + for (const QFileInfo &fileInfo : fileInfos) { + QString suffix = '.' + fileInfo.suffix(); + auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png")); + QPair<QSize, qint64> info = ImageUtils::imageInfo(fileInfo.path()); + QString dirPath = fileInfo.path(); + QSize imgDims = info.first; + qint64 imgFileSize = info.second; + + auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize); + m_userTextures.append(tex); + } + + int texSectionIdx = 1; + emit dataChanged(index(texSectionIdx), index(texSectionIdx)); +} + +bool ContentLibraryUserModel::hasRequiredQuick3DImport() const +{ + return m_widget->hasQuick3DImport() && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3; +} + +bool ContentLibraryUserModel::matBundleExists() const +{ + return m_matBundleExists; +} + +Internal::ContentLibraryBundleImporter *ContentLibraryUserModel::bundleImporter() const +{ + return m_importer; +} + +void ContentLibraryUserModel::setSearchText(const QString &searchText) +{ + QString lowerSearchText = searchText.toLower(); + + if (m_searchText == lowerSearchText) + return; + + m_searchText = lowerSearchText; + + for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) + mat->filter(m_searchText); + + updateIsEmpty(); +} + +void ContentLibraryUserModel::updateImportedState(const QStringList &importedMats) +{ + bool changed = false; + + for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) + changed |= mat->setImported(importedMats.contains(mat->qml().chopped(4))); + + if (changed) + resetModel(); +} + +void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) +{ + bool oldRequiredImport = hasRequiredQuick3DImport(); + + m_quick3dMajorVersion = major; + m_quick3dMinorVersion = minor; + + bool newRequiredImport = hasRequiredQuick3DImport(); + + if (oldRequiredImport == newRequiredImport) + return; + + emit hasRequiredQuick3DImportChanged(); + + updateIsEmpty(); +} + +void ContentLibraryUserModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool add) +{ + emit applyToSelectedTriggered(mat, add); +} + +void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat) +{ + QString err = m_importer->importComponent(mat->qml(), mat->files()); + + if (err.isEmpty()) { + m_importerRunning = true; + emit importerRunningChanged(); + } else { + qWarning() << __FUNCTION__ << err; + } +} + +void ContentLibraryUserModel::removeFromProject(ContentLibraryMaterial *mat) +{ + emit bundleMaterialAboutToUnimport(mat->type()); + + QString err = m_importer->unimportComponent(mat->qml()); + + if (err.isEmpty()) { + m_importerRunning = true; + emit importerRunningChanged(); + } else { + qWarning() << __FUNCTION__ << err; + } +} + +bool ContentLibraryUserModel::hasModelSelection() const +{ + return m_hasModelSelection; +} + +void ContentLibraryUserModel::setHasModelSelection(bool b) +{ + if (b == m_hasModelSelection) + return; + + m_hasModelSelection = b; + emit hasModelSelectionChanged(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h new file mode 100644 index 0000000000..3e9a96fd9d --- /dev/null +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -0,0 +1,132 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "modelfwd.h" + +#include <QAbstractListModel> +#include <QJsonObject> + +QT_FORWARD_DECLARE_CLASS(QUrl) + +namespace QmlDesigner { + +class ContentLibraryEffect; +class ContentLibraryMaterial; +class ContentLibraryTexture; +class ContentLibraryWidget; +class NodeMetaInfo; + +namespace Internal { +class ContentLibraryBundleImporter; +} + +class ContentLibraryUserModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) + Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) + Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) + Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) + Q_PROPERTY(QList<ContentLibraryMaterial *> userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) + Q_PROPERTY(QList<ContentLibraryTexture *> userTextures MEMBER m_userTextures NOTIFY userTexturesChanged) + Q_PROPERTY(QList<ContentLibraryEffect *> user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) + Q_PROPERTY(QList<ContentLibraryEffect *> userEffects MEMBER m_userEffects NOTIFY userEffectsChanged) + +public: + ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash<int, QByteArray> roleNames() const override; + + void setSearchText(const QString &searchText); + void updateImportedState(const QStringList &importedMats); + + QPair<QString, QString> getUniqueLibMaterialNameAndQml(const QString &matName) const; + TypeName qmlToModule(const QString &qmlName) const; + + void setQuick3DImportVersion(int major, int minor); + + bool hasRequiredQuick3DImport() const; + + bool matBundleExists() const; + + bool hasModelSelection() const; + void setHasModelSelection(bool b); + + void resetModel(); + void updateIsEmpty(); + + void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + void addTextures(const QStringList &paths); + + void setBundleObj(const QJsonObject &newBundleObj); + QJsonObject &bundleJsonObjectRef(); + + Internal::ContentLibraryBundleImporter *bundleImporter() const; + + Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); + Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); + Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); + +signals: + void isEmptyChanged(); + void hasRequiredQuick3DImportChanged(); + void hasModelSelectionChanged(); + void userMaterialsChanged(); + void userTexturesChanged(); + void user3DItemsChanged(); + void userEffectsChanged(); + + void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); + +#ifdef QDS_USE_PROJECTSTORAGE + void bundleMaterialImported(const QmlDesigner::TypeName &typeName); +#else + void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); +#endif + void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); + void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); + void importerRunningChanged(); + void matBundleExistsChanged(); + +private: + void loadMaterialBundle(); + void loadTextureBundle(); + bool isValidIndex(int idx) const; + void createImporter(const QString &bundlePath, const QString &bundleId, + const QStringList &sharedFiles); + + ContentLibraryWidget *m_widget = nullptr; + QString m_searchText; + QString m_bundleId; + + QList<ContentLibraryMaterial *> m_userMaterials; + QList<ContentLibraryTexture *> m_userTextures; + QList<ContentLibraryEffect *> m_userEffects; + QList<ContentLibraryEffect *> m_user3DItems; + QStringList m_userCategories; + + QJsonObject m_bundleObj; + Internal::ContentLibraryBundleImporter *m_importer = nullptr; + + bool m_isEmpty = true; + bool m_matBundleExists = false; + bool m_hasModelSelection = false; + bool m_importerRunning = false; + + int m_quick3dMajorVersion = -1; + int m_quick3dMinorVersion = -1; + + QString m_importerBundlePath; + QString m_importerBundleId; + QStringList m_importerSharedFiles; + + enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole }; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 61ae078ea8..dd8a4d9919 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -3,6 +3,8 @@ #include "contentlibraryview.h" +#include "asset.h" +#include "bindingproperty.h" #include "contentlibrarybundleimporter.h" #include "contentlibraryeffect.h" #include "contentlibraryeffectsmodel.h" @@ -10,13 +12,17 @@ #include "contentlibrarymaterialsmodel.h" #include "contentlibrarytexture.h" #include "contentlibrarytexturesmodel.h" +#include "contentlibraryusermodel.h" #include "contentlibrarywidget.h" +#include "documentmanager.h" #include "externaldependenciesinterface.h" #include "nodelistproperty.h" #include "qmldesignerconstants.h" #include "qmlobjectnode.h" #include "variantproperty.h" -#include <utils3d.h> +#include "utils3d.h" + +#include <designerpaths.h> #include <coreplugin/messagebox.h> #include <enumeration.h> @@ -30,6 +36,10 @@ #include <qtsupport/qtkitaspect.h> #endif +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QPixmap> #include <QVector3D> namespace QmlDesigner { @@ -204,6 +214,8 @@ WidgetInfo ContentLibraryView::widgetInfo() connect(effectsModel, &ContentLibraryEffectsModel::bundleItemUnimported, this, &ContentLibraryView::updateBundleEffectsImportedState); + + connectUserBundle(); } return createWidgetInfo(m_widget.data(), @@ -213,6 +225,64 @@ WidgetInfo ContentLibraryView::widgetInfo() tr("Content Library")); } +void ContentLibraryView::connectUserBundle() +{ + ContentLibraryUserModel *userModel = m_widget->userModel().data(); + + connect(userModel, + &ContentLibraryUserModel::applyToSelectedTriggered, + this, + [&](ContentLibraryMaterial *bundleMat, bool add) { + if (m_selectedModels.isEmpty()) + return; + + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; + + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->userModel()->addToProject(bundleMat); + }); + +#ifdef QDS_USE_PROJECTSTORAGE + connect(userModel, + &ContentLibraryUserModel::bundleMaterialImported, + this, + [&](const QmlDesigner::TypeName &typeName) { + applyBundleMaterialToDropTarget({}, typeName); + updateBundleUserMaterialsImportedState(); + }); +#else + connect(userModel, + &ContentLibraryUserModel::bundleMaterialImported, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + applyBundleMaterialToDropTarget({}, metaInfo); + updateBundleUserMaterialsImportedState(); + }); +#endif + + connect(userModel, &ContentLibraryUserModel::bundleMaterialAboutToUnimport, this, + [&] (const QmlDesigner::TypeName &type) { + // delete instances of the bundle material that is about to be unimported + executeInTransaction("ContentLibraryView::connectUserModel", [&] { + ModelNode matLib = Utils3D::materialLibraryNode(this); + if (!matLib.isValid()) + return; + + Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { + if (mat.isValid() && mat.type() == type) + QmlObjectNode(mat).destroy(); + }); + }); + }); + + connect(userModel, &ContentLibraryUserModel::bundleMaterialUnimported, this, + &ContentLibraryView::updateBundleUserMaterialsImportedState); +} + void ContentLibraryView::modelAttached(Model *model) { AbstractView::modelAttached(model); @@ -276,6 +346,7 @@ void ContentLibraryView::selectedNodesChanged(const QList<ModelNode> &selectedNo }); m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty()); + m_widget->userModel()->setHasModelSelection(!m_selectedModels.isEmpty()); } void ContentLibraryView::customNotification(const AbstractView *view, @@ -283,8 +354,6 @@ void ContentLibraryView::customNotification(const AbstractView *view, const QList<ModelNode> &nodeList, const QList<QVariant> &data) { - Q_UNUSED(data) - if (view == this) return; @@ -324,6 +393,12 @@ void ContentLibraryView::customNotification(const AbstractView *view, m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant(); m_widget->effectsModel()->addInstance(m_draggedBundleEffect); m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); + } else if (identifier == "add_material_to_content_lib") { + QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return); + + addLibMaterial(nodeList.first(), data.first().value<QPixmap>()); + } else if (identifier == "add_assets_to_content_lib") { + addLibAssets(data.first().toStringList()); } } @@ -452,6 +527,163 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle } #endif +// Add a project material to Content Library's user tab +void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &icon) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); + + auto [name, qml] = m_widget->userModel()->getUniqueLibMaterialNameAndQml( + mat.variantProperty("objectName").value().toString()); + + bundlePath.pathAppended("icons").createDir(); + bundlePath.pathAppended("images").createDir(); + bundlePath.pathAppended("shaders").createDir(); + + QString iconPath = QLatin1String("icons/%1.png").arg(mat.id()); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + + // save icon + bool iconSaved = icon.save(fullIconPath); + if (!iconSaved) + qWarning() << __FUNCTION__ << "icon save failed"; + + // generate and save material Qml file + const QStringList depAssets = writeLibMaterialQml(mat, qml); + + // add the material to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJsonObjectRef(); + QJsonObject matsObj = jsonRef.value("materials").toObject(); + QJsonObject matObj; + matObj.insert("qml", qml); + matObj.insert("icon", iconPath); + QJsonArray filesArr; + for (const QString &assetPath : depAssets) + filesArr.append(assetPath); + matObj.insert("files", filesArr); + + matsObj.insert(name, matObj); + jsonRef.insert("materials", matsObj); + auto result = bundlePath.pathAppended("user_materials_bundle.json") + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // copy material assets to bundle folder + for (const QString &assetPath : depAssets) { + Asset asset(assetPath); + QString subDir; + if (asset.isImage()) + subDir = "images"; + else if (asset.isShader()) + subDir = "shaders"; + + Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath); + Utils::FilePath assetPathTarget = bundlePath.pathAppended(QString("%1/%2") + .arg(subDir, "/" + asset.fileName())); + + auto result = assetPathSource.copyFile(assetPathTarget); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + } + + m_widget->userModel()->addMaterial(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets); +} + +QStringList ContentLibraryView::writeLibMaterialQml(const ModelNode &mat, const QString &qml) +{ + QStringList depListIds; + auto [qmlString, assets] = modelNodeToQmlString(mat, depListIds); + + qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n"); + + auto qmlPath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/" + qml); + auto result = qmlPath.writeFileContents(qmlString.toUtf8()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + return assets.values(); +} + +QPair<QString, QSet<QString>> ContentLibraryView::modelNodeToQmlString(const ModelNode &node, + QStringList &depListIds, + int depth) +{ + QString qml; + QSet<QString> assets; + + QString indent = QString(" ").repeated(depth * 4); + + qml += indent + node.simplifiedTypeName() + " {\n"; + + indent = QString(" ").repeated((depth + 1) * 4); + + qml += indent + "id: " + (depth == 0 ? "root" : node.id()) + " \n\n"; + + const QList<AbstractProperty> matProps = node.properties(); + for (const AbstractProperty &p : matProps) { + if (p.isVariantProperty()) { + QVariant pValue = p.toVariantProperty().value(); + QString val; + if (strcmp(pValue.typeName(), "QString") == 0 || strcmp(pValue.typeName(), "QColor") == 0) { + val = QLatin1String("\"%1\"").arg(pValue.toString()); + } else if (strcmp(pValue.typeName(), "QUrl") == 0) { + val = QLatin1String("\"%1\"").arg(pValue.toString()); + assets.insert(pValue.toString()); + } else if (strcmp(pValue.typeName(), "QmlDesigner::Enumeration") == 0) { + val = pValue.value<QmlDesigner::Enumeration>().toString(); + } else { + val = pValue.toString(); + } + + qml += indent + p.name() + ": " + val + "\n"; + } else if (p.isBindingProperty()) { + qml += indent + p.name() + ": " + p.toBindingProperty().expression() + "\n"; + + ModelNode depNode = modelNodeForId(p.toBindingProperty().expression()); + + if (depNode && !depListIds.contains(depNode.id())) { + depListIds.append(depNode.id()); + auto [depQml, depAssets] = modelNodeToQmlString(depNode, depListIds, depth + 1); + qml += "\n" + depQml + "\n"; + assets.unite(depAssets); + } + } + } + + indent = QString(" ").repeated(depth * 4); + + qml += indent + "}\n"; + + return {qml, assets}; +} + +void ContentLibraryView::addLibAssets(const QStringList &paths) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures"); + QStringList pathsInBundle; + + for (const QString &path : paths) { + Asset asset(path); + auto assetPath = Utils::FilePath::fromString(path); + + // save icon + QString iconSavePath = bundlePath.pathAppended("icons/" + assetPath.baseName() + ".png").toString(); + QPixmap icon = asset.pixmap({120, 120}); + bool iconSaved = icon.save(iconSavePath); + if (!iconSaved) + qWarning() << __FUNCTION__ << "icon save failed"; + + // save asset + auto result = assetPath.copyFile(bundlePath.pathAppended(asset.fileName())); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + pathsInBundle.append(bundlePath.pathAppended(asset.fileName()).toString()); + } + + m_widget->userModel()->addTextures(pathsInBundle); +} + ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { ModelNode matLib = Utils3D::materialLibraryNode(this); @@ -477,6 +709,7 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t return {}; } + #ifdef QDS_USE_PROJECTSTORAGE ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) { @@ -548,6 +781,25 @@ void ContentLibraryView::updateBundleMaterialsImportedState() m_widget->materialsModel()->updateImportedState(importedBundleMats); } +void ContentLibraryView::updateBundleUserMaterialsImportedState() +{ + using namespace Utils; + + if (!m_widget->userModel()->bundleImporter()) + return; + + QStringList importedBundleMats; + + FilePath bundlePath = m_widget->userModel()->bundleImporter()->resolveBundleImportPath(); + + if (bundlePath.exists()) { + importedBundleMats = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const FilePath &f) { return f.fileName().chopped(4); }); + } + + m_widget->userModel()->updateImportedState(importedBundleMats); +} + void ContentLibraryView::updateBundleEffectsImportedState() { using namespace Utils; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 3b57b7a4ab..03d42fa8bc 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -10,6 +10,8 @@ #include <QObject> #include <QPointer> +QT_FORWARD_DECLARE_CLASS(QPixmap) + namespace QmlDesigner { class ContentLibraryEffect; @@ -46,10 +48,18 @@ public: const QVariant &data) override; private: + void connectUserBundle(); void active3DSceneChanged(qint32 sceneId); void updateBundleMaterialsImportedState(); + void updateBundleUserMaterialsImportedState(); void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); + void addLibMaterial(const ModelNode &mat, const QPixmap &icon); + void addLibAssets(const QStringList &paths); + QStringList writeLibMaterialQml(const ModelNode &mat, const QString &qml); + QPair<QString, QSet<QString>> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds, + int depth = 0); + #ifdef QDS_USE_PROJECTSTORAGE void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const TypeName &typeName = {}); #else diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index c885a76ba7..9375d43fd4 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -10,6 +10,7 @@ #include "contentlibrarytexture.h" #include "contentlibrarytexturesmodel.h" #include "contentlibraryiconprovider.h" +#include "contentlibraryusermodel.h" #include "utils/filedownloader.h" #include "utils/fileextractor.h" @@ -100,10 +101,10 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) && m_textureToDrag->isDownloaded()) { QMimeData *mimeData = new QMimeData; mimeData->setData(Constants::MIME_TYPE_BUNDLE_TEXTURE, - {m_textureToDrag->downloadedTexturePath().toUtf8()}); + {m_textureToDrag->texturePath().toUtf8()}); // Allows standard file drag-n-drop. As of now needed to drop on Assets view - mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->downloadedTexturePath())}); + mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->texturePath())}); emit bundleTextureDragStarted(m_textureToDrag); model->startDrag(mimeData, m_textureToDrag->icon().toLocalFile()); @@ -126,6 +127,7 @@ ContentLibraryWidget::ContentLibraryWidget() , m_texturesModel(new ContentLibraryTexturesModel("Textures", this)) , m_environmentsModel(new ContentLibraryTexturesModel("Environments", this)) , m_effectsModel(new ContentLibraryEffectsModel(this)) + , m_userModel(new ContentLibraryUserModel(this)) { qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader"); qmlRegisterType<QmlDesigner::FileExtractor>("WebFetcher", 1, 0, "FileExtractor"); @@ -140,18 +142,12 @@ ContentLibraryWidget::ContentLibraryWidget() m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - m_baseUrl = QmlDesignerPlugin::settings() - .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() - + "/textures"; + m_textureBundleUrl = QmlDesignerPlugin::settings() + .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() + "/textures"; - m_texturesUrl = m_baseUrl + "/Textures"; - m_textureIconsUrl = m_baseUrl + "/icons/Textures"; - m_environmentIconsUrl = m_baseUrl + "/icons/Environments"; - m_environmentsUrl = m_baseUrl + "/Environments"; + m_bundlePath = Paths::bundlesPathSetting(); - m_downloadPath = Paths::bundlesPathSetting(); - - loadTextureBundle(); + loadTextureBundles(); Theme::setupTheme(m_quickWidget->engine()); m_quickWidget->quickWidget()->installEventFilter(this); @@ -177,38 +173,34 @@ ContentLibraryWidget::ContentLibraryWidget() {"materialsModel", QVariant::fromValue(m_materialsModel.data())}, {"texturesModel", QVariant::fromValue(m_texturesModel.data())}, {"environmentsModel", QVariant::fromValue(m_environmentsModel.data())}, - {"effectsModel", QVariant::fromValue(m_effectsModel.data())}}); + {"effectsModel", QVariant::fromValue(m_effectsModel.data())}, + {"userModel", QVariant::fromValue(m_userModel.data())}}); reloadQmlSource(); } -QVariantMap ContentLibraryWidget::readBundleMetadata() +QVariantMap ContentLibraryWidget::readTextureBundleJson() { - QVariantMap metaData; - QFile jsonFile(m_downloadPath + "/texture_bundle.json"); + QVariantMap jsonData; + QFile jsonFile(m_bundlePath + "/texture_bundle.json"); if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) - metaData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap(); + jsonData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap(); - int version = metaData["version"].toInt(); + int version = jsonData["version"].toInt(); if (version > TextureBundleMetadataVersion) { qWarning() << "Unrecognized texture metadata file version: " << version; return {}; } - return metaData; + return jsonData; } -void ContentLibraryWidget::loadTextureBundle() +void ContentLibraryWidget::loadTextureBundles() { - QDir bundleDir{m_downloadPath}; + QDir bundleDir{m_bundlePath}; - if (fetchTextureBundleMetadata(bundleDir) && fetchTextureBundleIcons(bundleDir)) { - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); - } + if (fetchTextureBundleJson(bundleDir) && fetchTextureBundleIcons(bundleDir)) + populateTextureBundleModels(); } std::tuple<QVariantMap, QVariantMap, QVariantMap> ContentLibraryWidget::compareTextureMetaFiles( @@ -272,9 +264,9 @@ void ContentLibraryWidget::fetchNewTextureIcons(const QVariantMap &existingFiles }); auto multidownloader = new MultiFileDownloader(this); - multidownloader->setBaseUrl(QString(m_baseUrl + "/icons")); + multidownloader->setBaseUrl(QString(m_textureBundleUrl + "/icons")); multidownloader->setFiles(fileList); - multidownloader->setTargetDirPath(m_downloadPath + "/TextureBundleIcons"); + multidownloader->setTargetDirPath(m_bundlePath + "/TextureBundleIcons"); auto downloader = new FileDownloader(this); downloader->setDownloadEnabled(true); @@ -314,15 +306,8 @@ void ContentLibraryWidget::fetchNewTextureIcons(const QVariantMap &existingFiles existingFile.flush(); } - if (fetchTextureBundleIcons(bundleDir)) { - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, - metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); - } - + if (fetchTextureBundleIcons(bundleDir)) + populateTextureBundleModels(); }); multidownloader->start(); @@ -433,50 +418,45 @@ QStringList ContentLibraryWidget::saveNewTextures(const QDir &bundleDir, const Q } } -bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir) +bool ContentLibraryWidget::fetchTextureBundleJson(const QDir &bundleDir) { QString filePath = bundleDir.filePath("texture_bundle.json"); QFileInfo fi(filePath); - bool metaFileExists = fi.exists() && fi.size() > 0; + bool jsonFileExists = fi.exists() && fi.size() > 0; - QString metaFileUrl = m_baseUrl + "/texture_bundle.zip"; + QString bundleZipUrl = m_textureBundleUrl + "/texture_bundle.zip"; FileDownloader *downloader = new FileDownloader(this); - downloader->setUrl(metaFileUrl); + downloader->setUrl(bundleZipUrl); downloader->setProbeUrl(false); downloader->setDownloadEnabled(true); + downloader->start(); QObject::connect(downloader, &FileDownloader::downloadFailed, this, - [this, metaFileExists, bundleDir] { - if (metaFileExists) { - if (fetchTextureBundleIcons(bundleDir)) { - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, - metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); - } + [this, jsonFileExists, bundleDir] { + if (jsonFileExists) { + if (fetchTextureBundleIcons(bundleDir)) + populateTextureBundleModels(); } }); QObject::connect(downloader, &FileDownloader::finishedChanged, this, - [this, downloader, bundleDir, metaFileExists, filePath] { + [this, downloader, bundleDir, jsonFileExists, filePath] { FileExtractor *extractor = new FileExtractor(this); extractor->setArchiveName(downloader->completeBaseName()); extractor->setSourceFile(downloader->outputFile()); - if (!metaFileExists) + if (!jsonFileExists) extractor->setTargetPath(bundleDir.absolutePath()); extractor->setAlwaysCreateDir(false); extractor->setClearTargetPathContents(false); QObject::connect(extractor, &FileExtractor::finishedChanged, this, - [this, downloader, bundleDir, extractor, metaFileExists, filePath] { + [this, downloader, bundleDir, extractor, jsonFileExists, filePath] { downloader->deleteLater(); extractor->deleteLater(); - if (metaFileExists) { + if (jsonFileExists) { QVariantMap newFiles, existing; QVariantMap modifiedFilesEntries; @@ -498,32 +478,35 @@ bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir) } } - if (fetchTextureBundleIcons(bundleDir)) { - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, - metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); - } + if (fetchTextureBundleIcons(bundleDir)) + populateTextureBundleModels(); }); extractor->extract(); }); - downloader->start(); return false; } +void ContentLibraryWidget::populateTextureBundleModels() +{ + QVariantMap jsonData = readTextureBundleJson(); + + QString bundleIconPath = m_bundlePath + "/TextureBundleIcons"; + + m_texturesModel->loadTextureBundle(m_textureBundleUrl, bundleIconPath, jsonData); + m_environmentsModel->loadTextureBundle(m_textureBundleUrl, bundleIconPath, jsonData); +} + bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir) { QString iconsPath = bundleDir.filePath("TextureBundleIcons"); QDir iconsDir(iconsPath); - if (iconsDir.exists() && iconsDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).length() > 0) + if (iconsDir.exists() && !iconsDir.isEmpty()) return true; - QString zipFileUrl = m_baseUrl + "/icons.zip"; + QString zipFileUrl = m_textureBundleUrl + "/icons.zip"; FileDownloader *downloader = new FileDownloader(this); downloader->setUrl(zipFileUrl); @@ -543,13 +526,7 @@ bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir) [this, downloader, extractor] { downloader->deleteLater(); extractor->deleteLater(); - - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, - metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); + populateTextureBundleModels(); }); extractor->extract(); @@ -572,7 +549,7 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) checksumOnServer = m_environmentsModel->removeModifiedFileEntry(textureKey); QJsonObject metaDataObj; - QFile jsonFile(m_downloadPath + "/texture_bundle.json"); + QFile jsonFile(m_bundlePath + "/texture_bundle.json"); if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) { metaDataObj = QJsonDocument::fromJson(jsonFile.readAll()).object(); jsonFile.close(); @@ -589,7 +566,7 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) QJsonDocument outDoc(metaDataObj); QByteArray data = outDoc.toJson(); - QFile outFile(m_downloadPath + "/texture_bundle.json"); + QFile outFile(m_bundlePath + "/texture_bundle.json"); if (outFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text)) { outFile.write(data); outFile.flush(); @@ -601,6 +578,12 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) m_environmentsModel->markTextureHasNoUpdates(subcategory, textureKey); } +bool ContentLibraryWidget::userBundleEnabled() const +{ + // TODO: this method is to be removed after user bundle implementation is complete + return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); +} + QSize ContentLibraryWidget::sizeHint() const { return {420, 420}; @@ -715,6 +698,7 @@ void ContentLibraryWidget::updateSearch() m_effectsModel->setSearchText(m_filterText); m_texturesModel->setSearchText(m_filterText); m_environmentsModel->setSearchText(m_filterText); + m_userModel->setSearchText(m_filterText); m_quickWidget->update(); } @@ -777,7 +761,7 @@ void ContentLibraryWidget::addImage(ContentLibraryTexture *tex) if (!tex->isDownloaded()) return; - emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Image); + emit addTextureRequested(tex->texturePath(), AddTextureMode::Image); } void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex) @@ -785,7 +769,7 @@ void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex) if (!tex->isDownloaded()) return; - emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Texture); + emit addTextureRequested(tex->texturePath(), AddTextureMode::Texture); } void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex) @@ -793,7 +777,7 @@ void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex) if (!tex->isDownloaded()) return; - emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::LightProbe); + emit addTextureRequested(tex->texturePath(), AddTextureMode::LightProbe); } void ContentLibraryWidget::updateSceneEnvState() @@ -821,4 +805,9 @@ QPointer<ContentLibraryEffectsModel> ContentLibraryWidget::effectsModel() const return m_effectsModel; } +QPointer<ContentLibraryUserModel> ContentLibraryWidget::userModel() const +{ + return m_userModel; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index ab71a3dc79..c4d51d0362 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -24,6 +24,7 @@ class ContentLibraryMaterial; class ContentLibraryMaterialsModel; class ContentLibraryTexture; class ContentLibraryTexturesModel; +class ContentLibraryUserModel; class ContentLibraryWidget : public QFrame { @@ -65,6 +66,7 @@ public: QPointer<ContentLibraryTexturesModel> texturesModel() const; QPointer<ContentLibraryTexturesModel> environmentsModel() const; QPointer<ContentLibraryEffectsModel> effectsModel() const; + QPointer<ContentLibraryUserModel> userModel() const; Q_INVOKABLE void startDragEffect(QmlDesigner::ContentLibraryEffect *eff, const QPointF &mousePos); Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos); @@ -74,6 +76,7 @@ public: Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void updateSceneEnvState(); Q_INVOKABLE void markTextureUpdated(const QString &textureKey); + Q_INVOKABLE bool userBundleEnabled() const; QSize sizeHint() const override; @@ -97,21 +100,23 @@ private: void updateSearch(); void setIsDragging(bool val); QString findTextureBundlePath(); - void loadTextureBundle(); - QVariantMap readBundleMetadata(); - bool fetchTextureBundleMetadata(const QDir &bundleDir); + void loadTextureBundles(); + QVariantMap readTextureBundleJson(); + bool fetchTextureBundleJson(const QDir &bundleDir); bool fetchTextureBundleIcons(const QDir &bundleDir); void fetchNewTextureIcons(const QVariantMap &existingFiles, const QVariantMap &newFiles, const QString &existingMetaFilePath, const QDir &bundleDir); std::tuple<QVariantMap, QVariantMap, QVariantMap> compareTextureMetaFiles( const QString &existingMetaFile, const QString downloadedMetaFile); QStringList saveNewTextures(const QDir &bundleDir, const QStringList &newFiles); + void populateTextureBundleModels(); QScopedPointer<StudioQuickWidget> m_quickWidget; QPointer<ContentLibraryMaterialsModel> m_materialsModel; QPointer<ContentLibraryTexturesModel> m_texturesModel; QPointer<ContentLibraryTexturesModel> m_environmentsModel; QPointer<ContentLibraryEffectsModel> m_effectsModel; + QPointer<ContentLibraryUserModel> m_userModel; QShortcut *m_qmlSourceUpdateShortcut = nullptr; @@ -127,12 +132,8 @@ private: bool m_hasQuick3DImport = false; bool m_isDragging = false; bool m_isQt6Project = false; - QString m_baseUrl; - QString m_texturesUrl; - QString m_textureIconsUrl; - QString m_environmentIconsUrl; - QString m_environmentsUrl; - QString m_downloadPath; + QString m_textureBundleUrl; + QString m_bundlePath; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp index b6e99ae972..56056e3fd2 100644 --- a/src/plugins/qmldesigner/components/createtexture.cpp +++ b/src/plugins/qmldesigner/components/createtexture.cpp @@ -17,6 +17,7 @@ #include <coreplugin/messagebox.h> #include <QTimer> +#include <QUrl> namespace QmlDesigner { @@ -94,7 +95,7 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat newTexNode.setIdWithoutRefactoring(m_view->model()->generateNewId(assetPath.baseName())); VariantProperty sourceProp = newTexNode.variantProperty("source"); - sourceProp.setValue(textureSource); + sourceProp.setValue(QUrl(textureSource)); matLib.defaultNodeListProperty().reparentHere(newTexNode); } diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp index e84623e26c..36ce3373e5 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp @@ -77,8 +77,8 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent) connect(m_toolbar, &CurveEditorToolBar::currentFrameChanged, [this, model](int frame) { model->setCurrentFrame(frame); + m_view->setCurrentFrame(frame, false); updateStatusLine(); - m_view->viewport()->update(); }); connect(m_toolbar, &CurveEditorToolBar::zoomChanged, [this](double zoom) { diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp index 72410ffb07..8da87ce119 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp @@ -129,6 +129,8 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent) m_currentSpin->setFrame(false); connect(m_currentSpin, &QSpinBox::valueChanged, this, &CurveEditorToolBar::currentFrameChanged); + connect(model, &CurveEditorModel::commitCurrentFrame, + this, [this](int frame) { m_currentSpin->setValue(frame); }); addSpacer(); diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp index 95a260c26f..a1dfcc8f98 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp @@ -15,6 +15,8 @@ #include "qmlobjectnode.h" #include "variantproperty.h" +#include <model/modelutils.h> + #include <utils3d.h> #include <utils/expected.h> @@ -292,7 +294,7 @@ bool BakeLightsDataModel::reset() if (!hasExposedProps && node.metaInfo().isFileComponent() && node.metaInfo().isQtQuick3DNode()) { - const QString compFile = node.metaInfo().componentFileName(); + const QString compFile = ModelUtils::componentFilePath(node); const QString projPath = m_view->externalDependencies().currentProjectDirPath(); if (compFile.startsWith(projPath)) { // Quick and dirty scan of the component source to check if it potentially has diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp index 76560ac192..f5a74ee864 100644 --- a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp +++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp @@ -66,9 +66,11 @@ void CameraSpeedConfiguration::resetDefaults() void CameraSpeedConfiguration::hideCursor() { - if (QGuiApplication::overrideCursor()) + if (m_cursorHidden) return; + m_cursorHidden = true; + QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); if (QWindow *w = QGuiApplication::focusWindow()) @@ -77,9 +79,11 @@ void CameraSpeedConfiguration::hideCursor() void CameraSpeedConfiguration::restoreCursor() { - if (!QGuiApplication::overrideCursor()) + if (!m_cursorHidden) return; + m_cursorHidden = false; + QGuiApplication::restoreOverrideCursor(); if (QWindow *w = QGuiApplication::focusWindow()) @@ -88,7 +92,7 @@ void CameraSpeedConfiguration::restoreCursor() void CameraSpeedConfiguration::holdCursorInPlace() { - if (!QGuiApplication::overrideCursor()) + if (!m_cursorHidden) return; if (QWindow *w = QGuiApplication::focusWindow()) diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h index 55256890cb..fef06efc49 100644 --- a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h +++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h @@ -71,6 +71,7 @@ private: double m_multiplier = 0.; bool m_changes = false; QPoint m_lastPos; + bool m_cursorHidden = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 911ee1fb15..a20904b2e5 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -279,6 +279,14 @@ void Edit3DView::modelAttached(Model *model) { AbstractView::modelAttached(model); + QString currProjectPath = QmlDesigner::DocumentManager::currentProjectDirPath().toString(); + if (m_currProjectPath != currProjectPath) { + // Opening a new project -> reset camera speeds + m_currProjectPath = currProjectPath; + m_previousCameraSpeed = -1.; + m_previousCameraMultiplier = -1.; + } + syncSnapAuxPropsToSettings(); rootModelNode().setAuxiliaryData(edit3dGridColorProperty, @@ -356,7 +364,12 @@ void Edit3DView::handleEntriesChanged() append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras); append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras); - auto assetsModule = model()->module("Quick3DAssets"); + Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix(); + + auto assetsModule = model()->module(import3dTypePrefix); for (const auto &metaInfo : model()->metaInfosForModule(assetsModule)) append(metaInfo, EK_importedModels); @@ -373,7 +386,8 @@ void Edit3DView::handleEntriesChanged() } else if (entry.typeName() == "QtQuick3D.OrthographicCamera" || entry.typeName() == "QtQuick3D.PerspectiveCamera") { entryKey = EK_cameras; - } else if (entry.typeName().startsWith("Quick3DAssets.") + } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix().toUtf8()) && NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) { entryKey = EK_importedModels; } else { diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 781b26d8d8..fad87aae1f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -192,6 +192,7 @@ private: double m_previousCameraSpeed = -1.; double m_previousCameraMultiplier = -1.; + QString m_currProjectPath; friend class Edit3DAction; }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 07102ae893..6f1cf2e183 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -766,7 +766,11 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) QString fileName = QFileInfo(assetPath).baseName(); fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter auto model = m_view->model(); - auto metaInfo = model->metaInfo(model->module("Quick3DAssets"), fileName.toUtf8()); + Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix(); + auto metaInfo = model->metaInfo(model->module(import3dTypePrefix), fileName.toUtf8()); if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()}; QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); @@ -780,7 +784,9 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) for (const QString &assetPath : added3DAssets) { QString fileName = QFileInfo(assetPath).baseName(); fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter - QString type = QString("Quick3DAssets.%1.%1").arg(fileName); + QString type = QString("%1.%2.%2").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix(), + fileName); QList<ItemLibraryEntry> entriesForType = itemLibInfo->entriesForType(type.toUtf8()); if (!entriesForType.isEmpty()) { // should always be true, but just in case QmlVisualNode::createQml3DNode(view(), diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp index 26c5fe0eb2..8890ea8964 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp @@ -87,9 +87,11 @@ void SnapConfiguration::resetDefaults() void SnapConfiguration::hideCursor() { - if (QGuiApplication::overrideCursor()) + if (m_cursorHidden) return; + m_cursorHidden = true; + QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); if (QWindow *w = QGuiApplication::focusWindow()) @@ -98,9 +100,11 @@ void SnapConfiguration::hideCursor() void SnapConfiguration::restoreCursor() { - if (!QGuiApplication::overrideCursor()) + if (!m_cursorHidden) return; + m_cursorHidden = false; + QGuiApplication::restoreOverrideCursor(); if (QWindow *w = QGuiApplication::focusWindow()) @@ -109,7 +113,7 @@ void SnapConfiguration::restoreCursor() void SnapConfiguration::holdCursorInPlace() { - if (!QGuiApplication::overrideCursor()) + if (!m_cursorHidden) return; if (QWindow *w = QGuiApplication::focusWindow()) diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h index 729e6ce7d1..ae401f60f9 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h @@ -103,6 +103,7 @@ private: double m_scaleInterval = 0.; bool m_changes = false; QPoint m_lastPos; + bool m_cursorHidden = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index c0bebbb82b..804ac076e6 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -64,13 +64,14 @@ namespace QmlDesigner { DesignDocument acts as a facade to a model representing a qml document, and the different views/widgets accessing it. */ -DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependencies, +DesignDocument::DesignDocument([[maybe_unused]] const QUrl &filePath, + ProjectStorageDependencies projectStorageDependencies, ExternalDependenciesInterface &externalDependencies) #ifdef QDS_USE_PROJECTSTORAGE : m_documentModel(Model::create(projectStorageDependencies, "Item", {Import::createLibraryImport("QtQuick")}, - {}, + filePath, std::make_unique<ModelResourceManagement>())) #else : m_documentModel( diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 0d75141205..52089d67c1 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -41,7 +41,8 @@ class QMLDESIGNERCOMPONENTS_EXPORT DesignDocument : public QObject Q_OBJECT public: - DesignDocument(ProjectStorageDependencies projectStorageDependencies, + DesignDocument(const QUrl &filePath, + ProjectStorageDependencies projectStorageDependencies, ExternalDependenciesInterface &externalDependencies); ~DesignDocument() override; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 29fff4b359..2c69072602 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -12,6 +12,7 @@ #include <variantproperty.h> #include <theme.h> +#include <utils/filepath.h> #include <utils/outputformatter.h> #include <projectexplorer/project.h> @@ -130,43 +131,8 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( if (targetDir.isEmpty()) targetDir = defaulTargetDirectory; - // Import is always done under known folder. The order of preference for folder is: - // 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path - // 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path - // 3) New QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path - // 4) New QUICK_3D_ASSETS_FOLDER under any project import path - // 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project - const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER); - const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER); - QString candidatePath = targetDir + defaultAssetFolder + quick3DFolder; - int candidatePriority = 5; - - for (const auto &importPath : std::as_const(importPaths)) { - if (importPath.startsWith(targetDir)) { - const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder); - const QString assetFolder = importPath + quick3DFolder; - const bool exists = QFileInfo::exists(assetFolder); - if (exists) { - if (isDefaultFolder) { - // Priority one location, stop looking - candidatePath = assetFolder; - break; - } else if (candidatePriority > 2) { - candidatePriority = 2; - candidatePath = assetFolder; - } - } else { - if (candidatePriority > 3 && isDefaultFolder) { - candidatePriority = 3; - candidatePath = assetFolder; - } else if (candidatePriority > 4) { - candidatePriority = 4; - candidatePath = assetFolder; - } - } - } - } - m_quick3DImportPath = candidatePath; + m_quick3DImportPath = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dBasePath().toString(); if (!m_quick3DFiles.isEmpty()) { QVector<QJsonObject> groups; @@ -294,11 +260,14 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, QFileInfo compFileInfo{compFileName}; // Find to top asset folder - const QString assetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER).mid(1); + const QString oldAssetFolder = QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER); + QString assetFolder = QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER); const QStringList parts = compFileName.split('/'); int i = parts.size() - 1; int previousSize = 0; for (; i >= 0; --i) { + if (parts[i] == oldAssetFolder) + assetFolder = oldAssetFolder; if (parts[i] == assetFolder) break; previousSize = parts[i].size(); @@ -733,8 +702,10 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( // and move the remaining member to ungrouped options // Note: <= 2 instead of < 2 because each group has group label member if (i != 0 && groupWidgets.size() <= 2) { - widgets[0].prepend(groupWidgets[1]); - groupWidgets[0].first->hide(); // hide group label + if (groupWidgets.size() == 2) + widgets[0].prepend(groupWidgets[1]); + if (groupWidgets.size() >= 1) + groupWidgets[0].first->hide(); // hide group label groupWidgets.clear(); } } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 48958ceec9..ed1f8041e9 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "itemlibraryassetimporter.h" + #include "assetimportupdatedialog.h" #include "qmldesignerplugin.h" #include "qmldesignerconstants.h" @@ -329,12 +330,15 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) if (qmldirFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QString qmlInfo; qmlInfo.append("module "); - qmlInfo.append(m_importPath.split('/').last()); + qmlInfo.append(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix()); qmlInfo.append("."); qmlInfo.append(pd.assetName); qmlInfo.append('\n'); m_requiredImports.append( - QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), pd.assetName)); + QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix(), + pd.assetName)); while (qmlIt.hasNext()) { qmlIt.next(); QFileInfo fi = QFileInfo(qmlIt.filePath()); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index dbeacc7595..3bff210520 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -296,15 +296,16 @@ void ItemLibraryModel::setSearchText(const QString &searchText) Import ItemLibraryModel::entryToImport(const ItemLibraryEntry &entry) { +#ifndef QDS_USE_PROJECTSTORAGE if (entry.majorVersion() == -1 && entry.minorVersion() == -1) return Import::createFileImport(entry.requiredImport()); - +#endif return Import::createLibraryImport(entry.requiredImport(), QString::number(entry.majorVersion()) + QLatin1Char('.') + QString::number(entry.minorVersion())); } -void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, Model *model) +void ItemLibraryModel::update(Model *model) { if (!model) return; @@ -312,9 +313,12 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, beginResetModel(); clearSections(); + GeneratedComponentUtils compUtils = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils(); + QStringList excludedImports { - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".MaterialBundle", - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".EffectBundle" + compUtils.componentBundlesTypePrefix() + ".MaterialBundle", + compUtils.componentBundlesTypePrefix() + ".EffectBundle" }; // create import sections @@ -323,10 +327,12 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, QHash<QString, ItemLibraryImport *> importHash; for (const Import &import : model->imports()) { if (import.url() != projectName) { - if (excludedImports.contains(import.url()) || import.url().startsWith("Effects.")) + if (excludedImports.contains(import.url()) + || import.url().startsWith(compUtils.composedEffectsTypePrefix())) { continue; + } bool addNew = true; - bool isQuick3DAsset = import.url().startsWith("Quick3DAssets."); + bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix()); QString importUrl = import.url(); if (isQuick3DAsset) importUrl = ItemLibraryImport::quick3DAssetsTitle(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h index 212ddf8e04..b04ea492d2 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h @@ -37,7 +37,7 @@ public: QString searchText() const; ItemLibraryImport *importByUrl(const QString &importName) const; - void update(ItemLibraryInfo *itemLibraryInfo, Model *model); + void update(Model *model); void updateUsedImports(const Imports &usedImports); QMimeData *getMimeData(const ItemLibraryEntry &itemLibraryEntry); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index b710a8226f..ffefc43178 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -118,9 +118,9 @@ void ItemLibraryWidget::resizeEvent(QResizeEvent *event) ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) : m_itemIconSize(24, 24) - , m_itemLibraryModel(new ItemLibraryModel(this)) - , m_addModuleModel(new ItemLibraryAddImportModel(this)) - , m_itemsWidget(new StudioQuickWidget(this)) + , m_itemLibraryModel(std::make_unique<ItemLibraryModel>()) + , m_addModuleModel(std::make_unique<ItemLibraryAddImportModel>()) + , m_itemsWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>()) , m_imageCache{imageCache} { m_compressionTimer.setInterval(1000); @@ -146,7 +146,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_itemsWidget.data()); + layout->addWidget(m_itemsWidget.get()); updateSearch(); @@ -167,8 +167,8 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) auto map = m_itemsWidget->registerPropertyMap("ItemLibraryBackend"); - map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.data())}, - {"addModuleModel", QVariant::fromValue(m_addModuleModel.data())}, + map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.get())}, + {"addModuleModel", QVariant::fromValue(m_addModuleModel.get())}, {"itemLibraryIconWidth", m_itemIconSize.width()}, {"itemLibraryIconHeight", m_itemIconSize.height()}, {"rootView", QVariant::fromValue(this)}, @@ -333,9 +333,7 @@ void ItemLibraryWidget::updateModel() m_compressionTimer.stop(); } -#ifndef QDS_USE_PROJECTSTORAGE - m_itemLibraryModel->update(m_itemLibraryInfo.data(), m_model.data()); -#endif + m_itemLibraryModel->update(m_model.data()); if (m_itemLibraryModel->rowCount() == 0 && !m_updateRetry) { m_updateRetry = true; // Only retry once to avoid endless loops diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index b56532b218..2940db7a73 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -10,9 +10,10 @@ #include <studioquickwidget.h> -#include <utils/fancylineedit.h> -#include <utils/dropsupport.h> #include <previewtooltip/previewtooltipbackend.h> +#include <utils/dropsupport.h> +#include <utils/fancylineedit.h> +#include <utils/uniqueobjectptr.h> #include <QFileIconProvider> #include <QFrame> @@ -104,10 +105,10 @@ private: #ifndef QDS_USE_PROJECTSTORAGE QPointer<ItemLibraryInfo> m_itemLibraryInfo; #endif - QPointer<ItemLibraryModel> m_itemLibraryModel; - QPointer<ItemLibraryAddImportModel> m_addModuleModel; + std::unique_ptr<ItemLibraryModel> m_itemLibraryModel; + std::unique_ptr<ItemLibraryAddImportModel> m_addModuleModel; - QScopedPointer<StudioQuickWidget> m_itemsWidget; + Utils::UniqueObjectPtr<StudioQuickWidget> m_itemsWidget; std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp index ec95f3e5d3..918956a04c 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp @@ -63,7 +63,7 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role) return tr("Texture has no source image."); ModelNode texNode = m_textureList.at(index.row()); - QString info = ImageUtils::imageInfo(source); + QString info = ImageUtils::imageInfoString(source); if (info.isEmpty()) return tr("Texture has no data."); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 47a1e8d293..7cf0a875bc 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -56,6 +56,13 @@ public: m_pixmaps.insert(node.internalId(), pixmap); } + QPixmap getPixmap(const ModelNode &node) + { + QTC_ASSERT(node, return {}); + + return m_pixmaps.value(node.internalId()); + } + void clearPixmapCache() { m_pixmaps.clear(); @@ -357,6 +364,13 @@ void MaterialBrowserWidget::focusMaterialSection(bool focusMatSec) } } +void MaterialBrowserWidget::addMaterialToContentLibrary() +{ + ModelNode mat = m_materialBrowserModel->selectedMaterial(); + m_materialBrowserView->emitCustomNotification("add_material_to_content_lib", {mat}, + {m_previewImageProvider->getPixmap(mat)}); +} + QString MaterialBrowserWidget::qmlSourcesPath() { #ifdef SHARE_QML_PATH @@ -420,4 +434,10 @@ QPointer<MaterialBrowserTexturesModel> MaterialBrowserWidget::materialBrowserTex return m_materialBrowserTexturesModel; } +bool MaterialBrowserWidget::userBundleEnabled() const +{ + // TODO: this method is to be removed after user bundle implementation is complete + return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index bfe7ace34d..97c994e565 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -60,6 +60,8 @@ public: Q_INVOKABLE void acceptAssetsDropOnMaterial(int matIndex, const QList<QUrl> &urls); Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId); Q_INVOKABLE void focusMaterialSection(bool focusMatSec); + Q_INVOKABLE void addMaterialToContentLibrary(); + Q_INVOKABLE bool userBundleEnabled() const; StudioQuickWidget *quickWidget() const; diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 58b4c42749..a3ab5f2cd7 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -5,6 +5,8 @@ #include "nodemetainfo.h" #include "ui_choosefrompropertylistdialog.h" +#include <qmldesignerplugin.h> + namespace QmlDesigner { // This will filter and return possible properties that the given type can be bound to @@ -100,7 +102,10 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i #ifdef QDS_USE_PROJECTSTORAGE // TODO add the types here or use the module #else - } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) { + } else if (insertInfo.typeName().startsWith( + QString("%1.MaterialBundle").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix()) + .toUtf8())) { if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); #endif diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index aacaf6dc0d..223e04fd96 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -9,9 +9,8 @@ #include "navigatortreeview.h" #include "navigatorwidget.h" #include "choosefrompropertylistdialog.h" -#include "qproxystyle.h" - #include <model/modelutils.h> +#include <dialogutils.h> #include <modelnodecontextmenu.h> #include <theme.h> #include <qmldesignerconstants.h> @@ -169,8 +168,7 @@ static void setId(const QModelIndex &index, const QString &newId) return; if (!ModelNode::isValidId(newId)) { - Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"), - NavigatorTreeView::tr("%1 is an invalid id.").arg(newId)); + DialogUtils::showWarningForInvalidId(newId); } else if (modelNode.view()->hasId(newId)) { Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"), NavigatorTreeView::tr("%1 already exists.").arg(newId)); diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp index 45f89ae339..fdd58c77a3 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp @@ -143,7 +143,7 @@ QString DynamicPropertiesProxyModel::newPropertyName() const { DynamicPropertiesModel *propsModel = dynamicPropertiesModel(); - return QString::fromUtf8(uniquePropertyName("property", propsModel->singleSelectedNode())); + return QString::fromUtf8(uniquePropertyName("newName", propsModel->singleSelectedNode())); } void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type) @@ -167,6 +167,10 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr QVariant value = defaultValueForType(typeName); VariantProperty variantProp = modelNode.variantProperty(name.toUtf8()); variantProp.setDynamicTypeNameAndValue(typeName, value); + } else if (type == "signal") { + SignalDeclarationProperty signalDeclarationProperty + = modelNode.signalDeclarationProperty(name.toUtf8()); + signalDeclarationProperty.setSignature("()"); } else { QString expression = defaultExpressionForType(typeName); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 7f1ab00bb9..6f3242a18e 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -284,6 +284,27 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml } } +void PropertyEditorQmlBackend::handleInstancePropertyChangedInModelNodeProxy( + const ModelNode &modelNode, const PropertyName &propertyName) +{ + m_backendModelNode.handleInstancePropertyChanged(modelNode, propertyName); +} + +void PropertyEditorQmlBackend::handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property) +{ + m_backendModelNode.handleVariantPropertyChanged(property); +} + +void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property) +{ + m_backendModelNode.handleBindingPropertyChanged(property); +} + +void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property) +{ + m_backendModelNode.handlePropertiesRemoved(property); +} + void PropertyEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value, diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index b677258488..d4e158fca4 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -3,10 +3,10 @@ #pragma once -#include "qmlanchorbindingproxy.h" #include "designerpropertymap.h" -#include "propertyeditorvalue.h" #include "propertyeditorcontextobject.h" +#include "propertyeditorvalue.h" +#include "qmlanchorbindingproxy.h" #include "qmlmodelnodeproxy.h" #include "quick2propertyeditorview.h" @@ -71,7 +71,15 @@ public: PropertyEditorView *propertyEditor); void setupInsightAttachedProperties(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor); - void setupAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor); + void setupAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, + PropertyEditorView *propertyEditor); + + void handleInstancePropertyChangedInModelNodeProxy(const ModelNode &modelNode, + const PropertyName &propertyName); + + void handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property); + void handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property); + void handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property); static NodeMetaInfo findCommonAncestor(const ModelNode &node); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 663ebafb65..70686f31ae 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -10,9 +10,12 @@ #include "designmodewidget.h" #include "nodemetainfo.h" #include "nodeproperty.h" +#include "propertyeditorview.h" +#include "qmldesignerplugin.h" #include "qmlitemnode.h" #include "qmlobjectnode.h" -#include "qmldesignerplugin.h" +#include "rewritertransaction.h" +#include "rewritingexception.h" #include <enumeration.h> @@ -153,7 +156,8 @@ void PropertyEditorValue::setExpressionWithEmit(const QString &expression) if (m_expression != expression) { setExpression(expression); m_value.clear(); - emit expressionChanged(nameAsQString()); // Note that we set the name in this case + emit expressionChanged(nameAsQString()); + emit expressionChangedQml();// Note that we set the name in this case } } @@ -180,7 +184,7 @@ bool PropertyEditorValue::isInSubState() const bool PropertyEditorValue::isBound() const { const QmlObjectNode objectNode(modelNode()); - return objectNode.isValid() && objectNode.hasBindingProperty(name()); + return m_forceBound || (objectNode.isValid() && objectNode.hasBindingProperty(name())); } bool PropertyEditorValue::isInModel() const @@ -334,6 +338,7 @@ void PropertyEditorValue::resetValue() m_expression = QString(); emit valueChanged(nameAsQString(), QVariant()); emit expressionChanged({}); + emit expressionChangedQml(); } } @@ -425,6 +430,34 @@ QStringList PropertyEditorValue::getExpressionAsList() const return generateStringList(expression()); } +QVector<double> PropertyEditorValue::getExpressionAsVector() const +{ + const QRegularExpression rx( + QRegularExpression::anchoredPattern("Qt.vector(2|3|4)d\\((.*?)\\)")); + const QRegularExpressionMatch match = rx.match(expression()); + if (!match.hasMatch()) + return {}; + + const QStringList floats = match.captured(2).split(',', Qt::SkipEmptyParts); + + bool ok; + + const int num = match.captured(1).toInt(); + + if (num != floats.count()) + return {}; + + QVector<double> ret; + for (const QString &number : floats) { + ret.append(number.toDouble(&ok)); + + if (!ok) + return {}; + } + + return ret; +} + bool PropertyEditorValue::idListAdd(const QString &value) { const QmlObjectNode objectNode(modelNode()); @@ -507,6 +540,15 @@ void PropertyEditorValue::openMaterialEditor(int idx) m_modelNode.view()->emitCustomNotification("select_material", {}, {idx}); } +void PropertyEditorValue::setForceBound(bool b) +{ + if (m_forceBound == b) + return; + m_forceBound = b; + + emit isBoundChanged(); +} + QStringList PropertyEditorValue::generateStringList(const QString &string) const { QString copy = string; @@ -687,4 +729,260 @@ void PropertyEditorNodeWrapper::update() emit typeChanged(); } +QQmlPropertyMap *PropertyEditorSubSelectionWrapper::properties() +{ + return &m_valuesPropertyMap; +} + +static QObject *variantToQObject(const QVariant &value) +{ + if (value.typeId() == QMetaType::QObjectStar || value.typeId() > QMetaType::User) + return *(QObject **)value.constData(); + + return nullptr; +} + +void PropertyEditorSubSelectionWrapper::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, + const PropertyName &name, + const QVariant &value) +{ + PropertyName propertyName(name); + propertyName.replace('.', '_'); + auto valueObject = qobject_cast<PropertyEditorValue*>(variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(propertyName)))); + if (!valueObject) { + valueObject = new PropertyEditorValue(&m_valuesPropertyMap); + QObject::connect(valueObject, &PropertyEditorValue::valueChanged, this, &PropertyEditorSubSelectionWrapper::changeValue); + QObject::connect(valueObject, &PropertyEditorValue::expressionChanged, this, &PropertyEditorSubSelectionWrapper::changeExpression); + QObject::connect(valueObject, &PropertyEditorValue::exportPropertyAsAliasRequested, this, &PropertyEditorSubSelectionWrapper::exportPropertyAsAlias); + QObject::connect(valueObject, &PropertyEditorValue::removeAliasExportRequested, this, &PropertyEditorSubSelectionWrapper::removeAliasExport); + m_valuesPropertyMap.insert(QString::fromUtf8(propertyName), QVariant::fromValue(valueObject)); + } + valueObject->setName(name); + valueObject->setModelNode(qmlObjectNode); + + if (qmlObjectNode.propertyAffectedByCurrentState(name) && !(qmlObjectNode.modelNode().property(name).isBindingProperty())) + valueObject->setValue(qmlObjectNode.modelValue(name)); + + else + valueObject->setValue(value); + + if (propertyName != "id" && + qmlObjectNode.currentState().isBaseState() && + qmlObjectNode.modelNode().property(propertyName).isBindingProperty()) { + valueObject->setExpression(qmlObjectNode.modelNode().bindingProperty(propertyName).expression()); + } else { + if (qmlObjectNode.hasBindingProperty(name)) + valueObject->setExpression(qmlObjectNode.expression(name)); + else + valueObject->setExpression(qmlObjectNode.instanceValue(name).toString()); + } +} + +void PropertyEditorSubSelectionWrapper::exportPropertyAsAlias(const QString &name) +{ + if (name.isNull()) + return; + + if (locked()) + return; + + QTC_ASSERT(m_modelNode.isValid(), return); + + view()->executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){ + PropertyEditorView::generateAliasForProperty(m_modelNode, name); + }); +} + +void PropertyEditorSubSelectionWrapper::removeAliasExport(const QString &name) +{ + if (name.isNull()) + return; + + if (locked()) + return; + + QTC_ASSERT(m_modelNode.isValid(), return ); + + view()->executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name]() { + PropertyEditorView::removeAliasForProperty(m_modelNode, name); + }); +} + +bool PropertyEditorSubSelectionWrapper::locked() const +{ + return m_locked; +} + +PropertyEditorSubSelectionWrapper::PropertyEditorSubSelectionWrapper(const ModelNode &modelNode) + : m_modelNode(modelNode) +{ + QmlObjectNode qmlObjectNode(modelNode); + + QTC_ASSERT(qmlObjectNode.isValid(), return ); + + for (const auto &property : qmlObjectNode.modelNode().metaInfo().properties()) { + auto propertyName = property.name(); + createPropertyEditorValue(qmlObjectNode, + propertyName, + qmlObjectNode.instanceValue(propertyName)); + } +} + +ModelNode PropertyEditorSubSelectionWrapper::modelNode() const +{ + return m_modelNode; +} + +void PropertyEditorSubSelectionWrapper::deleteModelNode() +{ + QmlObjectNode objectNode(m_modelNode); + + view()->executeInTransaction("PropertyEditorView::changeExpression", [&] { + if (objectNode.isValid()) + objectNode.destroy(); + }); +} + +void PropertyEditorSubSelectionWrapper::changeValue(const QString &name) +{ + QTC_ASSERT(m_modelNode.isValid(), return ); + + if (name.isNull()) + return; + + if (locked()) + return; + + const QScopeGuard cleanup([&] { m_locked = false; }); + m_locked = true; + + const NodeMetaInfo metaInfo = m_modelNode.metaInfo(); + QVariant castedValue; + PropertyEditorValue *value = qobject_cast<PropertyEditorValue *>( + variantToQObject(m_valuesPropertyMap.value(name))); + + if (auto property = metaInfo.property(name.toUtf8())) { + castedValue = property.castedValue(value->value()); + + if (castedValue.typeId() == QVariant::Color) { + QColor color = castedValue.value<QColor>(); + QColor newColor = QColor(color.name()); + newColor.setAlpha(color.alpha()); + castedValue = QVariant(newColor); + } + + if (!value->value().isValid()) { // reset + removePropertyFromModel(name.toUtf8()); + } else { + if (castedValue.isValid()) + commitVariantValueToModel(name.toUtf8(), castedValue); + } + } +} + +void PropertyEditorSubSelectionWrapper::setValueFromModel(const PropertyName &name, + const QVariant &value) +{ + m_locked = true; + + QmlObjectNode qmlObjectNode(m_modelNode); + + PropertyName propertyName = name; + propertyName.replace('.', '_'); + auto propertyValue = qobject_cast<PropertyEditorValue *>( + variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(propertyName)))); + if (propertyValue) + propertyValue->setValue(value); + m_locked = false; +} + +void PropertyEditorSubSelectionWrapper::resetValue(const PropertyName &name) +{ + auto propertyValue = qobject_cast<PropertyEditorValue *>( + variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(name)))); + if (propertyValue) + propertyValue->resetValue(); +} + +bool PropertyEditorSubSelectionWrapper::isRelevantModelNode(const ModelNode &modelNode) const +{ + QmlObjectNode objectNode(m_modelNode); + return modelNode == m_modelNode || objectNode.propertyChangeForCurrentState() == modelNode; +} + +void PropertyEditorSubSelectionWrapper::changeExpression(const QString &propertyName) +{ + PropertyName name = propertyName.toUtf8(); + + QTC_ASSERT(m_modelNode.isValid(), return ); + + if (name.isNull()) + return; + + if (locked()) + return; + + const QScopeGuard cleanup([&] { m_locked = false; }); + m_locked = true; + + view()->executeInTransaction("PropertyEditorView::changeExpression", [this, name, propertyName] { + QmlObjectNode qmlObjectNode{m_modelNode}; + PropertyEditorValue *value = qobject_cast<PropertyEditorValue *>( + variantToQObject(m_valuesPropertyMap.value(propertyName))); + + if (!value) { + qWarning() << "PropertyEditor::changeExpression no value for " << propertyName; + return; + } + + if (value->expression().isEmpty()) { + value->resetValue(); + return; + } + PropertyEditorView::setExpressionOnObjectNode(qmlObjectNode, name, value->expression()); + }); /* end of transaction */ +} + +void PropertyEditorSubSelectionWrapper::removePropertyFromModel(const PropertyName &propertyName) +{ + QTC_ASSERT(m_modelNode.isValid(), return ); + + m_locked = true; + try { + RewriterTransaction transaction = view()->beginRewriterTransaction( + "PropertyEditorView::removePropertyFromModel"); + + QmlObjectNode(m_modelNode).removeProperty(propertyName); + + transaction.commit(); + } catch (const RewritingException &e) { + e.showException(); + } + m_locked = false; +} + +void PropertyEditorSubSelectionWrapper::commitVariantValueToModel(const PropertyName &propertyName, + const QVariant &value) +{ + QTC_ASSERT(m_modelNode.isValid(), return ); + + try { + RewriterTransaction transaction = view()->beginRewriterTransaction( + "PropertyEditorView::commitVariantValueToMode"); + + QmlObjectNode(m_modelNode).setVariantProperty(propertyName, value); + + transaction.commit(); + } catch (const RewritingException &e) { + e.showException(); + } +} + +AbstractView *PropertyEditorSubSelectionWrapper::view() const +{ + QTC_CHECK(m_modelNode.isValid()); + + return m_modelNode.view(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 59236c4fe2..70a51fffc2 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -14,6 +14,43 @@ namespace QmlDesigner { class PropertyEditorValue; +class PropertyEditorSubSelectionWrapper : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QQmlPropertyMap *properties READ properties NOTIFY propertiesChanged) + +signals: + void propertiesChanged(); + +public: + QQmlPropertyMap *properties(); + PropertyEditorSubSelectionWrapper(const ModelNode &modelNode); + ModelNode modelNode() const; + + Q_INVOKABLE void deleteModelNode(); + + void setValueFromModel(const PropertyName &name, const QVariant &value); + void resetValue(const PropertyName &name); + + bool isRelevantModelNode(const ModelNode &modelNode) const; + +private: + void changeValue(const QString &name); + void changeExpression(const QString &propertyName); + void createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value); + void exportPropertyAsAlias(const QString &name); + void removeAliasExport(const QString &name); + bool locked() const; + + ModelNode m_modelNode; + QQmlPropertyMap m_valuesPropertyMap; + bool m_locked = false; + void removePropertyFromModel(const PropertyName &propertyName); + void commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value); + AbstractView *view() const; +}; + class PropertyEditorNodeWrapper : public QObject { Q_OBJECT @@ -126,12 +163,15 @@ public: bool isIdList() const; Q_INVOKABLE QStringList getExpressionAsList() const; + Q_INVOKABLE QVector<double> getExpressionAsVector() const; Q_INVOKABLE bool idListAdd(const QString &value); Q_INVOKABLE bool idListRemove(int idx); Q_INVOKABLE bool idListReplace(int idx, const QString &value); Q_INVOKABLE void commitDrop(const QString &dropData); Q_INVOKABLE void openMaterialEditor(int idx); + Q_INVOKABLE void setForceBound(bool b); + public slots: void resetValue(); void setEnumeration(const QString &scope, const QString &name); @@ -143,6 +183,8 @@ signals: void expressionChanged(const QString &name); // HACK - We use the same notifer for the backend and frontend. // If name is empty the signal is used for QML. + void expressionChangedQml(); + void exportPropertyAsAliasRequested(const QString &name); void removeAliasExportRequested(const QString &name); @@ -168,6 +210,7 @@ private: bool m_hasActiveDrag = false; bool m_isValid = false; // if the property value belongs to a non-existing complexProperty it is invalid PropertyEditorNodeWrapper *m_complexNode; + bool m_forceBound = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 1ff098f4ea..b22b39e238 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -256,66 +256,19 @@ void PropertyEditorView::changeExpression(const QString &propertyName) underscoreName.replace('.', '_'); QmlObjectNode qmlObjectNode{m_selectedNode}; - PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(QString::fromLatin1(underscoreName)); + PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName( + QString::fromUtf8(underscoreName)); if (!value) { qWarning() << "PropertyEditor::changeExpression no value for " << underscoreName; return; } - if (auto property = qmlObjectNode.modelNode().metaInfo().property(name)) { - const auto &propertType = property.propertyType(); - if (propertType.isColor()) { - if (QColor(value->expression().remove('"')).isValid()) { - qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"'))); - return; - } - } else if (propertType.isBool()) { - if (isTrueFalseLiteral(value->expression())) { - if (value->expression().compare("true", Qt::CaseInsensitive) == 0) - qmlObjectNode.setVariantProperty(name, true); - else - qmlObjectNode.setVariantProperty(name, false); - return; - } - } else if (propertType.isInteger()) { - bool ok; - int intValue = value->expression().toInt(&ok); - if (ok) { - qmlObjectNode.setVariantProperty(name, intValue); - return; - } - } else if (propertType.isFloat()) { - bool ok; - qreal realValue = value->expression().toDouble(&ok); - if (ok) { - qmlObjectNode.setVariantProperty(name, realValue); - return; - } - } else if (propertType.isVariant()) { - bool ok; - qreal realValue = value->expression().toDouble(&ok); - if (ok) { - qmlObjectNode.setVariantProperty(name, realValue); - return; - } else if (isTrueFalseLiteral(value->expression())) { - if (value->expression().compare("true", Qt::CaseInsensitive) == 0) - qmlObjectNode.setVariantProperty(name, true); - else - qmlObjectNode.setVariantProperty(name, false); - return; - } - } - } - if (value->expression().isEmpty()) { value->resetValue(); return; } - - if (qmlObjectNode.expression(name) != value->expression() - || !qmlObjectNode.propertyAffectedByCurrentState(name)) - qmlObjectNode.setBindingProperty(name, value->expression()); + setExpressionOnObjectNode(qmlObjectNode, name, value->expression()); }); /* end of transaction */ } @@ -330,21 +283,8 @@ void PropertyEditorView::exportPropertyAsAlias(const QString &name) if (noValidSelection()) return; - executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){ - const QString id = m_selectedNode.validId(); - QString upperCasePropertyName = name; - upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper()); - QString aliasName = id + upperCasePropertyName; - aliasName.replace(".", ""); //remove all dots - - PropertyName propertyName = aliasName.toUtf8(); - if (rootModelNode().hasProperty(propertyName)) { - Core::AsynchronousMessageBox::warning(tr("Cannot Export Property as Alias"), - tr("Property %1 does already exist for root component.").arg(aliasName)); - return; - } - rootModelNode().bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name); - }); + executeInTransaction("PropertyEditorView::exportPropertyAsAlias", + [this, name]() { generateAliasForProperty(m_selectedNode, name); }); } void PropertyEditorView::removeAliasExport(const QString &name) @@ -358,15 +298,8 @@ void PropertyEditorView::removeAliasExport(const QString &name) if (noValidSelection()) return; - executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){ - const QString id = m_selectedNode.validId(); - - for (const BindingProperty &property : rootModelNode().bindingProperties()) - if (property.expression() == (id + "." + name)) { - rootModelNode().removeProperty(property.name()); - break; - } - }); + executeInTransaction("PropertyEditorView::exportPropertyAsAlias", + [this, name]() { removeAliasForProperty(m_selectedNode, name); }); } bool PropertyEditorView::locked() const @@ -384,11 +317,113 @@ void PropertyEditorView::refreshMetaInfos(const TypeIds &deletedTypeIds) m_propertyComponentGenerator.refreshMetaInfos(deletedTypeIds); } +void PropertyEditorView::setExpressionOnObjectNode(const QmlObjectNode &constObjectNode, + const PropertyName &name, + const QString &newExpression) +{ + auto qmlObjectNode = constObjectNode; + auto expression = newExpression; + if (auto property = qmlObjectNode.modelNode().metaInfo().property(name)) { + const auto &propertType = property.propertyType(); + if (propertType.isColor()) { + if (QColor(expression.remove('"')).isValid()) { + qmlObjectNode.setVariantProperty(name, QColor(expression.remove('"'))); + return; + } + } else if (propertType.isBool()) { + if (isTrueFalseLiteral(expression)) { + if (expression.compare("true", Qt::CaseInsensitive) == 0) + qmlObjectNode.setVariantProperty(name, true); + else + qmlObjectNode.setVariantProperty(name, false); + return; + } + } else if (propertType.isInteger()) { + bool ok; + int intValue = expression.toInt(&ok); + if (ok) { + qmlObjectNode.setVariantProperty(name, intValue); + return; + } + } else if (propertType.isFloat()) { + bool ok; + qreal realValue = expression.toDouble(&ok); + if (ok) { + qmlObjectNode.setVariantProperty(name, realValue); + return; + } + } else if (propertType.isVariant()) { + bool ok; + qreal realValue = expression.toDouble(&ok); + if (ok) { + qmlObjectNode.setVariantProperty(name, realValue); + return; + } else if (isTrueFalseLiteral(expression)) { + if (expression.compare("true", Qt::CaseInsensitive) == 0) + qmlObjectNode.setVariantProperty(name, true); + else + qmlObjectNode.setVariantProperty(name, false); + return; + } + } + } + + if (qmlObjectNode.expression(name) != expression + || !qmlObjectNode.propertyAffectedByCurrentState(name)) + qmlObjectNode.setBindingProperty(name, expression); +} + +void PropertyEditorView::generateAliasForProperty(const ModelNode &modelNode, const QString &name) +{ + QTC_ASSERT(modelNode.isValid(), return ); + + auto view = modelNode.view(); + + auto rootNode = view->rootModelNode(); + + auto nonConstModelNode = modelNode; + const QString id = nonConstModelNode.validId(); + + QString upperCasePropertyName = name; + upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper()); + QString aliasName = id + upperCasePropertyName; + aliasName.replace(".", ""); //remove all dots + + PropertyName propertyName = aliasName.toUtf8(); + if (rootNode.hasProperty(propertyName)) { + Core::AsynchronousMessageBox::warning( + tr("Cannot Export Property as Alias"), + tr("Property %1 does already exist for root component.").arg(aliasName)); + return; + } + rootNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name); +} + +void PropertyEditorView::removeAliasForProperty(const ModelNode &modelNode, const QString &propertyName) +{ + QTC_ASSERT(modelNode.isValid(), return ); + + auto view = modelNode.view(); + + auto rootNode = view->rootModelNode(); + + auto nonConstModelNode = modelNode; + + const QString id = nonConstModelNode.validId(); + + for (const BindingProperty &property : rootNode.bindingProperties()) { + if (property.expression() == (id + "." + propertyName)) { + rootNode.removeProperty(property.name()); + break; + } + } +} + void PropertyEditorView::updateSize() { if (!m_qmlBackEndForCurrentType) return; - auto frame = m_qmlBackEndForCurrentType->widget()->findChild<QWidget*>("propertyEditorFrame"); + auto frame = m_qmlBackEndForCurrentType->widget()->findChild<QWidget *>("propertyEditorFrame"); if (frame) frame->resize(m_stackedWidget->size()); } @@ -747,7 +782,11 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &proper if (noValidSelection()) return; + QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + for (const AbstractProperty &property : propertyList) { + m_qmlBackEndForCurrentType->handlePropertiesRemovedInModelNodeProxy(property); + ModelNode node(property.parentModelNode()); if (node.isRootNode() && !m_selectedNode.isRootNode()) @@ -805,7 +844,11 @@ void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>& if (noValidSelection()) return; + QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + for (const VariantProperty &property : propertyList) { + m_qmlBackEndForCurrentType->handleVariantPropertyChangedInModelNodeProxy(property); + ModelNode node(property.parentModelNode()); if (propertyIsAttachedLayoutProperty(property.name())) @@ -830,7 +873,11 @@ void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty> & if (locked() || noValidSelection()) return; + QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + for (const BindingProperty &property : propertyList) { + m_qmlBackEndForCurrentType->handleBindingPropertyChangedInModelNodeProxy(property); + ModelNode node(property.parentModelNode()); if (property.isAliasExport()) @@ -952,6 +999,9 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr { if (!m_selectedNode.isValid()) return; + + QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + m_locked = true; using ModelNodePropertyPair = QPair<ModelNode, PropertyName>; @@ -960,7 +1010,11 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr const QmlObjectNode qmlObjectNode(modelNode); const PropertyName propertyName = propertyPair.second; - if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode && qmlObjectNode.currentState().isValid()) { + m_qmlBackEndForCurrentType->handleInstancePropertyChangedInModelNodeProxy(modelNode, + propertyName); + + if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode + && qmlObjectNode.currentState().isValid()) { const AbstractProperty property = modelNode.property(propertyName); if (modelNode == m_selectedNode || qmlObjectNode.propertyChangeForCurrentState() == qmlObjectNode) { if ( !modelNode.hasProperty(propertyName) || modelNode.property(property.name()).isBindingProperty() ) @@ -969,7 +1023,6 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name())); } } - } m_locked = false; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h index cc9b522839..ef2b71f558 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h @@ -84,6 +84,16 @@ public: void refreshMetaInfos(const TypeIds &deletedTypeIds) override; + static void setExpressionOnObjectNode(const QmlObjectNode &objectNode, + const PropertyName &name, + const QString &expression); + + static void generateAliasForProperty(const ModelNode &modelNode, + const QString &propertyName); + + static void removeAliasForProperty(const ModelNode &modelNode, + const QString &propertyName); + protected: void timerEvent(QTimerEvent *event) override; void setupPane(const TypeName &typeName); diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp index 96ec5f92e7..fc39b37137 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp @@ -4,6 +4,15 @@ #include "abstractview.h" #include "qmlmodelnodeproxy.h" +#include <nodemetainfo.h> + +#include <nodeabstractproperty.h> +#include <nodelistproperty.h> +#include <variantproperty.h> + +#include <utils/qtcassert.h> +#include <utils/algorithm.h> + #include <QtQml> namespace QmlDesigner { @@ -17,6 +26,8 @@ void QmlModelNodeProxy::setup(const QmlObjectNode &objectNode) { m_qmlObjectNode = objectNode; + m_subselection.clear(); + emit modelNodeChanged(); } @@ -75,4 +86,227 @@ QString QmlModelNodeProxy::simplifiedTypeName() const return m_qmlObjectNode.simplifiedTypeName(); } +static QList<int> toInternalIdList(const QList<ModelNode> &nodes) +{ + return Utils::transform(nodes, [](const ModelNode &node) { return node.internalId(); }); +} + +QList<int> QmlModelNodeProxy::allChildren(int internalId) const +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return {}); + + if (internalId >= 0) + modelNode = modelNode.view()->modelNodeForInternalId(internalId); + + return allChildren(modelNode); +} + +QList<int> QmlModelNodeProxy::allChildrenOfType(const QString &typeName, int internalId) const +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return {}); + + if (internalId >= 0) + modelNode = modelNode.view()->modelNodeForInternalId(internalId); + + return allChildrenOfType(modelNode, typeName); +} + +QString QmlModelNodeProxy::simplifiedTypeName(int internalId) const +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return {}); + + return modelNode.view()->modelNodeForInternalId(internalId).simplifiedTypeName(); +} + +PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::findWrapper(int internalId) const +{ + for (const auto &item : qAsConst(m_subselection)) { + if (item->modelNode().internalId() == internalId) + return item.data(); + } + + return nullptr; +} + +PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::registerSubSelectionWrapper(int internalId) +{ + auto result = findWrapper(internalId); + + if (result) + return result; + + QTC_ASSERT(m_qmlObjectNode.isValid(), return nullptr); + + ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalId); + + QTC_ASSERT(node.isValid(), return nullptr); + + QSharedPointer<PropertyEditorSubSelectionWrapper> wrapper( + new PropertyEditorSubSelectionWrapper(node)); + m_subselection.append(wrapper); + + QJSEngine::setObjectOwnership(wrapper.data(), QJSEngine::CppOwnership); + + return wrapper.data(); +} + +void QmlModelNodeProxy::createModelNode(int internalIdParent, + const QString &property, + const QString &typeName, + const QString &requiredImport) +{ + QTC_ASSERT(m_qmlObjectNode.isValid(), return ); + + auto modelNode = m_qmlObjectNode.modelNode(); + + AbstractView *view = modelNode.view(); + + auto parentModelNode = m_qmlObjectNode.modelNode(); + if (internalIdParent >= 0) + parentModelNode = view->modelNodeForInternalId(internalIdParent); + + QTC_ASSERT(parentModelNode.isValid(), return ); + + Import import; + Q_ASSERT(import.isEmpty()); + + if (!requiredImport.isEmpty() && !view->model()->hasImport(requiredImport)) + import = Import::createLibraryImport(requiredImport); + + view->executeInTransaction("QmlModelNodeProxy::createModelNode", [&] { + if (!import.isEmpty()) + view->model()->changeImports({import}, {}); + +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newNode = view->createModelNode(typeName.toUtf8()); +#else + NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); + ModelNode newNode = view->createModelNode(metaInfo.typeName(), + metaInfo.majorVersion(), + metaInfo.minorVersion()); +#endif + parentModelNode.nodeAbstractProperty(property.toUtf8()).reparentHere(newNode); + }); +} + +void QmlModelNodeProxy::moveNode(int internalIdParent, + const QString &propertyName, + int fromIndex, + int toIndex) +{ + QTC_ASSERT(m_qmlObjectNode.isValid(), return ); + + ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent); + + QTC_ASSERT(node.isValid(), return ); + AbstractView *view = m_qmlObjectNode.view(); + view->executeInTransaction("QmlModelNodeProxy::swapNode", [&] { + node.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex); + }); +} + +bool QmlModelNodeProxy::isInstanceOf(const QString &typeName, int internalId) const +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return {}); + + if (internalId >= 0) + modelNode = modelNode.view()->modelNodeForInternalId(internalId); + + NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); + + return modelNode.metaInfo().isBasedOn(metaInfo); +} + +void QmlModelNodeProxy::changeType(int internalId, const QString &typeName) +{ + QTC_ASSERT(m_qmlObjectNode.isValid(), return ); + + ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalId); + + QTC_ASSERT(node.isValid(), return ); + + QTC_ASSERT(!node.isRootNode(), return ); +#ifdef QDS_USE_PROJECTSTORAGE + node.changeType(typeName.toUtf8(), -1, -1); +#else + NodeMetaInfo metaInfo = node.model()->metaInfo(typeName.toUtf8()); + node.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif +} + +void QmlModelNodeProxy::handleInstancePropertyChanged(const ModelNode &modelNode, + const PropertyName &propertyName) +{ + const QmlObjectNode qmlObjectNode(modelNode); + + for (const auto &item : qAsConst(m_subselection)) { + if (item && item->isRelevantModelNode(modelNode)) { + if (!modelNode.hasProperty(propertyName) + || modelNode.property(propertyName).isBindingProperty()) { + item->setValueFromModel(propertyName, qmlObjectNode.instanceValue(propertyName)); + } else { + item->setValueFromModel(propertyName, qmlObjectNode.modelValue(propertyName)); + } + } + } +} + +void QmlModelNodeProxy::handleBindingPropertyChanged(const BindingProperty &property) +{ + for (const auto &item : qAsConst(m_subselection)) { + if (item && item->isRelevantModelNode(property.parentModelNode())) { + QmlObjectNode objectNode(item->modelNode()); + if (objectNode.modelNode().property(property.name()).isBindingProperty()) + item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); + else + item->setValueFromModel(property.name(), objectNode.modelValue(property.name())); + } + } +} + +void QmlModelNodeProxy::handleVariantPropertyChanged(const VariantProperty &property) +{ + for (const auto &item : qAsConst(m_subselection)) { + if (item && item->isRelevantModelNode(property.parentModelNode())) { + QmlObjectNode objectNode(item->modelNode()); + if (objectNode.modelNode().property(property.name()).isBindingProperty()) + item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); + else + item->setValueFromModel(property.name(), objectNode.modelValue(property.name())); + } + } +} + +void QmlModelNodeProxy::handlePropertiesRemoved(const AbstractProperty &property) +{ + for (const auto &item : qAsConst(m_subselection)) { + if (item && item->isRelevantModelNode(property.parentModelNode())) { + QmlObjectNode objectNode(item->modelNode()); + item->resetValue(property.name()); + item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); + } + } +} + +QList<int> QmlModelNodeProxy::allChildren(const ModelNode &modelNode) const +{ + return toInternalIdList(modelNode.directSubModelNodes()); +} + +QList<int> QmlModelNodeProxy::allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const +{ + QTC_ASSERT(modelNode.isValid(), return {}); + + NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); + + return toInternalIdList(modelNode.directSubModelNodesOfType(metaInfo)); } +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h index 4740b01fbd..d8a49d7e10 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h @@ -3,6 +3,8 @@ #pragma once +#include "propertyeditorvalue.h" + #include <qmlitemnode.h> #include <QObject> @@ -16,6 +18,7 @@ class QMLDESIGNERCORE_EXPORT QmlModelNodeProxy : public QObject Q_PROPERTY(QmlDesigner::ModelNode modelNode READ modelNode NOTIFY modelNodeChanged) Q_PROPERTY(bool multiSelection READ multiSelection NOTIFY modelNodeChanged) + public: explicit QmlModelNodeProxy(QObject *parent = nullptr); @@ -36,13 +39,45 @@ public: QString simplifiedTypeName() const; + Q_INVOKABLE QList<int> allChildren(int internalId = -1) const; + Q_INVOKABLE QList<int> allChildrenOfType(const QString &typeName, int internalId = -1) const; + + Q_INVOKABLE QString simplifiedTypeName(int internalId) const; + + Q_INVOKABLE PropertyEditorSubSelectionWrapper *registerSubSelectionWrapper(int internalId); + + Q_INVOKABLE void createModelNode(int internalIdParent, + const QString &property, + const QString &typeName, + const QString &requiredImport = {}); + + Q_INVOKABLE void moveNode(int internalIdParent, + const QString &propertyName, + int fromIndex, + int toIndex); + + Q_INVOKABLE bool isInstanceOf(const QString &typeName, int internalId = -1) const; + + Q_INVOKABLE void changeType(int internalId, const QString &typeName); + + void handleInstancePropertyChanged(const ModelNode &modelNode, const PropertyName &propertyName); + + void handleBindingPropertyChanged(const BindingProperty &property); + void handleVariantPropertyChanged(const VariantProperty &property); + void handlePropertiesRemoved(const AbstractProperty &property); + signals: void modelNodeChanged(); void selectionToBeChanged(); void selectionChanged(); private: + QList<int> allChildren(const ModelNode &modelNode) const; + QList<int> allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const; + PropertyEditorSubSelectionWrapper *findWrapper(int internalId) const; + QmlObjectNode m_qmlObjectNode; + QList<QSharedPointer<PropertyEditorSubSelectionWrapper>> m_subselection; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp index 2d8998404a..f289583cc3 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp @@ -40,7 +40,7 @@ SetFrameValueDialog::SetFrameValueDialog(qreal frame, const QVariant &value, valueLabel->setAlignment(Qt::AlignRight); valueLabel->setFixedWidth(labelWidth); - m_frameControl->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()); + m_frameControl->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max()); m_frameControl->setValue(static_cast<int>(frame)); m_frameControl->setAlignment(Qt::AlignRight); @@ -86,7 +86,6 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) switch (value.metaType().id()) { - case QMetaType::QColor: { auto* widget = new ColorControl(value.value<QColor>()); m_valueGetter = [widget]() { return widget->value(); }; @@ -102,7 +101,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) case QMetaType::Int: { auto* widget = new QSpinBox; - widget->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()); + widget->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max()); widget->setAlignment(Qt::AlignRight); widget->setValue(value.toInt()); m_valueGetter = [widget]() { return widget->value(); }; @@ -120,7 +119,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) case QMetaType::Float: { auto* widget = new QDoubleSpinBox; - widget->setRange(std::numeric_limits<float>::min(), std::numeric_limits<float>::max()); + widget->setRange(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max()); widget->setAlignment(Qt::AlignRight); widget->setValue(value.toFloat()); m_valueGetter = [widget]() { return static_cast<float>(widget->value()); }; @@ -132,7 +131,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) default: { auto* widget = new QDoubleSpinBox; - widget->setRange(std::numeric_limits<double>::min(), std::numeric_limits<double>::max()); + widget->setRange(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max()); widget->setAlignment(Qt::AlignRight); widget->setValue(value.toDouble()); m_valueGetter = [widget]() { return widget->value(); }; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp index 83551378ee..e65c05c39f 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp @@ -13,6 +13,7 @@ #include <variantproperty.h> #include <qmlitemnode.h> #include <qmlobjectnode.h> +#include <dialogutils.h> #include <coreplugin/messagebox.h> @@ -141,8 +142,7 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent) bool error = false; if (!ModelNode::isValidId(newId)) { - Core::AsynchronousMessageBox::warning(tr("Invalid Id"), - tr("%1 is an invalid id.").arg(newId)); + DialogUtils::showWarningForInvalidId(newId); error = true; } else if (animation().view()->hasId(newId)) { Core::AsynchronousMessageBox::warning(tr("Invalid Id"), diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp index b3d408dc0d..08915b6577 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp @@ -10,6 +10,7 @@ #include <nodemetainfo.h> #include <rewritertransaction.h> #include <variantproperty.h> +#include <dialogutils.h> #include <coreplugin/messagebox.h> @@ -125,8 +126,7 @@ TimelineForm::TimelineForm(QWidget *parent) bool error = false; if (!ModelNode::isValidId(newId)) { - Core::AsynchronousMessageBox::warning(tr("Invalid Id"), - tr("%1 is an invalid id.").arg(newId)); + DialogUtils::showWarningForInvalidId(newId); error = true; } else if (m_timeline.view()->hasId(newId)) { Core::AsynchronousMessageBox::warning(tr("Invalid Id"), diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index 7905df68e9..8288e69316 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -644,11 +644,12 @@ void TimelineView::registerActions() TimelineWidget *TimelineView::createWidget() { - if (!m_timelineWidget) + if (!m_timelineWidget) { m_timelineWidget = new TimelineWidget(this); - auto *timelineContext = new TimelineContext(m_timelineWidget); - Core::ICore::addContextObject(timelineContext); + auto *timelineContext = new TimelineContext(m_timelineWidget); + Core::ICore::addContextObject(timelineContext); + } return m_timelineWidget; } diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index e9df928c96..cb84183f92 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -21,6 +21,9 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> #include <coreplugin/modemanager.h> + +#include <texteditor/textdocument.h> + #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -302,6 +305,20 @@ ToolBarBackend::ToolBarBackend(QObject *parent) this, &ToolBarBackend::documentIndexChanged); + connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, this, [this]() { + static QMetaObject::Connection *lastConnection = nullptr; + delete lastConnection; + + if (auto textDocument = qobject_cast<TextEditor::TextDocument *>( + Core::EditorManager::currentDocument())) { + connect(textDocument->document(), + &QTextDocument::modificationChanged, + this, + &ToolBarBackend::isDocumentDirtyChanged); + emit isDocumentDirtyChanged(); + } + }); + connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, this, @@ -740,6 +757,12 @@ bool ToolBarBackend::isSharingEnabled() return QmlDesigner::checkEnterpriseLicense(); } +bool ToolBarBackend::isDocumentDirty() const +{ + return Core::EditorManager::currentDocument() + && Core::EditorManager::currentDocument()->isModified(); +} + void ToolBarBackend::launchGlobalAnnotations() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 5d0b0e712a..02bdae1717 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -97,6 +97,7 @@ class ToolBarBackend : public QObject Q_PROPERTY(bool isMCUs READ isMCUs NOTIFY isMCUsChanged) Q_PROPERTY(bool projectOpened READ projectOpened NOTIFY projectOpenedChanged) Q_PROPERTY(bool isSharingEnabled READ isSharingEnabled NOTIFY isSharingEnabledChanged) + Q_PROPERTY(bool isDocumentDirty READ isDocumentDirty NOTIFY isDocumentDirtyChanged) public: ToolBarBackend(QObject *parent = nullptr); @@ -147,6 +148,8 @@ public: bool isSharingEnabled(); + bool isDocumentDirty() const; + static void launchGlobalAnnotations(); signals: @@ -167,6 +170,7 @@ signals: void isMCUsChanged(); void projectOpenedChanged(); void isSharingEnabledChanged(); + void isDocumentDirtyChanged(); private: void setupWorkspaces(); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index 104127bd49..9f9e888823 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp @@ -318,11 +318,12 @@ ModelNode TransitionEditorView::addNewTransition() TransitionEditorWidget *TransitionEditorView::createWidget() { - if (!m_transitionEditorWidget) + if (!m_transitionEditorWidget) { m_transitionEditorWidget = new TransitionEditorWidget(this); - auto *transitionContext = new TransitionContext(m_transitionEditorWidget); - Core::ICore::addContextObject(transitionContext); + auto *transitionContext = new TransitionContext(m_transitionEditorWidget); + Core::ICore::addContextObject(transitionContext); + } return m_transitionEditorWidget; } diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp index 1770ba63fc..dcbb0b23b3 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp @@ -13,6 +13,7 @@ #include <rewritertransaction.h> #include <variantproperty.h> #include <qmlitemnode.h> +#include <dialogutils.h> #include <coreplugin/messagebox.h> @@ -45,8 +46,7 @@ TransitionForm::TransitionForm(QWidget *parent) bool error = false; if (!ModelNode::isValidId(newId)) { - Core::AsynchronousMessageBox::warning(tr("Invalid ID"), - tr("%1 is an invalid ID.").arg(newId)); + DialogUtils::showWarningForInvalidId(newId); error = true; } else if (m_transition.view()->hasId(newId)) { Core::AsynchronousMessageBox::warning(tr("Invalid ID"), diff --git a/src/plugins/qmldesigner/componentsplugin/components.metainfo b/src/plugins/qmldesigner/componentsplugin/components.metainfo index 8a1e365266..2e070aa322 100644 --- a/src/plugins/qmldesigner/componentsplugin/components.metainfo +++ b/src/plugins/qmldesigner/componentsplugin/components.metainfo @@ -12,6 +12,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } + toolTip: qsTr("Organizes items in a row.") } } @@ -28,6 +29,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } + toolTip: qsTr("Organizes items in a column.") } } @@ -44,6 +46,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } + toolTip: qsTr("Organizes items in a grid.") } } @@ -57,7 +60,7 @@ MetaInfo { } ItemLibraryEntry { - name: "StackLayout" + name: "Stack Layout" category: "Qt Quick - Layouts" libraryIcon: ":/componentsplugin/images/stack-layouts-icon.png" version: "1.0" @@ -65,6 +68,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } + toolTip: qsTr("Organizes items in a grid. Only the top item is visible.") } } } diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp new file mode 100644 index 0000000000..5ee5790b53 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -0,0 +1,139 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "generatedcomponentutils.h" + +#include <qmldesignerconstants.h> + +namespace QmlDesigner { + +GeneratedComponentUtils::GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies) + : m_externalDependencies(externalDependencies) +{ +} + +Utils::FilePath GeneratedComponentUtils::generatedComponentsPath() const +{ + Utils::FilePath projectPath = Utils::FilePath::fromString(m_externalDependencies.currentProjectDirPath()); + if (projectPath.isEmpty()) + return {}; + + Utils::FilePath assetImportsPath = projectPath.resolvePath(QLatin1String(Constants::OLD_ASSET_IMPORT_FOLDER)); + if (assetImportsPath.exists()) + return assetImportsPath; + + Utils::FilePath componentsPath = projectPath.resolvePath(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER)); + if (!componentsPath.exists()) + componentsPath.createDir(); + + return componentsPath; +} + +Utils::FilePath GeneratedComponentUtils::composedEffectsBasePath() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + if (basePath.isEmpty()) + return {}; + + QString effectsImportPath; + if (basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER)) + effectsImportPath = Constants::OLD_EFFECTS_FOLDER; + else + effectsImportPath = Constants::COMPOSED_EFFECTS_TYPE; + + return basePath.resolvePath(effectsImportPath); +} + +Utils::FilePath GeneratedComponentUtils::composedEffectPath(const QString &effectPath) const +{ + Utils::FilePath effectsBasePath = composedEffectsBasePath(); + + QString effectName = Utils::FilePath::fromString(effectPath).baseName(); + + return effectsBasePath.resolvePath(effectName + "/" + effectName + ".qml"); +} + +Utils::FilePath GeneratedComponentUtils::componentBundlesBasePath() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + + if (basePath.isEmpty()) + return {}; + + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE)); +} + +Utils::FilePath GeneratedComponentUtils::import3dBasePath() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + + if (basePath.isEmpty()) + return {}; + + Utils::FilePath import3dPath; + if (basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER)) + return basePath.resolvePath(QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)); + + return basePath.resolvePath(QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER)); +} + +bool GeneratedComponentUtils::isImport3dPath(const QString &path) const +{ + return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)) + || path.contains(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER) + '/' + + QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER)); +} + +bool GeneratedComponentUtils::isComposedEffectPath(const QString &path) const +{ + return path.contains(Constants::OLD_EFFECTS_IMPORT_FOLDER) + || path.contains('/' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE)); +} + +QString GeneratedComponentUtils::generatedComponentTypePrefix() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + if (basePath.isEmpty() || basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER)) + return {}; + + return Constants::GENERATED_COMPONENTS_FOLDER; +} + +QString GeneratedComponentUtils::import3dTypePrefix() const +{ + QString basePrefix = generatedComponentTypePrefix(); + + if (basePrefix == Constants::GENERATED_COMPONENTS_FOLDER) + return basePrefix + '.' + QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER); + + return Constants::OLD_QUICK_3D_ASSETS_FOLDER; +} + +QString GeneratedComponentUtils::import3dTypePath() const +{ + QString prefix = import3dTypePrefix(); + prefix.replace('.', '/'); + return prefix; +} + +QString GeneratedComponentUtils::componentBundlesTypePrefix() const +{ + QString basePrefix = generatedComponentTypePrefix(); + + if (basePrefix.endsWith(Constants::GENERATED_COMPONENTS_FOLDER)) + return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_TYPE); + + return Constants::COMPONENT_BUNDLES_TYPE; +} + +QString GeneratedComponentUtils::composedEffectsTypePrefix() const +{ + QString basePrefix = generatedComponentTypePrefix(); + + if (basePrefix == Constants::GENERATED_COMPONENTS_FOLDER) + return basePrefix + '.' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE); + + return Constants::OLD_EFFECTS_FOLDER; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h new file mode 100644 index 0000000000..e2e9baf3e7 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -0,0 +1,38 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <externaldependenciesinterface.h> +#include <qmldesignercorelib_exports.h> + +#include <utils/filepath.h> + +#include <QString> + +namespace QmlDesigner { + +class QMLDESIGNERCORE_EXPORT GeneratedComponentUtils { +public: + GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies); + + Utils::FilePath generatedComponentsPath() const; + Utils::FilePath composedEffectsBasePath() const; + Utils::FilePath composedEffectPath(const QString &effectPath) const; + Utils::FilePath componentBundlesBasePath() const; + Utils::FilePath import3dBasePath() const; + + bool isImport3dPath(const QString &path) const; + bool isComposedEffectPath(const QString &path) const; + + QString generatedComponentTypePrefix() const; + QString import3dTypePrefix() const; + QString import3dTypePath() const; + QString componentBundlesTypePrefix() const; + QString composedEffectsTypePrefix() const; + +private: + ExternalDependenciesInterface &m_externalDependencies; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 955e676d3b..97148e664f 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -76,8 +76,10 @@ void ImageCacheCollector::start(Utils::SmallStringView name, AbortCallback abortCallback, ImageCache::TraceToken traceToken) { +#ifdef QDS_USE_PROJECTSTORAGE if (!m_projectStorage || !m_pathCache) return; +#endif using namespace NanotraceHR::Literals; auto [collectorTraceToken, flowtoken] = traceToken.beginDurationWithFlow( diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h index c2a912cc3a..fec68c2894 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h @@ -64,8 +64,10 @@ private: QSize captureImageMaximumSize; ExternalDependenciesInterface &m_externalDependencies; ImageCacheCollectorNullImageHandling nullImageHandling{}; +#ifdef QDS_USE_PROJECTSTORAGE ProjectStorageType *m_projectStorage = nullptr; PathCacheType *m_pathCache = nullptr; +#endif }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index fac7e7d9bf..dc5ed3de23 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -31,7 +31,7 @@ public: { std::unique_lock lock{m_mutex}; - ensureThreadIsRunning(std::move(traceToken)); + ensureThreadIsRunning(lock, std::move(traceToken)); m_tasks.emplace_back(std::forward<Arguments>(arguments)...); } @@ -54,6 +54,15 @@ public: clearTasks(oldTasks); } + void putThreadToSleep() + { + { + std::unique_lock lock{m_mutex}; + m_sleeping = true; + } + m_condition.notify_all(); + } + private: void destroy() { @@ -66,19 +75,20 @@ private: { using namespace std::literals::chrono_literals; std::unique_lock lock{m_mutex}; - if (m_finishing) + + if (m_finishing || m_sleeping) return {std::move(lock), true}; + if (m_tasks.empty()) { auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { - return m_tasks.size() || m_finishing; + return m_tasks.size() || m_finishing || m_sleeping; }); - if (timedOutWithoutEntriesOrFinishing || m_finishing) { + if (timedOutWithoutEntriesOrFinishing) m_sleeping = true; - return {std::move(lock), true}; - } } - return {std::move(lock), false}; + + return {std::move(lock), m_finishing || m_sleeping}; } [[nodiscard]] std::optional<Task> getTask(std::unique_lock<std::mutex> lock) @@ -94,29 +104,38 @@ private: return {std::move(task)}; } - template<typename TraceToken> - void ensureThreadIsRunning(TraceToken traceToken) + template<typename Lock, typename TraceToken> + void ensureThreadIsRunning(Lock &lock, TraceToken traceToken) { using namespace NanotraceHR::Literals; if (m_finishing || !m_sleeping) return; + if (m_sleeping) { + lock.unlock(); + joinThread(); + lock.lock(); + + m_sleeping = false; + } + if (m_backgroundThread.joinable()) return; - m_sleeping = false; - auto [threadCreateToken, flowToken] = traceToken.beginDurationWithFlow( "thread is created in the task queue"_t); m_backgroundThread = std::thread{[this](auto traceToken) { auto duration = traceToken.beginDuration( "thread is ready"_t); + while (true) { auto [lock, abort] = waitForTasks(); duration.end(); + if (abort) return; + auto getTaskToken = duration.beginDuration( "get task from queue"_t); if (auto task = getTask(std::move(lock)); task) { diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index b907e6c5d8..85f129cdbc 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -166,6 +166,7 @@ public: NodeMetaInfo qtQmlConnectionsMetaInfo() const; NodeMetaInfo qtQmlModelsListModelMetaInfo() const; NodeMetaInfo qtQmlModelsListElementMetaInfo() const; + NodeMetaInfo qtQmlXmlListModelXmlListModelRoleMetaInfo() const; NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const; NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const; NodeMetaInfo qtQuick3DDirectionalLightMetaInfo() const; @@ -218,14 +219,17 @@ public: // Imports: const Imports &imports() const; - const Imports &possibleImports() const; - const Imports &usedImports() const; + Imports possibleImports() const; + Imports usedImports() const; void changeImports(Imports importsToBeAdded, Imports importsToBeRemoved); +#ifndef QDS_USE_PROJECTSTORAGE void setPossibleImports(Imports possibleImports); +#endif +#ifndef QDS_USE_PROJECTSTORAGE void setUsedImports(Imports usedImports); +#endif bool hasImport(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; bool isImportPossible(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; - QString pathForImport(const Import &import); QStringList importPaths() const; Import highestPossibleImport(const QString &importPath); diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h index 0a062289fd..91c533fe7b 100644 --- a/src/plugins/qmldesigner/designercore/include/modelfwd.h +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -77,7 +77,7 @@ constexpr bool useProjectStorage() using ProjectStorageType = ProjectStorageInterface; using PathCacheType = SourcePathCacheInterface; #else -using ProjectStorageType = ProjectStorage<Sqlite::Database>; +using ProjectStorageType = ProjectStorage; using PathCacheType = SourcePathCache<ProjectStorageType, NonLockingMutex>; #endif diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 53c755ddc8..ba2e2cda65 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -27,9 +27,11 @@ QT_END_NAMESPACE # define DEPRECATED_VERSION_NUMBER \ [[deprecated( \ "In most cases you don't need them anymore because the import is setting them!")]] +# define DEPRECATED_COMPONENT_FILE_NAME [[deprecated("Use sourceId() instead.")]] #else # define DEPRECATED_TYPENAME # define DEPRECATED_VERSION_NUMBER +# define DEPRECATED_COMPONENT_FILE_NAME #endif namespace QmlDesigner { @@ -116,7 +118,7 @@ public: Storage::Info::ItemLibraryEntries itemLibrariesEntries() const; SourceId sourceId() const; - QString componentFileName() const; + DEPRECATED_COMPONENT_FILE_NAME QString componentFileName() const; bool isBasedOn(const NodeMetaInfo &metaInfo) const; bool isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const; @@ -167,6 +169,7 @@ public: bool isQtMultimediaSoundEffect() const; bool isQtObject() const; bool isQtQmlConnections() const; + bool isQtQmlModelsListElement() const; bool isQtQuick3DBakedLightmap() const; bool isQtQuick3DBuffer() const; bool isQtQuick3DCamera() const; @@ -176,7 +179,6 @@ public: bool isQtQuick3DInstanceList() const; bool isQtQuick3DInstanceListEntry() const; bool isQtQuick3DLight() const; - bool isQtQuickListElement() const; bool isQtQuickListModel() const; bool isQtQuickListView() const; bool isQtQuick3DMaterial() const; @@ -265,12 +267,14 @@ public: private: const Storage::Info::Type &typeData() const; + PropertyDeclarationId defaultPropertyDeclarationId() const; bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const; private: TypeId m_typeId; NotNullPointer<const ProjectStorageType> m_projectStorage = {}; mutable std::optional<Storage::Info::Type> m_typeData; + mutable std::optional<PropertyDeclarationId> m_defaultPropertyId; std::shared_ptr<NodeMetaInfoPrivate> m_privateData; }; diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index bc66e0d2b2..0b157e55e7 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -7,6 +7,8 @@ #include <utils/span.h> +#include <QVarLengthArray> + namespace QmlDesigner { enum class BasicIdType { @@ -29,6 +31,8 @@ enum class BasicIdType { using TypeId = Sqlite::BasicId<BasicIdType::Type>; using TypeIds = std::vector<TypeId>; +template<std::size_t size> +using SmallTypeIds = QVarLengthArray<TypeId, size>; using PropertyDeclarationId = Sqlite::BasicId<BasicIdType::PropertyDeclaration>; using PropertyDeclarationIds = std::vector<PropertyDeclarationId>; @@ -47,6 +51,8 @@ using SourceContextIds = std::vector<SourceContextId>; using SourceId = Sqlite::BasicId<BasicIdType::Source, int>; using SourceIds = std::vector<SourceId>; +template<std::size_t size> +using SmallSourceIds = QVarLengthArray<SourceId, size>; using ModuleId = Sqlite::BasicId<BasicIdType::Module, int>; using ModuleIds = std::vector<ModuleId>; diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index dde5515a5a..e11f201cdb 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -174,6 +174,7 @@ public: ModelNode targetTransition() const; void assignTargetFlowItem(const QmlFlowTargetNode &flowItem); QmlFlowItemNode flowItemParent() const; +private: void destroyTarget(); }; diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 23841accda..0134349682 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -123,7 +123,10 @@ public: bool renameId(const QString& oldId, const QString& newId); const QmlJS::Document *document() const; + +#ifndef QDS_USE_PROJECTSTORAGE const QmlJS::ScopeChain *scopeChain() const; +#endif QString convertTypeToImportAlias(const QString &type) const; @@ -135,8 +138,6 @@ public: void setCheckLinkErrors(bool b) { m_checkLinkErrors = b; } - QString pathForImport(const Import &import); - QStringList importDirectories() const; QSet<QPair<QString, QString> > qrcMapping() const; diff --git a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h index 7fa2348854..a42164d1bd 100644 --- a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h +++ b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h @@ -7,6 +7,7 @@ # include "qmldesignercorelib_global.h" +# include <generatedcomponentutils.h> # include <import.h> # include <QObject> @@ -62,6 +63,7 @@ private: // variables QDir m_filePathDir; QPointer<Model> m_model; ExternalDependenciesInterface &m_externalDependencies; + GeneratedComponentUtils m_componentUtils; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 33a100f7b2..1b965db66a 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -64,6 +64,8 @@ #include <qmlitemnode.h> #include <rewriterview.h> +#include <projectstorage/projectstorage.h> + #include <utils/hdrimage.h> #include <coreplugin/messagemanager.h> @@ -205,22 +207,17 @@ NodeInstanceView::~NodeInstanceView() static bool isSkippedRootNode(const ModelNode &node) { - static const PropertyNameList skipList({"Qt.ListModel", "QtQuick.ListModel", "Qt.ListModel", "QtQuick.ListModel"}); - - if (skipList.contains(node.type())) - return true; - - return false; + return node.metaInfo().isQtQuickListModel(); } static bool isSkippedNode(const ModelNode &node) { - static const PropertyNameList skipList({"QtQuick.XmlRole", "Qt.XmlRole", "QtQuick.ListElement", "Qt.ListElement"}); + auto model = node.model(); - if (skipList.contains(node.type())) - return true; + auto listElement = model->qtQmlModelsListElementMetaInfo(); + auto xmlRole = model->qtQmlXmlListModelXmlListModelRoleMetaInfo(); - return false; + return node.metaInfo().isBasedOn(listElement, xmlRole); } static bool parentTakesOverRendering(const ModelNode &modelNode) @@ -644,7 +641,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, TypeName(), key.type}; m_nodeInstanceServer->changeAuxiliaryValues({{container}}); - }; + } break; case AuxiliaryDataType::NodeInstanceAuxiliary: @@ -656,7 +653,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, TypeName(), key.type}; m_nodeInstanceServer->changeAuxiliaryValues({{container}}); - }; + } break; case AuxiliaryDataType::NodeInstancePropertyOverwrite: @@ -991,6 +988,8 @@ QRectF NodeInstanceView::sceneRect() const return {}; } +namespace { + QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList) { QList<ModelNode> filteredNodeList; @@ -1003,14 +1002,12 @@ QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList) return filteredNodeList; } -namespace { bool shouldSendAuxiliary(const AuxiliaryDataKey &key) { return key.type == AuxiliaryDataType::NodeInstancePropertyOverwrite || key.type == AuxiliaryDataType::NodeInstanceAuxiliary || key == invisibleProperty || key == lockedProperty; } -} // namespace bool parentIsBehavior(ModelNode node) { @@ -1024,6 +1021,31 @@ bool parentIsBehavior(ModelNode node) return false; } +TypeName createQualifiedTypeName(const ModelNode &node) +{ + if (!node) + return {}; + +#ifdef QDS_USE_PROJECTSTORAGE + auto model = node.model(); + auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId()); + if (exportedTypes.size()) { + const auto &exportedType = exportedTypes.front(); + Utils::PathString typeName = model->projectStorage()->moduleName(exportedType.moduleId); + typeName += '/'; + typeName += exportedType.name; + + return typeName.toQByteArray(); + } + + return {}; +#else + return node.type(); +#endif +} + +} // namespace + CreateSceneCommand NodeInstanceView::createCreateSceneCommand() { QList<ModelNode> nodeList = allModelNodes(); @@ -1079,8 +1101,9 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() nodeFlags |= InstanceContainer::ParentTakesOverRendering; const auto modelNode = instance.modelNode(); + InstanceContainer container(instance.instanceId(), - modelNode.type(), + createQualifiedTypeName(modelNode), modelNode.majorVersion(), modelNode.minorVersion(), ModelUtils::componentFilePath(modelNode), @@ -1243,7 +1266,7 @@ CreateInstancesCommand NodeInstanceView::createCreateInstancesCommand(const QLis const auto modelNode = instance.modelNode(); InstanceContainer container(instance.instanceId(), - modelNode.type(), + createQualifiedTypeName(modelNode), modelNode.majorVersion(), modelNode.minorVersion(), ModelUtils::componentFilePath(modelNode), @@ -1850,7 +1873,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo ModelNodePreviewImageData imageData; imageData.id = modelNode.id(); - imageData.type = QString::fromLatin1(modelNode.type()); + imageData.type = QString::fromUtf8(createQualifiedTypeName(modelNode)); const double ratio = m_externalDependencies.formEditorDevicePixelRatio(); if (imageSource.isEmpty() && modelNode.metaInfo().isQtQuick3DTexture()) { @@ -1923,7 +1946,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo imageData.pixmap = originalPixmap.scaled(dim, dim, Qt::KeepAspectRatio); imageData.pixmap.setDevicePixelRatio(ratio); imageData.time = modified; - imageData.info = ImageUtils::imageInfo(imageSource); + imageData.info = ImageUtils::imageInfoString(imageSource); m_imageDataMap.insert(imageData.id, imageData); } } @@ -1958,7 +1981,7 @@ QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &model if (m_imageDataMap.contains(id)) { imageData = m_imageDataMap[id]; } else { - imageData.type = QString::fromLatin1(modelNode.type()); + imageData.type = QString::fromLatin1(createQualifiedTypeName(modelNode)); imageData.id = id; m_imageDataMap.insert(id, imageData); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 85f904666c..b8c3e610be 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -919,8 +919,11 @@ const ObjectValue *NodeMetaInfoPrivate::getObjectValue() const ContextPtr NodeMetaInfoPrivate::context() const { +#ifndef QDS_USE_PROJECTSTORAGE if (m_model && m_model->rewriterView() && m_model->rewriterView()->scopeChain()) return m_model->rewriterView()->scopeChain()->context(); +#endif + return ContextPtr(nullptr); } @@ -1832,7 +1835,7 @@ PropertyName NodeMetaInfo::defaultPropertyName() const { if constexpr (useProjectStorage()) { if (isValid()) { - if (auto name = m_projectStorage->propertyName(typeData().defaultPropertyId)) { + if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) { return name->toQByteArray(); } } @@ -1848,7 +1851,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const { if constexpr (useProjectStorage()) { if (isValid()) { - return PropertyMetaInfo(typeData().defaultPropertyId, m_projectStorage); + return PropertyMetaInfo(defaultPropertyDeclarationId(), m_projectStorage); } } else { return property(defaultPropertyName()); @@ -1859,7 +1862,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const bool NodeMetaInfo::hasDefaultProperty() const { if constexpr (useProjectStorage()) - return isValid() && bool(typeData().defaultPropertyId); + return isValid() && bool(defaultPropertyDeclarationId()); else return !defaultPropertyName().isEmpty(); } @@ -2086,6 +2089,14 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const return *m_typeData; } +PropertyDeclarationId NodeMetaInfo::defaultPropertyDeclarationId() const +{ + if (!m_defaultPropertyId) + m_defaultPropertyId.emplace(m_projectStorage->defaultPropertyDeclarationId(m_typeId)); + + return *m_defaultPropertyId; +} + bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const { if (!isValid()) { @@ -2444,12 +2455,9 @@ bool NodeMetaInfo::usesCustomParser() const if (!isValid()) return false; - auto type = typeName(); - return type == "QtQuick.VisualItemModel" || type == "Qt.VisualItemModel" - || type == "QtQuick.VisualDataModel" || type == "Qt.VisualDataModel" - || type == "QtQuick.ListModel" || type == "Qt.ListModel" - || type == "QtQml.Models.ListModel" || type == "QtQuick.XmlListModel" - || type == "Qt.XmlListModel" || type == "QtQml.XmlListModel.XmlListModel"; + auto type = simplifiedTypeName(); + return type == "VisualItemModel" || type == "VisualDataModel" || type == "ListModel" + || type == "XmlListModel"; } } @@ -2763,7 +2771,7 @@ bool NodeMetaInfo::isQtQuick3DLight() const } } -bool NodeMetaInfo::isQtQuickListElement() const +bool NodeMetaInfo::isQtQmlModelsListElement() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 403731d1c4..16d9217f6a 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -44,6 +44,7 @@ SubComponentManager::SubComponentManager(Model *model, ExternalDependenciesInterface &externalDependencies) : m_model(model) , m_externalDependencies{externalDependencies} + , m_componentUtils{externalDependencies} { connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &path) { parseDirectory(path); }); @@ -192,7 +193,7 @@ void SubComponentManager::parseDirectory(const QString &canonicalDirPath, bool a if (!model() || !model()->rewriterView()) return; - if (canonicalDirPath.endsWith(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER))) { + if (m_componentUtils.isImport3dPath(canonicalDirPath)) { parseQuick3DAssetsDir(canonicalDirPath); return; } @@ -345,8 +346,8 @@ void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QStri bool addToLibrary) { if (!addToLibrary || !model() - || fileInfo.path().contains(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER)) - || fileInfo.path().contains(QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER))) { + || m_componentUtils.isImport3dPath(fileInfo.path()) + || m_componentUtils.isComposedEffectPath(fileInfo.path())) { return; } @@ -395,7 +396,7 @@ void SubComponentManager::parseQuick3DAssetsDir(const QString &quick3DAssetsPath QDir quick3DAssetsDir(quick3DAssetsPath); QStringList assets = quick3DAssetsDir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot); for (QString &asset : assets) - asset.prepend(QString(Constants::QUICK_3D_ASSETS_FOLDER).mid(1) + '.'); + asset.prepend(m_componentUtils.import3dTypePrefix() + '.'); // Create item library entries for Quick3D assets that are imported by document for (auto &import : std::as_const(m_imports)) { @@ -460,7 +461,8 @@ QStringList SubComponentManager::quick3DAssetPaths() const const auto impPaths = importPaths(); QStringList retPaths; for (const auto &impPath : impPaths) { - const QString assetPath = impPath + QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER); + QString path3d = m_componentUtils.import3dTypePath(); + const QString assetPath = impPath + '/' + path3d; if (QFileInfo::exists(assetPath)) retPaths << assetPath; } @@ -520,7 +522,7 @@ void SubComponentManager::update(const QUrl &filePath, const Imports &imports) // Remove old watched asset paths const QStringList watchPaths = m_watcher.directories(); - const QString &quick3DAssetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER); + const QString &quick3DAssetFolder = m_componentUtils.import3dTypePath(); for (const auto &watchPath : watchPaths) { if (watchPath.endsWith(quick3DAssetFolder)) m_watcher.removePath(watchPath); @@ -580,7 +582,7 @@ void SubComponentManager::addAndParseImport(const Import &import) } else { QString url = import.url(); - if (url.startsWith(QString(Constants::QUICK_3D_ASSETS_FOLDER).mid(1))) { + if (url.startsWith(m_componentUtils.import3dTypePrefix())) { parseQuick3DAssetsItem(import.url()); return; } diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 061ab8ae2b..125c7195a8 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -567,8 +567,10 @@ void AbstractView::disableWidget() void AbstractView::enableWidget() { - if (hasWidget() && widgetInfo().widgetFlags == DesignerWidgetFlags::DisableOnError) - widgetInfo().widget->setEnabled(true); + if (hasWidget()) { + if (auto info = widgetInfo(); info.widgetFlags == DesignerWidgetFlags::DisableOnError) + info.widget->setEnabled(true); + } } QString AbstractView::contextHelpId() const diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index 141548047e..23c17dc61a 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -37,10 +37,11 @@ BindingProperty::BindingProperty(const PropertyName &propertyName, const Interna void BindingProperty::setExpression(const QString &expression) { - Internal::WriteLocker locker(model()); if (!isValid()) return; + Internal::WriteLocker locker(model()); + if (isDynamic()) qWarning() << "Calling BindingProperty::setExpression on dynamic property."; diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index a4cd31b2a8..4f0bfba1ce 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1758,14 +1758,22 @@ Storage::Info::ExportedTypeName Model::exportedTypeNameForMetaInfo(const NodeMet return {}; } -const Imports &Model::possibleImports() const +Imports Model::possibleImports() const { +#ifdef QDS_USE_PROJECTSTORAGE + return {}; +#else return d->m_possibleImportList; +#endif } -const Imports &Model::usedImports() const +Imports Model::usedImports() const { +#ifdef QDS_USE_PROJECTSTORAGE + return {}; +#else return d->m_usedImportList; +#endif } void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved) @@ -1773,6 +1781,7 @@ void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved) d->changeImports(std::move(importsToBeAdded), std::move(importsToBeRemoved)); } +#ifndef QDS_USE_PROJECTSTORAGE void Model::setPossibleImports(Imports possibleImports) { auto tracer = d->traceToken.begin("possible imports"_t); @@ -1784,7 +1793,9 @@ void Model::setPossibleImports(Imports possibleImports) d->notifyPossibleImportsChanged(d->m_possibleImportList); } } +#endif +#ifndef QDS_USE_PROJECTSTORAGE void Model::setUsedImports(Imports usedImports) { auto tracer = d->traceToken.begin("used imports"_t); @@ -1796,6 +1807,7 @@ void Model::setUsedImports(Imports usedImports) d->notifyUsedImportsChanged(d->m_usedImportList); } } +#endif static bool compareVersions(const Import &import1, const Import &import2, bool allowHigherVersion) { @@ -1869,8 +1881,9 @@ QString Model::generateNewId(const QString &prefixName, int counter = 0; - QString newBaseId = QStringView(u"%1").arg(firstCharToLower(prefixName)); - newBaseId.remove(QRegularExpression(QStringLiteral("[^a-zA-Z0-9_]"))); + static const QRegularExpression nonWordCharsRegex("\\W"); + QString newBaseId = firstCharToLower(prefixName); + newBaseId.remove(nonWordCharsRegex); if (!newBaseId.isEmpty()) { QChar firstChar = newBaseId.at(0); @@ -2021,14 +2034,6 @@ bool Model::isImportPossible(const Import &import, bool ignoreAlias, bool allowH return false; } -QString Model::pathForImport(const Import &import) -{ - if (!rewriterView()) - return QString(); - - return rewriterView()->pathForImport(import); -} - QStringList Model::importPaths() const { if (rewriterView()) @@ -2205,6 +2210,16 @@ NodeMetaInfo Model::qtQmlModelsListElementMetaInfo() const } } +NodeMetaInfo Model::qtQmlXmlListModelXmlListModelRoleMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo<QtQml_XmlListModel, XmlListModelRole>(); + } else { + return metaInfo("QtQml.XmlListModel.XmlListModelRole"); + } +} + NodeMetaInfo Model::qmlQtObjectMetaInfo() const { if constexpr (useProjectStorage()) { diff --git a/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp b/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp index a61f1001f9..27ac2e67ab 100644 --- a/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp @@ -515,8 +515,7 @@ struct BindingFilter struct TargetFilter { TargetFilter(NodeDependencies &dependencies, Model *model) - : flowViewFlowActionAreaMetaInfo{model->flowViewFlowActionAreaMetaInfo()} - , flowViewFlowTransitionMetaInfo{model->flowViewFlowTransitionMetaInfo()} + : flowViewFlowTransitionMetaInfo{model->flowViewFlowTransitionMetaInfo()} , qtQuickPropertyChangesMetaInfo{model->qtQuickPropertyChangesMetaInfo()} , qtQuickTimelineKeyframeGroupMetaInfo{model->qtQuickTimelineKeyframeGroupMetaInfo()} , qtQuickPropertyAnimationMetaInfo{model->qtQuickPropertyAnimationMetaInfo()} diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index cb3f482289..6c3e1ea50f 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -9,6 +9,8 @@ #include <projectstorage/projectstorage.h> #include <projectstorage/sourcepathcache.h> +#include <coreplugin/messagebox.h> + #include <utils/expected.h> #include <utils/ranges.h> @@ -107,19 +109,19 @@ PropertyMetaInfo metainfo(const ModelNode &node, const PropertyName &propertyNam return node.metaInfo().property(propertyName); } -QString componentFilePath(const PathCacheType &pathCache, const NodeMetaInfo &metaInfo) +QString componentFilePath([[maybe_unused]] const PathCacheType &pathCache, const NodeMetaInfo &metaInfo) { - if constexpr (useProjectStorage()) { - auto typeSourceId = metaInfo.sourceId(); +#ifdef QDS_USE_PROJECTSTORAGE + auto typeSourceId = metaInfo.sourceId(); - if (typeSourceId && metaInfo.isFileComponent()) { - return pathCache.sourcePath(typeSourceId).toQString(); - } - } else { - return metaInfo.componentFileName(); + if (typeSourceId && metaInfo.isFileComponent()) { + return pathCache.sourcePath(typeSourceId).toQString(); } return {}; +#else + return metaInfo.componentFileName(); +#endif } QString componentFilePath(const ModelNode &node) diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 826856428b..6e3b739096 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -9,8 +9,9 @@ #include "bindingproperty.h" #include "qmlanchors.h" -#include <model.h> #include <abstractview.h> +#include <generatedcomponentutils.h> +#include <model.h> #include <coreplugin/icore.h> @@ -196,7 +197,9 @@ QmlItemNode QmlItemNode::createQmlItemNodeForEffect(AbstractView *view, auto createEffectNode = [=, &newQmlItemNode, &parentProperty]() { const QString effectName = QFileInfo(effectPath).baseName(); - Import import = Import::createLibraryImport("Effects." + effectName, "1.0"); + Import import = Import::createLibraryImport(GeneratedComponentUtils(view->externalDependencies()) + .composedEffectsTypePrefix() + + '.' + effectName, "1.0"); try { if (!view->model()->hasImport(import, true, true)) view->model()->changeImports({import}, {}); @@ -748,7 +751,6 @@ void QmlFlowActionAreaNode::assignTargetFlowItem(const QmlFlowTargetNode &flowIt ModelNode transition = flowView.addTransition(flowParent.modelNode(), flowItem.modelNode()); - modelNode().bindingProperty("target").setExpression(transition.validId()); } diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 4ae2261e60..17d40daca3 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -933,10 +933,12 @@ bool RewriterView::renameId(const QString& oldId, const QString& newId) return false; } +#ifndef QDS_USE_PROJECTSTORAGE const QmlJS::ScopeChain *RewriterView::scopeChain() const { return textToModelMerger()->scopeChain(); } +#endif const QmlJS::Document *RewriterView::document() const { @@ -989,25 +991,6 @@ QString RewriterView::convertTypeToImportAlias(const QString &type) const return result; } -QString RewriterView::pathForImport(const Import &import) -{ - if (scopeChain() && scopeChain()->context() && document()) { - const QString importStr = import.isFileImport() ? import.file() : import.url(); - const QmlJS::Imports *imports = scopeChain()->context()->imports(document()); - - QmlJS::ImportInfo importInfo; - - for (const QmlJS::Import &qmljsImport : imports->all()) { - if (qmljsImport.info.name() == importStr) - importInfo = qmljsImport.info; - } - const QString importPath = importInfo.path(); - return importPath; - } - - return QString(); -} - QStringList RewriterView::importDirectories() const { const QList<Utils::FilePath> list(m_textToModelMerger->vContext().paths.begin(), diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index f7be6cf5e4..1c1aba5feb 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -57,7 +57,7 @@ using namespace QmlJS; using namespace Qt::StringLiterals; static Q_LOGGING_CATEGORY(rewriterBenchmark, "qtc.rewriter.load", QtWarningMsg) -static Q_LOGGING_CATEGORY(texttomodelMergerDebug, "qtc.texttomodelmerger.debug", QtDebugMsg) +static Q_LOGGING_CATEGORY(texttomodelMergerLog, "qtc.texttomodelmerger", QtWarningMsg) namespace { @@ -67,17 +67,6 @@ bool isSupportedAttachedProperties(const QString &propertyName) || propertyName.startsWith(QLatin1String("InsightCategory.")); } -bool isSupportedVersion(QmlDesigner::Version version) -{ - if (version.major == 2) - return version.minor <= 15; - - if (version.major == 6) - return version.minor <= 6; - - return false; -} - bool isGlobalQtEnums(QStringView value) { static constexpr auto list = Utils::to_array<std::u16string_view>( @@ -95,6 +84,9 @@ bool isGlobalQtEnums(QStringView value) u"TopToBottom", u"UpArrowCursor", u"Vertical", u"WaitCursor", u"WhatsThisCursor", u"WheelFocus"}); + if (value.toString().startsWith("Key_")) + return true; + return std::binary_search(std::begin(list), std::end(list), QmlDesigner::ModelUtils::toStdStringView(value)); @@ -122,12 +114,6 @@ bool isKnownEnumScopes(QStringView value) != std::end(list); } -bool supportedQtQuickVersion(const QmlDesigner::Import &import) -{ - auto version = import.toVersion(); - return version.isEmpty() || isSupportedVersion(version); -} - QString stripQuotes(const QString &str) { if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"'))) @@ -431,14 +417,19 @@ namespace Internal { class ReadingContext { public: - ReadingContext(const Snapshot &snapshot, const Document::Ptr &doc, - const ViewerContext &vContext, Model *model) + ReadingContext([[maybe_unused]] const Snapshot &snapshot, + [[maybe_unused]] const Document::Ptr &doc, + [[maybe_unused]] const ViewerContext &vContext, + Model *model) : m_doc(doc) +#ifndef QDS_USE_PROJECTSTORAGE , m_context( - Link(snapshot, vContext, ModelManagerInterface::instance()->builtins(doc)) - (doc, &m_diagnosticLinkMessages)) + Link(snapshot, + vContext, + ModelManagerInterface::instance()->builtins(doc))(doc, &m_diagnosticLinkMessages)) , m_scopeChain(doc, m_context) , m_scopeBuilder(&m_scopeChain) +#endif , m_model(model) { } @@ -446,12 +437,19 @@ public: ~ReadingContext() = default; Document::Ptr doc() const - { return m_doc; } + { + return m_doc; + } +#ifndef QDS_USE_PROJECTSTORAGE void enterScope(AST::Node *node) { m_scopeBuilder.push(node); } - void leaveScope() { m_scopeBuilder.pop(); } + void leaveScope() + { + m_scopeBuilder.pop(); + } +#endif std::tuple<NodeMetaInfo, TypeName> lookup(AST::UiQualifiedId *astTypeNode) { @@ -481,113 +479,6 @@ public: return node.metaInfo().hasProperty(propertyName.toUtf8()); } - /// When something is changed here, also change Check::checkScopeObjectMember in - /// qmljscheck.cpp - /// ### Maybe put this into the context as a helper function. - /// - bool lookupProperty(const QString &prefix, - const AST::UiQualifiedId *id, - const Value **property = nullptr, - const ObjectValue **parentObject = nullptr, - QString *name = nullptr) - { - QList<const ObjectValue *> scopeObjects = m_scopeChain.qmlScopeObjects(); - if (scopeObjects.isEmpty()) - return false; - - if (!id) - return false; // ### error? - - if (id->name.isEmpty()) // possible after error recovery - return false; - - QString propertyName; - if (prefix.isEmpty()) - propertyName = id->name.toString(); - else - propertyName = prefix; - - if (name) - *name = propertyName; - - if (propertyName == u"id" && !id->next) - return false; // ### should probably be a special value - - // attached properties - bool isAttachedProperty = false; - if (! propertyName.isEmpty() && propertyName[0].isUpper()) { - isAttachedProperty = true; - if (const ObjectValue *qmlTypes = m_scopeChain.qmlTypes()) - scopeObjects += qmlTypes; - } - - if (scopeObjects.isEmpty()) - return false; - - // global lookup for first part of id - const ObjectValue *objectValue = nullptr; - const Value *value = nullptr; - for (int i = scopeObjects.size() - 1; i >= 0; --i) { - objectValue = scopeObjects[i]; - value = objectValue->lookupMember(propertyName, m_context); - if (value) - break; - } - if (parentObject) - *parentObject = objectValue; - if (!value) { - qCInfo(texttomodelMergerDebug) << Q_FUNC_INFO << "Skipping invalid property name" << propertyName; - return false; - } - - // can't look up members for attached properties - if (isAttachedProperty) - return false; - - // resolve references - if (const Reference *ref = value->asReference()) - value = m_context->lookupReference(ref); - - // member lookup - const AST::UiQualifiedId *idPart = id; - if (prefix.isEmpty()) - idPart = idPart->next; - for (; idPart; idPart = idPart->next) { - objectValue = value_cast<ObjectValue>(value); - if (! objectValue) { -// if (idPart->name) -// qDebug() << idPart->name->asString() << "has no property named" -// << propertyName; - return false; - } - if (parentObject) - *parentObject = objectValue; - - if (idPart->name.isEmpty()) { - // somebody typed "id." and error recovery still gave us a valid tree, - // so just bail out here. - return false; - } - - propertyName = idPart->name.toString(); - if (name) - *name = propertyName; - - value = objectValue->lookupMember(propertyName, m_context); - if (! value) { -// if (idPart->name) -// qDebug() << "In" << idPart->name->asString() << ":" -// << objectValue->className() << "has no property named" -// << propertyName; - return false; - } - } - - if (property) - *property = value; - return true; - } - bool isArrayProperty(const AbstractProperty &property) { return ModelUtils::metainfo(property).isListProperty(); @@ -610,9 +501,9 @@ public: if (!propertyMetaInfo.isValid()) { const bool isAttached = !propertyName.isEmpty() && propertyName[0].isUpper(); // Only list elements might have unknown properties. - if (!node.metaInfo().isQtQuickListElement() && !isAttached) { - qCInfo(texttomodelMergerDebug) - << Q_FUNC_INFO << "Unknown property" + if (!node.metaInfo().isQtQmlModelsListElement() && !isAttached) { + qCInfo(texttomodelMergerLog) + << Q_FUNC_INFO << "\nUnknown property" << propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line" << propertyId->identifierToken.startLine << "column" << propertyId->identifierToken.startColumn; @@ -685,9 +576,12 @@ public: return QVariant(); } - +#ifndef QDS_USE_PROJECTSTORAGE const ScopeChain &scopeChain() const - { return m_scopeChain; } + { + return m_scopeChain; + } +#endif QList<DiagnosticMessage> diagnosticLinkMessages() const { return m_diagnosticLinkMessages; } @@ -695,9 +589,11 @@ public: private: Document::Ptr m_doc; QList<DiagnosticMessage> m_diagnosticLinkMessages; +#ifndef QDS_USE_PROJECTSTORAGE ContextPtr m_context; ScopeChain m_scopeChain; ScopeBuilder m_scopeBuilder; +#endif Model *m_model; }; @@ -841,6 +737,7 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), Equals(u"QtQuick.Controls.NativeStyle"), Equals(u"QtQuick.Controls.Universal"), Equals(u"QtQuick.Controls.Windows"), + Equals(u"QtQuick3D.MaterialEditor"), StartsWith(u"QtQuick.LocalStorage"), StartsWith(u"QtQuick.NativeStyle"), StartsWith(u"QtQuick.Pdf"), @@ -866,6 +763,7 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), StartsWith(u"QtWebSockets"), StartsWith(u"QtWebView")); +#ifndef QDS_USE_PROJECTSTORAGE bool skipModule(QStringView moduleName) { return std::apply([=](const auto &...skipModule) { return (skipModule(moduleName) || ...); }, @@ -931,9 +829,11 @@ QmlDesigner::Imports createQt5Modules() QmlDesigner::Import::createLibraryImport("QtQuick.Studio.MultiText", "1.0"), QmlDesigner::Import::createLibraryImport("Qt.SafeRenderer", "2.0")}; } +#endif } // namespace +#ifndef QDS_USE_PROJECTSTORAGE void TextToModelMerger::setupPossibleImports() { if (!m_rewriterView->possibleImportsEnabled()) @@ -942,10 +842,10 @@ void TextToModelMerger::setupPossibleImports() static QUrl lastProjectUrl; auto &externalDependencies = m_rewriterView->externalDependencies(); auto projectUrl = externalDependencies.projectUrl(); + auto allUsedImports = m_scopeChain->context()->imports(m_document.data())->all(); if (m_possibleModules.isEmpty() || projectUrl != lastProjectUrl) { - auto &externalDependencies = m_rewriterView->externalDependencies(); if (externalDependencies.isQt6Project()) { ModuleScanner moduleScanner{[&](QStringView moduleName) { @@ -975,7 +875,9 @@ void TextToModelMerger::setupPossibleImports() if (m_rewriterView->isAttached()) m_rewriterView->model()->setPossibleImports(modules); } +#endif +#ifndef QDS_USE_PROJECTSTORAGE void TextToModelMerger::setupUsedImports() { const QmlJS::Imports *imports = m_scopeChain->context()->imports(m_document.data()); @@ -1010,6 +912,7 @@ void TextToModelMerger::setupUsedImports() if (m_rewriterView->isAttached()) m_rewriterView->model()->setUsedImports(usedImports); } +#endif Document::MutablePtr TextToModelMerger::createParsedDocument(const QUrl &url, const QString &data, QList<DocumentMessage> *errors) { @@ -1100,15 +1003,16 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH m_vContext = ModelManagerInterface::instance()->projectVContext(Dialect::Qml, m_document); ReadingContext ctxt(snapshot, m_document, m_vContext, m_rewriterView->model()); - m_scopeChain = QSharedPointer<const ScopeChain>( - new ScopeChain(ctxt.scopeChain())); + +#ifndef QDS_USE_PROJECTSTORAGE + m_scopeChain = QSharedPointer<const ScopeChain>(new ScopeChain(ctxt.scopeChain())); if (view()->checkLinkErrors()) { qCInfo(rewriterBenchmark) << "linked:" << time.elapsed(); collectLinkErrors(&errors, ctxt); } - setupPossibleImports(); +#endif qCInfo(rewriterBenchmark) << "possible imports:" << time.elapsed(); @@ -1142,7 +1046,9 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH qCInfo(rewriterBenchmark) << "synced nodes:" << time.elapsed(); +#ifndef QDS_USE_PROJECTSTORAGE setupUsedImports(); +#endif setActive(false); @@ -1239,8 +1145,9 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, else if (!modelNode.nodeSource().isEmpty() || modelNode.nodeSourceType() != ModelNode::NodeWithoutSource) clearImplicitComponentDelayed(modelNode, differenceHandler.isAmender()); - +#ifndef QDS_USE_PROJECTSTORAGE context->enterScope(astNode); +#endif QSet<PropertyName> modelPropertyNames = Utils::toSet(modelNode.propertyNames()); if (!modelNode.id().isEmpty()) @@ -1254,7 +1161,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, if (auto array = AST::cast<AST::UiArrayBinding *>(member)) { const QString astPropertyName = toString(array->qualifiedId); - if (isPropertyChangesType(typeName) || isConnectionsType(typeName) || context->lookupProperty(QString(), array->qualifiedId)) { + if (isPropertyChangesType(typeName) || isConnectionsType(typeName) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); QList<AST::UiObjectMember *> arrayMembers; for (AST::UiArrayMemberList *iter = array->members; iter; iter = iter->next) @@ -1286,13 +1194,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, // Store Behaviours in the default property defaultPropertyItems.append(member); } else { - const Value *propertyType = nullptr; - const ObjectValue *containingObject = nullptr; - if (context->lookupProperty({}, - binding->qualifiedId, - &propertyType, - &containingObject) - || isPropertyChangesType(typeName) || isConnectionsType(typeName)) { + if (isPropertyChangesType(typeName) || isConnectionsType(typeName) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); if (context->isArrayProperty(modelProperty)) syncArrayProperty(modelProperty, {member}, context, differenceHandler); @@ -1396,7 +1299,9 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, differenceHandler.propertyAbsentFromQml(modelProperty); } +#ifndef QDS_USE_PROJECTSTORAGE context->leaveScope(); +#endif } static QVariant parsePropertyExpression(AST::ExpressionNode *expressionNode) @@ -1476,9 +1381,8 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN } if (isLiteralValue(script)) { - if (isPropertyChangesType(modelNode.type()) - || isConnectionsType(modelNode.type()) - || isListElementType(modelNode.type())) { + if (isPropertyChangesType(modelNode.type()) || isConnectionsType(modelNode.type()) + || isListElementType(modelNode.type())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); QVariant variantValue = parsePropertyScriptBinding(script); if (!variantValue.isValid()) @@ -1512,15 +1416,14 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN syncVariantProperty(modelProperty, enumValue, TypeName(), differenceHandler); // TODO: parse type return astPropertyName.toUtf8(); } else { // Not an enum, so: - if (isPropertyChangesType(modelNode.type()) - || isConnectionsType(modelNode.type()) - || context->lookupProperty(prefix, script->qualifiedId) - || isSupportedAttachedProperties(astPropertyName)) { + if (isPropertyChangesType(modelNode.type()) || isConnectionsType(modelNode.type()) + || isSupportedAttachedProperties(astPropertyName) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); syncExpressionProperty(modelProperty, astValue, TypeName(), differenceHandler); // TODO: parse type return astPropertyName.toUtf8(); } else { - qWarning() << Q_FUNC_INFO << "Skipping invalid expression property" << astPropertyName + qCInfo(texttomodelMergerLog) << Q_FUNC_INFO << "\nSkipping invalid expression property" << astPropertyName << "for node type" << modelNode.type(); return PropertyName(); } @@ -2232,42 +2135,31 @@ void TextToModelMerger::collectImportErrors(QList<DocumentMessage> *errors) bool hasQtQuick = false; for (const QmlDesigner::Import &import : m_rewriterView->model()->imports()) { if (import.isLibraryImport() && import.url() == u"QtQuick") { - if (supportedQtQuickVersion(import)) { - hasQtQuick = true; - - auto &externalDependencies = m_rewriterView->externalDependencies(); - if (externalDependencies.hasStartupTarget()) { - const bool qt6import = !import.hasVersion() || import.majorVersion() == 6; - - if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) { - const QmlJS::DiagnosticMessage diagnosticMessage( - QmlJS::Severity::Error, - SourceLocation(0, 0, 0, 0), - QCoreApplication::translate( - "QmlDesigner::TextToModelMerger", - "Qt Quick 6 is not supported with a Qt 5 kit.")); - errors->prepend( - DocumentMessage(diagnosticMessage, - QUrl::fromLocalFile(m_document->fileName().path()))); - } - } else { + hasQtQuick = true; + + auto &externalDependencies = m_rewriterView->externalDependencies(); + if (externalDependencies.hasStartupTarget()) { + const bool qt6import = !import.hasVersion() || import.majorVersion() == 6; + + if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) { const QmlJS::DiagnosticMessage diagnosticMessage( QmlJS::Severity::Error, SourceLocation(0, 0, 0, 0), - QCoreApplication::translate("QmlDesigner::TextToModelMerger", - "The Design Mode requires a valid Qt kit.")); + QCoreApplication::translate( + "QmlDesigner::TextToModelMerger", + "Qt Quick 6 is not supported with a Qt 5 kit.")); errors->prepend( DocumentMessage(diagnosticMessage, QUrl::fromLocalFile(m_document->fileName().path()))); } } else { - const QmlJS::DiagnosticMessage - diagnosticMessage(QmlJS::Severity::Error, - SourceLocation(0, 0, 0, 0), - QCoreApplication::translate("QmlDesigner::TextToModelMerger", - "Unsupported Qt Quick version.")); - errors->append(DocumentMessage(diagnosticMessage, - QUrl::fromLocalFile(m_document->fileName().path()))); + const QmlJS::DiagnosticMessage diagnosticMessage( + QmlJS::Severity::Error, + SourceLocation(0, 0, 0, 0), + QCoreApplication::translate("QmlDesigner::TextToModelMerger", + "The Design Mode requires a valid Qt kit.")); + errors->prepend(DocumentMessage(diagnosticMessage, + QUrl::fromLocalFile(m_document->fileName().path()))); } } } @@ -2276,8 +2168,10 @@ void TextToModelMerger::collectImportErrors(QList<DocumentMessage> *errors) errors->append(DocumentMessage(QCoreApplication::translate("QmlDesigner::TextToModelMerger", "No import for Qt Quick found."))); } -void TextToModelMerger::collectSemanticErrorsAndWarnings(QList<DocumentMessage> *errors, QList<DocumentMessage> *warnings) +void TextToModelMerger::collectSemanticErrorsAndWarnings( + [[maybe_unused]] QList<DocumentMessage> *errors, [[maybe_unused]] QList<DocumentMessage> *warnings) { +#ifndef QDS_USE_PROJECTSTORAGE Check check(m_document, m_scopeChain->context()); check.disableMessage(StaticAnalysis::ErrPrototypeCycle); check.disableMessage(StaticAnalysis::ErrCouldNotResolvePrototype); @@ -2306,6 +2200,7 @@ void TextToModelMerger::collectSemanticErrorsAndWarnings(QList<DocumentMessage> if (message.severity == Severity::Warning) warnings->append(DocumentMessage(message.toDiagnosticMessage(), fileNameUrl)); } +#endif } void TextToModelMerger::populateQrcMapping(const QString &filePath) @@ -2410,6 +2305,9 @@ QSet<QPair<QString, QString> > TextToModelMerger::qrcMapping() const QList<QmlTypeData> TextToModelMerger::getQMLSingletons() const { +#ifdef QDS_USE_PROJECTSTORAGE + return {}; +#else QList<QmlTypeData> list; if (!m_scopeChain || !m_scopeChain->document()) return list; @@ -2440,6 +2338,7 @@ QList<QmlTypeData> TextToModelMerger::getQMLSingletons() const } } return list; +#endif } void TextToModelMerger::clearPossibleImportKeys() diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h index f511906040..e22f747718 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h @@ -37,15 +37,19 @@ public: bool isActive() const; void setupImports(const QmlJS::Document::Ptr &doc, DifferenceHandler &differenceHandler); +#ifndef QDS_USE_PROJECTSTORAGE void setupPossibleImports(); +#endif void setupUsedImports(); bool load(const QString &data, DifferenceHandler &differenceHandler); RewriterView *view() const { return m_rewriterView; } +#ifndef QDS_USE_PROJECTSTORAGE const QmlJS::ScopeChain *scopeChain() const { return m_scopeChain.data(); } +#endif const QmlJS::Document *document() const { return m_document.data(); } @@ -141,7 +145,9 @@ private: private: RewriterView *m_rewriterView; bool m_isActive; +#ifndef QDS_USE_PROJECTSTORAGE QSharedPointer<const QmlJS::ScopeChain> m_scopeChain; +#endif QmlJS::Document::Ptr m_document; QTimer m_setupTimer; QSet<ModelNode> m_setupComponentList; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 03c25dfac7..35658c005f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -91,6 +91,7 @@ inline constexpr char QtMultimedia[] = "QtMultimedia"; inline constexpr char QtObject[] = "QtObject"; inline constexpr char QtQml[] = "QtQml"; inline constexpr char QtQml_Models[] = "QtQml.Models"; +inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel"; inline constexpr char QtQuick3D[] = "QtQuick3D"; inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D"; inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative"; @@ -131,6 +132,7 @@ inline constexpr char Transition[] = "Transition"; inline constexpr char UIntType[] = "uint"; inline constexpr char View3D[] = "View3D"; inline constexpr char Window[] = "Window"; +inline constexpr char XmlListModelRole[] = "XmlListModelRole"; inline constexpr char color[] = "color"; inline constexpr char date[] = "date"; inline constexpr char font[] = "font"; @@ -176,6 +178,7 @@ class CommonTypeCache CacheType<QtMultimedia, SoundEffect>, CacheType<QtQml_Models, ListElement>, CacheType<QtQml_Models, ListModel>, + CacheType<QtQml_XmlListModel, XmlListModelRole>, CacheType<QtQuick, BorderImage>, CacheType<QtQuick, GridView>, CacheType<QtQuick, Image>, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h index f3e275b8f3..48b3ba2700 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h @@ -50,6 +50,18 @@ public: explicit operator bool() const { return isValid(); } + template<typename String> + friend void convertToString(String &string, const FileStatus &fileStatus) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("source id", fileStatus.sourceId), + keyValue("size", fileStatus.size), + keyValue("last modified", fileStatus.lastModified)); + + convertToString(string, dict); + } + public: SourceId sourceId; long long size = -1; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h index 078fd1ee98..28754a8560 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h @@ -6,6 +6,7 @@ #include "filestatuscache.h" #include "filesysteminterface.h" #include "nonlockingmutex.h" +#include "projectstoragefwd.h" namespace Sqlite { class Database; @@ -16,12 +17,9 @@ namespace QmlDesigner { template<typename ProjectStorage, typename Mutex> class SourcePathCache; -template<typename Database> -class ProjectStorage; - class FileSystem : public FileSystemInterface { - using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>; + using PathCache = SourcePathCache<ProjectStorage, NonLockingMutex>; public: FileSystem(PathCache &sourcePathCache) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 3e493e8772..a7577d3ab7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -3,20 +3,4542 @@ #include "projectstorage.h" -#include <tracing/qmldesignertracing.h> - #include <sqlitedatabase.h> namespace QmlDesigner { -NanotraceHR::StringViewCategory<projectStorageTracingStatus()> &projectStorageCategory() +struct ProjectStorage::Statements +{ + Statements(Sqlite::Database &database) + : database{database} + {} + + Sqlite::Database &database; + Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{ + "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; + Sqlite::WriteStatement<5> updatePrototypeAndExtensionStatement{ + "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " + "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId " + "IS NOT ?4 OR extensionNameId IS NOT ?5)", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{ + "SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; + mutable Sqlite::ReadStatement<1, 2> selectTypeIdByModuleIdAndExportedNameStatement{ + "SELECT typeId FROM exportedTypeNames " + "WHERE moduleId=?1 AND name=?2 " + "ORDER BY majorVersion DESC, minorVersion DESC " + "LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement{ + "SELECT typeId FROM exportedTypeNames " + "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3" + "ORDER BY minorVersion DESC " + "LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 4> selectTypeIdByModuleIdAndExportedNameAndVersionStatement{ + "SELECT typeId FROM exportedTypeNames " + "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3 AND minorVersion<=?4" + "ORDER BY minorVersion DESC " + "LIMIT 1", + database}; + mutable Sqlite::ReadStatement<3, 1> selectPropertyDeclarationResultByPropertyDeclarationIdStatement{ + "SELECT propertyTypeId, propertyDeclarationId, propertyTraits " + "FROM propertyDeclarations " + "WHERE propertyDeclarationId=?1 " + "LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{ + "SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database}; + mutable Sqlite::ReadStatement<1, 1> selectSourceContextPathFromSourceContextsBySourceContextIdStatement{ + "SELECT sourceContextPath FROM sourceContexts WHERE sourceContextId = ?", database}; + mutable Sqlite::ReadStatement<2> selectAllSourceContextsStatement{ + "SELECT sourceContextPath, sourceContextId FROM sourceContexts", database}; + Sqlite::WriteStatement<1> insertIntoSourceContextsStatement{ + "INSERT INTO sourceContexts(sourceContextPath) VALUES (?)", database}; + mutable Sqlite::ReadStatement<1, 2> selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement{ + "SELECT sourceId FROM sources WHERE sourceContextId = ? AND sourceName = ?", database}; + mutable Sqlite::ReadStatement<2, 1> selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement{ + "SELECT sourceName, sourceContextId FROM sources WHERE sourceId = ?", database}; + mutable Sqlite::ReadStatement<1, 1> selectSourceContextIdFromSourcesBySourceIdStatement{ + "SELECT sourceContextId FROM sources WHERE sourceId = ?", database}; + Sqlite::WriteStatement<2> insertIntoSourcesStatement{ + "INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database}; + mutable Sqlite::ReadStatement<3> selectAllSourcesStatement{ + "SELECT sourceName, sourceContextId, sourceId FROM sources", database}; + mutable Sqlite::ReadStatement<8, 1> selectTypeByTypeIdStatement{ + "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " + "pd.name " + "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " + "defaultPropertyId=propertyDeclarationId " + "WHERE t.typeId=?", + database}; + mutable Sqlite::ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{ + "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM " + "exportedTypeNames WHERE typeId=?", + database}; + mutable Sqlite::ReadStatement<4, 2> selectExportedTypesByTypeIdAndSourceIdStatement{ + "SELECT etn.moduleId, name, ifnull(etn.majorVersion, -1), ifnull(etn.minorVersion, -1) " + "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND " + "sourceId=?2", + database}; + mutable Sqlite::ReadStatement<8> selectTypesStatement{ + "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " + "pd.name " + "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " + "defaultPropertyId=propertyDeclarationId", + database}; + Sqlite::WriteStatement<2> updateTypeTraitStatement{ + "UPDATE types SET traits = ?2 WHERE typeId=?1", database}; + Sqlite::WriteStatement<2> updateTypeAnnotationTraitStatement{ + "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database}; + Sqlite::ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{ + "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN " + "carray(?2))", + database}; + Sqlite::WriteStatement<1> deleteTypeNamesByTypeIdStatement{ + "DELETE FROM exportedTypeNames WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deleteEnumerationDeclarationByTypeIdStatement{ + "DELETE FROM enumerationDeclarations WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deletePropertyDeclarationByTypeIdStatement{ + "DELETE FROM propertyDeclarations WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deleteFunctionDeclarationByTypeIdStatement{ + "DELETE FROM functionDeclarations WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deleteSignalDeclarationByTypeIdStatement{ + "DELETE FROM signalDeclarations WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<4, 1> selectPropertyDeclarationsByTypeIdStatement{ + "SELECT name, propertyTypeId, propertyTraits, (SELECT name FROM " + "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM " + "propertyDeclarations AS pd WHERE typeId=?", + database}; + Sqlite::ReadStatement<6, 1> selectPropertyDeclarationsForTypeIdStatement{ + "SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, " + "propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " + "WHERE typeId=? ORDER BY name", + database}; + Sqlite::ReadWriteStatement<1, 5> insertPropertyDeclarationStatement{ + "INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, " + "propertyImportedTypeNameId, aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5, NULL) " + "RETURNING propertyDeclarationId", + database}; + Sqlite::WriteStatement<4> updatePropertyDeclarationStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " + "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=NULL WHERE " + "propertyDeclarationId=?1", + database}; + Sqlite::WriteStatement<3> updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement{ + "WITH RECURSIVE " + " properties(aliasPropertyDeclarationId) AS ( " + " SELECT propertyDeclarationId FROM propertyDeclarations WHERE " + " aliasPropertyDeclarationId=?1 " + " UNION ALL " + " SELECT pd.propertyDeclarationId FROM " + " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) " + "UPDATE propertyDeclarations AS pd " + "SET propertyTypeId=?2, propertyTraits=?3 " + "FROM properties AS p " + "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId", + database}; + Sqlite::WriteStatement<1> updatePropertyAliasDeclarationRecursivelyStatement{ + "WITH RECURSIVE " + " propertyValues(propertyTypeId, propertyTraits) AS (" + " SELECT propertyTypeId, propertyTraits FROM propertyDeclarations " + " WHERE propertyDeclarationId=?1), " + " properties(aliasPropertyDeclarationId) AS ( " + " SELECT propertyDeclarationId FROM propertyDeclarations WHERE " + " aliasPropertyDeclarationId=?1 " + " UNION ALL " + " SELECT pd.propertyDeclarationId FROM " + " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) " + "UPDATE propertyDeclarations AS pd " + "SET propertyTypeId=pv.propertyTypeId, propertyTraits=pv.propertyTraits " + "FROM properties AS p, propertyValues AS pv " + "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId", + database}; + Sqlite::WriteStatement<1> deletePropertyDeclarationStatement{ + "DELETE FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; + Sqlite::ReadStatement<3, 1> selectPropertyDeclarationsWithAliasForTypeIdStatement{ + "SELECT name, propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " + "WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL ORDER BY name", + database}; + Sqlite::WriteStatement<5> updatePropertyDeclarationWithAliasAndTypeStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " + "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=?5 WHERE " + "propertyDeclarationId=?1", + database}; + Sqlite::ReadWriteStatement<1, 2> insertAliasPropertyDeclarationStatement{ + "INSERT INTO propertyDeclarations(typeId, name) VALUES(?1, ?2) RETURNING " + "propertyDeclarationId", + database}; + mutable Sqlite::ReadStatement<4, 1> selectFunctionDeclarationsForTypeIdStatement{ + "SELECT name, returnTypeName, signature, functionDeclarationId FROM " + "functionDeclarations WHERE typeId=? ORDER BY name, signature", + database}; + mutable Sqlite::ReadStatement<3, 1> selectFunctionDeclarationsForTypeIdWithoutSignatureStatement{ + "SELECT name, returnTypeName, functionDeclarationId FROM " + "functionDeclarations WHERE typeId=? ORDER BY name", + database}; + mutable Sqlite::ReadStatement<3, 1> selectFunctionParameterDeclarationsStatement{ + "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), " + "json_extract(json_each.value, '$.tr') FROM functionDeclarations, " + "json_each(functionDeclarations.signature) WHERE functionDeclarationId=?", + database}; + Sqlite::WriteStatement<4> insertFunctionDeclarationStatement{ + "INSERT INTO functionDeclarations(typeId, name, returnTypeName, signature) VALUES(?1, ?2, " + "?3, ?4)", + database}; + Sqlite::WriteStatement<3> updateFunctionDeclarationStatement{ + "UPDATE functionDeclarations " + "SET returnTypeName=?2, signature=?3 " + "WHERE functionDeclarationId=?1", + database}; + Sqlite::WriteStatement<1> deleteFunctionDeclarationStatement{ + "DELETE FROM functionDeclarations WHERE functionDeclarationId=?", database}; + mutable Sqlite::ReadStatement<3, 1> selectSignalDeclarationsForTypeIdStatement{ + "SELECT name, signature, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER " + "BY name, signature", + database}; + mutable Sqlite::ReadStatement<2, 1> selectSignalDeclarationsForTypeIdWithoutSignatureStatement{ + "SELECT name, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER BY name", + database}; + mutable Sqlite::ReadStatement<3, 1> selectSignalParameterDeclarationsStatement{ + "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), " + "json_extract(json_each.value, '$.tr') FROM signalDeclarations, " + "json_each(signalDeclarations.signature) WHERE signalDeclarationId=?", + database}; + Sqlite::WriteStatement<3> insertSignalDeclarationStatement{ + "INSERT INTO signalDeclarations(typeId, name, signature) VALUES(?1, ?2, ?3)", database}; + Sqlite::WriteStatement<2> updateSignalDeclarationStatement{ + "UPDATE signalDeclarations SET signature=?2 WHERE signalDeclarationId=?1", database}; + Sqlite::WriteStatement<1> deleteSignalDeclarationStatement{ + "DELETE FROM signalDeclarations WHERE signalDeclarationId=?", database}; + mutable Sqlite::ReadStatement<3, 1> selectEnumerationDeclarationsForTypeIdStatement{ + "SELECT name, enumeratorDeclarations, enumerationDeclarationId FROM " + "enumerationDeclarations WHERE typeId=? ORDER BY name", + database}; + mutable Sqlite::ReadStatement<2, 1> selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement{ + "SELECT name, enumerationDeclarationId FROM enumerationDeclarations WHERE typeId=? ORDER " + "BY name", + database}; + mutable Sqlite::ReadStatement<3, 1> selectEnumeratorDeclarationStatement{ + "SELECT json_each.key, json_each.value, json_each.type!='null' FROM " + "enumerationDeclarations, json_each(enumerationDeclarations.enumeratorDeclarations) WHERE " + "enumerationDeclarationId=?", + database}; + Sqlite::WriteStatement<3> insertEnumerationDeclarationStatement{ + "INSERT INTO enumerationDeclarations(typeId, name, enumeratorDeclarations) VALUES(?1, ?2, " + "?3)", + database}; + Sqlite::WriteStatement<2> updateEnumerationDeclarationStatement{ + "UPDATE enumerationDeclarations SET enumeratorDeclarations=?2 WHERE " + "enumerationDeclarationId=?1", + database}; + Sqlite::WriteStatement<1> deleteEnumerationDeclarationStatement{ + "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectModuleIdByNameStatement{ + "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database}; + mutable Sqlite::ReadWriteStatement<1, 1> insertModuleNameStatement{ + "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database}; + mutable Sqlite::ReadStatement<1, 1> selectModuleNameStatement{ + "SELECT name FROM modules WHERE moduleId =?1", database}; + mutable Sqlite::ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", + database}; + mutable Sqlite::ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{ + "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database}; + mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{ + "SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND " + "name=?3", + database}; + mutable Sqlite::ReadStatement<4> selectAllDocumentImportForSourceIdStatement{ + "SELECT moduleId, majorVersion, minorVersion, sourceId " + "FROM documentImports ", + database}; + mutable Sqlite::ReadStatement<5, 2> selectDocumentImportForSourceIdStatement{ + "SELECT importId, sourceId, moduleId, majorVersion, minorVersion " + "FROM documentImports WHERE sourceId IN carray(?1) AND kind=?2 ORDER BY sourceId, " + "moduleId, majorVersion, minorVersion", + database}; + Sqlite::ReadWriteStatement<1, 5> insertDocumentImportWithoutVersionStatement{ + "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, " + "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING importId", + database}; + Sqlite::ReadWriteStatement<1, 6> insertDocumentImportWithMajorVersionStatement{ + "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " + "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6) RETURNING importId", + database}; + Sqlite::ReadWriteStatement<1, 7> insertDocumentImportWithVersionStatement{ + "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " + "minorVersion, parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) RETURNING " + "importId", + database}; + Sqlite::WriteStatement<1> deleteDocumentImportStatement{ + "DELETE FROM documentImports WHERE importId=?1", database}; + Sqlite::WriteStatement<2> deleteDocumentImportsWithParentImportIdStatement{ + "DELETE FROM documentImports WHERE sourceId=?1 AND parentImportId=?2", database}; + Sqlite::WriteStatement<1> deleteDocumentImportsWithSourceIdsStatement{ + "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database}; + mutable Sqlite::ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{ + "SELECT propertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=?1 AND name=?2 " + "LIMIT 1", + database}; + Sqlite::WriteStatement<2> updateAliasIdPropertyDeclarationStatement{ + "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2 WHERE " + "aliasPropertyDeclarationId=?1", + database}; + Sqlite::WriteStatement<2> updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=new.propertyTypeId, " + "propertyTraits=new.propertyTraits, aliasPropertyDeclarationId=?1 FROM (SELECT " + "propertyTypeId, propertyTraits FROM propertyDeclarations WHERE propertyDeclarationId=?1) " + "AS new WHERE aliasPropertyDeclarationId=?2", + database}; + Sqlite::WriteStatement<1> updateAliasPropertyDeclarationToNullStatement{ + "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL, propertyTypeId=NULL, " + "propertyTraits=NULL WHERE propertyDeclarationId=? AND (aliasPropertyDeclarationId IS NOT " + "NULL OR propertyTypeId IS NOT NULL OR propertyTraits IS NOT NULL)", + database}; + Sqlite::ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{ + "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE alias.propertyTypeId=?1 " + "UNION ALL " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE target.typeId=?1 " + "UNION ALL " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE alias.propertyImportedTypeNameId IN " + " (SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) " + " WHERE typeId=?1)", + database}; + Sqlite::ReadStatement<3, 1> selectAliasPropertiesDeclarationForPropertiesWithAliasIdStatement{ + "WITH RECURSIVE " + " properties(propertyDeclarationId, propertyImportedTypeNameId, typeId, " + " aliasPropertyDeclarationId) AS (" + " SELECT propertyDeclarationId, propertyImportedTypeNameId, typeId, " + " aliasPropertyDeclarationId FROM propertyDeclarations WHERE " + " aliasPropertyDeclarationId=?1" + " UNION ALL " + " SELECT pd.propertyDeclarationId, pd.propertyImportedTypeNameId, pd.typeId, " + " pd.aliasPropertyDeclarationId FROM propertyDeclarations AS pd JOIN properties AS " + " p ON pd.aliasPropertyDeclarationId=p.propertyDeclarationId)" + "SELECT propertyDeclarationId, propertyImportedTypeNameId, aliasPropertyDeclarationId " + " FROM properties", + database}; + Sqlite::ReadWriteStatement<3, 1> updatesPropertyDeclarationPropertyTypeToNullStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=NULL WHERE propertyTypeId=?1 AND " + "aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, " + "propertyImportedTypeNameId", + database}; + mutable Sqlite::ReadStatement<1, 1> selectPropertyNameStatement{ + "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; + Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database}; + Sqlite::ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{ + "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING " + "typeId, prototypeNameId", + database}; + Sqlite::ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{ + "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING " + "typeId, extensionNameId", + database}; + Sqlite::WriteStatement<2> updateTypePrototypeStatement{ + "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; + Sqlite::WriteStatement<2> updateTypeExtensionStatement{ + "UPDATE types SET extensionId=?2 WHERE typeId=?1", database}; + mutable Sqlite::ReadStatement<1, 1> selectPrototypeAndExtensionIdsStatement{ + "WITH RECURSIVE " + " prototypes(typeId) AS ( " + " SELECT prototypeId FROM types WHERE typeId=?1 " + " UNION ALL " + " SELECT extensionId FROM types WHERE typeId=?1 " + " UNION ALL " + " SELECT prototypeId FROM types JOIN prototypes USING(typeId) " + " UNION ALL " + " SELECT extensionId FROM types JOIN prototypes USING(typeId)) " + "SELECT typeId FROM prototypes WHERE typeId IS NOT NULL", + database}; + Sqlite::WriteStatement<3> updatePropertyDeclarationAliasIdAndTypeNameIdStatement{ + "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2, " + "propertyImportedTypeNameId=?3 WHERE propertyDeclarationId=?1 AND " + "(aliasPropertyDeclarationId IS NOT ?2 OR propertyImportedTypeNameId IS NOT ?3)", + database}; + Sqlite::WriteStatement<1> updatetPropertiesDeclarationValuesOfAliasStatement{ + "WITH RECURSIVE " + " properties(propertyDeclarationId, propertyTypeId, propertyTraits) AS ( " + " SELECT aliasPropertyDeclarationId, propertyTypeId, propertyTraits FROM " + " propertyDeclarations WHERE propertyDeclarationId=?1 " + " UNION ALL " + " SELECT pd.aliasPropertyDeclarationId, pd.propertyTypeId, pd.propertyTraits FROM " + " propertyDeclarations AS pd JOIN properties USING(propertyDeclarationId)) " + "UPDATE propertyDeclarations AS pd SET propertyTypeId=p.propertyTypeId, " + " propertyTraits=p.propertyTraits " + "FROM properties AS p " + "WHERE pd.propertyDeclarationId=?1 AND p.propertyDeclarationId IS NULL AND " + " (pd.propertyTypeId IS NOT p.propertyTypeId OR pd.propertyTraits IS NOT " + " p.propertyTraits)", + database}; + Sqlite::WriteStatement<1> updatePropertyDeclarationAliasIdToNullStatement{ + "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL WHERE " + "propertyDeclarationId=?1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectPropertyDeclarationIdsForAliasChainStatement{ + "WITH RECURSIVE " + " properties(propertyDeclarationId) AS ( " + " SELECT aliasPropertyDeclarationId FROM propertyDeclarations WHERE " + " propertyDeclarationId=?1 " + " UNION ALL " + " SELECT aliasPropertyDeclarationId FROM propertyDeclarations JOIN properties " + " USING(propertyDeclarationId)) " + "SELECT propertyDeclarationId FROM properties", + database}; + mutable Sqlite::ReadStatement<3> selectAllFileStatusesStatement{ + "SELECT sourceId, size, lastModified FROM fileStatuses ORDER BY sourceId", database}; + mutable Sqlite::ReadStatement<3, 1> selectFileStatusesForSourceIdsStatement{ + "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId IN carray(?1) ORDER " + "BY sourceId", + database}; + mutable Sqlite::ReadStatement<3, 1> selectFileStatusesForSourceIdStatement{ + "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId=?1 ORDER BY sourceId", + database}; + Sqlite::WriteStatement<3> insertFileStatusStatement{ + "INSERT INTO fileStatuses(sourceId, size, lastModified) VALUES(?1, ?2, ?3)", database}; + Sqlite::WriteStatement<1> deleteFileStatusStatement{ + "DELETE FROM fileStatuses WHERE sourceId=?1", database}; + Sqlite::WriteStatement<3> updateFileStatusStatement{ + "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database}; + Sqlite::ReadStatement<1, 1> selectTypeIdBySourceIdStatement{ + "SELECT typeId FROM types WHERE sourceId=?", database}; + mutable Sqlite::ReadStatement<1, 3> selectImportedTypeNameIdStatement{ + "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 " + "AND name=?3 LIMIT 1", + database}; + mutable Sqlite::ReadWriteStatement<1, 3> insertImportedTypeNameIdStatement{ + "INSERT INTO importedTypeNames(kind, importOrSourceId, name) VALUES (?1, ?2, ?3) " + "RETURNING importedTypeNameId", + database}; + mutable Sqlite::ReadStatement<1, 2> selectImportIdBySourceIdAndModuleIdStatement{ + "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND majorVersion " + "IS NULL AND minorVersion IS NULL LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 3> selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement{ + "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " + "majorVersion=?3 AND minorVersion IS NULL LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 4> selectImportIdBySourceIdAndModuleIdAndVersionStatement{ + "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " + "majorVersion=?3 AND minorVersion=?4 LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectKindFromImportedTypeNamesStatement{ + "SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database}; + mutable Sqlite::ReadStatement<1, 1> selectNameFromImportedTypeNamesStatement{ + "SELECT name FROM importedTypeNames WHERE importedTypeNameId=?1", database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{ + "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " + "importOrSourceId=di.importId JOIN documentImports AS di2 ON di.sourceId=di2.sourceId AND " + "di.moduleId=di2.sourceModuleId " + "JOIN exportedTypeNames AS etn ON di2.moduleId=etn.moduleId WHERE " + "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND " + "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS " + "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY etn.majorVersion DESC NULLS FIRST, " + "etn.minorVersion DESC NULLS FIRST LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{ + "WITH " + " importTypeNames(moduleId, name, kind, majorVersion, minorVersion) AS ( " + " SELECT moduleId, name, di.kind, majorVersion, minorVersion " + " FROM importedTypeNames AS itn JOIN documentImports AS di ON " + " importOrSourceId=sourceId " + " WHERE " + " importedTypeNameId=?1 AND itn.kind=1) " + "SELECT typeId FROM importTypeNames AS itn " + " JOIN exportedTypeNames AS etn USING(moduleId, name) " + "WHERE (itn.majorVersion IS NULL OR (itn.majorVersion=etn.majorVersion " + " AND (itn.minorVersion IS NULL OR itn.minorVersion>=etn.minorVersion))) " + "ORDER BY itn.kind, etn.majorVersion DESC NULLS FIRST, etn.minorVersion DESC NULLS FIRST " + "LIMIT 1", + database}; + Sqlite::WriteStatement<0> deleteAllSourcesStatement{"DELETE FROM sources", database}; + Sqlite::WriteStatement<0> deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database}; + mutable Sqlite::ReadStatement<6, 1> selectExportedTypesForSourceIdsStatement{ + "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, " + "exportedTypeNameId FROM exportedTypeNames WHERE typeId in carray(?1) ORDER BY moduleId, " + "name, majorVersion, minorVersion", + database}; + Sqlite::WriteStatement<5> insertExportedTypeNamesWithVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, minorVersion, typeId) " + "VALUES(?1, ?2, ?3, ?4, ?5)", + database}; + Sqlite::WriteStatement<4> insertExportedTypeNamesWithMajorVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, typeId) " + "VALUES(?1, ?2, ?3, ?4)", + database}; + Sqlite::WriteStatement<3> insertExportedTypeNamesWithoutVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, typeId) VALUES(?1, ?2, ?3)", database}; + Sqlite::WriteStatement<1> deleteExportedTypeNameStatement{ + "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; + Sqlite::WriteStatement<2> updateExportedTypeNameTypeIdStatement{ + "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; + mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{ + "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId", + database}; + Sqlite::WriteStatement<4> insertProjectDataStatement{ + "INSERT INTO projectDatas(projectSourceId, sourceId, " + "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)", + database}; + Sqlite::WriteStatement<2> deleteProjectDataStatement{ + "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database}; + Sqlite::WriteStatement<4> updateProjectDataStatement{ + "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2", + database}; + mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{ + "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + "projectSourceId=?1", + database}; + mutable Sqlite::ReadStatement<4, 1> selectProjectDataForSourceIdStatement{ + "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + "sourceId=?1 LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{ + "SELECT typeId FROM types WHERE sourceId IN carray(?1)", database}; + mutable Sqlite::ReadStatement<6, 1> selectModuleExportedImportsForSourceIdStatement{ + "SELECT moduleExportedImportId, moduleId, exportedModuleId, ifnull(majorVersion, -1), " + "ifnull(minorVersion, -1), isAutoVersion FROM moduleExportedImports WHERE moduleId IN " + "carray(?1) ORDER BY moduleId, exportedModuleId", + database}; + Sqlite::WriteStatement<3> insertModuleExportedImportWithoutVersionStatement{ + "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion) " + "VALUES (?1, ?2, ?3)", + database}; + Sqlite::WriteStatement<4> insertModuleExportedImportWithMajorVersionStatement{ + "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, " + "majorVersion) VALUES (?1, ?2, ?3, ?4)", + database}; + Sqlite::WriteStatement<5> insertModuleExportedImportWithVersionStatement{ + "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, " + "majorVersion, minorVersion) VALUES (?1, ?2, ?3, ?4, ?5)", + database}; + Sqlite::WriteStatement<1> deleteModuleExportedImportStatement{ + "DELETE FROM moduleExportedImports WHERE moduleExportedImportId=?1", database}; + mutable Sqlite::ReadStatement<3, 3> selectModuleExportedImportsForModuleIdStatement{ + "WITH RECURSIVE " + " imports(moduleId, majorVersion, minorVersion, moduleExportedImportId) AS ( " + " SELECT exportedModuleId, " + " iif(isAutoVersion=1, ?2, majorVersion), " + " iif(isAutoVersion=1, ?3, minorVersion), " + " moduleExportedImportId " + " FROM moduleExportedImports WHERE moduleId=?1 " + " UNION ALL " + " SELECT exportedModuleId, " + " iif(mei.isAutoVersion=1, i.majorVersion, mei.majorVersion), " + " iif(mei.isAutoVersion=1, i.minorVersion, mei.minorVersion), " + " mei.moduleExportedImportId " + " FROM moduleExportedImports AS mei JOIN imports AS i USING(moduleId)) " + "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) " + "FROM imports", + database}; + mutable Sqlite::ReadStatement<1, 1> selectLocalPropertyDeclarationIdsForTypeStatement{ + "SELECT propertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=? " + "ORDER BY propertyDeclarationId", + database}; + mutable Sqlite::ReadStatement<1, 2> selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement{ + "SELECT propertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=?1 AND name=?2 LIMIT 1", + database}; + mutable Sqlite::ReadStatement<4, 1> selectPropertyDeclarationForPropertyDeclarationIdStatement{ + "SELECT typeId, name, propertyTraits, propertyTypeId " + "FROM propertyDeclarations " + "WHERE propertyDeclarationId=?1 LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectSignalDeclarationNamesForTypeStatement{ + "WITH RECURSIVE " + " all_prototype_and_extension(typeId, prototypeId) AS (" + " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" + " UNION ALL " + " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," + " typeChain(typeId) AS (" + " VALUES(?1)" + " UNION ALL " + " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " + " USING(typeId)) " + "SELECT name FROM typeChain JOIN signalDeclarations " + " USING(typeId) ORDER BY name", + database}; + mutable Sqlite::ReadStatement<1, 1> selectFuncionDeclarationNamesForTypeStatement{ + "WITH RECURSIVE " + " all_prototype_and_extension(typeId, prototypeId) AS (" + " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" + " UNION ALL " + " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," + " typeChain(typeId) AS (" + " VALUES(?1)" + " UNION ALL " + " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " + " USING(typeId))" + "SELECT name FROM typeChain JOIN functionDeclarations " + " USING(typeId) ORDER BY name", + database}; + mutable Sqlite::ReadStatement<2> selectTypesWithDefaultPropertyStatement{ + "SELECT typeId, defaultPropertyId FROM types ORDER BY typeId", database}; + Sqlite::WriteStatement<2> updateDefaultPropertyIdStatement{ + "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database}; + Sqlite::WriteStatement<1> updateDefaultPropertyIdToNullStatement{ + "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; + mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ + "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{ + "SELECT defaultPropertyId FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ + "WITH RECURSIVE " + " all_prototype_and_extension(typeId, prototypeId) AS (" + " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" + " UNION ALL " + " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," + " prototypes(typeId, level) AS (" + " SELECT prototypeId, 0 FROM all_prototype_and_extension WHERE typeId=?" + " UNION ALL " + " SELECT prototypeId, p.level+1 FROM all_prototype_and_extension JOIN " + " prototypes AS p USING(typeId)) " + "SELECT typeId FROM prototypes ORDER BY level", + database}; + Sqlite::WriteStatement<2> upsertPropertyEditorPathIdStatement{ + "INSERT INTO propertyEditorPaths(typeId, pathSourceId) VALUES(?1, ?2) ON CONFLICT DO " + "UPDATE SET pathSourceId=excluded.pathSourceId WHERE pathSourceId IS NOT " + "excluded.pathSourceId", + database}; + mutable Sqlite::ReadStatement<1, 1> selectPropertyEditorPathIdStatement{ + "SELECT pathSourceId FROM propertyEditorPaths WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<3, 1> selectPropertyEditorPathsForForSourceIdsStatement{ + "SELECT typeId, pathSourceId, directoryId " + "FROM propertyEditorPaths " + "WHERE directoryId IN carray(?1) " + "ORDER BY typeId", + database}; + Sqlite::WriteStatement<3> insertPropertyEditorPathStatement{ + "INSERT INTO propertyEditorPaths(typeId, pathSourceId, directoryId) VALUES (?1, ?2, ?3)", + database}; + Sqlite::WriteStatement<3> updatePropertyEditorPathsStatement{ + "UPDATE propertyEditorPaths " + "SET pathSourceId=?2, directoryId=?3 " + "WHERE typeId=?1", + database}; + Sqlite::WriteStatement<1> deletePropertyEditorPathStatement{ + "DELETE FROM propertyEditorPaths WHERE typeId=?1", database}; + mutable Sqlite::ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{ + "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " + "sourceId IN carray(?1) ORDER BY typeId", + database}; + Sqlite::WriteStatement<6> insertTypeAnnotationStatement{ + "INSERT INTO " + " typeAnnotations(typeId, sourceId, directorySourceId, iconPath, itemLibrary, hints) " + "VALUES(?1, ?2, ?3, ?4, ?5, ?6)", + database}; + Sqlite::WriteStatement<4> updateTypeAnnotationStatement{ + "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; + Sqlite::WriteStatement<1> deleteTypeAnnotationStatement{ + "DELETE FROM typeAnnotations WHERE typeId=?1", database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIconPathStatement{ + "SELECT iconPath FROM typeAnnotations WHERE typeId=?1", database}; + mutable Sqlite::ReadStatement<2, 1> selectTypeHintsStatement{ + "SELECT hints.key, hints.value " + "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints " + "WHERE typeId=?1 AND hints IS NOT NULL", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeAnnotationSourceIdsStatement{ + "SELECT sourceId FROM typeAnnotations WHERE directorySourceId=?1 ORDER BY sourceId", database}; + mutable Sqlite::ReadStatement<1, 0> selectTypeAnnotationDirectorySourceIdsStatement{ + "SELECT DISTINCT directorySourceId FROM typeAnnotations ORDER BY directorySourceId", database}; + mutable Sqlite::ReadStatement<9> selectItemLibraryEntriesStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations AS ta , json_each(ta.itemLibrary) AS i " + "WHERE ta.itemLibrary IS NOT NULL", + database}; + mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations AS ta, json_each(ta.itemLibrary) AS i " + "WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL", + database}; + mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', " + "i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " + "WHERE typeId IN (SELECT DISTINCT typeId " + " FROM documentImports AS di JOIN exportedTypeNames " + " USING(moduleId) " + " WHERE di.sourceId=?)", + database}; + mutable Sqlite::ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ + "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database}; + mutable Sqlite::ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{ + "SELECT p.value FROM json_each(?1) AS p", database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdsByModuleIdStatement{ + "SELECT DISTINCT typeId FROM exportedTypeNames WHERE moduleId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectHeirTypeIdsStatement{ + "WITH RECURSIVE " + " typeSelection(typeId) AS (" + " SELECT typeId FROM types WHERE prototypeId=?1 OR extensionId=?1" + " UNION ALL " + " SELECT t.typeId " + " FROM types AS t JOIN typeSelection AS ts " + " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)" + "SELECT typeId FROM typeSelection", + database}; +}; + +class ProjectStorage::Initializer +{ +public: + Initializer(Database &database, bool isInitialized) + { + if (!isInitialized) { + auto moduleIdColumn = createModulesTable(database); + createSourceContextsTable(database); + createSourcesTable(database); + createTypesAndePropertyDeclarationsTables(database, moduleIdColumn); + createExportedTypeNamesTable(database, moduleIdColumn); + createImportedTypeNamesTable(database); + createEnumerationsTable(database); + createFunctionsTable(database); + createSignalsTable(database); + createModuleExportedImportsTable(database, moduleIdColumn); + createDocumentImportsTable(database, moduleIdColumn); + createFileStatusesTable(database); + createProjectDatasTable(database); + createPropertyEditorPathsTable(database); + createTypeAnnotionsTable(database); + } + database.setIsInitialized(true); + } + + void createSourceContextsTable(Database &database) + { + Sqlite::Table table; + table.setUseIfNotExists(true); + table.setName("sourceContexts"); + table.addColumn("sourceContextId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}}); + const Sqlite::Column &sourceContextPathColumn = table.addColumn("sourceContextPath"); + + table.addUniqueIndex({sourceContextPathColumn}); + + table.initialize(database); + } + + void createSourcesTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("sources"); + table.addColumn("sourceId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + const auto &sourceContextIdColumn = table.addColumn( + "sourceContextId", + Sqlite::StrictColumnType::Integer, + {Sqlite::NotNull{}, + Sqlite::ForeignKey{"sourceContexts", + "sourceContextId", + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade}}); + const auto &sourceNameColumn = table.addColumn("sourceName", Sqlite::StrictColumnType::Text); + table.addUniqueIndex({sourceContextIdColumn, sourceNameColumn}); + + table.initialize(database); + } + + void createTypesAndePropertyDeclarationsTables( + Database &database, [[maybe_unused]] const Sqlite::StrictColumn &foreignModuleIdColumn) + { + Sqlite::StrictTable typesTable; + typesTable.setUseIfNotExists(true); + typesTable.setName("types"); + typesTable.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text); + typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer); + auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer); + auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer); + auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId", + Sqlite::StrictColumnType::Integer); + typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer); + typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); + typesTable.addIndex({defaultPropertyIdColumn}); + typesTable.addIndex({prototypeIdColumn}); + typesTable.addIndex({extensionIdColumn}); + + typesTable.initialize(database); + + { + Sqlite::StrictTable propertyDeclarationTable; + propertyDeclarationTable.setUseIfNotExists(true); + propertyDeclarationTable.setName("propertyDeclarations"); + propertyDeclarationTable.addColumn("propertyDeclarationId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &typeIdColumn = propertyDeclarationTable.addColumn("typeId"); + auto &nameColumn = propertyDeclarationTable.addColumn("name"); + auto &propertyTypeIdColumn = propertyDeclarationTable.addForeignKeyColumn( + "propertyTypeId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + propertyDeclarationTable.addColumn("propertyTraits", Sqlite::StrictColumnType::Integer); + propertyDeclarationTable.addColumn("propertyImportedTypeNameId", + Sqlite::StrictColumnType::Integer); + auto &aliasPropertyDeclarationIdColumn = propertyDeclarationTable.addForeignKeyColumn( + "aliasPropertyDeclarationId", + propertyDeclarationTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + auto &aliasPropertyDeclarationTailIdColumn = propertyDeclarationTable.addForeignKeyColumn( + "aliasPropertyDeclarationTailId", + propertyDeclarationTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + + propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn}); + propertyDeclarationTable.addIndex({propertyTypeIdColumn}); + propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn}, + "aliasPropertyDeclarationId IS NOT NULL"); + propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn}, + "aliasPropertyDeclarationTailId IS NOT NULL"); + + propertyDeclarationTable.initialize(database); + } + } + + void createExportedTypeNamesTable(Database &database, + const Sqlite::StrictColumn &foreignModuleIdColumn) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("exportedTypeNames"); + table.addColumn("exportedTypeNameId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", + foreignModuleIdColumn, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::NoAction); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); + auto &majorVersionColumn = table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer); + auto &minorVersionColumn = table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); + + table.addUniqueIndex({moduleIdColumn, nameColumn}, + "majorVersion IS NULL AND minorVersion IS NULL"); + table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NULL"); + table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn, minorVersionColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); + + table.addIndex({typeIdColumn}); + table.addIndex({moduleIdColumn, nameColumn}); + + table.initialize(database); + } + + void createImportedTypeNamesTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("importedTypeNames"); + table.addColumn("importedTypeNameId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &importOrSourceIdColumn = table.addColumn("importOrSourceId"); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); + + table.addUniqueIndex({kindColumn, importOrSourceIdColumn, nameColumn}); + table.addIndex({nameColumn}); + + table.initialize(database); + } + + void createEnumerationsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("enumerationDeclarations"); + table.addColumn("enumerationDeclarationId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + table.addColumn("enumeratorDeclarations", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({typeIdColumn, nameColumn}); + + table.initialize(database); + } + + void createFunctionsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("functionDeclarations"); + table.addColumn("functionDeclarationId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text); + table.addColumn("returnTypeName"); + + table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn}); + + table.initialize(database); + } + + void createSignalsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("signalDeclarations"); + table.addColumn("signalDeclarationId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn}); + + table.initialize(database); + } + + Sqlite::StrictColumn createModulesTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("modules"); + auto &modelIdColumn = table.addColumn("moduleId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({nameColumn}); + + table.initialize(database); + + return std::move(modelIdColumn); + } + + void createModuleExportedImportsTable(Database &database, + const Sqlite::StrictColumn &foreignModuleIdColumn) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("moduleExportedImports"); + table.addColumn("moduleExportedImportId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", + foreignModuleIdColumn, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade, + Sqlite::Enforment::Immediate); + auto &sourceIdColumn = table.addColumn("exportedModuleId", Sqlite::StrictColumnType::Integer); + table.addColumn("isAutoVersion", Sqlite::StrictColumnType::Integer); + table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer); + table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); + + table.addUniqueIndex({sourceIdColumn, moduleIdColumn}); + + table.initialize(database); + } + + void createDocumentImportsTable(Database &database, + const Sqlite::StrictColumn &foreignModuleIdColumn) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("documentImports"); + table.addColumn("importId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", + foreignModuleIdColumn, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade, + Sqlite::Enforment::Immediate); + auto &sourceModuleIdColumn = table.addForeignKeyColumn("sourceModuleId", + foreignModuleIdColumn, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade, + Sqlite::Enforment::Immediate); + auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); + auto &majorVersionColumn = table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer); + auto &minorVersionColumn = table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); + auto &parentImportIdColumn = table.addColumn("parentImportId", + Sqlite::StrictColumnType::Integer); + + table.addUniqueIndex( + {sourceIdColumn, moduleIdColumn, kindColumn, sourceModuleIdColumn, parentImportIdColumn}, + "majorVersion IS NULL AND minorVersion IS NULL"); + table.addUniqueIndex({sourceIdColumn, + moduleIdColumn, + kindColumn, + sourceModuleIdColumn, + majorVersionColumn, + parentImportIdColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NULL"); + table.addUniqueIndex({sourceIdColumn, + moduleIdColumn, + kindColumn, + sourceModuleIdColumn, + majorVersionColumn, + minorVersionColumn, + parentImportIdColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); + + table.addIndex({sourceIdColumn, kindColumn}); + + table.initialize(database); + } + + void createFileStatusesTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("fileStatuses"); + table.addColumn("sourceId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}, + Sqlite::ForeignKey{"sources", + "sourceId", + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade}}); + table.addColumn("size", Sqlite::StrictColumnType::Integer); + table.addColumn("lastModified", Sqlite::StrictColumnType::Integer); + + table.initialize(database); + } + + void createProjectDatasTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("projectDatas"); + auto &projectSourceIdColumn = table.addColumn("projectSourceId", + Sqlite::StrictColumnType::Integer); + auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + table.addColumn("moduleId", Sqlite::StrictColumnType::Integer); + table.addColumn("fileType", Sqlite::StrictColumnType::Integer); + + table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn}); + table.addUniqueIndex({sourceIdColumn}); + + table.initialize(database); + } + + void createPropertyEditorPathsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("propertyEditorPaths"); + table.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + table.addColumn("pathSourceId", Sqlite::StrictColumnType::Integer); + auto &directoryIdColumn = table.addColumn("directoryId", Sqlite::StrictColumnType::Integer); + + table.addIndex({directoryIdColumn}); + + table.initialize(database); + } + + void createTypeAnnotionsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("typeAnnotations"); + auto &typeIdColumn = table.addColumn("typeId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + auto &directorySourceIdColumn = table.addColumn("directorySourceId", + Sqlite::StrictColumnType::Integer); + + table.addColumn("iconPath", Sqlite::StrictColumnType::Text); + table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); + table.addColumn("hints", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({sourceIdColumn, typeIdColumn}); + table.addIndex({directorySourceIdColumn}); + + table.initialize(database); + } +}; + +ProjectStorage::ProjectStorage(Database &database, bool isInitialized) + : database{database} + , exclusiveTransaction{database} + , initializer{std::make_unique<ProjectStorage::Initializer>(database, isInitialized)} + , moduleCache{ModuleStorageAdapter{*this}} + , s{std::make_unique<ProjectStorage::Statements>(database)} { - thread_local NanotraceHR::StringViewCategory<projectStorageTracingStatus()> - projectStorageCategory_{"project storage"_t, Tracing::eventQueue(), projectStorageCategory}; + NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; + + exclusiveTransaction.commit(); - return projectStorageCategory_; + database.walCheckpointFull(); + + moduleCache.populate(); } -} // namespace QmlDesigner +ProjectStorage::~ProjectStorage() = default; + +void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackage package) +{ + NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory()}; + + TypeIds deletedTypeIds; + Sqlite::withImmediateTransaction(database, [&] { + AliasPropertyDeclarations insertedAliasPropertyDeclarations; + AliasPropertyDeclarations updatedAliasPropertyDeclarations; + + AliasPropertyDeclarations relinkableAliasPropertyDeclarations; + PropertyDeclarations relinkablePropertyDeclarations; + Prototypes relinkablePrototypes; + Prototypes relinkableExtensions; + + TypeIds updatedTypeIds; + updatedTypeIds.reserve(package.types.size()); + + TypeIds typeIdsToBeDeleted; + + std::sort(package.updatedSourceIds.begin(), package.updatedSourceIds.end()); + + synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds); + synchronizeImports(package.imports, + package.updatedSourceIds, + package.moduleDependencies, + package.updatedModuleDependencySourceIds, + package.moduleExportedImports, + package.updatedModuleIds); + synchronizeTypes(package.types, + updatedTypeIds, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + package.updatedSourceIds); + synchronizeTypeAnnotations(package.typeAnnotations, package.updatedTypeAnnotationSourceIds); + synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, + package.updatedPropertyEditorQmlPathSourceIds); + + deleteNotUpdatedTypes(updatedTypeIds, + package.updatedSourceIds, + typeIdsToBeDeleted, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); + + relink(relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); + + linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); + + synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); + + commonTypeCache_.resetTypeIds(); + }); + + callRefreshMetaInfoCallback(deletedTypeIds); +} + +void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize document imports"_t, + projectStorageCategory(), + keyValue("imports", imports), + keyValue("source id", sourceId)}; + + Sqlite::withImmediateTransaction(database, [&] { + synchronizeDocumentImports(imports, {sourceId}, Storage::Synchronization::ImportKind::Import); + }); +} + +void ProjectStorage::addObserver(ProjectStorageObserver *observer) +{ + NanotraceHR::Tracer tracer{"add observer"_t, projectStorageCategory()}; + observers.push_back(observer); +} + +void ProjectStorage::removeObserver(ProjectStorageObserver *observer) +{ + NanotraceHR::Tracer tracer{"remove observer"_t, projectStorageCategory()}; + observers.removeOne(observer); +} + +ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module id"_t, + projectStorageCategory(), + keyValue("module name", moduleName)}; + + auto moduleId = moduleCache.id(moduleName); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module name"_t, + projectStorageCategory(), + keyValue("module id", moduleId)}; + + if (!moduleId) + throw ModuleDoesNotExists{}; + + auto moduleName = moduleCache.value(moduleId); + + tracer.end(keyValue("module name", moduleName)); + + return moduleName; +} + +TypeId ProjectStorage::typeId(ModuleId moduleId, + Utils::SmallStringView exportedTypeName, + Storage::Version version) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type id by exported name"_t, + projectStorageCategory(), + keyValue("module id", moduleId), + keyValue("exported type name", exportedTypeName), + keyValue("version", version)}; + + TypeId typeId; + + if (version.minor) { + typeId = s->selectTypeIdByModuleIdAndExportedNameAndVersionStatement.valueWithTransaction<TypeId>( + moduleId, exportedTypeName, version.major.value, version.minor.value); + + } else if (version.major) { + typeId = s->selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement + .valueWithTransaction<TypeId>(moduleId, exportedTypeName, version.major.value); + + } else { + typeId = s->selectTypeIdByModuleIdAndExportedNameStatement + .valueWithTransaction<TypeId>(moduleId, exportedTypeName); + } + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +TypeId ProjectStorage::typeId(ImportedTypeNameId typeNameId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type id by imported type name"_t, + projectStorageCategory(), + keyValue("imported type name id", typeNameId)}; + + auto typeId = Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +QVarLengthArray<TypeId, 256> ProjectStorage::typeIds(ModuleId moduleId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type ids by module id"_t, + projectStorageCategory(), + keyValue("module id", moduleId)}; + + auto typeIds = s->selectTypeIdsByModuleIdStatement + .valuesWithTransaction<QVarLengthArray<TypeId, 256>>(moduleId); + + tracer.end(keyValue("type ids", typeIds)); + + return typeIds; +} + +Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto exportedTypenames = s->selectExportedTypesByTypeIdStatement + .valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId); + + tracer.end(keyValue("exported type names", exportedTypenames)); + + return exportedTypenames; +} + +Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId, SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names by source id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("source id", sourceId)}; + + auto exportedTypenames = s->selectExportedTypesByTypeIdAndSourceIdStatement + .valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId, + sourceId); + + tracer.end(keyValue("exported type names", exportedTypenames)); + + return exportedTypenames; +} + +ImportId ProjectStorage::importId(const Storage::Import &import) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get import id by import"_t, + projectStorageCategory(), + keyValue("import", import)}; + + auto importId = Sqlite::withDeferredTransaction(database, [&] { + return fetchImportId(import.sourceId, import); + }); + + tracer.end(keyValue("import id", importId)); + + return importId; +} + +ImportedTypeNameId ProjectStorage::importedTypeNameId(ImportId importId, + Utils::SmallStringView typeName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, + projectStorageCategory(), + keyValue("import id", importId), + keyValue("imported type name", typeName)}; + + auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] { + return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, + importId, + typeName); + }); + + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; +} + +ImportedTypeNameId ProjectStorage::importedTypeNameId(SourceId sourceId, + Utils::SmallStringView typeName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("imported type name", typeName)}; + + auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] { + return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, + sourceId, + typeName); + }); + + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; +} + +QVarLengthArray<PropertyDeclarationId, 128> ProjectStorage::propertyDeclarationIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration ids"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarationIds = Sqlite::withDeferredTransaction(database, [&] { + return fetchPropertyDeclarationIds(typeId); + }); + + std::sort(propertyDeclarationIds.begin(), propertyDeclarationIds.end()); + + tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); + + return propertyDeclarationIds; +} + +QVarLengthArray<PropertyDeclarationId, 128> ProjectStorage::localPropertyDeclarationIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local property declaration ids"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarationIds = s->selectLocalPropertyDeclarationIdsForTypeStatement + .valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>( + typeId); + + tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); + + return propertyDeclarationIds; +} + +PropertyDeclarationId ProjectStorage::propertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; + + auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] { + return fetchPropertyDeclarationId(typeId, propertyName); + }); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; +} + +PropertyDeclarationId ProjectStorage::localPropertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; + + auto propertyDeclarationId = s->selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement + .valueWithTransaction<PropertyDeclarationId>(typeId, + propertyName); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; +} + +PropertyDeclarationId ProjectStorage::defaultPropertyDeclarationId(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] { + return fetchDefaultPropertyDeclarationId(typeId); + }); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; +} + +std::optional<Storage::Info::PropertyDeclaration> ProjectStorage::propertyDeclaration( + PropertyDeclarationId propertyDeclarationId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; + + auto propertyDeclaration = s->selectPropertyDeclarationForPropertyDeclarationIdStatement + .optionalValueWithTransaction<Storage::Info::PropertyDeclaration>( + propertyDeclarationId); + + tracer.end(keyValue("property declaration", propertyDeclaration)); + + return propertyDeclaration; +} + +std::optional<Storage::Info::Type> ProjectStorage::type(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)}; + + auto type = s->selectInfoTypeByTypeIdStatement.optionalValueWithTransaction<Storage::Info::Type>( + typeId); + + tracer.end(keyValue("type", type)); + + return type; +} + +Utils::PathString ProjectStorage::typeIconPath(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type icon path"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto typeIconPath = s->selectTypeIconPathStatement.valueWithTransaction<Utils::PathString>(typeId); + + tracer.end(keyValue("type icon path", typeIconPath)); + + return typeIconPath; +} + +Storage::Info::TypeHints ProjectStorage::typeHints(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type hints"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto typeHints = s->selectTypeHintsStatement.valuesWithTransaction<Storage::Info::TypeHints, 4>( + typeId); + + tracer.end(keyValue("type hints", typeHints)); + + return typeHints; +} + +SmallSourceIds<4> ProjectStorage::typeAnnotationSourceIds(SourceId directoryId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, + projectStorageCategory(), + keyValue("source id", directoryId)}; + + auto sourceIds = s->selectTypeAnnotationSourceIdsStatement.valuesWithTransaction<SmallSourceIds<4>>( + directoryId); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; +} + +SmallSourceIds<64> ProjectStorage::typeAnnotationDirectorySourceIds() const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, projectStorageCategory()}; + + auto sourceIds = s->selectTypeAnnotationDirectorySourceIdsStatement + .valuesWithTransaction<SmallSourceIds<64>>(); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; +} + +Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId_, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + s->selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId importId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries by import id"_t, + projectStorageCategory(), + keyValue("import id", importId)}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId_, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + s->selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, importId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries by source id"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + s->selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + s->selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +std::vector<Utils::SmallString> ProjectStorage::signalDeclarationNames(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get signal names"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto signalDeclarationNames = s->selectSignalDeclarationNamesForTypeStatement + .valuesWithTransaction<Utils::SmallString, 32>(typeId); + + tracer.end(keyValue("signal names", signalDeclarationNames)); + + return signalDeclarationNames; +} + +std::vector<Utils::SmallString> ProjectStorage::functionDeclarationNames(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get function names"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto functionDeclarationNames = s->selectFuncionDeclarationNamesForTypeStatement + .valuesWithTransaction<Utils::SmallString, 32>(typeId); + + tracer.end(keyValue("function names", functionDeclarationNames)); + + return functionDeclarationNames; +} + +std::optional<Utils::SmallString> ProjectStorage::propertyName( + PropertyDeclarationId propertyDeclarationId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property name"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; + + auto propertyName = s->selectPropertyNameStatement.optionalValueWithTransaction<Utils::SmallString>( + propertyDeclarationId); + + tracer.end(keyValue("property name", propertyName)); + + return propertyName; +} + +SmallTypeIds<16> ProjectStorage::prototypeIds(TypeId type) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory(), keyValue("type id", type)}; + + auto prototypeIds = s->selectPrototypeAndExtensionIdsStatement + .valuesWithTransaction<SmallTypeIds<16>>(type); + + tracer.end(keyValue("type ids", prototypeIds)); + + return prototypeIds; +} + +SmallTypeIds<16> ProjectStorage::prototypeAndSelfIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; + + SmallTypeIds<16> prototypeAndSelfIds; + prototypeAndSelfIds.push_back(typeId); + + s->selectPrototypeAndExtensionIdsStatement.readToWithTransaction(prototypeAndSelfIds, typeId); + + tracer.end(keyValue("type ids", prototypeAndSelfIds)); + + return prototypeAndSelfIds; +} + +SmallTypeIds<64> ProjectStorage::heirIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; + + auto heirIds = s->selectHeirTypeIdsStatement.valuesWithTransaction<SmallTypeIds<64>>(typeId); + + tracer.end(keyValue("type ids", heirIds)); + + return heirIds; +} + +bool ProjectStorage::isBasedOn(TypeId) const +{ + return false; +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1) const +{ + return isBasedOn_(typeId, id1); +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const +{ + return isBasedOn_(typeId, id1, id2); +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const +{ + return isBasedOn_(typeId, id1, id2, id3); +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const +{ + return isBasedOn_(typeId, id1, id2, id3, id4); +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const +{ + return isBasedOn_(typeId, id1, id2, id3, id4, id5); +} + +bool ProjectStorage::isBasedOn( + TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const +{ + return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6); +} + +bool ProjectStorage::isBasedOn( + TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6, TypeId id7) const +{ + return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7); +} + +TypeId ProjectStorage::fetchTypeIdByExportedName(Utils::SmallStringView name) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + projectStorageCategory(), + keyValue("exported type name", name)}; + + auto typeId = s->selectTypeIdByExportedNameStatement.valueWithTransaction<TypeId>(name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +TypeId ProjectStorage::fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, + Utils::SmallStringView name) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by module ids and exported name"_t, + projectStorageCategory(), + keyValue("module ids", NanotraceHR::array(moduleIds)), + keyValue("exported type name", name)}; + auto typeId = s->selectTypeIdByModuleIdsAndExportedNameStatement.valueWithTransaction<TypeId>( + static_cast<void *>(moduleIds.data()), static_cast<long long>(moduleIds.size()), name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +TypeId ProjectStorage::fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by name"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("internal type name", name)}; + + auto typeId = s->selectTypeIdBySourceIdAndNameStatement.valueWithTransaction<TypeId>(sourceId, + name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +Storage::Synchronization::Type ProjectStorage::fetchTypeByTypeId(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto type = Sqlite::withDeferredTransaction(database, [&] { + auto type = s->selectTypeByTypeIdStatement.value<Storage::Synchronization::Type>(typeId); + + type.exportedTypes = fetchExportedTypes(typeId); + type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); + type.functionDeclarations = fetchFunctionDeclarations(type.typeId); + type.signalDeclarations = fetchSignalDeclarations(type.typeId); + type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); + + return type; + }); + + tracer.end(keyValue("type", type)); + + return type; +} + +Storage::Synchronization::Types ProjectStorage::fetchTypes() +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch types"_t, projectStorageCategory()}; + + auto types = Sqlite::withDeferredTransaction(database, [&] { + auto types = s->selectTypesStatement.values<Storage::Synchronization::Type, 64>(); + + for (Storage::Synchronization::Type &type : types) { + type.exportedTypes = fetchExportedTypes(type.typeId); + type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); + type.functionDeclarations = fetchFunctionDeclarations(type.typeId); + type.signalDeclarations = fetchSignalDeclarations(type.typeId); + type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); + } + + return types; + }); + + tracer.end(keyValue("type", types)); + + return types; +} + +SourceContextId ProjectStorage::fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()}; + + auto sourceContextId = readSourceContextId(sourceContextPath); + + return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath); +} + +SourceContextId ProjectStorage::fetchSourceContextId(Utils::SmallStringView sourceContextPath) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + + SourceContextId sourceContextId; + try { + sourceContextId = Sqlite::withDeferredTransaction(database, [&] { + return fetchSourceContextIdUnguarded(sourceContextPath); + }); + } catch (const Sqlite::ConstraintPreventsModification &) { + sourceContextId = fetchSourceContextId(sourceContextPath); + } + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; +} + +Utils::PathString ProjectStorage::fetchSourceContextPath(SourceContextId sourceContextId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context path"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId)}; + + auto path = Sqlite::withDeferredTransaction(database, [&] { + auto optionalSourceContextPath = s->selectSourceContextPathFromSourceContextsBySourceContextIdStatement + .optionalValue<Utils::PathString>(sourceContextId); + + if (!optionalSourceContextPath) + throw SourceContextIdDoesNotExists(); + + return std::move(*optionalSourceContextPath); + }); + + tracer.end(keyValue("source context path", path)); + + return path; +} + +Cache::SourceContexts ProjectStorage::fetchAllSourceContexts() const +{ + NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()}; + + return s->selectAllSourceContextsStatement.valuesWithTransaction<Cache::SourceContext, 128>(); +} + +SourceId ProjectStorage::fetchSourceId(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + auto sourceId = Sqlite::withDeferredTransaction(database, [&] { + return fetchSourceIdUnguarded(sourceContextId, sourceName); + }); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +Cache::SourceNameAndSourceContextId ProjectStorage::fetchSourceNameAndSourceContextId(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source name and source context id"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto value = s->selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement + .valueWithTransaction<Cache::SourceNameAndSourceContextId>(sourceId); + + if (!value.sourceContextId) + throw SourceIdDoesNotExists(); + + tracer.end(keyValue("source name", value.sourceName), + keyValue("source context id", value.sourceContextId)); + + return value; +} + +void ProjectStorage::clearSources() +{ + Sqlite::withImmediateTransaction(database, [&] { + s->deleteAllSourceContextsStatement.execute(); + s->deleteAllSourcesStatement.execute(); + }); +} + +SourceContextId ProjectStorage::fetchSourceContextId(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context id"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto sourceContextId = s->selectSourceContextIdFromSourcesBySourceIdStatement + .valueWithTransaction<SourceContextId>(sourceId); + + if (!sourceContextId) + throw SourceIdDoesNotExists(); + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; +} + +Cache::Sources ProjectStorage::fetchAllSources() const +{ + NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()}; + + return s->selectAllSourcesStatement.valuesWithTransaction<Cache::Source, 1024>(); +} + +SourceId ProjectStorage::fetchSourceIdUnguarded(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + auto sourceId = readSourceId(sourceContextId, sourceName); + + if (!sourceId) + sourceId = writeSourceId(sourceContextId, sourceName); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +FileStatuses ProjectStorage::fetchAllFileStatuses() const +{ + NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; + + return s->selectAllFileStatusesStatement.valuesWithTransaction<FileStatus>(); +} + +FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch file status"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto fileStatus = s->selectFileStatusesForSourceIdStatement.valueWithTransaction<FileStatus>( + sourceId); + + tracer.end(keyValue("file status", fileStatus)); + + return fileStatus; +} + +std::optional<Storage::Synchronization::ProjectData> ProjectStorage::fetchProjectData(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project data"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto projectData = s->selectProjectDataForSourceIdStatement + .optionalValueWithTransaction<Storage::Synchronization::ProjectData>( + sourceId); + + tracer.end(keyValue("project data", projectData)); + + return projectData; +} + +Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(SourceId projectSourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, + projectStorageCategory(), + keyValue("source id", projectSourceId)}; + + auto projectDatas = s->selectProjectDatasForSourceIdStatement + .valuesWithTransaction<Storage::Synchronization::ProjectData, 1024>( + projectSourceId); + + tracer.end(keyValue("project datas", projectDatas)); + + return projectDatas; +} + +Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas( + const SourceIds &projectSourceIds) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, + projectStorageCategory(), + keyValue("source ids", projectSourceIds)}; + + auto projectDatas = s->selectProjectDatasForSourceIdsStatement + .valuesWithTransaction<Storage::Synchronization::ProjectData, 64>( + toIntegers(projectSourceIds)); + + tracer.end(keyValue("project datas", projectDatas)); + + return projectDatas; +} + +void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId) +{ + Sqlite::ImmediateSessionTransaction transaction{database}; + + s->upsertPropertyEditorPathIdStatement.write(typeId, pathId); + + transaction.commit(); +} + +SourceId ProjectStorage::propertyEditorPathId(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"property editor path id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto sourceId = s->selectPropertyEditorPathIdStatement.valueWithTransaction<SourceId>(typeId); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +Storage::Imports ProjectStorage::fetchDocumentImports() const +{ + NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; + + return s->selectAllDocumentImportForSourceIdStatement.valuesWithTransaction<Storage::Imports>(); +} + +void ProjectStorage::resetForTestsOnly() +{ + database.clearAllTablesForTestsOnly(); + commonTypeCache_.clearForTestsOnly(); + moduleCache.clearForTestOnly(); +} + +bool ProjectStorage::moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept +{ + return first < second; +} + +ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module id"_t, + projectStorageCategory(), + keyValue("module name", moduleName)}; + + auto moduleId = Sqlite::withDeferredTransaction(database, [&] { + return fetchModuleIdUnguarded(moduleName); + }); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Utils::PathString ProjectStorage::fetchModuleName(ModuleId id) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module name"_t, + projectStorageCategory(), + keyValue("module id", id)}; + + auto moduleName = Sqlite::withDeferredTransaction(database, + [&] { return fetchModuleNameUnguarded(id); }); + + tracer.end(keyValue("module name", moduleName)); + + return moduleName; +} + +ProjectStorage::Modules ProjectStorage::fetchAllModules() const +{ + NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; + + return s->selectAllModulesStatement.valuesWithTransaction<Module, 128>(); +} + +void ProjectStorage::callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"call refresh meta info callback"_t, + projectStorageCategory(), + keyValue("type ids", deletedTypeIds)}; + + if (deletedTypeIds.size()) { + for (ProjectStorageObserver *observer : observers) + observer->removedTypeIds(deletedTypeIds); + } +} + +SourceIds ProjectStorage::filterSourceIdsWithoutType(const SourceIds &updatedSourceIds, + SourceIds &sourceIdsOfTypes) +{ + std::sort(sourceIdsOfTypes.begin(), sourceIdsOfTypes.end()); + + SourceIds sourceIdsWithoutTypeSourceIds; + sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size()); + std::set_difference(updatedSourceIds.begin(), + updatedSourceIds.end(), + sourceIdsOfTypes.begin(), + sourceIdsOfTypes.end(), + std::back_inserter(sourceIdsWithoutTypeSourceIds)); + + return sourceIdsWithoutTypeSourceIds; +} + +TypeIds ProjectStorage::fetchTypeIds(const SourceIds &sourceIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type ids"_t, + projectStorageCategory(), + keyValue("source ids", sourceIds)}; + + return s->selectTypeIdsForSourceIdsStatement.values<TypeId, 128>(toIntegers(sourceIds)); +} + +void ProjectStorage::unique(SourceIds &sourceIds) +{ + std::sort(sourceIds.begin(), sourceIds.end()); + auto newEnd = std::unique(sourceIds.begin(), sourceIds.end()); + sourceIds.erase(newEnd, sourceIds.end()); +} + +void ProjectStorage::synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize type traits"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("type traits", traits)}; + + s->updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); +} + +void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) +{ + NanotraceHR::Tracer tracer{"update type id in type annotations"_t, projectStorageCategory()}; + + for (auto &annotation : typeAnnotations) { + annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, + annotation.typeName); + } + + for (auto &annotation : typeAnnotations) { + if (!annotation.typeId) + qWarning() << moduleName(annotation.moduleId).toQString() + << annotation.typeName.toQString(); + } + + typeAnnotations.erase(std::remove_if(typeAnnotations.begin(), + typeAnnotations.end(), + [](const auto &annotation) { return !annotation.typeId; }), + typeAnnotations.end()); +} + +void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, + const SourceIds &updatedTypeAnnotationSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory()}; + + using Storage::Synchronization::TypeAnnotation; + + updateTypeIdInTypeAnnotations(typeAnnotations); + + auto compareKey = [](auto &&first, auto &&second) { return first.typeId - second.typeId; }; + + std::sort(typeAnnotations.begin(), typeAnnotations.end(), [&](auto &&first, auto &&second) { + return first.typeId < second.typeId; + }); + + auto range = s->selectTypeAnnotationsForSourceIdsStatement.range<TypeAnnotationView>( + toIntegers(updatedTypeAnnotationSourceIds)); + + auto insert = [&](const TypeAnnotation &annotation) { + if (!annotation.sourceId) + throw TypeAnnotationHasInvalidSourceId{}; + + synchronizeTypeTraits(annotation.typeId, annotation.traits); + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert type annotations"_t, + projectStorageCategory(), + keyValue("type annotation", annotation)}; + + s->insertTypeAnnotationStatement.write(annotation.typeId, + annotation.sourceId, + annotation.directorySourceId, + annotation.iconPath, + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); + }; + + auto update = [&](const TypeAnnotationView &annotationFromDatabase, + const TypeAnnotation &annotation) { + synchronizeTypeTraits(annotation.typeId, annotation.traits); + + if (annotationFromDatabase.iconPath != annotation.iconPath + || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson + || annotationFromDatabase.hintsJson != annotation.hintsJson) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update type annotations"_t, + projectStorageCategory(), + keyValue("type annotation from database", + annotationFromDatabase), + keyValue("type annotation", annotation)}; + + s->updateTypeAnnotationStatement.write(annotation.typeId, + annotation.iconPath, + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); + return Sqlite::UpdateChange::Update; + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { + synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove type annotations"_t, + projectStorageCategory(), + keyValue("type annotation", annotationFromDatabase)}; + + s->deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); + }; + + Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeTypeTrait(const Storage::Synchronization::Type &type) +{ + s->updateTypeTraitStatement.write(type.typeId, type.traits.type); +} + +void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, + TypeIds &updatedTypeIds, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions, + const SourceIds &updatedSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory()}; + + Storage::Synchronization::ExportedTypes exportedTypes; + exportedTypes.reserve(types.size() * 3); + SourceIds sourceIdsOfTypes; + sourceIdsOfTypes.reserve(updatedSourceIds.size()); + SourceIds notUpdatedExportedSourceIds; + notUpdatedExportedSourceIds.reserve(updatedSourceIds.size()); + SourceIds exportedSourceIds; + exportedSourceIds.reserve(types.size()); + + for (auto &type : types) { + if (!type.sourceId) + throw TypeHasInvalidSourceId{}; + + TypeId typeId = declareType(type); + synchronizeTypeTrait(type); + sourceIdsOfTypes.push_back(type.sourceId); + updatedTypeIds.push_back(typeId); + if (type.changeLevel != Storage::Synchronization::ChangeLevel::ExcludeExportedTypes) { + exportedSourceIds.push_back(type.sourceId); + extractExportedTypes(typeId, type, exportedTypes); + } + } + + std::sort(types.begin(), types.end(), [](const auto &first, const auto &second) { + return first.typeId < second.typeId; + }); + + unique(exportedSourceIds); + + SourceIds sourceIdsWithoutType = filterSourceIdsWithoutType(updatedSourceIds, sourceIdsOfTypes); + exportedSourceIds.insert(exportedSourceIds.end(), + sourceIdsWithoutType.begin(), + sourceIdsWithoutType.end()); + TypeIds exportedTypeIds = fetchTypeIds(exportedSourceIds); + synchronizeExportedTypes(exportedTypeIds, + exportedTypes, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions); + + syncPrototypesAndExtensions(types, relinkablePrototypes, relinkableExtensions); + resetDefaultPropertiesIfChanged(types); + resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations); + syncDeclarations(types, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + relinkablePropertyDeclarations); + syncDefaultProperties(types); +} + +void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, + const SourceIds &updatedProjectSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()}; + + auto compareKey = [](auto &&first, auto &&second) { + auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId; + if (projectSourceIdDifference != 0) + return projectSourceIdDifference; + + return first.sourceId - second.sourceId; + }; + + std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) { + return std::tie(first.projectSourceId, first.sourceId) + < std::tie(second.projectSourceId, second.sourceId); + }); + + auto range = s->selectProjectDatasForSourceIdsStatement.range<Storage::Synchronization::ProjectData>( + toIntegers(updatedProjectSourceIds)); + + auto insert = [&](const Storage::Synchronization::ProjectData &projectData) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert project data"_t, + projectStorageCategory(), + keyValue("project data", projectData)}; + + if (!projectData.projectSourceId) + throw ProjectDataHasInvalidProjectSourceId{}; + if (!projectData.sourceId) + throw ProjectDataHasInvalidSourceId{}; + + s->insertProjectDataStatement.write(projectData.projectSourceId, + projectData.sourceId, + projectData.moduleId, + projectData.fileType); + }; + + auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase, + const Storage::Synchronization::ProjectData &projectData) { + if (projectDataFromDatabase.fileType != projectData.fileType + || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update project data"_t, + projectStorageCategory(), + keyValue("project data", projectData), + keyValue("project data from database", projectDataFromDatabase)}; + + s->updateProjectDataStatement.write(projectData.projectSourceId, + projectData.sourceId, + projectData.moduleId, + projectData.fileType); + return Sqlite::UpdateChange::Update; + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::ProjectData &projectData) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove project data"_t, + projectStorageCategory(), + keyValue("project data", projectData)}; + + s->deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); + }; + + Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, + const SourceIds &updatedSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory()}; + + auto compareKey = [](auto &&first, auto &&second) { return first.sourceId - second.sourceId; }; + + std::sort(fileStatuses.begin(), fileStatuses.end(), [&](auto &&first, auto &&second) { + return first.sourceId < second.sourceId; + }); + + auto range = s->selectFileStatusesForSourceIdsStatement.range<FileStatus>( + toIntegers(updatedSourceIds)); + + auto insert = [&](const FileStatus &fileStatus) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus)}; + + if (!fileStatus.sourceId) + throw FileStatusHasInvalidSourceId{}; + s->insertFileStatusStatement.write(fileStatus.sourceId, + fileStatus.size, + fileStatus.lastModified); + }; + + auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) { + if (fileStatusFromDatabase.lastModified != fileStatus.lastModified + || fileStatusFromDatabase.size != fileStatus.size) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus), + keyValue("file status from database", fileStatusFromDatabase)}; + + s->updateFileStatusStatement.write(fileStatus.sourceId, + fileStatus.size, + fileStatus.lastModified); + return Sqlite::UpdateChange::Update; + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const FileStatus &fileStatus) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus)}; + + s->deleteFileStatusStatement.write(fileStatus.sourceId); + }; + + Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeImports(Storage::Imports &imports, + const SourceIds &updatedSourceIds, + Storage::Imports &moduleDependencies, + const SourceIds &updatedModuleDependencySourceIds, + Storage::Synchronization::ModuleExportedImports &moduleExportedImports, + const ModuleIds &updatedModuleIds) +{ + NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; -template class QmlDesigner::ProjectStorage<Sqlite::Database>; + synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); + NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()}; + synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import); + importTracer.end(); + NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, + projectStorageCategory()}; + synchronizeDocumentImports(moduleDependencies, + updatedModuleDependencySourceIds, + Storage::Synchronization::ImportKind::ModuleDependency); + moduleDependenciesTracer.end(); +} + +void ProjectStorage::synchromizeModuleExportedImports( + Storage::Synchronization::ModuleExportedImports &moduleExportedImports, + const ModuleIds &updatedModuleIds) +{ + NanotraceHR::Tracer tracer{"synchronize module exported imports"_t, projectStorageCategory()}; + std::sort(moduleExportedImports.begin(), + moduleExportedImports.end(), + [](auto &&first, auto &&second) { + return std::tie(first.moduleId, first.exportedModuleId) + < std::tie(second.moduleId, second.exportedModuleId); + }); + + auto range = s->selectModuleExportedImportsForSourceIdStatement + .range<Storage::Synchronization::ModuleExportedImportView>( + toIntegers(updatedModuleIds)); + + auto compareKey = [](const Storage::Synchronization::ModuleExportedImportView &view, + const Storage::Synchronization::ModuleExportedImport &import) -> long long { + auto moduleIdDifference = view.moduleId - import.moduleId; + if (moduleIdDifference != 0) + return moduleIdDifference; + + return view.exportedModuleId - import.exportedModuleId; + }; + + auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert module exported import"_t, + projectStorageCategory(), + keyValue("module exported import", import), + keyValue("module id", import.moduleId)}; + tracer.tick("exported module"_t, keyValue("module id", import.exportedModuleId)); + + if (import.version.minor) { + s->insertModuleExportedImportWithVersionStatement.write(import.moduleId, + import.exportedModuleId, + import.isAutoVersion, + import.version.major.value, + import.version.minor.value); + } else if (import.version.major) { + s->insertModuleExportedImportWithMajorVersionStatement.write(import.moduleId, + import.exportedModuleId, + import.isAutoVersion, + import.version.major.value); + } else { + s->insertModuleExportedImportWithoutVersionStatement.write(import.moduleId, + import.exportedModuleId, + import.isAutoVersion); + } + }; + + auto update = [](const Storage::Synchronization::ModuleExportedImportView &, + const Storage::Synchronization::ModuleExportedImport &) { + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove module exported import"_t, + projectStorageCategory(), + keyValue("module exported import view", view), + keyValue("module id", view.moduleId)}; + tracer.tick("exported module"_t, keyValue("module id", view.exportedModuleId)); + + s->deleteModuleExportedImportStatement.write(view.moduleExportedImportId); + }; + + Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); +} + +ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module id ungarded"_t, + projectStorageCategory(), + keyValue("module name", name)}; + + auto moduleId = s->selectModuleIdByNameStatement.value<ModuleId>(name); + + if (!moduleId) + moduleId = s->insertModuleNameStatement.value<ModuleId>(name); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Utils::PathString ProjectStorage::fetchModuleNameUnguarded(ModuleId id) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module name ungarded"_t, + projectStorageCategory(), + keyValue("module id", id)}; + + auto moduleName = s->selectModuleNameStatement.value<Utils::PathString>(id); + + if (moduleName.empty()) + throw ModuleDoesNotExists{}; + + tracer.end(keyValue("module name", moduleName)); + + return moduleName; +} + +void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( + TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle alias property declarations with property type"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("relinkable alias property declarations", + relinkableAliasPropertyDeclarations)}; + + auto callback = [&](TypeId typeId_, + PropertyDeclarationId propertyDeclarationId, + ImportedTypeNameId propertyImportedTypeNameId, + PropertyDeclarationId aliasPropertyDeclarationId, + PropertyDeclarationId aliasPropertyDeclarationTailId) { + auto aliasPropertyName = s->selectPropertyNameStatement.value<Utils::SmallString>( + aliasPropertyDeclarationId); + Utils::SmallString aliasPropertyNameTail; + if (aliasPropertyDeclarationTailId) + aliasPropertyNameTail = s->selectPropertyNameStatement.value<Utils::SmallString>( + aliasPropertyDeclarationTailId); + + relinkableAliasPropertyDeclarations.emplace_back(TypeId{typeId_}, + PropertyDeclarationId{propertyDeclarationId}, + ImportedTypeNameId{propertyImportedTypeNameId}, + std::move(aliasPropertyName), + std::move(aliasPropertyNameTail)); + + s->updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); + }; + + s->selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback, typeId); +} + +void ProjectStorage::handlePropertyDeclarationWithPropertyType( + TypeId typeId, PropertyDeclarations &relinkablePropertyDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle property declarations with property type"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("relinkable property declarations", + relinkablePropertyDeclarations)}; + + s->updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, + typeId); +} + +void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes"_t, + projectStorageCategory(), + keyValue("type id", prototypeId), + keyValue("relinkable prototypes", relinkablePrototypes)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + s->updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); +} + +void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle extension"_t, + projectStorageCategory(), + keyValue("type id", extensionId), + keyValue("relinkable extensions", relinkableExtensions)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->updateExtensionIdToNullStatement.readCallback(callback, extensionId); +} + +void ProjectStorage::deleteType(TypeId typeId, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"delete type"_t, projectStorageCategory(), keyValue("type id", typeId)}; + + handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations); + handlePrototypes(typeId, relinkablePrototypes); + handleExtensions(typeId, relinkableExtensions); + s->deleteTypeNamesByTypeIdStatement.write(typeId); + s->deleteEnumerationDeclarationByTypeIdStatement.write(typeId); + s->deletePropertyDeclarationByTypeIdStatement.write(typeId); + s->deleteFunctionDeclarationByTypeIdStatement.write(typeId); + s->deleteSignalDeclarationByTypeIdStatement.write(typeId); + s->deleteTypeStatement.write(typeId); +} + +void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, + const TypeIds &deletedTypeIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink alias properties"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasPropertyDeclarations), + keyValue("deleted type ids", deletedTypeIds)}; + + std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); + + Utils::set_greedy_difference( + aliasPropertyDeclarations.cbegin(), + aliasPropertyDeclarations.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const AliasPropertyDeclaration &alias) { + auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); + + if (!typeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(alias.aliasImportedTypeNameId)}; + + auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( + typeId, alias.aliasPropertyName); + + s->updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, + propertyTypeId, + propertyTraits, + alias.aliasImportedTypeNameId, + aliasId); + }, + TypeCompare<AliasPropertyDeclaration>{}); +} + +void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, + const TypeIds &deletedTypeIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink property declarations"_t, + projectStorageCategory(), + keyValue("relinkable property declarations", + relinkablePropertyDeclaration), + keyValue("deleted type ids", deletedTypeIds)}; + + std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); + + Utils::set_greedy_difference( + relinkablePropertyDeclaration.cbegin(), + relinkablePropertyDeclaration.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const PropertyDeclaration &property) { + TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); + + if (!propertyTypeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)}; + + s->updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, + propertyTypeId); + }, + TypeCompare<PropertyDeclaration>{}); +} + +void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, + const SourceIds &updatedSourceIds, + const TypeIds &typeIdsToBeDeleted, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions, + TypeIds &deletedTypeIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"delete not updated types"_t, + projectStorageCategory(), + keyValue("updated type ids", updatedTypeIds), + keyValue("updated source ids", updatedSourceIds), + keyValue("type ids to be deleted", typeIdsToBeDeleted)}; + + auto callback = [&](TypeId typeId) { + deletedTypeIds.push_back(typeId); + deleteType(typeId, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions); + }; + + s->selectNotUpdatedTypesInSourcesStatement.readCallback(callback, + toIntegers(updatedSourceIds), + toIntegers(updatedTypeIds)); + for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted) + callback(typeIdToBeDeleted); +} + +void ProjectStorage::relink(AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions, + TypeIds &deletedTypeIds) +{ + NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory()}; + + std::sort(deletedTypeIds.begin(), deletedTypeIds.end()); + + relinkPrototypes(relinkablePrototypes, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { + s->updateTypePrototypeStatement.write(typeId, prototypeId); + }); + relinkPrototypes(relinkableExtensions, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { + s->updateTypeExtensionStatement.write(typeId, prototypeId); + }); + relinkPropertyDeclarations(relinkablePropertyDeclarations, deletedTypeIds); + relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds); +} + +PropertyDeclarationId ProjectStorage::fetchAliasId(TypeId aliasTypeId, + Utils::SmallStringView aliasPropertyName, + Utils::SmallStringView aliasPropertyNameTail) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch alias id"_t, + projectStorageCategory(), + keyValue("alias type id", aliasTypeId), + keyValue("alias property name", aliasPropertyName), + keyValue("alias property name tail", aliasPropertyNameTail)}; + + if (aliasPropertyNameTail.empty()) + return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); + + auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); + + return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId, + aliasPropertyNameTail); +} + +void ProjectStorage::linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"link alias property declarations alias ids"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; + + for (const auto &aliasDeclaration : aliasDeclarations) { + auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId); + + if (!aliasTypeId) { + throw TypeNameDoesNotExists{ + fetchImportedTypeName(aliasDeclaration.aliasImportedTypeNameId)}; + } + + auto aliasId = fetchAliasId(aliasTypeId, + aliasDeclaration.aliasPropertyName, + aliasDeclaration.aliasPropertyNameTail); + + s->updatePropertyDeclarationAliasIdAndTypeNameIdStatement.write( + aliasDeclaration.propertyDeclarationId, aliasId, aliasDeclaration.aliasImportedTypeNameId); + } +} + +void ProjectStorage::updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update alias property declarations"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; + + for (const auto &aliasDeclaration : aliasDeclarations) { + s->updatetPropertiesDeclarationValuesOfAliasStatement.write( + aliasDeclaration.propertyDeclarationId); + s->updatePropertyAliasDeclarationRecursivelyStatement.write( + aliasDeclaration.propertyDeclarationId); + } +} + +void ProjectStorage::checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check alias property declarations cycles"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; + for (const auto &aliasDeclaration : aliasDeclarations) + checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId); +} + +void ProjectStorage::linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()}; + + linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); + linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations); + + checkAliasPropertyDeclarationCycles(insertedAliasPropertyDeclarations); + checkAliasPropertyDeclarationCycles(updatedAliasPropertyDeclarations); + + updateAliasPropertyDeclarationValues(insertedAliasPropertyDeclarations); + updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); +} + +void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, + Storage::Synchronization::ExportedTypes &exportedTypes, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()}; + + std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { + if (first.moduleId < second.moduleId) + return true; + else if (first.moduleId > second.moduleId) + return false; + + auto nameCompare = Sqlite::compare(first.name, second.name); + + if (nameCompare < 0) + return true; + else if (nameCompare > 0) + return false; + + return first.version < second.version; + }); + + auto range = s->selectExportedTypesForSourceIdsStatement + .range<Storage::Synchronization::ExportedTypeView>(toIntegers(updatedTypeIds)); + + auto compareKey = [](const Storage::Synchronization::ExportedTypeView &view, + const Storage::Synchronization::ExportedType &type) -> long long { + auto moduleIdDifference = view.moduleId - type.moduleId; + if (moduleIdDifference != 0) + return moduleIdDifference; + + auto nameDifference = Sqlite::compare(view.name, type.name); + if (nameDifference != 0) + return nameDifference; + + auto versionDifference = view.version.major.value - type.version.major.value; + if (versionDifference != 0) + return versionDifference; + + return view.version.minor.value - type.version.minor.value; + }; + + auto insert = [&](const Storage::Synchronization::ExportedType &type) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert exported type"_t, + projectStorageCategory(), + keyValue("exported type", type), + keyValue("type id", type.typeId), + keyValue("module id", type.moduleId)}; + if (!type.moduleId) + throw QmlDesigner::ModuleDoesNotExists{}; + + try { + if (type.version) { + s->insertExportedTypeNamesWithVersionStatement.write(type.moduleId, + type.name, + type.version.major.value, + type.version.minor.value, + type.typeId); + + } else if (type.version.major) { + s->insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId, + type.name, + type.version.major.value, + type.typeId); + } else { + s->insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId, + type.name, + type.typeId); + } + } catch (const Sqlite::ConstraintPreventsModification &) { + throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; + } + }; + + auto update = [&](const Storage::Synchronization::ExportedTypeView &view, + const Storage::Synchronization::ExportedType &type) { + if (view.typeId != type.typeId) { + NanotraceHR::Tracer tracer{"update exported type"_t, + projectStorageCategory(), + keyValue("exported type", type), + keyValue("exported type view", view), + keyValue("type id", type.typeId), + keyValue("module id", type.typeId)}; + + handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithPropertyType(view.typeId, + relinkableAliasPropertyDeclarations); + handlePrototypes(view.typeId, relinkablePrototypes); + handleExtensions(view.typeId, relinkableExtensions); + s->updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId); + return Sqlite::UpdateChange::Update; + } + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) { + NanotraceHR::Tracer tracer{"remove exported type"_t, + projectStorageCategory(), + keyValue("exported type", view), + keyValue("type id", view.typeId), + keyValue("module id", view.moduleId)}; + + handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithPropertyType(view.typeId, + relinkableAliasPropertyDeclarations); + handlePrototypes(view.typeId, relinkablePrototypes); + handleExtensions(view.typeId, relinkableExtensions); + s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId); + }; + + Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizePropertyDeclarationsInsertAlias( + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + const Storage::Synchronization::PropertyDeclaration &value, + SourceId sourceId, + TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property declaration to alias"_t, + projectStorageCategory(), + keyValue("property declaration", value)}; + + auto callback = [&](PropertyDeclarationId propertyDeclarationId) { + insertedAliasPropertyDeclarations.emplace_back(typeId, + propertyDeclarationId, + fetchImportedTypeNameId(value.typeName, + sourceId), + value.aliasPropertyName, + value.aliasPropertyNameTail); + return Sqlite::CallbackControl::Abort; + }; + + s->insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); +} + +QVarLengthArray<PropertyDeclarationId, 128> ProjectStorage::fetchPropertyDeclarationIds( + TypeId baseTypeId) const +{ + QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds; + + s->selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, baseTypeId); + + auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId); + + for (TypeId prototype : range) { + s->selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, prototype); + } + + return propertyDeclarationIds; +} + +PropertyDeclarationId ProjectStorage::fetchNextPropertyDeclarationId( + TypeId baseTypeId, Utils::SmallStringView propertyName) const +{ + auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId); + + for (TypeId prototype : range) { + auto propertyDeclarationId = s->selectPropertyDeclarationIdByTypeIdAndNameStatement + .value<PropertyDeclarationId>(prototype, propertyName); + + if (propertyDeclarationId) + return propertyDeclarationId; + } + + return PropertyDeclarationId{}; +} + +PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const +{ + auto propertyDeclarationId = s->selectPropertyDeclarationIdByTypeIdAndNameStatement + .value<PropertyDeclarationId>(typeId, propertyName); + + if (propertyDeclarationId) + return propertyDeclarationId; + + return fetchNextPropertyDeclarationId(typeId, propertyName); +} + +PropertyDeclarationId ProjectStorage::fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const +{ + auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId); + + for (TypeId prototype : range) { + auto propertyDeclarationId = s->selectDefaultPropertyDeclarationIdStatement + .value<PropertyDeclarationId>(prototype); + + if (propertyDeclarationId) + return propertyDeclarationId; + } + + return PropertyDeclarationId{}; +} + +PropertyDeclarationId ProjectStorage::fetchDefaultPropertyDeclarationId(TypeId typeId) const +{ + auto propertyDeclarationId = s->selectDefaultPropertyDeclarationIdStatement + .value<PropertyDeclarationId>(typeId); + + if (propertyDeclarationId) + return propertyDeclarationId; + + return fetchNextDefaultPropertyDeclarationId(typeId); +} + +void ProjectStorage::synchronizePropertyDeclarationsInsertProperty( + const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property declaration"_t, + projectStorageCategory(), + keyValue("property declaration", value)}; + + auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); + auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); + + if (!propertyTypeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; + + auto propertyDeclarationId = s->insertPropertyDeclarationStatement.value<PropertyDeclarationId>( + typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); + + auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, value.name); + if (nextPropertyDeclarationId) { + s->updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId, + propertyDeclarationId); + s->updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement + .write(propertyDeclarationId, propertyTypeId, value.traits); + } +} + +void ProjectStorage::synchronizePropertyDeclarationsUpdateAlias( + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + const Storage::Synchronization::PropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value, + SourceId sourceId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property declaration to alias"_t, + projectStorageCategory(), + keyValue("property declaration", value), + keyValue("property declaration view", view)}; + + updatedAliasPropertyDeclarations.emplace_back(view.typeId, + view.id, + fetchImportedTypeNameId(value.typeName, sourceId), + value.aliasPropertyName, + value.aliasPropertyNameTail, + view.aliasId); +} + +Sqlite::UpdateChange ProjectStorage::synchronizePropertyDeclarationsUpdateProperty( + const Storage::Synchronization::PropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value, + SourceId sourceId, + PropertyDeclarationIds &propertyDeclarationIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property declaration"_t, + projectStorageCategory(), + keyValue("property declaration", value), + keyValue("property declaration view", view)}; + + auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); + + auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); + + if (!propertyTypeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; + + if (view.traits == value.traits && propertyTypeId == view.typeId + && propertyImportedTypeNameId == view.typeNameId) + return Sqlite::UpdateChange::No; + + s->updatePropertyDeclarationStatement.write(view.id, + propertyTypeId, + value.traits, + propertyImportedTypeNameId); + s->updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(view.id, + propertyTypeId, + value.traits); + propertyDeclarationIds.push_back(view.id); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; +} + +void ProjectStorage::synchronizePropertyDeclarations( + TypeId typeId, + Storage::Synchronization::PropertyDeclarations &propertyDeclarations, + SourceId sourceId, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarationIds &propertyDeclarationIds) +{ + NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()}; + + std::sort(propertyDeclarations.begin(), propertyDeclarations.end(), [](auto &&first, auto &&second) { + return Sqlite::compare(first.name, second.name) < 0; + }); + + auto range = s->selectPropertyDeclarationsForTypeIdStatement + .range<Storage::Synchronization::PropertyDeclarationView>(typeId); + + auto compareKey = [](const Storage::Synchronization::PropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value) { + return Sqlite::compare(view.name, value.name); + }; + + auto insert = [&](const Storage::Synchronization::PropertyDeclaration &value) { + if (value.kind == Storage::Synchronization::PropertyKind::Alias) { + synchronizePropertyDeclarationsInsertAlias(insertedAliasPropertyDeclarations, + value, + sourceId, + typeId); + } else { + synchronizePropertyDeclarationsInsertProperty(value, sourceId, typeId); + } + }; + + auto update = [&](const Storage::Synchronization::PropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value) { + if (value.kind == Storage::Synchronization::PropertyKind::Alias) { + synchronizePropertyDeclarationsUpdateAlias(updatedAliasPropertyDeclarations, + view, + value, + sourceId); + propertyDeclarationIds.push_back(view.id); + } else { + return synchronizePropertyDeclarationsUpdateProperty(view, + value, + sourceId, + propertyDeclarationIds); + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove property declaration"_t, + projectStorageCategory(), + keyValue("property declaratio viewn", view)}; + + auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, view.name); + + if (nextPropertyDeclarationId) { + s->updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement + .write(nextPropertyDeclarationId, view.id); + } + + s->updateDefaultPropertyIdToNullStatement.write(view.id); + s->deletePropertyDeclarationStatement.write(view.id); + propertyDeclarationIds.push_back(view.id); + }; + + Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove); +} + +void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( + Storage::Synchronization::Type &type, PropertyDeclarationIds &propertyDeclarationIds) +{ + NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, + projectStorageCategory()}; + + if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) + return; + + Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations; + + std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) { + return Sqlite::compare(first.name, second.name) < 0; + }); + + auto range = s->selectPropertyDeclarationsWithAliasForTypeIdStatement + .range<AliasPropertyDeclarationView>(type.typeId); + + auto compareKey = [](const AliasPropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value) { + return Sqlite::compare(view.name, value.name); + }; + + auto insert = [&](const Storage::Synchronization::PropertyDeclaration &) {}; + + auto update = [&](const AliasPropertyDeclarationView &, + const Storage::Synchronization::PropertyDeclaration &) { + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const AliasPropertyDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, + projectStorageCategory(), + keyValue("alias property declaration view", view)}; + + s->updatePropertyDeclarationAliasIdToNullStatement.write(view.id); + propertyDeclarationIds.push_back(view.id); + }; + + Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove); +} + +void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( + Storage::Synchronization::Types &types, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) +{ + NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, projectStorageCategory()}; + + PropertyDeclarationIds propertyDeclarationIds; + propertyDeclarationIds.reserve(types.size()); + + for (auto &&type : types) + resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds); + + removeRelinkableEntries(relinkableAliasPropertyDeclarations, + propertyDeclarationIds, + PropertyCompare<AliasPropertyDeclaration>{}); +} + +ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, + Storage::Synchronization::ImportKind importKind, + ModuleId sourceModuleId, + ImportId parentImportId) +{ + if (import.version.minor) { + return s->insertDocumentImportWithVersionStatement.value<ImportId>(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + import.version.minor.value, + parentImportId); + } else if (import.version.major) { + return s->insertDocumentImportWithMajorVersionStatement.value<ImportId>(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + parentImportId); + } else { + return s->insertDocumentImportWithoutVersionStatement.value<ImportId>(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + parentImportId); + } +} + +void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, + const SourceIds &updatedSourceIds, + Storage::Synchronization::ImportKind importKind) +{ + std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { + return std::tie(first.sourceId, first.moduleId, first.version) + < std::tie(second.sourceId, second.moduleId, second.version); + }); + + auto range = s->selectDocumentImportForSourceIdStatement + .range<Storage::Synchronization::ImportView>(toIntegers(updatedSourceIds), + importKind); + + auto compareKey = [](const Storage::Synchronization::ImportView &view, + const Storage::Import &import) -> long long { + auto sourceIdDifference = view.sourceId - import.sourceId; + if (sourceIdDifference != 0) + return sourceIdDifference; + + auto moduleIdDifference = view.moduleId - import.moduleId; + if (moduleIdDifference != 0) + return moduleIdDifference; + + auto versionDifference = view.version.major.value - import.version.major.value; + if (versionDifference != 0) + return versionDifference; + + return view.version.minor.value - import.version.minor.value; + }; + + auto insert = [&](const Storage::Import &import) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert import"_t, + projectStorageCategory(), + keyValue("import", import), + keyValue("import kind", importKind), + keyValue("source id", import.sourceId), + keyValue("module id", import.moduleId)}; + + auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); + auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { + Storage::Import additionImport{exportedModuleId, + Storage::Version{majorVersion, minorVersion}, + import.sourceId}; + + auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import + ? Storage::Synchronization::ImportKind::ModuleExportedImport + : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency; + + NanotraceHR::Tracer tracer{"insert indirect import"_t, + projectStorageCategory(), + keyValue("import", import), + keyValue("import kind", exportedImportKind), + keyValue("source id", import.sourceId), + keyValue("module id", import.moduleId)}; + + auto indirectImportId = insertDocumentImport(additionImport, + exportedImportKind, + import.moduleId, + importId); + + tracer.end(keyValue("import id", indirectImportId)); + }; + + s->selectModuleExportedImportsForModuleIdStatement.readCallback(callback, + import.moduleId, + import.version.major.value, + import.version.minor.value); + tracer.end(keyValue("import id", importId)); + }; + + auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) { + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::ImportView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove import"_t, + projectStorageCategory(), + keyValue("import", view), + keyValue("import id", view.importId), + keyValue("source id", view.sourceId), + keyValue("module id", view.moduleId)}; + + s->deleteDocumentImportStatement.write(view.importId); + s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); + }; + + Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); +} + +Utils::PathString ProjectStorage::createJson(const Storage::Synchronization::ParameterDeclarations ¶meters) +{ + NanotraceHR::Tracer tracer{"create json from parameter declarations"_t, projectStorageCategory()}; + + Utils::PathString json; + json.append("["); + + Utils::SmallStringView comma{""}; + + for (const auto ¶meter : parameters) { + json.append(comma); + comma = ","; + json.append(R"({"n":")"); + json.append(parameter.name); + json.append(R"(","tn":")"); + json.append(parameter.typeName); + if (parameter.traits == Storage::PropertyDeclarationTraits::None) { + json.append("\"}"); + } else { + json.append(R"(","tr":)"); + json.append(Utils::SmallString::number(to_underlying(parameter.traits))); + json.append("}"); + } + } + + json.append("]"); + + return json; +} + +TypeId ProjectStorage::fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, + Utils::SmallStringView name) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by module id and exported name"_t, + projectStorageCategory(), + keyValue("module id", moduleId), + keyValue("exported name", name)}; + + return s->selectTypeIdByModuleIdAndExportedNameStatement.value<TypeId>(moduleId, name); +} + +void ProjectStorage::addTypeIdToPropertyEditorQmlPaths( + Storage::Synchronization::PropertyEditorQmlPaths &paths) +{ + NanotraceHR::Tracer tracer{"add type id to property editor qml paths"_t, projectStorageCategory()}; + + for (auto &path : paths) + path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName); +} + +void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, + SourceIds updatedPropertyEditorQmlPathsSourceIds) +{ + using Storage::Synchronization::PropertyEditorQmlPath; + std::sort(paths.begin(), paths.end(), [](auto &&first, auto &&second) { + return first.typeId < second.typeId; + }); + + auto range = s->selectPropertyEditorPathsForForSourceIdsStatement.range<PropertyEditorQmlPathView>( + toIntegers(updatedPropertyEditorQmlPathsSourceIds)); + + auto compareKey = [](const PropertyEditorQmlPathView &view, + const PropertyEditorQmlPath &value) -> long long { + return view.typeId - value.typeId; + }; + + auto insert = [&](const PropertyEditorQmlPath &path) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path", path)}; + + if (path.typeId) + s->insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); + }; + + auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path", value), + keyValue("property editor qml path view", view)}; + + if (value.pathId != view.pathId || value.directoryId != view.directoryId) { + s->updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; + } + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const PropertyEditorQmlPathView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path view", view)}; + + s->deletePropertyEditorPathStatement.write(view.typeId); + }; + + Sqlite::insertUpdateDelete(range, paths, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizePropertyEditorQmlPaths( + Storage::Synchronization::PropertyEditorQmlPaths &paths, + SourceIds updatedPropertyEditorQmlPathsSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, projectStorageCategory()}; + + addTypeIdToPropertyEditorQmlPaths(paths); + synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds); +} + +void ProjectStorage::synchronizeFunctionDeclarations( + TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations) +{ + NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory()}; + + std::sort(functionsDeclarations.begin(), + functionsDeclarations.end(), + [](auto &&first, auto &&second) { + auto compare = Sqlite::compare(first.name, second.name); + + if (compare == 0) { + Utils::PathString firstSignature{createJson(first.parameters)}; + Utils::PathString secondSignature{createJson(second.parameters)}; + + return Sqlite::compare(firstSignature, secondSignature) < 0; + } + + return compare < 0; + }); + + auto range = s->selectFunctionDeclarationsForTypeIdStatement + .range<Storage::Synchronization::FunctionDeclarationView>(typeId); + + auto compareKey = [](const Storage::Synchronization::FunctionDeclarationView &view, + const Storage::Synchronization::FunctionDeclaration &value) { + auto nameKey = Sqlite::compare(view.name, value.name); + if (nameKey != 0) + return nameKey; + + Utils::PathString valueSignature{createJson(value.parameters)}; + + return Sqlite::compare(view.signature, valueSignature); + }; + + auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert function declaration"_t, + projectStorageCategory(), + keyValue("function declaration", value)}; + + Utils::PathString signature{createJson(value.parameters)}; + + s->insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature); + }; + + auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view, + const Storage::Synchronization::FunctionDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update function declaration"_t, + projectStorageCategory(), + keyValue("function declaration", value), + keyValue("function declaration view", view)}; + + Utils::PathString signature{createJson(value.parameters)}; + + if (value.returnTypeName == view.returnTypeName && signature == view.signature) + return Sqlite::UpdateChange::No; + + s->updateFunctionDeclarationStatement.write(view.id, value.returnTypeName, signature); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; + }; + + auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove function declaration"_t, + projectStorageCategory(), + keyValue("function declaration view", view)}; + + s->deleteFunctionDeclarationStatement.write(view.id); + }; + + Sqlite::insertUpdateDelete(range, functionsDeclarations, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeSignalDeclarations( + TypeId typeId, Storage::Synchronization::SignalDeclarations &signalDeclarations) +{ + NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory()}; + + std::sort(signalDeclarations.begin(), signalDeclarations.end(), [](auto &&first, auto &&second) { + auto compare = Sqlite::compare(first.name, second.name); + + if (compare == 0) { + Utils::PathString firstSignature{createJson(first.parameters)}; + Utils::PathString secondSignature{createJson(second.parameters)}; + + return Sqlite::compare(firstSignature, secondSignature) < 0; + } + + return compare < 0; + }); + + auto range = s->selectSignalDeclarationsForTypeIdStatement + .range<Storage::Synchronization::SignalDeclarationView>(typeId); + + auto compareKey = [](const Storage::Synchronization::SignalDeclarationView &view, + const Storage::Synchronization::SignalDeclaration &value) { + auto nameKey = Sqlite::compare(view.name, value.name); + if (nameKey != 0) + return nameKey; + + Utils::PathString valueSignature{createJson(value.parameters)}; + + return Sqlite::compare(view.signature, valueSignature); + }; + + auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert signal declaration"_t, + projectStorageCategory(), + keyValue("signal declaration", value)}; + + Utils::PathString signature{createJson(value.parameters)}; + + s->insertSignalDeclarationStatement.write(typeId, value.name, signature); + }; + + auto update = [&]([[maybe_unused]] const Storage::Synchronization::SignalDeclarationView &view, + [[maybe_unused]] const Storage::Synchronization::SignalDeclaration &value) { + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove signal declaration"_t, + projectStorageCategory(), + keyValue("signal declaration view", view)}; + + s->deleteSignalDeclarationStatement.write(view.id); + }; + + Sqlite::insertUpdateDelete(range, signalDeclarations, compareKey, insert, update, remove); +} + +Utils::PathString ProjectStorage::createJson( + const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"create json from enumerator declarations"_t, projectStorageCategory()}; + + Utils::PathString json; + json.append("{"); + + Utils::SmallStringView comma{"\""}; + + for (const auto &enumerator : enumeratorDeclarations) { + json.append(comma); + comma = ",\""; + json.append(enumerator.name); + if (enumerator.hasValue) { + json.append("\":\""); + json.append(Utils::SmallString::number(enumerator.value)); + json.append("\""); + } else { + json.append("\":null"); + } + } + + json.append("}"); + + return json; +} + +void ProjectStorage::synchronizeEnumerationDeclarations( + TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations) +{ + NanotraceHR::Tracer tracer{"synchronize enumeration declaration"_t, projectStorageCategory()}; + + std::sort(enumerationDeclarations.begin(), + enumerationDeclarations.end(), + [](auto &&first, auto &&second) { + return Sqlite::compare(first.name, second.name) < 0; + }); + + auto range = s->selectEnumerationDeclarationsForTypeIdStatement + .range<Storage::Synchronization::EnumerationDeclarationView>(typeId); + + auto compareKey = [](const Storage::Synchronization::EnumerationDeclarationView &view, + const Storage::Synchronization::EnumerationDeclaration &value) { + return Sqlite::compare(view.name, value.name); + }; + + auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration", value)}; + + Utils::PathString signature{createJson(value.enumeratorDeclarations)}; + + s->insertEnumerationDeclarationStatement.write(typeId, value.name, signature); + }; + + auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view, + const Storage::Synchronization::EnumerationDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration", value), + keyValue("enumeration declaration view", view)}; + + Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)}; + + if (enumeratorDeclarations == view.enumeratorDeclarations) + return Sqlite::UpdateChange::No; + + s->updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; + }; + + auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration view", view)}; + + s->deleteEnumerationDeclarationStatement.write(view.id); + }; + + Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove); +} + +void ProjectStorage::extractExportedTypes(TypeId typeId, + const Storage::Synchronization::Type &type, + Storage::Synchronization::ExportedTypes &exportedTypes) +{ + for (const auto &exportedType : type.exportedTypes) + exportedTypes.emplace_back(exportedType.name, exportedType.version, typeId, exportedType.moduleId); +} + +TypeId ProjectStorage::declareType(Storage::Synchronization::Type &type) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"declare type"_t, + projectStorageCategory(), + keyValue("source id", type.sourceId), + keyValue("type name", type.typeName)}; + + if (type.typeName.isEmpty()) { + type.typeId = s->selectTypeIdBySourceIdStatement.value<TypeId>(type.sourceId); + + tracer.end(keyValue("type id", type.typeId)); + + return type.typeId; + } + + type.typeId = s->insertTypeStatement.value<TypeId>(type.sourceId, type.typeName); + + if (!type.typeId) + type.typeId = s->selectTypeIdBySourceIdAndNameStatement.value<TypeId>(type.sourceId, + type.typeName); + + tracer.end(keyValue("type id", type.typeId)); + + return type.typeId; +} + +void ProjectStorage::syncDeclarations(Storage::Synchronization::Type &type, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarationIds &propertyDeclarationIds) +{ + NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()}; + + if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) + return; + + synchronizePropertyDeclarations(type.typeId, + type.propertyDeclarations, + type.sourceId, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + propertyDeclarationIds); + synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations); + synchronizeSignalDeclarations(type.typeId, type.signalDeclarations); + synchronizeEnumerationDeclarations(type.typeId, type.enumerationDeclarations); +} + +void ProjectStorage::syncDeclarations(Storage::Synchronization::Types &types, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations) +{ + NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()}; + + PropertyDeclarationIds propertyDeclarationIds; + propertyDeclarationIds.reserve(types.size() * 10); + + for (auto &&type : types) + syncDeclarations(type, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + propertyDeclarationIds); + + removeRelinkableEntries(relinkablePropertyDeclarations, + propertyDeclarationIds, + PropertyCompare<PropertyDeclaration>{}); +} + +void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &types) +{ + NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; + + auto range = s->selectTypesWithDefaultPropertyStatement.range<TypeWithDefaultPropertyView>(); + + auto compareKey = [](const TypeWithDefaultPropertyView &view, + const Storage::Synchronization::Type &value) { + return view.typeId - value.typeId; + }; + + auto insert = [&](const Storage::Synchronization::Type &) { + + }; + + auto update = [&](const TypeWithDefaultPropertyView &view, + const Storage::Synchronization::Type &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize default properties by update"_t, + projectStorageCategory(), + keyValue("type id", value.typeId), + keyValue("value", value), + keyValue("view", view)}; + + PropertyDeclarationId valueDefaultPropertyId; + if (value.defaultPropertyName.size()) + valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded(value.typeId, + value.defaultPropertyName) + .propertyDeclarationId; + + if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) + return Sqlite::UpdateChange::No; + + s->updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); + + tracer.end(keyValue("updated", "yes"), + keyValue("default property id", valueDefaultPropertyId)); + + return Sqlite::UpdateChange::Update; + }; + + auto remove = [&](const TypeWithDefaultPropertyView &) {}; + + Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); +} + +void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types) +{ + NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()}; + + auto range = s->selectTypesWithDefaultPropertyStatement.range<TypeWithDefaultPropertyView>(); + + auto compareKey = [](const TypeWithDefaultPropertyView &view, + const Storage::Synchronization::Type &value) { + return view.typeId - value.typeId; + }; + + auto insert = [&](const Storage::Synchronization::Type &) { + + }; + + auto update = [&](const TypeWithDefaultPropertyView &view, + const Storage::Synchronization::Type &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"reset changed default properties by update"_t, + projectStorageCategory(), + keyValue("type id", value.typeId), + keyValue("value", value), + keyValue("view", view)}; + + PropertyDeclarationId valueDefaultPropertyId; + if (value.defaultPropertyName.size()) { + auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( + value.typeId, value.defaultPropertyName); + if (optionalValueDefaultPropertyId) + valueDefaultPropertyId = optionalValueDefaultPropertyId->propertyDeclarationId; + } + + if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) + return Sqlite::UpdateChange::No; + + s->updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{}); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; + }; + + auto remove = [&](const TypeWithDefaultPropertyView &) {}; + + Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); +} + +void ProjectStorage::checkForPrototypeChainCycle(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check for prototype chain cycle"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto callback = [=](TypeId currentTypeId) { + if (typeId == currentTypeId) + throw PrototypeChainCycle{}; + }; + + s->selectPrototypeAndExtensionIdsStatement.readCallback(callback, typeId); +} + +void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check for alias chain cycle"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; + auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) { + if (propertyDeclarationId == currentPropertyDeclarationId) + throw AliasChainCycle{}; + }; + + s->selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback, + propertyDeclarationId); +} + +std::pair<TypeId, ImportedTypeNameId> ProjectStorage::fetchImportedTypeNameIdAndTypeId( + const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, + projectStorageCategory(), + keyValue("imported type name", typeName), + keyValue("source id", sourceId)}; + + TypeId typeId; + ImportedTypeNameId typeNameId; + if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { + typeNameId = fetchImportedTypeNameId(typeName, sourceId); + + typeId = fetchTypeId(typeNameId); + + tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId)); + + if (!typeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId}; + } + + return {typeId, typeNameId}; +} + +void ProjectStorage::syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds) +{ + if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) + return; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize prototype and extension"_t, + projectStorageCategory(), + keyValue("prototype", type.prototype), + keyValue("extension", type.extension), + keyValue("type id", type.typeId), + keyValue("source id", type.sourceId)}; + + auto [prototypeId, prototypeTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.prototype, + type.sourceId); + auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension, + type.sourceId); + + s->updatePrototypeAndExtensionStatement.write(type.typeId, + prototypeId, + prototypeTypeNameId, + extensionId, + extensionTypeNameId); + + if (prototypeId || extensionId) + checkForPrototypeChainCycle(type.typeId); + + typeIds.push_back(type.typeId); + + tracer.end(keyValue("prototype id", prototypeId), + keyValue("prototype type name id", prototypeTypeNameId), + keyValue("extension id", extensionId), + keyValue("extension type name id", extensionTypeNameId)); +} + +void ProjectStorage::syncPrototypesAndExtensions(Storage::Synchronization::Types &types, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + NanotraceHR::Tracer tracer{"synchronize prototypes and extensions"_t, projectStorageCategory()}; + + TypeIds typeIds; + typeIds.reserve(types.size()); + + for (auto &type : types) + syncPrototypeAndExtension(type, typeIds); + + removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare<Prototype>{}); + removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare<Prototype>{}); +} + +ImportId ProjectStorage::fetchImportId(SourceId sourceId, const Storage::Import &import) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("import", import), + keyValue("source id", sourceId)}; + + ImportId importId; + if (import.version) { + importId = s->selectImportIdBySourceIdAndModuleIdAndVersionStatement.value<ImportId>( + sourceId, import.moduleId, import.version.major.value, import.version.minor.value); + } else if (import.version.major) { + importId = s->selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement + .value<ImportId>(sourceId, import.moduleId, import.version.major.value); + } else { + importId = s->selectImportIdBySourceIdAndModuleIdStatement.value<ImportId>(sourceId, + import.moduleId); + } + + tracer.end(keyValue("import id", importId)); + + return importId; +} + +ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId( + const Storage::Synchronization::ImportedTypeName &name, SourceId sourceId) +{ + struct Inspect + { + auto operator()(const Storage::Synchronization::ImportedType &importedType) + { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", importedType.name), + keyValue("source id", sourceId), + keyValue("type name kind", "exported"sv)}; + + return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, + sourceId, + importedType.name); + } + + auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType) + { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", importedType.name), + keyValue("import", importedType.import), + keyValue("type name kind", "qualified exported"sv)}; + + ImportId importId = storage.fetchImportId(sourceId, importedType.import); + + auto importedTypeNameId = storage.fetchImportedTypeNameId( + Storage::Synchronization::TypeNameKind::QualifiedExported, importId, importedType.name); + + tracer.end(keyValue("import id", importId), keyValue("source id", sourceId)); + + return importedTypeNameId; + } + + ProjectStorage &storage; + SourceId sourceId; + }; + + return std::visit(Inspect{*this, sourceId}, name); +} + +TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id with type name kind"_t, + projectStorageCategory(), + keyValue("type name id", typeNameId)}; + + auto kind = s->selectKindFromImportedTypeNamesStatement.value<Storage::Synchronization::TypeNameKind>( + typeNameId); + + auto typeId = fetchTypeId(typeNameId, kind); + + tracer.end(keyValue("type id", typeId), keyValue("type name kind", kind)); + + return typeId; +} + +Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId typeNameId) const +{ + return s->selectNameFromImportedTypeNamesStatement.value<Utils::SmallString>(typeNameId); +} + +TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, + Storage::Synchronization::TypeNameKind kind) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id"_t, + projectStorageCategory(), + keyValue("type name id", typeNameId), + keyValue("type name kind", kind)}; + + TypeId typeId; + if (kind == Storage::Synchronization::TypeNameKind::Exported) { + typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value<TypeId>(typeNameId); + } else { + typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<TypeId>(typeNameId); + } + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +std::optional<ProjectStorage::FetchPropertyDeclarationResult> +ProjectStorage::fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId, + Utils::SmallStringView name) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + + auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); + auto propertyDeclaration = s->selectPropertyDeclarationResultByPropertyDeclarationIdStatement + .optionalValue<FetchPropertyDeclarationResult>( + propertyDeclarationId); + + tracer.end(keyValue("property declaration", propertyDeclaration)); + + return propertyDeclaration; +} + +ProjectStorage::FetchPropertyDeclarationResult ProjectStorage::fetchPropertyDeclarationByTypeIdAndNameUngarded( + TypeId typeId, Utils::SmallStringView name) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declaration by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + + auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId, name); + tracer.end(keyValue("property declaration", propertyDeclaration)); + + if (propertyDeclaration) + return *propertyDeclaration; + + throw PropertyNameDoesNotExists{}; +} + +PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationIdByTypeIdAndNameUngarded( + TypeId typeId, Utils::SmallStringView name) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declaration id by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + + auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + if (propertyDeclarationId) + return propertyDeclarationId; + + throw PropertyNameDoesNotExists{}; +} + +SourceContextId ProjectStorage::readSourceContextId(Utils::SmallStringView sourceContextPath) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"read source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + + auto sourceContextId = s->selectSourceContextIdFromSourceContextsBySourceContextPathStatement + .value<SourceContextId>(sourceContextPath); + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; +} + +SourceContextId ProjectStorage::writeSourceContextId(Utils::SmallStringView sourceContextPath) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"write source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + + s->insertIntoSourceContextsStatement.write(sourceContextPath); + + auto sourceContextId = SourceContextId::create(static_cast<int>(database.lastInsertedRowId())); + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; +} + +SourceId ProjectStorage::writeSourceId(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"write source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + s->insertIntoSourcesStatement.write(sourceContextId, sourceName); + + auto sourceId = SourceId::create(static_cast<int>(database.lastInsertedRowId())); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +SourceId ProjectStorage::readSourceId(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"read source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + auto sourceId = s->selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement + .value<SourceId>(sourceContextId, sourceName); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +Storage::Synchronization::ExportedTypes ProjectStorage::fetchExportedTypes(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch exported type"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto exportedTypes = s->selectExportedTypesByTypeIdStatement + .values<Storage::Synchronization::ExportedType, 12>(typeId); + + tracer.end(keyValue("exported types", exportedTypes)); + + return exportedTypes; +} + +Storage::Synchronization::PropertyDeclarations ProjectStorage::fetchPropertyDeclarations(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarations = s->selectPropertyDeclarationsByTypeIdStatement + .values<Storage::Synchronization::PropertyDeclaration, 24>(typeId); + + tracer.end(keyValue("property declarations", propertyDeclarations)); + + return propertyDeclarations; +} + +Storage::Synchronization::FunctionDeclarations ProjectStorage::fetchFunctionDeclarations(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch signal declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + Storage::Synchronization::FunctionDeclarations functionDeclarations; + + auto callback = [&](Utils::SmallStringView name, + Utils::SmallStringView returnType, + FunctionDeclarationId functionDeclarationId) { + auto &functionDeclaration = functionDeclarations.emplace_back(name, returnType); + functionDeclaration.parameters = s->selectFunctionParameterDeclarationsStatement + .values<Storage::Synchronization::ParameterDeclaration, 8>( + functionDeclarationId); + }; + + s->selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + + tracer.end(keyValue("function declarations", functionDeclarations)); + + return functionDeclarations; +} + +Storage::Synchronization::SignalDeclarations ProjectStorage::fetchSignalDeclarations(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch signal declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + Storage::Synchronization::SignalDeclarations signalDeclarations; + + auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) { + auto &signalDeclaration = signalDeclarations.emplace_back(name); + signalDeclaration.parameters = s->selectSignalParameterDeclarationsStatement + .values<Storage::Synchronization::ParameterDeclaration, 8>( + signalDeclarationId); + }; + + s->selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + + tracer.end(keyValue("signal declarations", signalDeclarations)); + + return signalDeclarations; +} + +Storage::Synchronization::EnumerationDeclarations ProjectStorage::fetchEnumerationDeclarations(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch enumeration declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + Storage::Synchronization::EnumerationDeclarations enumerationDeclarations; + + auto callback = [&](Utils::SmallStringView name, + EnumerationDeclarationId enumerationDeclarationId) { + enumerationDeclarations.emplace_back( + name, + s->selectEnumeratorDeclarationStatement + .values<Storage::Synchronization::EnumeratorDeclaration, 8>(enumerationDeclarationId)); + }; + + s->selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement + .readCallback(callback, typeId); + + tracer.end(keyValue("enumeration declarations", enumerationDeclarations)); + + return enumerationDeclarations; +} + +template<typename... TypeIds> +bool ProjectStorage::isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("base type ids", NanotraceHR::array(baseTypeIds...))}; + + static_assert(((std::is_same_v<TypeId, TypeIds>) &&...), "Parameter must be a TypeId!"); + + if (((typeId == baseTypeIds) || ...)) { + tracer.end(keyValue("is based on", true)); + return true; + } + + auto range = s->selectPrototypeAndExtensionIdsStatement.rangeWithTransaction<TypeId>(typeId); + + auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) { + return ((currentTypeId == baseTypeIds) || ...); + }); + + tracer.end(keyValue("is based on", isBasedOn)); + + return isBasedOn; +} + +template<typename Id> +ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind kind, + Id id, + Utils::SmallStringView typeName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", typeName), + keyValue("kind", kind)}; + + auto importedTypeNameId = s->selectImportedTypeNameIdStatement.value<ImportedTypeNameId>(kind, + id, + typeName); + + if (!importedTypeNameId) + importedTypeNameId = s->insertImportedTypeNameIdStatement.value<ImportedTypeNameId>(kind, + id, + typeName); + + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index a770577a65..e7826f531b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -6,9 +6,12 @@ #include "commontypecache.h" #include "projectstorageexceptions.h" #include "projectstorageinterface.h" +#include "projectstoragetypes.h" #include "sourcepathcachetypes.h" #include "storagecache.h" +#include <tracing/qmldesignertracing.h> + #include <sqlitealgorithms.h> #include <sqlitedatabase.h> #include <sqlitetable.h> @@ -28,414 +31,87 @@ namespace QmlDesigner { using namespace NanotraceHR::Literals; -constexpr NanotraceHR::Tracing projectStorageTracingStatus() -{ -#ifdef ENABLE_PROJECT_STORAGE_TRACING - return NanotraceHR::Tracing::IsEnabled; -#else - return NanotraceHR::Tracing::IsDisabled; -#endif -} - -[[gnu::pure]] NanotraceHR::StringViewCategory<projectStorageTracingStatus()> &projectStorageCategory(); +using ProjectStorageTracing::projectStorageCategory; -template<typename Database> class ProjectStorage final : public ProjectStorageInterface { - friend Storage::Info::CommonTypeCache<Database>; + using Database = Sqlite::Database; + friend Storage::Info::CommonTypeCache<ProjectStorageType>; public: - template<int ResultCount, int BindParameterCount = 0> - using ReadStatement = typename Database::template ReadStatement<ResultCount, BindParameterCount>; - template<int ResultCount, int BindParameterCount = 0> - using ReadWriteStatement = typename Database::template ReadWriteStatement<ResultCount, BindParameterCount>; - template<int BindParameterCount> - using WriteStatement = typename Database::template WriteStatement<BindParameterCount>; - - ProjectStorage(Database &database, bool isInitialized) - : database{database} - , exclusiveTransaction{database} - , initializer{database, isInitialized} - { - NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; - - exclusiveTransaction.commit(); - - database.walCheckpointFull(); - - moduleCache.populate(); - } - - void synchronize(Storage::Synchronization::SynchronizationPackage package) override - { - NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory()}; - - TypeIds deletedTypeIds; - Sqlite::withImmediateTransaction(database, [&] { - AliasPropertyDeclarations insertedAliasPropertyDeclarations; - AliasPropertyDeclarations updatedAliasPropertyDeclarations; - - AliasPropertyDeclarations relinkableAliasPropertyDeclarations; - PropertyDeclarations relinkablePropertyDeclarations; - Prototypes relinkablePrototypes; - Prototypes relinkableExtensions; - - TypeIds updatedTypeIds; - updatedTypeIds.reserve(package.types.size()); - - TypeIds typeIdsToBeDeleted; - - std::sort(package.updatedSourceIds.begin(), package.updatedSourceIds.end()); - - synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds); - synchronizeImports(package.imports, - package.updatedSourceIds, - package.moduleDependencies, - package.updatedModuleDependencySourceIds, - package.moduleExportedImports, - package.updatedModuleIds); - synchronizeTypes(package.types, - updatedTypeIds, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions, - package.updatedSourceIds); - synchronizeTypeAnnotations(package.typeAnnotations, - package.updatedTypeAnnotationSourceIds); - synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, - package.updatedPropertyEditorQmlPathSourceIds); - - deleteNotUpdatedTypes(updatedTypeIds, - package.updatedSourceIds, - typeIdsToBeDeleted, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions, - deletedTypeIds); - - relink(relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions, - deletedTypeIds); - - linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); - - synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); - - commonTypeCache_.resetTypeIds(); - }); - - callRefreshMetaInfoCallback(deletedTypeIds); - } - - void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override - { - NanotraceHR::Tracer tracer{"synchronize document imports"_t, projectStorageCategory()}; - - Sqlite::withImmediateTransaction(database, [&] { - synchronizeDocumentImports(imports, - {sourceId}, - Storage::Synchronization::ImportKind::Import); - }); - } + ProjectStorage(Database &database, bool isInitialized); + ~ProjectStorage(); - void addObserver(ProjectStorageObserver *observer) override { observers.push_back(observer); } + void synchronize(Storage::Synchronization::SynchronizationPackage package) override; - void removeObserver(ProjectStorageObserver *observer) override - { - observers.removeOne(observer); - } + void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override; - ModuleId moduleId(Utils::SmallStringView moduleName) const override - { - NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory()}; - - return moduleCache.id(moduleName); - } + void addObserver(ProjectStorageObserver *observer) override; - Utils::SmallString moduleName(ModuleId moduleId) const - { - NanotraceHR::Tracer tracer{"get module name"_t, projectStorageCategory()}; + void removeObserver(ProjectStorageObserver *observer) override; - if (!moduleId) - throw ModuleDoesNotExists{}; + ModuleId moduleId(Utils::SmallStringView moduleName) const override; - return moduleCache.value(moduleId); - } + Utils::SmallString moduleName(ModuleId moduleId) const override; TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, - Storage::Version version) const override - { - NanotraceHR::Tracer tracer{"get type id by exported name"_t, projectStorageCategory()}; - - if (version.minor) - return selectTypeIdByModuleIdAndExportedNameAndVersionStatement - .template valueWithTransaction<TypeId>(moduleId, - exportedTypeName, - version.major.value, - version.minor.value); - - if (version.major) - return selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement - .template valueWithTransaction<TypeId>(moduleId, exportedTypeName, version.major.value); + Storage::Version version) const override; - return selectTypeIdByModuleIdAndExportedNameStatement - .template valueWithTransaction<TypeId>(moduleId, exportedTypeName); - } - - TypeId typeId(ImportedTypeNameId typeNameId) const override - { - NanotraceHR::Tracer tracer{"get type id by imported type name"_t, projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); - } + TypeId typeId(ImportedTypeNameId typeNameId) const override; - QVarLengthArray<TypeId, 256> typeIds(ModuleId moduleId) const override - { - NanotraceHR::Tracer tracer{"get type ids by module id"_t, projectStorageCategory()}; + QVarLengthArray<TypeId, 256> typeIds(ModuleId moduleId) const override; - return selectTypeIdsByModuleIdStatement - .template valuesWithTransaction<QVarLengthArray<TypeId, 256>>(moduleId); - } + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override; - Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get exported type names by type id"_t, projectStorageCategory()}; + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override; - return selectExportedTypesByTypeIdStatement - .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId); - } + ImportId importId(const Storage::Import &import) const override; - Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, - SourceId sourceId) const override - { - NanotraceHR::Tracer tracer{"get exported type names by source id"_t, projectStorageCategory()}; + ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override; - return selectExportedTypesByTypeIdAndSourceIdStatement - .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId, sourceId); - } + ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override; - ImportId importId(const Storage::Import &import) const override - { - NanotraceHR::Tracer tracer{"get import id by import"_t, projectStorageCategory()}; + QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds(TypeId typeId) const override; - return Sqlite::withDeferredTransaction(database, [&] { - return fetchImportId(import.sourceId, import); - }); - } - - ImportedTypeNameId importedTypeNameId(ImportId importId, - Utils::SmallStringView typeName) override - { - NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, - projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { - return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, - importId, - typeName); - }); - } - - ImportedTypeNameId importedTypeNameId(SourceId sourceId, - Utils::SmallStringView typeName) override - { - NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, - projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { - return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, - sourceId, - typeName); - }); - } - - QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get property declaration ids"_t, projectStorageCategory()}; - - return selectPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(typeId); - } - - QVarLengthArray<PropertyDeclarationId, 128> localPropertyDeclarationIds(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get local property declaration ids"_t, projectStorageCategory()}; - - return selectLocalPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(typeId); - } + QVarLengthArray<PropertyDeclarationId, 128> localPropertyDeclarationIds(TypeId typeId) const override; PropertyDeclarationId propertyDeclarationId(TypeId typeId, - Utils::SmallStringView propertyName) const override - { - NanotraceHR::Tracer tracer{"get property declaration id"_t, projectStorageCategory()}; - - return selectPropertyDeclarationIdForTypeAndPropertyNameStatement - .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName); - } + Utils::SmallStringView propertyName) const override; PropertyDeclarationId localPropertyDeclarationId(TypeId typeId, - Utils::SmallStringView propertyName) const - { - NanotraceHR::Tracer tracer{"get local property declaration id"_t, projectStorageCategory()}; + Utils::SmallStringView propertyName) const; - return selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement - .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName); - } + PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const override; std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration( - PropertyDeclarationId propertyDeclarationId) const override - { - NanotraceHR::Tracer tracer{"get property declaration"_t, projectStorageCategory()}; - - return selectPropertyDeclarationForPropertyDeclarationIdStatement - .template optionalValueWithTransaction<Storage::Info::PropertyDeclaration>( - propertyDeclarationId); - } - - std::optional<Storage::Info::Type> type(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory()}; + PropertyDeclarationId propertyDeclarationId) const override; - return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction<Storage::Info::Type>( - typeId); - } + std::optional<Storage::Info::Type> type(TypeId typeId) const override; - Utils::PathString typeIconPath(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get type icon path"_t, projectStorageCategory()}; + Utils::PathString typeIconPath(TypeId typeId) const override; - return selectTypeIconPathStatement.template valueWithTransaction<Utils::PathString>(typeId); - } + Storage::Info::TypeHints typeHints(TypeId typeId) const override; - Storage::Info::TypeHints typeHints(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get type hints"_t, projectStorageCategory()}; + SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const override; - return selectTypeHintsStatement.template valuesWithTransaction<Storage::Info::TypeHints, 4>( - typeId); - } - - Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get item library entries by type id"_t, projectStorageCategory()}; - - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId_, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId_, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); - - return entries; - } - - Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override - { - NanotraceHR::Tracer tracer{"get item library entries by source id"_t, - projectStorageCategory()}; - - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); - - return entries; - } + SmallSourceIds<64> typeAnnotationDirectorySourceIds() const override; - Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override - { - NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()}; - - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); - - return entries; - } + Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override; - std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get signal names"_t, projectStorageCategory()}; + Storage::Info::ItemLibraryEntries itemLibraryEntries(ImportId importId) const; - return selectSignalDeclarationNamesForTypeStatement - .template valuesWithTransaction<Utils::SmallString, 32>(typeId); - } + Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override; - std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get function names"_t, projectStorageCategory()}; + Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override; - return selectFuncionDeclarationNamesForTypeStatement - .template valuesWithTransaction<Utils::SmallString, 32>(typeId); - } + std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const override; - std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const override - { - NanotraceHR::Tracer tracer{"get property name"_t, projectStorageCategory()}; + std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const override; - return selectPropertyNameStatement.template optionalValueWithTransaction<Utils::SmallString>( - propertyDeclarationId); - } + std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const override; - const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const override + const Storage::Info::CommonTypeCache<ProjectStorageType> &commonTypeCache() const override { return commonTypeCache_; } @@ -443,101 +119,70 @@ public: template<const char *moduleName, const char *typeName> TypeId commonTypeId() const { - NanotraceHR::Tracer tracer{"get type id from common type cache"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type id from common type cache"_t, + projectStorageCategory(), + keyValue("module name", std::string_view{moduleName}), + keyValue("type name", std::string_view{typeName})}; + + auto typeId = commonTypeCache_.typeId<moduleName, typeName>(); + + tracer.end(keyValue("type id", typeId)); - return commonTypeCache_.template typeId<moduleName, typeName>(); + return typeId; } template<typename BuiltinType> TypeId builtinTypeId() const { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, projectStorageCategory()}; - return commonTypeCache_.template builtinTypeId<BuiltinType>(); + auto typeId = commonTypeCache_.builtinTypeId<BuiltinType>(); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } template<const char *builtinType> TypeId builtinTypeId() const { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, projectStorageCategory()}; - return commonTypeCache_.template builtinTypeId<builtinType>(); - } + auto typeId = commonTypeCache_.builtinTypeId<builtinType>(); - TypeIds prototypeIds(TypeId type) const override - { - NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory()}; + tracer.end(keyValue("type id", typeId)); - return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction<TypeId, 16>( - type); + return typeId; } - TypeIds prototypeAndSelfIds(TypeId type) const override - { - NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; - - return selectPrototypeAndSelfIdsForTypeIdInOrderStatement - .template valuesWithTransaction<TypeId, 16>(type); - } + SmallTypeIds<16> prototypeIds(TypeId type) const override; - TypeIds heirIds(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; + SmallTypeIds<16> prototypeAndSelfIds(TypeId typeId) const override; - return selectHeirTypeIdsStatement.template valuesWithTransaction<TypeId, 64>(typeId); - } + SmallTypeIds<64> heirIds(TypeId typeId) const override; template<typename... TypeIds> - bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const - { - NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()}; + bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const; - static_assert(((std::is_same_v<TypeId, TypeIds>) &&...), "Parameter must be a TypeId!"); + bool isBasedOn(TypeId) const; - if (((typeId == baseTypeIds) || ...)) - return true; + bool isBasedOn(TypeId typeId, TypeId id1) const override; - auto range = selectPrototypeIdsStatement.template rangeWithTransaction<TypeId>(typeId); + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override; - for ([[maybe_unused]] TypeId currentTypeId : range) { - if (((currentTypeId == baseTypeIds) || ...)) - return true; - } - - return false; - } - - bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); } - - bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); } - - bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override - { - return isBasedOn_(typeId, id1, id2); - } - - bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override - { - return isBasedOn_(typeId, id1, id2, id3); - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override; - bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override - { - return isBasedOn_(typeId, id1, id2, id3, id4); - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override; - bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override - { - return isBasedOn_(typeId, id1, id2, id3, id4, id5); - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override; - bool isBasedOn( - TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override - { - return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6); - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) + const override; bool isBasedOn(TypeId typeId, TypeId id1, @@ -546,251 +191,57 @@ public: TypeId id4, TypeId id5, TypeId id6, - TypeId id7) const override - { - return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7); - } - - TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const - { - NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()}; - - return selectTypeIdByExportedNameStatement.template valueWithTransaction<TypeId>(name); - } - - TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, Utils::SmallStringView name) const - { - return selectTypeIdByModuleIdsAndExportedNameStatement.template valueWithTransaction<TypeId>( - static_cast<void *>(moduleIds.data()), static_cast<long long>(moduleIds.size()), name); - } - - TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name) - { - return selectTypeIdBySourceIdAndNameStatement.template valueWithTransaction<TypeId>(sourceId, - name); - } - - Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId) - { - return Sqlite::withDeferredTransaction(database, [&] { - auto type = selectTypeByTypeIdStatement.template value<Storage::Synchronization::Type>( - typeId); - - type.exportedTypes = fetchExportedTypes(typeId); - type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); - type.functionDeclarations = fetchFunctionDeclarations(type.typeId); - type.signalDeclarations = fetchSignalDeclarations(type.typeId); - type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); - - return type; - }); - } - - Storage::Synchronization::Types fetchTypes() - { - return Sqlite::withDeferredTransaction(database, [&] { - auto types = selectTypesStatement.template values<Storage::Synchronization::Type, 64>(); - - for (Storage::Synchronization::Type &type : types) { - type.exportedTypes = fetchExportedTypes(type.typeId); - type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); - type.functionDeclarations = fetchFunctionDeclarations(type.typeId); - type.signalDeclarations = fetchSignalDeclarations(type.typeId); - type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); - } - - return types; - }); - } - - bool fetchIsProtype(TypeId type, TypeId prototype) - { - return bool(selectPrototypeIdStatement.template valueWithTransaction<TypeId>(type, prototype)); - } - - auto fetchPrototypes(TypeId type) - { - return selectPrototypeIdsInOrderStatement.template rangeWithTransaction<TypeId>(type); - } - - SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) - { - NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()}; - - auto sourceContextId = readSourceContextId(sourceContextPath); - - return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath); - } - - SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) - { - NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()}; - - try { - return Sqlite::withDeferredTransaction(database, [&] { - return fetchSourceContextIdUnguarded(sourceContextPath); - }); - } catch (const Sqlite::ConstraintPreventsModification &) { - return fetchSourceContextId(sourceContextPath); - } - } - - Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const - { - NanotraceHR::Tracer tracer{"fetch source context path"_t, projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { - auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement - .template optionalValue<Utils::PathString>( - sourceContextId); - - if (!optionalSourceContextPath) - throw SourceContextIdDoesNotExists(); - - return std::move(*optionalSourceContextPath); - }); - } - - auto fetchAllSourceContexts() const - { - NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()}; - - return selectAllSourceContextsStatement - .template valuesWithTransaction<Cache::SourceContext, 128>(); - } - - SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - NanotraceHR::Tracer tracer{"fetch source id"_t, projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { - return fetchSourceIdUnguarded(sourceContextId, sourceName); - }); - } - - auto fetchSourceNameAndSourceContextId(SourceId sourceId) const - { - NanotraceHR::Tracer tracer{"fetch source name and source context id"_t, - projectStorageCategory()}; - - auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement - .template valueWithTransaction<Cache::SourceNameAndSourceContextId>(sourceId); - - if (!value.sourceContextId) - throw SourceIdDoesNotExists(); + TypeId id7) const override; - return value; - } - - void clearSources() - { - Sqlite::withImmediateTransaction(database, [&] { - deleteAllSourceContextsStatement.execute(); - deleteAllSourcesStatement.execute(); - }); - } - - SourceContextId fetchSourceContextId(SourceId sourceId) const - { - NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()}; - - auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement - .template valueWithTransaction<SourceContextId>(sourceId); + TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const; - if (!sourceContextId) - throw SourceIdDoesNotExists(); + TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, + Utils::SmallStringView name) const; - return sourceContextId; - } - - auto fetchAllSources() const - { - NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()}; + TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name); - return selectAllSourcesStatement.template valuesWithTransaction<Cache::Source, 1024>(); - } + Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId); - SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, projectStorageCategory()}; + Storage::Synchronization::Types fetchTypes(); - auto sourceId = readSourceId(sourceContextId, sourceName); + SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath); - if (sourceId) - return sourceId; + SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath); - return writeSourceId(sourceContextId, sourceName); - } + Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const; - auto fetchAllFileStatuses() const - { - NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; + Cache::SourceContexts fetchAllSourceContexts() const; - return selectAllFileStatusesStatement.template rangeWithTransaction<FileStatus>(); - } + SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - FileStatus fetchFileStatus(SourceId sourceId) const override - { - NanotraceHR::Tracer tracer{"fetch file status"_t, projectStorageCategory()}; + Cache::SourceNameAndSourceContextId fetchSourceNameAndSourceContextId(SourceId sourceId) const; - return selectFileStatusesForSourceIdStatement.template valueWithTransaction<FileStatus>( - sourceId); - } + void clearSources(); - std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const override - { - NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory()}; + SourceContextId fetchSourceContextId(SourceId sourceId) const; - return selectProjectDataForSourceIdStatement - .template optionalValueWithTransaction<Storage::Synchronization::ProjectData>(sourceId); - } + Cache::Sources fetchAllSources() const; - Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override - { - NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory()}; + SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, + Utils::SmallStringView sourceName); - return selectProjectDatasForSourceIdStatement - .template valuesWithTransaction<Storage::Synchronization::ProjectData, 1024>( - projectSourceId); - } + FileStatuses fetchAllFileStatuses() const; - Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const - { - NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory()}; + FileStatus fetchFileStatus(SourceId sourceId) const override; - return selectProjectDatasForSourceIdsStatement - .template valuesWithTransaction<Storage::Synchronization::ProjectData, 64>( - toIntegers(projectSourceIds)); - } + std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const override; - void setPropertyEditorPathId(TypeId typeId, SourceId pathId) - { - Sqlite::ImmediateSessionTransaction transaction{database}; + Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override; - upsertPropertyEditorPathIdStatement.write(typeId, pathId); + Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const; - transaction.commit(); - } + void setPropertyEditorPathId(TypeId typeId, SourceId pathId); - SourceId propertyEditorPathId(TypeId typeId) const override - { - return selectPropertyEditorPathIdStatement.template valueWithTransaction<SourceId>(typeId); - } + SourceId propertyEditorPathId(TypeId typeId) const override; - Storage::Imports fetchDocumentImports() const - { - NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; + Storage::Imports fetchDocumentImports() const; - return selectAllDocumentImportForSourceIdStatement - .template valuesWithTransaction<Storage::Imports>(); - } - - void resetForTestsOnly() - { - database.clearAllTablesForTestsOnly(); - commonTypeCache_.clearForTestsOnly(); - moduleCache.clearForTestOnly(); - } + void resetForTestsOnly(); private: class ModuleStorageAdapter @@ -818,12 +269,11 @@ private: } }; + using Modules = std::vector<Module>; + friend ModuleStorageAdapter; - static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept - { - return first < second; - } + static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept; using ModuleCache = StorageCache<Utils::PathString, Utils::SmallStringView, @@ -833,29 +283,13 @@ private: moduleNameLess, Module>; - ModuleId fetchModuleId(Utils::SmallStringView moduleName) - { - return Sqlite::withDeferredTransaction(database, - [&] { return fetchModuleIdUnguarded(moduleName); }); - } + ModuleId fetchModuleId(Utils::SmallStringView moduleName); - auto fetchModuleName(ModuleId id) - { - return Sqlite::withDeferredTransaction(database, [&] { return fetchModuleNameUnguarded(id); }); - } + Utils::PathString fetchModuleName(ModuleId id); - auto fetchAllModules() const - { - return selectAllModulesStatement.template valuesWithTransaction<Module, 128>(); - } + Modules fetchAllModules() const; - void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) - { - if (deletedTypeIds.size()) { - for (ProjectStorageObserver *observer : observers) - observer->removedTypeIds(deletedTypeIds); - } - } + void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds); class AliasPropertyDeclaration { @@ -882,6 +316,25 @@ private: < std::tie(second.typeId, second.propertyDeclarationId); } + template<typename String> + friend void convertToString(String &string, + const AliasPropertyDeclaration &aliasPropertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary( + keyValue("type id", aliasPropertyDeclaration.typeId), + keyValue("property declaration id", aliasPropertyDeclaration.propertyDeclarationId), + keyValue("alias imported type name id", + aliasPropertyDeclaration.aliasImportedTypeNameId), + keyValue("alias property name", aliasPropertyDeclaration.aliasPropertyName), + keyValue("alias property name tail", aliasPropertyDeclaration.aliasPropertyNameTail), + keyValue("alias property declaration id", + aliasPropertyDeclaration.aliasPropertyDeclarationId)); + + convertToString(string, dict); + } + public: TypeId typeId; PropertyDeclarationId propertyDeclarationId; @@ -910,6 +363,20 @@ private: < std::tie(second.typeId, second.propertyDeclarationId); } + template<typename String> + friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", propertyDeclaration.typeId), + keyValue("property declaration id", + propertyDeclaration.propertyDeclarationId), + keyValue("imported type name id", + propertyDeclaration.importedTypeNameId)); + + convertToString(string, dict); + } + public: TypeId typeId; PropertyDeclarationId propertyDeclarationId; @@ -931,6 +398,17 @@ private: return first.typeId < second.typeId; } + template<typename String> + friend void convertToString(String &string, const Prototype &prototype) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", prototype.typeId), + keyValue("prototype name id", prototype.prototypeNameId)); + + convertToString(string, dict); + } + public: TypeId typeId; ImportedTypeNameId prototypeNameId; @@ -970,37 +448,14 @@ private: } }; - SourceIds filterSourceIdsWithoutType(const SourceIds &updatedSourceIds, SourceIds &sourceIdsOfTypes) - { - std::sort(sourceIdsOfTypes.begin(), sourceIdsOfTypes.end()); + SourceIds filterSourceIdsWithoutType(const SourceIds &updatedSourceIds, + SourceIds &sourceIdsOfTypes); - SourceIds sourceIdsWithoutTypeSourceIds; - sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size()); - std::set_difference(updatedSourceIds.begin(), - updatedSourceIds.end(), - sourceIdsOfTypes.begin(), - sourceIdsOfTypes.end(), - std::back_inserter(sourceIdsWithoutTypeSourceIds)); + TypeIds fetchTypeIds(const SourceIds &sourceIds); - return sourceIdsWithoutTypeSourceIds; - } + void unique(SourceIds &sourceIds); - TypeIds fetchTypeIds(const SourceIds &sourceIds) - { - return selectTypeIdsForSourceIdsStatement.template values<TypeId, 128>(toIntegers(sourceIds)); - } - - void unique(SourceIds &sourceIds) - { - std::sort(sourceIds.begin(), sourceIds.end()); - auto newEnd = std::unique(sourceIds.begin(), sourceIds.end()); - sourceIds.erase(newEnd, sourceIds.end()); - } - - void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) - { - updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); - } + void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits); class TypeAnnotationView { @@ -1015,6 +470,19 @@ private: , hintsJson{hintsJson} {} + template<typename String> + friend void convertToString(String &string, const TypeAnnotationView &typeAnnotationView) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", typeAnnotationView.typeId), + keyValue("icon path", typeAnnotationView.iconPath), + keyValue("item library json", typeAnnotationView.itemLibraryJson), + keyValue("hints json", typeAnnotationView.hintsJson)); + + convertToString(string, dict); + } + public: TypeId typeId; Utils::SmallStringView iconPath; @@ -1022,75 +490,21 @@ private: Utils::PathString hintsJson; }; - void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) - { - for (auto &annotation : typeAnnotations) { - annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, - annotation.typeName); - } - } + void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations); - void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, - const SourceIds &updatedTypeAnnotationSourceIds) + template<typename Value> + static Sqlite::ValueView createEmptyAsNull(const Value &value) { - NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory()}; - - using Storage::Synchronization::TypeAnnotation; - - updateTypeIdInTypeAnnotations(typeAnnotations); - - auto compareKey = [](auto &&first, auto &&second) { return first.typeId - second.typeId; }; - - std::sort(typeAnnotations.begin(), typeAnnotations.end(), [&](auto &&first, auto &&second) { - return first.typeId < second.typeId; - }); + if (value.size()) + return Sqlite::ValueView::create(value); - auto range = selectTypeAnnotationsForSourceIdsStatement.template range<TypeAnnotationView>( - toIntegers(updatedTypeAnnotationSourceIds)); - - auto insert = [&](const TypeAnnotation &annotation) { - if (!annotation.sourceId) - throw TypeAnnotationHasInvalidSourceId{}; - - synchronizeTypeTraits(annotation.typeId, annotation.traits); - - insertTypeAnnotationStatement.write(annotation.typeId, - annotation.sourceId, - annotation.iconPath, - annotation.itemLibraryJson, - annotation.hintsJson); - }; - - auto update = [&](const TypeAnnotationView &annotationFromDatabase, - const TypeAnnotation &annotation) { - synchronizeTypeTraits(annotation.typeId, annotation.traits); - - if (annotationFromDatabase.iconPath != annotation.iconPath - || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson - || annotationFromDatabase.hintsJson != annotation.hintsJson) { - updateTypeAnnotationStatement.write(annotation.typeId, - annotation.iconPath, - annotation.itemLibraryJson, - annotation.hintsJson); - return Sqlite::UpdateChange::Update; - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { - synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); - - deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); - }; - - Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); + return Sqlite::ValueView{}; } - void synchronizeTypeTrait(const Storage::Synchronization::Type &type) - { - updateTypeTraitStatement.write(type.typeId, type.traits.type); - } + void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, + const SourceIds &updatedTypeAnnotationSourceIds); + + void synchronizeTypeTrait(const Storage::Synchronization::Type &type); void synchronizeTypes(Storage::Synchronization::Types &types, TypeIds &updatedTypeIds, @@ -1100,384 +514,60 @@ private: PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, - const SourceIds &updatedSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory()}; - - Storage::Synchronization::ExportedTypes exportedTypes; - exportedTypes.reserve(types.size() * 3); - SourceIds sourceIdsOfTypes; - sourceIdsOfTypes.reserve(updatedSourceIds.size()); - SourceIds notUpdatedExportedSourceIds; - notUpdatedExportedSourceIds.reserve(updatedSourceIds.size()); - SourceIds exportedSourceIds; - exportedSourceIds.reserve(types.size()); - - for (auto &type : types) { - if (!type.sourceId) - throw TypeHasInvalidSourceId{}; - - TypeId typeId = declareType(type); - synchronizeTypeTrait(type); - sourceIdsOfTypes.push_back(type.sourceId); - updatedTypeIds.push_back(typeId); - if (type.changeLevel != Storage::Synchronization::ChangeLevel::ExcludeExportedTypes) { - exportedSourceIds.push_back(type.sourceId); - extractExportedTypes(typeId, type, exportedTypes); - } - } - - std::sort(types.begin(), types.end(), [](const auto &first, const auto &second) { - return first.typeId < second.typeId; - }); - - unique(exportedSourceIds); - - SourceIds sourceIdsWithoutType = filterSourceIdsWithoutType(updatedSourceIds, - sourceIdsOfTypes); - exportedSourceIds.insert(exportedSourceIds.end(), - sourceIdsWithoutType.begin(), - sourceIdsWithoutType.end()); - TypeIds exportedTypeIds = fetchTypeIds(exportedSourceIds); - synchronizeExportedTypes(exportedTypeIds, - exportedTypes, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions); - - syncPrototypesAndExtensions(types, relinkablePrototypes, relinkableExtensions); - resetDefaultPropertiesIfChanged(types); - resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations); - syncDeclarations(types, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - relinkablePropertyDeclarations); - syncDefaultProperties(types); - } + const SourceIds &updatedSourceIds); void synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, - const SourceIds &updatedProjectSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()}; - - auto compareKey = [](auto &&first, auto &&second) { - auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId; - if (projectSourceIdDifference != 0) - return projectSourceIdDifference; - - return first.sourceId - second.sourceId; - }; - - std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) { - return std::tie(first.projectSourceId, first.sourceId) - < std::tie(second.projectSourceId, second.sourceId); - }); - - auto range = selectProjectDatasForSourceIdsStatement - .template range<Storage::Synchronization::ProjectData>( - toIntegers(updatedProjectSourceIds)); - - auto insert = [&](const Storage::Synchronization::ProjectData &projectData) { - if (!projectData.projectSourceId) - throw ProjectDataHasInvalidProjectSourceId{}; - if (!projectData.sourceId) - throw ProjectDataHasInvalidSourceId{}; - - insertProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); - }; - - auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase, - const Storage::Synchronization::ProjectData &projectData) { - if (projectDataFromDatabase.fileType != projectData.fileType - || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) { - updateProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); - return Sqlite::UpdateChange::Update; - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ProjectData &projectData) { - deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); - }; - - Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove); - } + const SourceIds &updatedProjectSourceIds); - void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory()}; - - auto compareKey = [](auto &&first, auto &&second) { - return first.sourceId - second.sourceId; - }; - - std::sort(fileStatuses.begin(), fileStatuses.end(), [&](auto &&first, auto &&second) { - return first.sourceId < second.sourceId; - }); - - auto range = selectFileStatusesForSourceIdsStatement.template range<FileStatus>( - toIntegers(updatedSourceIds)); - - auto insert = [&](const FileStatus &fileStatus) { - if (!fileStatus.sourceId) - throw FileStatusHasInvalidSourceId{}; - insertFileStatusStatement.write(fileStatus.sourceId, - fileStatus.size, - fileStatus.lastModified); - }; - - auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) { - if (fileStatusFromDatabase.lastModified != fileStatus.lastModified - || fileStatusFromDatabase.size != fileStatus.size) { - updateFileStatusStatement.write(fileStatus.sourceId, - fileStatus.size, - fileStatus.lastModified); - return Sqlite::UpdateChange::Update; - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const FileStatus &fileStatus) { - deleteFileStatusStatement.write(fileStatus.sourceId); - }; - - Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove); - } + void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds); void synchronizeImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, Storage::Imports &moduleDependencies, const SourceIds &updatedModuleDependencySourceIds, Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds) - { - NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; - - synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); - synchronizeDocumentImports(imports, - updatedSourceIds, - Storage::Synchronization::ImportKind::Import); - synchronizeDocumentImports(moduleDependencies, - updatedModuleDependencySourceIds, - Storage::Synchronization::ImportKind::ModuleDependency); - } + const ModuleIds &updatedModuleIds); void synchromizeModuleExportedImports( Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds) - { - std::sort(moduleExportedImports.begin(), - moduleExportedImports.end(), - [](auto &&first, auto &&second) { - return std::tie(first.moduleId, first.exportedModuleId) - < std::tie(second.moduleId, second.exportedModuleId); - }); - - auto range = selectModuleExportedImportsForSourceIdStatement - .template range<Storage::Synchronization::ModuleExportedImportView>( - toIntegers(updatedModuleIds)); - - auto compareKey = [](const Storage::Synchronization::ModuleExportedImportView &view, - const Storage::Synchronization::ModuleExportedImport &import) -> long long { - auto moduleIdDifference = view.moduleId - import.moduleId; - if (moduleIdDifference != 0) - return moduleIdDifference; - - return view.exportedModuleId - import.exportedModuleId; - }; - - auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) { - if (import.version.minor) { - insertModuleExportedImportWithVersionStatement.write(import.moduleId, - import.exportedModuleId, - import.isAutoVersion, - import.version.major.value, - import.version.minor.value); - } else if (import.version.major) { - insertModuleExportedImportWithMajorVersionStatement.write(import.moduleId, - import.exportedModuleId, - import.isAutoVersion, - import.version.major.value); - } else { - insertModuleExportedImportWithoutVersionStatement.write(import.moduleId, - import.exportedModuleId, - import.isAutoVersion); - } - }; - - auto update = [](const Storage::Synchronization::ModuleExportedImportView &, - const Storage::Synchronization::ModuleExportedImport &) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) { - deleteModuleExportedImportStatement.write(view.moduleExportedImportId); - }; - - Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); - } + const ModuleIds &updatedModuleIds); - ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override - { - auto moduleId = selectModuleIdByNameStatement.template value<ModuleId>(name); + ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override; - if (moduleId) - return moduleId; - - return insertModuleNameStatement.template value<ModuleId>(name); - } - - auto fetchModuleNameUnguarded(ModuleId id) const - { - auto moduleName = selectModuleNameStatement.template value<Utils::PathString>(id); - - if (moduleName.empty()) - throw ModuleDoesNotExists{}; - - return moduleName; - } + Utils::PathString fetchModuleNameUnguarded(ModuleId id) const; void handleAliasPropertyDeclarationsWithPropertyType( - TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) - { - auto callback = [&](TypeId typeId_, - PropertyDeclarationId propertyDeclarationId, - ImportedTypeNameId propertyImportedTypeNameId, - PropertyDeclarationId aliasPropertyDeclarationId, - PropertyDeclarationId aliasPropertyDeclarationTailId) { - auto aliasPropertyName = selectPropertyNameStatement.template value<Utils::SmallString>( - aliasPropertyDeclarationId); - Utils::SmallString aliasPropertyNameTail; - if (aliasPropertyDeclarationTailId) - aliasPropertyNameTail = selectPropertyNameStatement.template value<Utils::SmallString>( - aliasPropertyDeclarationTailId); - - relinkableAliasPropertyDeclarations - .emplace_back(TypeId{typeId_}, - PropertyDeclarationId{propertyDeclarationId}, - ImportedTypeNameId{propertyImportedTypeNameId}, - std::move(aliasPropertyName), - std::move(aliasPropertyNameTail)); - - updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); - }; - - selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback, - typeId); - } + TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); void handlePropertyDeclarationWithPropertyType(TypeId typeId, - PropertyDeclarations &relinkablePropertyDeclarations) - { - updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, - typeId); - } - - void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) - { - auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { - relinkablePrototypes.emplace_back(typeId, prototypeNameId); - }; + PropertyDeclarations &relinkablePropertyDeclarations); - updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); - } + void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes); - void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) - { - auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { - relinkableExtensions.emplace_back(typeId, extensionNameId); - }; - - updateExtensionIdToNullStatement.readCallback(callback, extensionId); - } + void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions); void deleteType(TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions) - { - handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations); - handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations); - handlePrototypes(typeId, relinkablePrototypes); - handleExtensions(typeId, relinkableExtensions); - deleteTypeNamesByTypeIdStatement.write(typeId); - deleteEnumerationDeclarationByTypeIdStatement.write(typeId); - deletePropertyDeclarationByTypeIdStatement.write(typeId); - deleteFunctionDeclarationByTypeIdStatement.write(typeId); - deleteSignalDeclarationByTypeIdStatement.write(typeId); - deleteTypeStatement.write(typeId); - } + Prototypes &relinkableExtensions); void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, - const TypeIds &deletedTypeIds) - { - NanotraceHR::Tracer tracer{"relink alias properties"_t, projectStorageCategory()}; - - std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); - - Utils::set_greedy_difference( - aliasPropertyDeclarations.cbegin(), - aliasPropertyDeclarations.cend(), - deletedTypeIds.begin(), - deletedTypeIds.end(), - [&](const AliasPropertyDeclaration &alias) { - auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); - - if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(alias.aliasImportedTypeNameId)}; - - auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( - typeId, alias.aliasPropertyName); - - updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, - propertyTypeId, - propertyTraits, - alias.aliasImportedTypeNameId, - aliasId); - }, - TypeCompare<AliasPropertyDeclaration>{}); - } + const TypeIds &deletedTypeIds); void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, - const TypeIds &deletedTypeIds) - { - NanotraceHR::Tracer tracer{"relink properties"_t, projectStorageCategory()}; - - std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); - - Utils::set_greedy_difference( - relinkablePropertyDeclaration.cbegin(), - relinkablePropertyDeclaration.cend(), - deletedTypeIds.begin(), - deletedTypeIds.end(), - [&](const PropertyDeclaration &property) { - TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); - - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)}; - - updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, - propertyTypeId); - }, - TypeCompare<PropertyDeclaration>{}); - } + const TypeIds &deletedTypeIds); template<typename Callable> void relinkPrototypes(Prototypes &relinkablePrototypes, const TypeIds &deletedTypeIds, Callable updateStatement) { - NanotraceHR::Tracer tracer{"relink prototypes"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink prototypes"_t, + projectStorageCategory(), + keyValue("relinkable prototypes", relinkablePrototypes), + keyValue("deleted type ids", deletedTypeIds)}; std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); @@ -1505,295 +595,66 @@ private: PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, - TypeIds &deletedTypeIds) - { - NanotraceHR::Tracer tracer{"delete not updated types"_t, projectStorageCategory()}; - - auto callback = [&](TypeId typeId) { - deletedTypeIds.push_back(typeId); - deleteType(typeId, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions); - }; - - selectNotUpdatedTypesInSourcesStatement.readCallback(callback, - toIntegers(updatedSourceIds), - toIntegers(updatedTypeIds)); - for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted) - callback(typeIdToBeDeleted); - } + TypeIds &deletedTypeIds); void relink(AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, - TypeIds &deletedTypeIds) - { - NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory()}; - - std::sort(deletedTypeIds.begin(), deletedTypeIds.end()); - - relinkPrototypes(relinkablePrototypes, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { - updateTypePrototypeStatement.write(typeId, prototypeId); - }); - relinkPrototypes(relinkableExtensions, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { - updateTypeExtensionStatement.write(typeId, prototypeId); - }); - relinkPropertyDeclarations(relinkablePropertyDeclarations, deletedTypeIds); - relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds); - } + TypeIds &deletedTypeIds); PropertyDeclarationId fetchAliasId(TypeId aliasTypeId, Utils::SmallStringView aliasPropertyName, - Utils::SmallStringView aliasPropertyNameTail) - { - if (aliasPropertyNameTail.empty()) - return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); - - auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, - aliasPropertyName); - - return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId, - aliasPropertyNameTail); - } + Utils::SmallStringView aliasPropertyNameTail); - void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations) - { - for (const auto &aliasDeclaration : aliasDeclarations) { - auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId); - - if (!aliasTypeId) { - throw TypeNameDoesNotExists{ - fetchImportedTypeName(aliasDeclaration.aliasImportedTypeNameId)}; - } - - auto aliasId = fetchAliasId(aliasTypeId, - aliasDeclaration.aliasPropertyName, - aliasDeclaration.aliasPropertyNameTail); - - updatePropertyDeclarationAliasIdAndTypeNameIdStatement - .write(aliasDeclaration.propertyDeclarationId, - aliasId, - aliasDeclaration.aliasImportedTypeNameId); - } - } + void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations); - void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations) - { - for (const auto &aliasDeclaration : aliasDeclarations) { - updatetPropertiesDeclarationValuesOfAliasStatement.write( - aliasDeclaration.propertyDeclarationId); - updatePropertyAliasDeclarationRecursivelyStatement.write( - aliasDeclaration.propertyDeclarationId); - } - } + void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations); - void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations) - { - for (const auto &aliasDeclaration : aliasDeclarations) - checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId); - } + void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations); void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) - { - NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()}; - - linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); - linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations); - - checkAliasPropertyDeclarationCycles(insertedAliasPropertyDeclarations); - checkAliasPropertyDeclarationCycles(updatedAliasPropertyDeclarations); - - updateAliasPropertyDeclarationValues(insertedAliasPropertyDeclarations); - updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); - } + const AliasPropertyDeclarations &updatedAliasPropertyDeclarations); void synchronizeExportedTypes(const TypeIds &updatedTypeIds, Storage::Synchronization::ExportedTypes &exportedTypes, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions) - { - NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()}; - - std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { - if (first.moduleId < second.moduleId) - return true; - else if (first.moduleId > second.moduleId) - return false; - - auto nameCompare = Sqlite::compare(first.name, second.name); - - if (nameCompare < 0) - return true; - else if (nameCompare > 0) - return false; - - return first.version < second.version; - }); - - auto range = selectExportedTypesForSourceIdsStatement - .template range<Storage::Synchronization::ExportedTypeView>( - toIntegers(updatedTypeIds)); - - auto compareKey = [](const Storage::Synchronization::ExportedTypeView &view, - const Storage::Synchronization::ExportedType &type) -> long long { - auto moduleIdDifference = view.moduleId - type.moduleId; - if (moduleIdDifference != 0) - return moduleIdDifference; - - auto nameDifference = Sqlite::compare(view.name, type.name); - if (nameDifference != 0) - return nameDifference; - - auto versionDifference = view.version.major.value - type.version.major.value; - if (versionDifference != 0) - return versionDifference; - - return view.version.minor.value - type.version.minor.value; - }; - - auto insert = [&](const Storage::Synchronization::ExportedType &type) { - if (!type.moduleId) - throw QmlDesigner::ModuleDoesNotExists{}; - - try { - if (type.version) { - insertExportedTypeNamesWithVersionStatement.write(type.moduleId, - type.name, - type.version.major.value, - type.version.minor.value, - type.typeId); - - } else if (type.version.major) { - insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId, - type.name, - type.version.major.value, - type.typeId); - } else { - insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId, - type.name, - type.typeId); - } - } catch (const Sqlite::ConstraintPreventsModification &) { - throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; - } - }; - - auto update = [&](const Storage::Synchronization::ExportedTypeView &view, - const Storage::Synchronization::ExportedType &type) { - if (view.typeId != type.typeId) { - handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); - handleAliasPropertyDeclarationsWithPropertyType(view.typeId, - relinkableAliasPropertyDeclarations); - handlePrototypes(view.typeId, relinkablePrototypes); - handleExtensions(view.typeId, relinkableExtensions); - updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId); - return Sqlite::UpdateChange::Update; - } - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) { - handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); - handleAliasPropertyDeclarationsWithPropertyType(view.typeId, - relinkableAliasPropertyDeclarations); - handlePrototypes(view.typeId, relinkablePrototypes); - handleExtensions(view.typeId, relinkableExtensions); - deleteExportedTypeNameStatement.write(view.exportedTypeNameId); - }; - - Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove); - } + Prototypes &relinkableExtensions); void synchronizePropertyDeclarationsInsertAlias( AliasPropertyDeclarations &insertedAliasPropertyDeclarations, const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, - TypeId typeId) - { - auto callback = [&](PropertyDeclarationId propertyDeclarationId) { - insertedAliasPropertyDeclarations.emplace_back(typeId, - propertyDeclarationId, - fetchImportedTypeNameId(value.typeName, - sourceId), - value.aliasPropertyName, - value.aliasPropertyNameTail); - return Sqlite::CallbackControl::Abort; - }; - - insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); - } + TypeId typeId); + + QVarLengthArray<PropertyDeclarationId, 128> fetchPropertyDeclarationIds(TypeId baseTypeId) const; + + PropertyDeclarationId fetchNextPropertyDeclarationId(TypeId baseTypeId, + Utils::SmallStringView propertyName) const; + + PropertyDeclarationId fetchPropertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const; + + PropertyDeclarationId fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const; + + PropertyDeclarationId fetchDefaultPropertyDeclarationId(TypeId typeId) const; void synchronizePropertyDeclarationsInsertProperty( - const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) - { - auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); - auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); - - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)}; - - auto propertyDeclarationId = insertPropertyDeclarationStatement.template value<PropertyDeclarationId>( - typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); - - auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement - .template value<PropertyDeclarationId>(typeId, - value.name); - if (nextPropertyDeclarationId) { - updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId, - propertyDeclarationId); - updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement - .write(propertyDeclarationId, propertyTypeId, value.traits); - } - } + const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId); void synchronizePropertyDeclarationsUpdateAlias( AliasPropertyDeclarations &updatedAliasPropertyDeclarations, const Storage::Synchronization::PropertyDeclarationView &view, const Storage::Synchronization::PropertyDeclaration &value, - SourceId sourceId) - { - auto last = updatedAliasPropertyDeclarations.emplace_back(view.typeId, - view.id, - fetchImportedTypeNameId(value.typeName, - sourceId), - value.aliasPropertyName, - value.aliasPropertyNameTail, - view.aliasId); - } + SourceId sourceId); - auto synchronizePropertyDeclarationsUpdateProperty( + Sqlite::UpdateChange synchronizePropertyDeclarationsUpdateProperty( const Storage::Synchronization::PropertyDeclarationView &view, const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, - PropertyDeclarationIds &propertyDeclarationIds) - { - auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); - - auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); - - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)}; - - if (view.traits == value.traits && propertyTypeId == view.typeId - && propertyImportedTypeNameId == view.typeNameId) - return Sqlite::UpdateChange::No; - - updatePropertyDeclarationStatement.write(view.id, - propertyTypeId, - value.traits, - propertyImportedTypeNameId); - updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(view.id, - propertyTypeId, - value.traits); - propertyDeclarationIds.push_back(view.id); - return Sqlite::UpdateChange::Update; - } + PropertyDeclarationIds &propertyDeclarationIds); void synchronizePropertyDeclarations( TypeId typeId, @@ -1801,270 +662,60 @@ private: SourceId sourceId, AliasPropertyDeclarations &insertedAliasPropertyDeclarations, AliasPropertyDeclarations &updatedAliasPropertyDeclarations, - PropertyDeclarationIds &propertyDeclarationIds) - { - NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()}; - - std::sort(propertyDeclarations.begin(), - propertyDeclarations.end(), - [](auto &&first, auto &&second) { - return Sqlite::compare(first.name, second.name) < 0; - }); - - auto range = selectPropertyDeclarationsForTypeIdStatement - .template range<Storage::Synchronization::PropertyDeclarationView>(typeId); - - auto compareKey = [](const Storage::Synchronization::PropertyDeclarationView &view, - const Storage::Synchronization::PropertyDeclaration &value) { - return Sqlite::compare(view.name, value.name); - }; - - auto insert = [&](const Storage::Synchronization::PropertyDeclaration &value) { - if (value.kind == Storage::Synchronization::PropertyKind::Alias) { - synchronizePropertyDeclarationsInsertAlias(insertedAliasPropertyDeclarations, - value, - sourceId, - typeId); - } else { - synchronizePropertyDeclarationsInsertProperty(value, sourceId, typeId); - } - }; - - auto update = [&](const Storage::Synchronization::PropertyDeclarationView &view, - const Storage::Synchronization::PropertyDeclaration &value) { - if (value.kind == Storage::Synchronization::PropertyKind::Alias) { - synchronizePropertyDeclarationsUpdateAlias(updatedAliasPropertyDeclarations, - view, - value, - sourceId); - propertyDeclarationIds.push_back(view.id); - } else { - return synchronizePropertyDeclarationsUpdateProperty(view, - value, - sourceId, - propertyDeclarationIds); - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) { - auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement - .template value<PropertyDeclarationId>(typeId, - view.name); - if (nextPropertyDeclarationId) { - updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement - .write(nextPropertyDeclarationId, view.id); - } - - updateDefaultPropertyIdToNullStatement.write(view.id); - deletePropertyDeclarationStatement.write(view.id); - propertyDeclarationIds.push_back(view.id); - }; - - Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove); - } + PropertyDeclarationIds &propertyDeclarationIds); - void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type, - PropertyDeclarationIds &propertyDeclarationIds) + class AliasPropertyDeclarationView { - if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) - return; - - Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations; + public: + explicit AliasPropertyDeclarationView(Utils::SmallStringView name, + PropertyDeclarationId id, + PropertyDeclarationId aliasId) + : name{name} + , id{id} + , aliasId{aliasId} + {} - class AliasPropertyDeclarationView + template<typename String> + friend void convertToString(String &string, + const AliasPropertyDeclarationView &aliasPropertyDeclarationView) { - public: - explicit AliasPropertyDeclarationView(Utils::SmallStringView name, - PropertyDeclarationId id, - PropertyDeclarationId aliasId) - : name{name} - , id{id} - , aliasId{aliasId} - {} - - public: - Utils::SmallStringView name; - PropertyDeclarationId id; - PropertyDeclarationId aliasId; - }; - - std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) { - return Sqlite::compare(first.name, second.name) < 0; - }); - - auto range = selectPropertyDeclarationsWithAliasForTypeIdStatement - .template range<AliasPropertyDeclarationView>(type.typeId); - - auto compareKey = [](const AliasPropertyDeclarationView &view, - const Storage::Synchronization::PropertyDeclaration &value) { - return Sqlite::compare(view.name, value.name); - }; - - auto insert = [&](const Storage::Synchronization::PropertyDeclaration &) {}; - - auto update = [&](const AliasPropertyDeclarationView &, - const Storage::Synchronization::PropertyDeclaration &) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const AliasPropertyDeclarationView &view) { - updatePropertyDeclarationAliasIdToNullStatement.write(view.id); - propertyDeclarationIds.push_back(view.id); - }; - - Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove); - } + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", aliasPropertyDeclarationView.name), + keyValue("id", aliasPropertyDeclarationView.id), + keyValue("alias id", aliasPropertyDeclarationView.aliasId)); - void resetRemovedAliasPropertyDeclarationsToNull( - Storage::Synchronization::Types &types, - AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) - { - NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, - projectStorageCategory()}; + convertToString(string, dict); + } - PropertyDeclarationIds propertyDeclarationIds; - propertyDeclarationIds.reserve(types.size()); + public: + Utils::SmallStringView name; + PropertyDeclarationId id; + PropertyDeclarationId aliasId; + }; - for (auto &&type : types) - resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds); + void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type, + PropertyDeclarationIds &propertyDeclarationIds); - removeRelinkableEntries(relinkableAliasPropertyDeclarations, - propertyDeclarationIds, - PropertyCompare<AliasPropertyDeclaration>{}); - } + void resetRemovedAliasPropertyDeclarationsToNull( + Storage::Synchronization::Types &types, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); ImportId insertDocumentImport(const Storage::Import &import, Storage::Synchronization::ImportKind importKind, ModuleId sourceModuleId, - ImportId parentImportId) - { - if (import.version.minor) { - return insertDocumentImportWithVersionStatement - .template value<ImportId>(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - import.version.minor.value, - parentImportId); - } else if (import.version.major) { - return insertDocumentImportWithMajorVersionStatement - .template value<ImportId>(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - parentImportId); - } else { - return insertDocumentImportWithoutVersionStatement.template value<ImportId>( - import.sourceId, import.moduleId, sourceModuleId, importKind, parentImportId); - } - } + ImportId parentImportId); void synchronizeDocumentImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, - Storage::Synchronization::ImportKind importKind) - { - std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { - return std::tie(first.sourceId, first.moduleId, first.version) - < std::tie(second.sourceId, second.moduleId, second.version); - }); - - auto range = selectDocumentImportForSourceIdStatement - .template range<Storage::Synchronization::ImportView>(toIntegers( - updatedSourceIds), - importKind); - - auto compareKey = [](const Storage::Synchronization::ImportView &view, - const Storage::Import &import) -> long long { - auto sourceIdDifference = view.sourceId - import.sourceId; - if (sourceIdDifference != 0) - return sourceIdDifference; - - auto moduleIdDifference = view.moduleId - import.moduleId; - if (moduleIdDifference != 0) - return moduleIdDifference; - - auto versionDifference = view.version.major.value - import.version.major.value; - if (versionDifference != 0) - return versionDifference; - - return view.version.minor.value - import.version.minor.value; - }; - - auto insert = [&](const Storage::Import &import) { - auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); - auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { - Storage::Import additionImport{exportedModuleId, - Storage::Version{majorVersion, minorVersion}, - import.sourceId}; - - auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import - ? Storage::Synchronization::ImportKind::ModuleExportedImport - : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency; - - insertDocumentImport(additionImport, exportedImportKind, import.moduleId, importId); - }; - - selectModuleExportedImportsForModuleIdStatement.readCallback(callback, - import.moduleId, - import.version.major.value, - import.version.minor.value); - }; - - auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ImportView &view) { - deleteDocumentImportStatement.write(view.importId); - deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); - }; - - Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); - } + Storage::Synchronization::ImportKind importKind); - static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations ¶meters) - { - Utils::PathString json; - json.append("["); - - Utils::SmallStringView comma{""}; - - for (const auto ¶meter : parameters) { - json.append(comma); - comma = ","; - json.append(R"({"n":")"); - json.append(parameter.name); - json.append(R"(","tn":")"); - json.append(parameter.typeName); - if (parameter.traits == Storage::PropertyDeclarationTraits::None) { - json.append("\"}"); - } else { - json.append(R"(","tr":)"); - json.append(Utils::SmallString::number(to_underlying(parameter.traits))); - json.append("}"); - } - } - - json.append("]"); - - return json; - } + static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations ¶meters); TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, - Utils::SmallStringView name) const override - { - return selectTypeIdByModuleIdAndExportedNameStatement.template value<TypeId>(moduleId, name); - } + Utils::SmallStringView name) const override; - void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths) - { - for (auto &path : paths) - path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName); - } + void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths); class PropertyEditorQmlPathView { @@ -2075,6 +726,19 @@ private: , directoryId{directoryId} {} + template<typename String> + friend void convertToString(String &string, + const PropertyEditorQmlPathView &propertyEditorQmlPathView) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", propertyEditorQmlPathView.typeId), + keyValue("source id", propertyEditorQmlPathView.pathId), + keyValue("directory id", propertyEditorQmlPathView.directoryId)); + + convertToString(string, dict); + } + public: TypeId typeId; SourceId pathId; @@ -2082,279 +746,33 @@ private: }; void synchronizePropertyEditorPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, - SourceIds updatedPropertyEditorQmlPathsSourceIds) - { - using Storage::Synchronization::PropertyEditorQmlPath; - std::sort(paths.begin(), paths.end(), [](auto &&first, auto &&second) { - return first.typeId < second.typeId; - }); - - auto range = selectPropertyEditorPathsForForSourceIdsStatement - .template range<PropertyEditorQmlPathView>( - toIntegers(updatedPropertyEditorQmlPathsSourceIds)); - - auto compareKey = [](const PropertyEditorQmlPathView &view, - const PropertyEditorQmlPath &value) -> long long { - return view.typeId - value.typeId; - }; - - auto insert = [&](const PropertyEditorQmlPath &path) { - if (path.typeId) - insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); - }; - - auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { - if (value.pathId != view.pathId || value.directoryId != view.directoryId) { - updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); - return Sqlite::UpdateChange::Update; - } - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const PropertyEditorQmlPathView &view) { - deletePropertyEditorPathStatement.write(view.typeId); - }; - - Sqlite::insertUpdateDelete(range, paths, compareKey, insert, update, remove); - } + SourceIds updatedPropertyEditorQmlPathsSourceIds); void synchronizePropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, - SourceIds updatedPropertyEditorQmlPathsSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, - projectStorageCategory()}; - - addTypeIdToPropertyEditorQmlPaths(paths); - synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds); - } + SourceIds updatedPropertyEditorQmlPathsSourceIds); void synchronizeFunctionDeclarations( - TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations) - { - NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory()}; - - std::sort(functionsDeclarations.begin(), - functionsDeclarations.end(), - [](auto &&first, auto &&second) { - auto compare = Sqlite::compare(first.name, second.name); - - if (compare == 0) { - Utils::PathString firstSignature{createJson(first.parameters)}; - Utils::PathString secondSignature{createJson(second.parameters)}; - - return Sqlite::compare(firstSignature, secondSignature) < 0; - } - - return compare < 0; - }); - - auto range = selectFunctionDeclarationsForTypeIdStatement - .template range<Storage::Synchronization::FunctionDeclarationView>(typeId); - - auto compareKey = [](const Storage::Synchronization::FunctionDeclarationView &view, - const Storage::Synchronization::FunctionDeclaration &value) { - auto nameKey = Sqlite::compare(view.name, value.name); - if (nameKey != 0) - return nameKey; - - Utils::PathString valueSignature{createJson(value.parameters)}; - - return Sqlite::compare(view.signature, valueSignature); - }; - - auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) { - Utils::PathString signature{createJson(value.parameters)}; - - insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature); - }; - - auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view, - const Storage::Synchronization::FunctionDeclaration &value) { - Utils::PathString signature{createJson(value.parameters)}; - - if (value.returnTypeName == view.returnTypeName) - return Sqlite::UpdateChange::No; - - updateFunctionDeclarationStatement.write(view.id, value.returnTypeName); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) { - deleteFunctionDeclarationStatement.write(view.id); - }; - - Sqlite::insertUpdateDelete(range, functionsDeclarations, compareKey, insert, update, remove); - } + TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations); void synchronizeSignalDeclarations(TypeId typeId, - Storage::Synchronization::SignalDeclarations &signalDeclarations) - { - NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory()}; - - std::sort(signalDeclarations.begin(), signalDeclarations.end(), [](auto &&first, auto &&second) { - auto compare = Sqlite::compare(first.name, second.name); - - if (compare == 0) { - Utils::PathString firstSignature{createJson(first.parameters)}; - Utils::PathString secondSignature{createJson(second.parameters)}; - - return Sqlite::compare(firstSignature, secondSignature) < 0; - } - - return compare < 0; - }); - - auto range = selectSignalDeclarationsForTypeIdStatement - .template range<Storage::Synchronization::SignalDeclarationView>(typeId); - - auto compareKey = [](const Storage::Synchronization::SignalDeclarationView &view, - const Storage::Synchronization::SignalDeclaration &value) { - auto nameKey = Sqlite::compare(view.name, value.name); - if (nameKey != 0) - return nameKey; - - Utils::PathString valueSignature{createJson(value.parameters)}; - - return Sqlite::compare(view.signature, valueSignature); - }; - - auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) { - Utils::PathString signature{createJson(value.parameters)}; - - insertSignalDeclarationStatement.write(typeId, value.name, signature); - }; - - auto update = [&]([[maybe_unused]] const Storage::Synchronization::SignalDeclarationView &view, - [[maybe_unused]] const Storage::Synchronization::SignalDeclaration &value) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) { - deleteSignalDeclarationStatement.write(view.id); - }; - - Sqlite::insertUpdateDelete(range, signalDeclarations, compareKey, insert, update, remove); - } + Storage::Synchronization::SignalDeclarations &signalDeclarations); static Utils::PathString createJson( - const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations) - { - Utils::PathString json; - json.append("{"); - - Utils::SmallStringView comma{"\""}; - - for (const auto &enumerator : enumeratorDeclarations) { - json.append(comma); - comma = ",\""; - json.append(enumerator.name); - if (enumerator.hasValue) { - json.append("\":\""); - json.append(Utils::SmallString::number(enumerator.value)); - json.append("\""); - } else { - json.append("\":null"); - } - } - - json.append("}"); - - return json; - } + const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations); void synchronizeEnumerationDeclarations( - TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations) - { - NanotraceHR::Tracer tracer{"synchronize enumeation declaration"_t, projectStorageCategory()}; - - std::sort(enumerationDeclarations.begin(), - enumerationDeclarations.end(), - [](auto &&first, auto &&second) { - return Sqlite::compare(first.name, second.name) < 0; - }); - - auto range = selectEnumerationDeclarationsForTypeIdStatement - .template range<Storage::Synchronization::EnumerationDeclarationView>(typeId); - - auto compareKey = [](const Storage::Synchronization::EnumerationDeclarationView &view, - const Storage::Synchronization::EnumerationDeclaration &value) { - return Sqlite::compare(view.name, value.name); - }; - - auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) { - Utils::PathString signature{createJson(value.enumeratorDeclarations)}; - - insertEnumerationDeclarationStatement.write(typeId, value.name, signature); - }; - - auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view, - const Storage::Synchronization::EnumerationDeclaration &value) { - Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)}; - - if (enumeratorDeclarations == view.enumeratorDeclarations) - return Sqlite::UpdateChange::No; - - updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) { - deleteEnumerationDeclarationStatement.write(view.id); - }; - - Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove); - } + TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations); void extractExportedTypes(TypeId typeId, const Storage::Synchronization::Type &type, - Storage::Synchronization::ExportedTypes &exportedTypes) - { - for (const auto &exportedType : type.exportedTypes) - exportedTypes.emplace_back(exportedType.name, - exportedType.version, - typeId, - exportedType.moduleId); - } - - TypeId declareType(Storage::Synchronization::Type &type) - { - if (type.typeName.isEmpty()) { - type.typeId = selectTypeIdBySourceIdStatement.template value<TypeId>(type.sourceId); - - return type.typeId; - } - - type.typeId = insertTypeStatement.template value<TypeId>(type.sourceId, type.typeName); + Storage::Synchronization::ExportedTypes &exportedTypes); - if (!type.typeId) - type.typeId = selectTypeIdBySourceIdAndNameStatement.template value<TypeId>(type.sourceId, - type.typeName); - - return type.typeId; - } + TypeId declareType(Storage::Synchronization::Type &type); void syncDeclarations(Storage::Synchronization::Type &type, AliasPropertyDeclarations &insertedAliasPropertyDeclarations, AliasPropertyDeclarations &updatedAliasPropertyDeclarations, - PropertyDeclarationIds &propertyDeclarationIds) - { - NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()}; - - if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) - return; - - synchronizePropertyDeclarations(type.typeId, - type.propertyDeclarations, - type.sourceId, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - propertyDeclarationIds); - synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations); - synchronizeSignalDeclarations(type.typeId, type.signalDeclarations); - synchronizeEnumerationDeclarations(type.typeId, type.enumerationDeclarations); - } + PropertyDeclarationIds &propertyDeclarationIds); template<typename Relinkable, typename Ids, typename Compare> void removeRelinkableEntries(std::vector<Relinkable> &relinkables, Ids &ids, Compare compare) @@ -2381,23 +799,7 @@ private: void syncDeclarations(Storage::Synchronization::Types &types, AliasPropertyDeclarations &insertedAliasPropertyDeclarations, AliasPropertyDeclarations &updatedAliasPropertyDeclarations, - PropertyDeclarations &relinkablePropertyDeclarations) - { - NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()}; - - PropertyDeclarationIds propertyDeclarationIds; - propertyDeclarationIds.reserve(types.size() * 10); - - for (auto &&type : types) - syncDeclarations(type, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - propertyDeclarationIds); - - removeRelinkableEntries(relinkablePropertyDeclarations, - propertyDeclarationIds, - PropertyCompare<PropertyDeclaration>{}); - } + PropertyDeclarations &relinkablePropertyDeclarations); class TypeWithDefaultPropertyView { @@ -2407,240 +809,54 @@ private: , defaultPropertyId{defaultPropertyId} {} + template<typename String> + friend void convertToString(String &string, const TypeWithDefaultPropertyView &view) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", view.typeId), + keyValue("property id", view.defaultPropertyId)); + + convertToString(string, dict); + } + TypeId typeId; PropertyDeclarationId defaultPropertyId; }; - void syncDefaultProperties(Storage::Synchronization::Types &types) - { - NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; - - auto range = selectTypesWithDefaultPropertyStatement.template range<TypeWithDefaultPropertyView>(); - - auto compareKey = [](const TypeWithDefaultPropertyView &view, - const Storage::Synchronization::Type &value) { - return view.typeId - value.typeId; - }; - - auto insert = [&](const Storage::Synchronization::Type &) { - - }; - - auto update = [&](const TypeWithDefaultPropertyView &view, - const Storage::Synchronization::Type &value) { - PropertyDeclarationId valueDefaultPropertyId; - if (value.defaultPropertyName.size()) - valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded( - value.typeId, value.defaultPropertyName) - .propertyDeclarationId; - - if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) - return Sqlite::UpdateChange::No; - - updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); - - return Sqlite::UpdateChange::Update; - }; + void syncDefaultProperties(Storage::Synchronization::Types &types); - auto remove = [&](const TypeWithDefaultPropertyView &) {}; + void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types); - Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); - } - - void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types) - { - NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()}; - - auto range = selectTypesWithDefaultPropertyStatement.template range<TypeWithDefaultPropertyView>(); - - auto compareKey = [](const TypeWithDefaultPropertyView &view, - const Storage::Synchronization::Type &value) { - return view.typeId - value.typeId; - }; - - auto insert = [&](const Storage::Synchronization::Type &) { - - }; - - auto update = [&](const TypeWithDefaultPropertyView &view, - const Storage::Synchronization::Type &value) { - PropertyDeclarationId valueDefaultPropertyId; - if (value.defaultPropertyName.size()) { - auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( - value.typeId, value.defaultPropertyName); - if (optionalValueDefaultPropertyId) - valueDefaultPropertyId = optionalValueDefaultPropertyId->propertyDeclarationId; - } - - if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) - return Sqlite::UpdateChange::No; - - updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{}); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const TypeWithDefaultPropertyView &) {}; - - Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); - } - - void checkForPrototypeChainCycle(TypeId typeId) const - { - auto callback = [=](TypeId currentTypeId) { - if (typeId == currentTypeId) - throw PrototypeChainCycle{}; - }; - - selectTypeIdsForPrototypeChainIdStatement.readCallback(callback, typeId); - } - - void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const - { - auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) { - if (propertyDeclarationId == currentPropertyDeclarationId) - throw AliasChainCycle{}; - }; + void checkForPrototypeChainCycle(TypeId typeId) const; - selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback, - propertyDeclarationId); - } + void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const; std::pair<TypeId, ImportedTypeNameId> fetchImportedTypeNameIdAndTypeId( - const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId) - { - TypeId typeId; - ImportedTypeNameId typeNameId; - if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { - typeNameId = fetchImportedTypeNameId(typeName, sourceId); - - typeId = fetchTypeId(typeNameId); - - if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId)}; - } - - return {typeId, typeNameId}; - } - - void syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds) - { - if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) - return; + const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId); - auto [prototypeId, prototypeTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.prototype, - type.sourceId); - auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension, - type.sourceId); - - updatePrototypeAndExtensionStatement.write(type.typeId, - prototypeId, - prototypeTypeNameId, - extensionId, - extensionTypeNameId); - - if (prototypeId || extensionId) - checkForPrototypeChainCycle(type.typeId); - - typeIds.push_back(type.typeId); - } + void syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds); void syncPrototypesAndExtensions(Storage::Synchronization::Types &types, Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions) - { - NanotraceHR::Tracer tracer{"synchronize prototypes"_t, projectStorageCategory()}; + Prototypes &relinkableExtensions); - TypeIds typeIds; - typeIds.reserve(types.size()); - - for (auto &type : types) - syncPrototypeAndExtension(type, typeIds); - - removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare<Prototype>{}); - removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare<Prototype>{}); - } - - ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const - { - if (import.version) { - return selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value<ImportId>( - sourceId, import.moduleId, import.version.major.value, import.version.minor.value); - } - - if (import.version.major) { - return selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement - .template value<ImportId>(sourceId, import.moduleId, import.version.major.value); - } - - return selectImportIdBySourceIdAndModuleIdStatement.template value<ImportId>(sourceId, - import.moduleId); - } + ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const; ImportedTypeNameId fetchImportedTypeNameId(const Storage::Synchronization::ImportedTypeName &name, - SourceId sourceId) - { - struct Inspect - { - auto operator()(const Storage::Synchronization::ImportedType &importedType) - { - return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, - sourceId, - importedType.name); - } - - auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType) - { - ImportId importId = storage.fetchImportId(sourceId, importedType.import); - - return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, - importId, - importedType.name); - } - - ProjectStorage &storage; - SourceId sourceId; - }; - - return std::visit(Inspect{*this, sourceId}, name); - } + SourceId sourceId); template<typename Id> ImportedTypeNameId fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind kind, Id id, - Utils::SmallStringView typeName) - { - auto importedTypeNameId = selectImportedTypeNameIdStatement - .template value<ImportedTypeNameId>(kind, id, typeName); - - if (importedTypeNameId) - return importedTypeNameId; + Utils::SmallStringView typeName); - return insertImportedTypeNameIdStatement.template value<ImportedTypeNameId>(kind, id, typeName); - } - - TypeId fetchTypeId(ImportedTypeNameId typeNameId) const - { - auto kind = selectKindFromImportedTypeNamesStatement - .template value<Storage::Synchronization::TypeNameKind>(typeNameId); - - return fetchTypeId(typeNameId, kind); - } + TypeId fetchTypeId(ImportedTypeNameId typeNameId) const; - Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const - { - return selectNameFromImportedTypeNamesStatement.template value<Utils::SmallString>(typeNameId); - } + Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const; - TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const - { - if (kind == Storage::Synchronization::TypeNameKind::QualifiedExported) { - return selectTypeIdForQualifiedImportedTypeNameNamesStatement.template value<TypeId>( - typeNameId); - } - - return selectTypeIdForImportedTypeNameNamesStatement.template value<TypeId>(typeNameId); - } + TypeId fetchTypeId(ImportedTypeNameId typeNameId, + Storage::Synchronization::TypeNameKind kind) const; class FetchPropertyDeclarationResult { @@ -2653,1303 +869,64 @@ private: , propertyTraits{propertyTraits} {} + template<typename String> + friend void convertToString(String &string, const FetchPropertyDeclarationResult &result) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("property type id", result.propertyTypeId), + keyValue("property declaration id", result.propertyDeclarationId), + keyValue("property traits", result.propertyTraits)); + + convertToString(string, dict); + } + public: TypeId propertyTypeId; PropertyDeclarationId propertyDeclarationId; Storage::PropertyDeclarationTraits propertyTraits; }; - auto fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId, - Utils::SmallStringView name) - { - return selectPropertyDeclarationByTypeIdAndNameStatement - .template optionalValue<FetchPropertyDeclarationResult>(typeId, name); - } + std::optional<FetchPropertyDeclarationResult> fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( + TypeId typeId, Utils::SmallStringView name); FetchPropertyDeclarationResult fetchPropertyDeclarationByTypeIdAndNameUngarded( - TypeId typeId, Utils::SmallStringView name) - { - auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId, - name); - - if (propertyDeclaration) - return *propertyDeclaration; - - throw PropertyNameDoesNotExists{}; - } + TypeId typeId, Utils::SmallStringView name); PropertyDeclarationId fetchPropertyDeclarationIdByTypeIdAndNameUngarded(TypeId typeId, - Utils::SmallStringView name) - { - auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement - .template value<PropertyDeclarationId>(typeId, name); - - if (propertyDeclarationId) - return propertyDeclarationId; - - throw PropertyNameDoesNotExists{}; - } - - SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath) - { - return selectSourceContextIdFromSourceContextsBySourceContextPathStatement - .template value<SourceContextId>(sourceContextPath); - } - - SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath) - { - insertIntoSourceContextsStatement.write(sourceContextPath); - - return SourceContextId::create(database.lastInsertedRowId()); - } - - SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - insertIntoSourcesStatement.write(sourceContextId, sourceName); - - return SourceId::create(database.lastInsertedRowId()); - } - - SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - return selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement - .template value<SourceId>(sourceContextId, sourceName); - } - - auto fetchExportedTypes(TypeId typeId) - { - return selectExportedTypesByTypeIdStatement - .template values<Storage::Synchronization::ExportedType, 12>(typeId); - } - - auto fetchPropertyDeclarations(TypeId typeId) - { - return selectPropertyDeclarationsByTypeIdStatement - .template values<Storage::Synchronization::PropertyDeclaration, 24>(typeId); - } - - auto fetchFunctionDeclarations(TypeId typeId) - { - Storage::Synchronization::FunctionDeclarations functionDeclarations; - - auto callback = [&](Utils::SmallStringView name, - Utils::SmallStringView returnType, - FunctionDeclarationId functionDeclarationId) { - auto &functionDeclaration = functionDeclarations.emplace_back(name, returnType); - functionDeclaration.parameters = selectFunctionParameterDeclarationsStatement - .template values<Storage::Synchronization::ParameterDeclaration, - 8>(functionDeclarationId); - }; - - selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + Utils::SmallStringView name); - return functionDeclarations; - } - - auto fetchSignalDeclarations(TypeId typeId) - { - Storage::Synchronization::SignalDeclarations signalDeclarations; - - auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) { - auto &signalDeclaration = signalDeclarations.emplace_back(name); - signalDeclaration.parameters = selectSignalParameterDeclarationsStatement - .template values<Storage::Synchronization::ParameterDeclaration, - 8>(signalDeclarationId); - }; - - selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath); - return signalDeclarations; - } - - auto fetchEnumerationDeclarations(TypeId typeId) - { - Storage::Synchronization::EnumerationDeclarations enumerationDeclarations; + SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath); - auto callback = [&](Utils::SmallStringView name, - EnumerationDeclarationId enumerationDeclarationId) { - enumerationDeclarations.emplace_back( - name, - selectEnumeratorDeclarationStatement - .template values<Storage::Synchronization::EnumeratorDeclaration, 8>( - enumerationDeclarationId)); - }; + SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement - .readCallback(callback, typeId); - - return enumerationDeclarations; - } + SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - class Initializer - { - public: - Initializer(Database &database, bool isInitialized) - { - if (!isInitialized) { - auto moduleIdColumn = createModulesTable(database); - createSourceContextsTable(database); - createSourcesTable(database); - createTypesAndePropertyDeclarationsTables(database, moduleIdColumn); - createExportedTypeNamesTable(database, moduleIdColumn); - createImportedTypeNamesTable(database); - createEnumerationsTable(database); - createFunctionsTable(database); - createSignalsTable(database); - createModuleExportedImportsTable(database, moduleIdColumn); - createDocumentImportsTable(database, moduleIdColumn); - createFileStatusesTable(database); - createProjectDatasTable(database); - createPropertyEditorPathsTable(database); - createTypeAnnotionsTable(database); - } - database.setIsInitialized(true); - } + Storage::Synchronization::ExportedTypes fetchExportedTypes(TypeId typeId); - void createSourceContextsTable(Database &database) - { - Sqlite::Table table; - table.setUseIfNotExists(true); - table.setName("sourceContexts"); - table.addColumn("sourceContextId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}}); - const Sqlite::Column &sourceContextPathColumn = table.addColumn("sourceContextPath"); + Storage::Synchronization::PropertyDeclarations fetchPropertyDeclarations(TypeId typeId); - table.addUniqueIndex({sourceContextPathColumn}); + Storage::Synchronization::FunctionDeclarations fetchFunctionDeclarations(TypeId typeId); - table.initialize(database); - } + Storage::Synchronization::SignalDeclarations fetchSignalDeclarations(TypeId typeId); - void createSourcesTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("sources"); - table.addColumn("sourceId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); - const auto &sourceContextIdColumn = table.addColumn( - "sourceContextId", - Sqlite::StrictColumnType::Integer, - {Sqlite::NotNull{}, - Sqlite::ForeignKey{"sourceContexts", - "sourceContextId", - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade}}); - const auto &sourceNameColumn = table.addColumn("sourceName", - Sqlite::StrictColumnType::Text); - table.addUniqueIndex({sourceContextIdColumn, sourceNameColumn}); - - table.initialize(database); - } + Storage::Synchronization::EnumerationDeclarations fetchEnumerationDeclarations(TypeId typeId); - void createTypesAndePropertyDeclarationsTables( - Database &database, [[maybe_unused]] const Sqlite::StrictColumn &foreignModuleIdColumn) - { - Sqlite::StrictTable typesTable; - typesTable.setUseIfNotExists(true); - typesTable.setName("types"); - typesTable.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); - auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text); - typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer); - auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer); - auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer); - auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId", - Sqlite::StrictColumnType::Integer); - typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer); - typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); - typesTable.addIndex({defaultPropertyIdColumn}); - typesTable.addIndex({prototypeIdColumn}); - typesTable.addIndex({extensionIdColumn}); - - typesTable.initialize(database); - - { - Sqlite::StrictTable propertyDeclarationTable; - propertyDeclarationTable.setUseIfNotExists(true); - propertyDeclarationTable.setName("propertyDeclarations"); - propertyDeclarationTable.addColumn("propertyDeclarationId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &typeIdColumn = propertyDeclarationTable.addColumn("typeId"); - auto &nameColumn = propertyDeclarationTable.addColumn("name"); - propertyDeclarationTable.addForeignKeyColumn("propertyTypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - propertyDeclarationTable.addColumn("propertyTraits", - Sqlite::StrictColumnType::Integer); - propertyDeclarationTable.addColumn("propertyImportedTypeNameId", - Sqlite::StrictColumnType::Integer); - auto &aliasPropertyDeclarationIdColumn = propertyDeclarationTable.addForeignKeyColumn( - "aliasPropertyDeclarationId", - propertyDeclarationTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - auto &aliasPropertyDeclarationTailIdColumn = propertyDeclarationTable.addForeignKeyColumn( - "aliasPropertyDeclarationTailId", - propertyDeclarationTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - - propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn}); - propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn}, - "aliasPropertyDeclarationId IS NOT NULL"); - propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn}, - "aliasPropertyDeclarationTailId IS NOT NULL"); - - propertyDeclarationTable.initialize(database); - } - } + class Initializer; - void createExportedTypeNamesTable(Database &database, - const Sqlite::StrictColumn &foreignModuleIdColumn) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("exportedTypeNames"); - table.addColumn("exportedTypeNameId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::NoAction); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); - auto &majorVersionColumn = table.addColumn("majorVersion", - Sqlite::StrictColumnType::Integer); - auto &minorVersionColumn = table.addColumn("minorVersion", - Sqlite::StrictColumnType::Integer); - - table.addUniqueIndex({moduleIdColumn, nameColumn}, - "majorVersion IS NULL AND minorVersion IS NULL"); - table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NULL"); - table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn, minorVersionColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); - - table.addIndex({typeIdColumn}); - - table.initialize(database); - } - - void createImportedTypeNamesTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("importedTypeNames"); - table.addColumn("importedTypeNameId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &importOrSourceIdColumn = table.addColumn("importOrSourceId"); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); - - table.addUniqueIndex({kindColumn, importOrSourceIdColumn, nameColumn}); - - table.initialize(database); - } - - void createEnumerationsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("enumerationDeclarations"); - table.addColumn("enumerationDeclarationId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - table.addColumn("enumeratorDeclarations", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({typeIdColumn, nameColumn}); - - table.initialize(database); - } - - void createFunctionsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("functionDeclarations"); - table.addColumn("functionDeclarationId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text); - table.addColumn("returnTypeName"); - - table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn}); - - table.initialize(database); - } - - void createSignalsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("signalDeclarations"); - table.addColumn("signalDeclarationId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn}); - - table.initialize(database); - } - - Sqlite::StrictColumn createModulesTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("modules"); - auto &modelIdColumn = table.addColumn("moduleId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({nameColumn}); - - table.initialize(database); - - return std::move(modelIdColumn); - } - - void createModuleExportedImportsTable(Database &database, - const Sqlite::StrictColumn &foreignModuleIdColumn) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("moduleExportedImports"); - table.addColumn("moduleExportedImportId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade, - Sqlite::Enforment::Immediate); - auto &sourceIdColumn = table.addColumn("exportedModuleId", - Sqlite::StrictColumnType::Integer); - table.addColumn("isAutoVersion", Sqlite::StrictColumnType::Integer); - table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer); - table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); - - table.addUniqueIndex({sourceIdColumn, moduleIdColumn}); - - table.initialize(database); - } - - void createDocumentImportsTable(Database &database, - const Sqlite::StrictColumn &foreignModuleIdColumn) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("documentImports"); - table.addColumn("importId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); - auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade, - Sqlite::Enforment::Immediate); - auto &sourceModuleIdColumn = table.addForeignKeyColumn("sourceModuleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade, - Sqlite::Enforment::Immediate); - auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); - auto &majorVersionColumn = table.addColumn("majorVersion", - Sqlite::StrictColumnType::Integer); - auto &minorVersionColumn = table.addColumn("minorVersion", - Sqlite::StrictColumnType::Integer); - auto &parentImportIdColumn = table.addColumn("parentImportId", - Sqlite::StrictColumnType::Integer); - - table.addUniqueIndex({sourceIdColumn, - moduleIdColumn, - kindColumn, - sourceModuleIdColumn, - parentImportIdColumn}, - "majorVersion IS NULL AND minorVersion IS NULL"); - table.addUniqueIndex({sourceIdColumn, - moduleIdColumn, - kindColumn, - sourceModuleIdColumn, - majorVersionColumn, - parentImportIdColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NULL"); - table.addUniqueIndex({sourceIdColumn, - moduleIdColumn, - kindColumn, - sourceModuleIdColumn, - majorVersionColumn, - minorVersionColumn, - parentImportIdColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); - - table.initialize(database); - } - - void createFileStatusesTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("fileStatuses"); - table.addColumn("sourceId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}, - Sqlite::ForeignKey{"sources", - "sourceId", - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade}}); - table.addColumn("size", Sqlite::StrictColumnType::Integer); - table.addColumn("lastModified", Sqlite::StrictColumnType::Integer); - - table.initialize(database); - } - - void createProjectDatasTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setUseWithoutRowId(true); - table.setName("projectDatas"); - auto &projectSourceIdColumn = table.addColumn("projectSourceId", - Sqlite::StrictColumnType::Integer); - auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - table.addColumn("moduleId", Sqlite::StrictColumnType::Integer); - table.addColumn("fileType", Sqlite::StrictColumnType::Integer); - - table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn}); - table.addUniqueIndex({sourceIdColumn}); - - table.initialize(database); - } - - void createPropertyEditorPathsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setUseWithoutRowId(true); - table.setName("propertyEditorPaths"); - table.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); - table.addColumn("pathSourceId", Sqlite::StrictColumnType::Integer); - auto &directoryIdColumn = table.addColumn("directoryId", - Sqlite::StrictColumnType::Integer); - - table.addIndex({directoryIdColumn}); - - table.initialize(database); - } - - void createTypeAnnotionsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setUseWithoutRowId(true); - table.setName("typeAnnotations"); - auto &typeIdColumn = table.addColumn("typeId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - table.addColumn("iconPath", Sqlite::StrictColumnType::Text); - table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); - table.addColumn("hints", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({sourceIdColumn, typeIdColumn}); - - table.initialize(database); - } - }; + struct Statements; public: Database &database; Sqlite::ExclusiveNonThrowingDestructorTransaction<Database> exclusiveTransaction; - Initializer initializer; + std::unique_ptr<Initializer> initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; - Storage::Info::CommonTypeCache<ProjectStorageInterface> commonTypeCache_{*this}; + Storage::Info::CommonTypeCache<ProjectStorageType> commonTypeCache_{*this}; QVarLengthArray<ProjectStorageObserver *, 24> observers; - ReadWriteStatement<1, 2> insertTypeStatement{ - "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; - WriteStatement<5> updatePrototypeAndExtensionStatement{ - "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " - "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId " - "IS NOT ?4 OR extensionNameId IS NOT ?5)", - database}; - mutable ReadStatement<1, 1> selectTypeIdByExportedNameStatement{ - "SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; - mutable ReadStatement<1, 2> selectTypeIdByModuleIdAndExportedNameStatement{ - "SELECT typeId FROM exportedTypeNames " - "WHERE moduleId=?1 AND name=?2 " - "ORDER BY majorVersion DESC, minorVersion DESC " - "LIMIT 1", - database}; - mutable ReadStatement<1, 3> selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement{ - "SELECT typeId FROM exportedTypeNames " - "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3" - "ORDER BY minorVersion DESC " - "LIMIT 1", - database}; - mutable ReadStatement<1, 4> selectTypeIdByModuleIdAndExportedNameAndVersionStatement{ - "SELECT typeId FROM exportedTypeNames " - "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3 AND minorVersion<=?4" - "ORDER BY minorVersion DESC " - "LIMIT 1", - database}; - mutable ReadStatement<1, 2> selectPrototypeIdStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId) AS (" - " VALUES(?1) " - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeSelection " - " USING(typeId))" - "SELECT typeId FROM typeSelection WHERE typeId=?2 LIMIT 1", - database}; - mutable ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId, level) AS (" - " VALUES(?1, 0) " - " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN " - " typeSelection USING(typeId)) " - "SELECT propertyDeclarationId FROM propertyDeclarations JOIN typeSelection USING(typeId) " - " WHERE name=?2 ORDER BY level LIMIT 1", - database}; - mutable ReadStatement<3, 2> selectPropertyDeclarationByTypeIdAndNameStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId, level) AS (" - " VALUES(?1, 0) " - " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN " - " typeSelection USING(typeId))" - "SELECT propertyTypeId, propertyDeclarationId, propertyTraits " - " FROM propertyDeclarations JOIN typeSelection USING(typeId) " - " WHERE name=?2 ORDER BY level LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectPrototypeIdsInOrderStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId, level) AS (" - " VALUES(?1, 0) " - " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN " - " typeSelection USING(typeId) WHERE prototypeId IS NOT NULL) " - "SELECT typeId FROM typeSelection ORDER BY level DESC", - database}; - mutable ReadStatement<1, 1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{ - "SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database}; - mutable ReadStatement<1, 1> selectSourceContextPathFromSourceContextsBySourceContextIdStatement{ - "SELECT sourceContextPath FROM sourceContexts WHERE sourceContextId = ?", database}; - mutable ReadStatement<2> selectAllSourceContextsStatement{ - "SELECT sourceContextPath, sourceContextId FROM sourceContexts", database}; - WriteStatement<1> insertIntoSourceContextsStatement{ - "INSERT INTO sourceContexts(sourceContextPath) VALUES (?)", database}; - mutable ReadStatement<1, 2> selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement{ - "SELECT sourceId FROM sources WHERE sourceContextId = ? AND sourceName = ?", database}; - mutable ReadStatement<2, 1> selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement{ - "SELECT sourceName, sourceContextId FROM sources WHERE sourceId = ?", database}; - mutable ReadStatement<1, 1> selectSourceContextIdFromSourcesBySourceIdStatement{ - "SELECT sourceContextId FROM sources WHERE sourceId = ?", database}; - WriteStatement<2> insertIntoSourcesStatement{ - "INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database}; - mutable ReadStatement<3> selectAllSourcesStatement{ - "SELECT sourceName, sourceContextId, sourceId FROM sources", database}; - mutable ReadStatement<8, 1> selectTypeByTypeIdStatement{ - "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " - "pd.name " - "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " - "defaultPropertyId=propertyDeclarationId " - "WHERE t.typeId=?", - database}; - mutable ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{ - "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM " - "exportedTypeNames WHERE typeId=?", - database}; - mutable ReadStatement<4, 2> selectExportedTypesByTypeIdAndSourceIdStatement{ - "SELECT etn.moduleId, name, ifnull(etn.majorVersion, -1), ifnull(etn.minorVersion, -1) " - "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND " - "sourceId=?2", - database}; - mutable ReadStatement<8> selectTypesStatement{ - "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " - "pd.name " - "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " - "defaultPropertyId=propertyDeclarationId", - database}; - WriteStatement<2> updateTypeTraitStatement{"UPDATE types SET traits = ?2 WHERE typeId=?1", - database}; - WriteStatement<2> updateTypeAnnotationTraitStatement{ - "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database}; - ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{ - "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN " - "carray(?2))", - database}; - WriteStatement<1> deleteTypeNamesByTypeIdStatement{ - "DELETE FROM exportedTypeNames WHERE typeId=?", database}; - WriteStatement<1> deleteEnumerationDeclarationByTypeIdStatement{ - "DELETE FROM enumerationDeclarations WHERE typeId=?", database}; - WriteStatement<1> deletePropertyDeclarationByTypeIdStatement{ - "DELETE FROM propertyDeclarations WHERE typeId=?", database}; - WriteStatement<1> deleteFunctionDeclarationByTypeIdStatement{ - "DELETE FROM functionDeclarations WHERE typeId=?", database}; - WriteStatement<1> deleteSignalDeclarationByTypeIdStatement{ - "DELETE FROM signalDeclarations WHERE typeId=?", database}; - WriteStatement<1> deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database}; - mutable ReadStatement<4, 1> selectPropertyDeclarationsByTypeIdStatement{ - "SELECT name, propertyTypeId, propertyTraits, (SELECT name FROM " - "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM " - "propertyDeclarations AS pd WHERE typeId=?", - database}; - ReadStatement<6, 1> selectPropertyDeclarationsForTypeIdStatement{ - "SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, " - "propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " - "WHERE typeId=? ORDER BY name", - database}; - ReadWriteStatement<1, 5> insertPropertyDeclarationStatement{ - "INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, " - "propertyImportedTypeNameId, aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5, NULL) " - "RETURNING propertyDeclarationId", - database}; - WriteStatement<4> updatePropertyDeclarationStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " - "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=NULL WHERE " - "propertyDeclarationId=?1", - database}; - WriteStatement<3> updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement{ - "WITH RECURSIVE " - " properties(aliasPropertyDeclarationId) AS ( " - " SELECT propertyDeclarationId FROM propertyDeclarations WHERE " - " aliasPropertyDeclarationId=?1 " - " UNION ALL " - " SELECT pd.propertyDeclarationId FROM " - " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) " - "UPDATE propertyDeclarations AS pd " - "SET propertyTypeId=?2, propertyTraits=?3 " - "FROM properties AS p " - "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId", - database}; - WriteStatement<1> updatePropertyAliasDeclarationRecursivelyStatement{ - "WITH RECURSIVE " - " propertyValues(propertyTypeId, propertyTraits) AS (" - " SELECT propertyTypeId, propertyTraits FROM propertyDeclarations " - " WHERE propertyDeclarationId=?1), " - " properties(aliasPropertyDeclarationId) AS ( " - " SELECT propertyDeclarationId FROM propertyDeclarations WHERE " - " aliasPropertyDeclarationId=?1 " - " UNION ALL " - " SELECT pd.propertyDeclarationId FROM " - " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) " - "UPDATE propertyDeclarations AS pd " - "SET propertyTypeId=pv.propertyTypeId, propertyTraits=pv.propertyTraits " - "FROM properties AS p, propertyValues AS pv " - "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId", - database}; - WriteStatement<1> deletePropertyDeclarationStatement{ - "DELETE FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; - ReadStatement<3, 1> selectPropertyDeclarationsWithAliasForTypeIdStatement{ - "SELECT name, propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " - "WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL ORDER BY name", - database}; - WriteStatement<5> updatePropertyDeclarationWithAliasAndTypeStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " - "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=?5 WHERE " - "propertyDeclarationId=?1", - database}; - ReadWriteStatement<1, 2> insertAliasPropertyDeclarationStatement{ - "INSERT INTO propertyDeclarations(typeId, name) VALUES(?1, ?2) RETURNING " - "propertyDeclarationId", - database}; - mutable ReadStatement<4, 1> selectFunctionDeclarationsForTypeIdStatement{ - "SELECT name, returnTypeName, signature, functionDeclarationId FROM " - "functionDeclarations WHERE typeId=? ORDER BY name, signature", - database}; - mutable ReadStatement<3, 1> selectFunctionDeclarationsForTypeIdWithoutSignatureStatement{ - "SELECT name, returnTypeName, functionDeclarationId FROM " - "functionDeclarations WHERE typeId=? ORDER BY name", - database}; - mutable ReadStatement<3, 1> selectFunctionParameterDeclarationsStatement{ - "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), " - "json_extract(json_each.value, '$.tr') FROM functionDeclarations, " - "json_each(functionDeclarations.signature) WHERE functionDeclarationId=?", - database}; - WriteStatement<4> insertFunctionDeclarationStatement{ - "INSERT INTO functionDeclarations(typeId, name, returnTypeName, signature) VALUES(?1, ?2, " - "?3, ?4)", - database}; - WriteStatement<2> updateFunctionDeclarationStatement{ - "UPDATE functionDeclarations SET returnTypeName=?2 WHERE functionDeclarationId=?1", database}; - WriteStatement<1> deleteFunctionDeclarationStatement{ - "DELETE FROM functionDeclarations WHERE functionDeclarationId=?", database}; - mutable ReadStatement<3, 1> selectSignalDeclarationsForTypeIdStatement{ - "SELECT name, signature, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER " - "BY name, signature", - database}; - mutable ReadStatement<2, 1> selectSignalDeclarationsForTypeIdWithoutSignatureStatement{ - "SELECT name, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER BY name", - database}; - mutable ReadStatement<3, 1> selectSignalParameterDeclarationsStatement{ - "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), " - "json_extract(json_each.value, '$.tr') FROM signalDeclarations, " - "json_each(signalDeclarations.signature) WHERE signalDeclarationId=?", - database}; - WriteStatement<3> insertSignalDeclarationStatement{ - "INSERT INTO signalDeclarations(typeId, name, signature) VALUES(?1, ?2, ?3)", database}; - WriteStatement<2> updateSignalDeclarationStatement{ - "UPDATE signalDeclarations SET signature=?2 WHERE signalDeclarationId=?1", database}; - WriteStatement<1> deleteSignalDeclarationStatement{ - "DELETE FROM signalDeclarations WHERE signalDeclarationId=?", database}; - mutable ReadStatement<3, 1> selectEnumerationDeclarationsForTypeIdStatement{ - "SELECT name, enumeratorDeclarations, enumerationDeclarationId FROM " - "enumerationDeclarations WHERE typeId=? ORDER BY name", - database}; - mutable ReadStatement<2, 1> selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement{ - "SELECT name, enumerationDeclarationId FROM enumerationDeclarations WHERE typeId=? ORDER " - "BY name", - database}; - mutable ReadStatement<3, 1> selectEnumeratorDeclarationStatement{ - "SELECT json_each.key, json_each.value, json_each.type!='null' FROM " - "enumerationDeclarations, json_each(enumerationDeclarations.enumeratorDeclarations) WHERE " - "enumerationDeclarationId=?", - database}; - WriteStatement<3> insertEnumerationDeclarationStatement{ - "INSERT INTO enumerationDeclarations(typeId, name, enumeratorDeclarations) VALUES(?1, ?2, " - "?3)", - database}; - WriteStatement<2> updateEnumerationDeclarationStatement{ - "UPDATE enumerationDeclarations SET enumeratorDeclarations=?2 WHERE " - "enumerationDeclarationId=?1", - database}; - WriteStatement<1> deleteEnumerationDeclarationStatement{ - "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database}; - mutable ReadStatement<1, 1> selectModuleIdByNameStatement{ - "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database}; - mutable ReadWriteStatement<1, 1> insertModuleNameStatement{ - "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database}; - mutable ReadStatement<1, 1> selectModuleNameStatement{ - "SELECT name FROM modules WHERE moduleId =?1", database}; - mutable ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", database}; - mutable ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{ - "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database}; - mutable ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{ - "SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND " - "name=?3", - database}; - mutable ReadStatement<4> selectAllDocumentImportForSourceIdStatement{ - "SELECT moduleId, majorVersion, minorVersion, sourceId " - "FROM documentImports ", - database}; - mutable ReadStatement<5, 2> selectDocumentImportForSourceIdStatement{ - "SELECT importId, sourceId, moduleId, majorVersion, minorVersion " - "FROM documentImports WHERE sourceId IN carray(?1) AND kind=?2 ORDER BY sourceId, " - "moduleId, majorVersion, minorVersion", - database}; - ReadWriteStatement<1, 5> insertDocumentImportWithoutVersionStatement{ - "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, " - "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING importId", - database}; - ReadWriteStatement<1, 6> insertDocumentImportWithMajorVersionStatement{ - "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " - "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6) RETURNING importId", - database}; - ReadWriteStatement<1, 7> insertDocumentImportWithVersionStatement{ - "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " - "minorVersion, parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) RETURNING " - "importId", - database}; - WriteStatement<1> deleteDocumentImportStatement{"DELETE FROM documentImports WHERE importId=?1", - database}; - WriteStatement<2> deleteDocumentImportsWithParentImportIdStatement{ - "DELETE FROM documentImports WHERE sourceId=?1 AND parentImportId=?2", database}; - WriteStatement<1> deleteDocumentImportsWithSourceIdsStatement{ - "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database}; - ReadStatement<1, 2> selectPropertyDeclarationIdPrototypeChainDownStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId, level) AS (" - " SELECT prototypeId, 0 FROM types WHERE typeId=?1 AND prototypeId IS NOT NULL" - " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN " - " typeSelection USING(typeId))" - "SELECT propertyDeclarationId FROM typeSelection JOIN propertyDeclarations " - " USING(typeId) WHERE name=?2 ORDER BY level LIMIT 1", - database}; - WriteStatement<2> updateAliasIdPropertyDeclarationStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2 WHERE " - "aliasPropertyDeclarationId=?1", - database}; - WriteStatement<2> updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=new.propertyTypeId, " - "propertyTraits=new.propertyTraits, aliasPropertyDeclarationId=?1 FROM (SELECT " - "propertyTypeId, propertyTraits FROM propertyDeclarations WHERE propertyDeclarationId=?1) " - "AS new WHERE aliasPropertyDeclarationId=?2", - database}; - WriteStatement<1> updateAliasPropertyDeclarationToNullStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL, propertyTypeId=NULL, " - "propertyTraits=NULL WHERE propertyDeclarationId=? AND (aliasPropertyDeclarationId IS NOT " - "NULL OR propertyTypeId IS NOT NULL OR propertyTraits IS NOT NULL)", - database}; - ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{ - "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " - "alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId FROM " - "propertyDeclarations AS alias JOIN propertyDeclarations AS target ON " - "alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " - "alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId WHERE " - "alias.propertyTypeId=?1 OR target.typeId=?1 OR alias.propertyImportedTypeNameId IN " - "(SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) " - "WHERE typeId=?1)", - database}; - ReadStatement<3, 1> selectAliasPropertiesDeclarationForPropertiesWithAliasIdStatement{ - "WITH RECURSIVE " - " properties(propertyDeclarationId, propertyImportedTypeNameId, typeId, " - " aliasPropertyDeclarationId) AS (" - " SELECT propertyDeclarationId, propertyImportedTypeNameId, typeId, " - " aliasPropertyDeclarationId FROM propertyDeclarations WHERE " - " aliasPropertyDeclarationId=?1" - " UNION ALL " - " SELECT pd.propertyDeclarationId, pd.propertyImportedTypeNameId, pd.typeId, " - " pd.aliasPropertyDeclarationId FROM propertyDeclarations AS pd JOIN properties AS " - " p ON pd.aliasPropertyDeclarationId=p.propertyDeclarationId)" - "SELECT propertyDeclarationId, propertyImportedTypeNameId, aliasPropertyDeclarationId " - " FROM properties", - database}; - ReadWriteStatement<3, 1> updatesPropertyDeclarationPropertyTypeToNullStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=NULL WHERE propertyTypeId=?1 AND " - "aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, " - "propertyImportedTypeNameId", - database}; - mutable ReadStatement<1, 1> selectPropertyNameStatement{ - "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; - WriteStatement<2> updatePropertyDeclarationTypeStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database}; - ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{ - "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING " - "typeId, prototypeNameId", - database}; - ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{ - "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING " - "typeId, extensionNameId", - database}; - WriteStatement<2> updateTypePrototypeStatement{ - "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; - WriteStatement<2> updateTypeExtensionStatement{ - "UPDATE types SET extensionId=?2 WHERE typeId=?1", database}; - mutable ReadStatement<1, 1> selectTypeIdsForPrototypeChainIdStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " prototypes(typeId) AS (" - " SELECT prototypeId FROM all_prototype_and_extension WHERE typeId=?" - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN " - " prototypes USING(typeId)) " - "SELECT typeId FROM prototypes", - database}; - WriteStatement<3> updatePropertyDeclarationAliasIdAndTypeNameIdStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2, " - "propertyImportedTypeNameId=?3 WHERE propertyDeclarationId=?1 AND " - "(aliasPropertyDeclarationId IS NOT ?2 OR propertyImportedTypeNameId IS NOT ?3)", - database}; - WriteStatement<1> updatetPropertiesDeclarationValuesOfAliasStatement{ - "WITH RECURSIVE " - " properties(propertyDeclarationId, propertyTypeId, propertyTraits) AS ( " - " SELECT aliasPropertyDeclarationId, propertyTypeId, propertyTraits FROM " - " propertyDeclarations WHERE propertyDeclarationId=?1 " - " UNION ALL " - " SELECT pd.aliasPropertyDeclarationId, pd.propertyTypeId, pd.propertyTraits FROM " - " propertyDeclarations AS pd JOIN properties USING(propertyDeclarationId)) " - "UPDATE propertyDeclarations AS pd SET propertyTypeId=p.propertyTypeId, " - " propertyTraits=p.propertyTraits " - "FROM properties AS p " - "WHERE pd.propertyDeclarationId=?1 AND p.propertyDeclarationId IS NULL AND " - " (pd.propertyTypeId IS NOT p.propertyTypeId OR pd.propertyTraits IS NOT " - " p.propertyTraits)", - database}; - WriteStatement<1> updatePropertyDeclarationAliasIdToNullStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL WHERE " - "propertyDeclarationId=?1", - database}; - mutable ReadStatement<1, 1> selectPropertyDeclarationIdsForAliasChainStatement{ - "WITH RECURSIVE " - " properties(propertyDeclarationId) AS ( " - " SELECT aliasPropertyDeclarationId FROM propertyDeclarations WHERE " - " propertyDeclarationId=?1 " - " UNION ALL " - " SELECT aliasPropertyDeclarationId FROM propertyDeclarations JOIN properties " - " USING(propertyDeclarationId)) " - "SELECT propertyDeclarationId FROM properties", - database}; - mutable ReadStatement<3> selectAllFileStatusesStatement{ - "SELECT sourceId, size, lastModified FROM fileStatuses ORDER BY sourceId", database}; - mutable ReadStatement<3, 1> selectFileStatusesForSourceIdsStatement{ - "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId IN carray(?1) ORDER " - "BY sourceId", - database}; - mutable ReadStatement<3, 1> selectFileStatusesForSourceIdStatement{ - "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId=?1 ORDER BY sourceId", - database}; - WriteStatement<3> insertFileStatusStatement{ - "INSERT INTO fileStatuses(sourceId, size, lastModified) VALUES(?1, ?2, ?3)", database}; - WriteStatement<1> deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1", - database}; - WriteStatement<3> updateFileStatusStatement{ - "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database}; - ReadStatement<1, 1> selectTypeIdBySourceIdStatement{"SELECT typeId FROM types WHERE sourceId=?", - database}; - mutable ReadStatement<1, 3> selectImportedTypeNameIdStatement{ - "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 " - "AND name=?3 LIMIT 1", - database}; - mutable ReadWriteStatement<1, 3> insertImportedTypeNameIdStatement{ - "INSERT INTO importedTypeNames(kind, importOrSourceId, name) VALUES (?1, ?2, ?3) " - "RETURNING importedTypeNameId", - database}; - mutable ReadStatement<1, 2> selectImportIdBySourceIdAndModuleIdStatement{ - "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND majorVersion " - "IS NULL AND minorVersion IS NULL LIMIT 1", - database}; - mutable ReadStatement<1, 3> selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement{ - "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " - "majorVersion=?3 AND minorVersion IS NULL LIMIT 1", - database}; - mutable ReadStatement<1, 4> selectImportIdBySourceIdAndModuleIdAndVersionStatement{ - "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " - "majorVersion=?3 AND minorVersion=?4 LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectKindFromImportedTypeNamesStatement{ - "SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database}; - mutable ReadStatement<1, 1> selectNameFromImportedTypeNamesStatement{ - "SELECT name FROM importedTypeNames WHERE importedTypeNameId=?1", database}; - mutable ReadStatement<1, 1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{ - "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " - "importOrSourceId=di.importId JOIN documentImports AS di2 ON di.sourceId=di2.sourceId AND " - "di.moduleId=di2.sourceModuleId " - "JOIN exportedTypeNames AS etn ON di2.moduleId=etn.moduleId WHERE " - "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND " - "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS " - "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY etn.majorVersion DESC NULLS FIRST, " - "etn.minorVersion DESC NULLS FIRST LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{ - "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " - "importOrSourceId=sourceId JOIN exportedTypeNames AS etn USING(moduleId) WHERE " - "itn.kind=1 AND importedTypeNameId=?1 AND itn.name=etn.name AND " - "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS " - "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY di.kind, etn.majorVersion DESC " - "NULLS FIRST, etn.minorVersion DESC NULLS FIRST LIMIT 1", - database}; - WriteStatement<0> deleteAllSourcesStatement{"DELETE FROM sources", database}; - WriteStatement<0> deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database}; - mutable ReadStatement<6, 1> selectExportedTypesForSourceIdsStatement{ - "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, " - "exportedTypeNameId FROM exportedTypeNames WHERE typeId in carray(?1) ORDER BY moduleId, " - "name, majorVersion, minorVersion", - database}; - WriteStatement<5> insertExportedTypeNamesWithVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, minorVersion, typeId) " - "VALUES(?1, ?2, ?3, ?4, ?5)", - database}; - WriteStatement<4> insertExportedTypeNamesWithMajorVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, typeId) " - "VALUES(?1, ?2, ?3, ?4)", - database}; - WriteStatement<3> insertExportedTypeNamesWithoutVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, typeId) VALUES(?1, ?2, ?3)", database}; - WriteStatement<1> deleteExportedTypeNameStatement{ - "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; - WriteStatement<2> updateExportedTypeNameTypeIdStatement{ - "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; - mutable ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " - "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId", - database}; - WriteStatement<4> insertProjectDataStatement{ - "INSERT INTO projectDatas(projectSourceId, sourceId, " - "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)", - database}; - WriteStatement<2> deleteProjectDataStatement{ - "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database}; - WriteStatement<4> updateProjectDataStatement{ - "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2", - database}; - mutable ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " - "projectSourceId=?1", - database}; - mutable ReadStatement<4, 1> selectProjectDataForSourceIdStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " - "sourceId=?1 LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{ - "SELECT typeId FROM types WHERE sourceId IN carray(?1)", database}; - mutable ReadStatement<6, 1> selectModuleExportedImportsForSourceIdStatement{ - "SELECT moduleExportedImportId, moduleId, exportedModuleId, ifnull(majorVersion, -1), " - "ifnull(minorVersion, -1), isAutoVersion FROM moduleExportedImports WHERE moduleId IN " - "carray(?1) ORDER BY moduleId, exportedModuleId", - database}; - WriteStatement<3> insertModuleExportedImportWithoutVersionStatement{ - "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion) " - "VALUES (?1, ?2, ?3)", - database}; - WriteStatement<4> insertModuleExportedImportWithMajorVersionStatement{ - "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, " - "majorVersion) VALUES (?1, ?2, ?3, ?4)", - database}; - WriteStatement<5> insertModuleExportedImportWithVersionStatement{ - "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, " - "majorVersion, minorVersion) VALUES (?1, ?2, ?3, ?4, ?5)", - database}; - WriteStatement<1> deleteModuleExportedImportStatement{ - "DELETE FROM moduleExportedImports WHERE moduleExportedImportId=?1", database}; - mutable ReadStatement<3, 3> selectModuleExportedImportsForModuleIdStatement{ - "WITH RECURSIVE " - " imports(moduleId, majorVersion, minorVersion, moduleExportedImportId) AS ( " - " SELECT exportedModuleId, " - " iif(isAutoVersion=1, ?2, majorVersion), " - " iif(isAutoVersion=1, ?3, minorVersion), " - " moduleExportedImportId " - " FROM moduleExportedImports WHERE moduleId=?1 " - " UNION ALL " - " SELECT exportedModuleId, " - " iif(mei.isAutoVersion=1, i.majorVersion, mei.majorVersion), " - " iif(mei.isAutoVersion=1, i.minorVersion, mei.minorVersion), " - " mei.moduleExportedImportId " - " FROM moduleExportedImports AS mei JOIN imports AS i USING(moduleId)) " - "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) " - "FROM imports", - database}; - mutable ReadStatement<1, 1> selectPropertyDeclarationIdsForTypeStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId) AS (" - " VALUES(?1)" - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " - " USING(typeId))" - "SELECT propertyDeclarationId FROM typeChain JOIN propertyDeclarations " - " USING(typeId) ORDER BY propertyDeclarationId", - database}; - mutable ReadStatement<1, 1> selectLocalPropertyDeclarationIdsForTypeStatement{ - "SELECT propertyDeclarationId " - "FROM propertyDeclarations " - "WHERE typeId=? " - "ORDER BY propertyDeclarationId", - database}; - mutable ReadStatement<1, 2> selectPropertyDeclarationIdForTypeAndPropertyNameStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId, level) AS (" - " VALUES(?1, 0)" - " UNION ALL " - " SELECT prototypeId, typeChain.level + 1 FROM all_prototype_and_extension JOIN " - " typeChain USING(typeId))" - "SELECT propertyDeclarationId FROM typeChain JOIN propertyDeclarations " - " USING(typeId) WHERE name=?2 ORDER BY level LIMIT 1", - database}; - mutable ReadStatement<1, 2> selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement{ - "SELECT propertyDeclarationId " - "FROM propertyDeclarations " - "WHERE typeId=?1 AND name=?2 LIMIT 1", - database}; - mutable ReadStatement<4, 1> selectPropertyDeclarationForPropertyDeclarationIdStatement{ - "SELECT typeId, name, propertyTraits, propertyTypeId " - "FROM propertyDeclarations " - "WHERE propertyDeclarationId=?1 LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectSignalDeclarationNamesForTypeStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId) AS (" - " VALUES(?1)" - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " - " USING(typeId)) " - "SELECT name FROM typeChain JOIN signalDeclarations " - " USING(typeId) ORDER BY name", - database}; - mutable ReadStatement<1, 1> selectFuncionDeclarationNamesForTypeStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId) AS (" - " VALUES(?1)" - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " - " USING(typeId))" - "SELECT name FROM typeChain JOIN functionDeclarations " - " USING(typeId) ORDER BY name", - database}; - mutable ReadStatement<2> selectTypesWithDefaultPropertyStatement{ - "SELECT typeId, defaultPropertyId FROM types ORDER BY typeId", database}; - WriteStatement<2> updateDefaultPropertyIdStatement{ - "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database}; - WriteStatement<1> updateDefaultPropertyIdToNullStatement{ - "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; - mutable ReadStatement<4, 1> selectInfoTypeByTypeIdStatement{ - "SELECT defaultPropertyId, sourceId, traits, annotationTraits FROM types WHERE typeId=?", - database}; - mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " prototypes(typeId, level) AS (" - " SELECT prototypeId, 0 FROM all_prototype_and_extension WHERE typeId=?" - " UNION ALL " - " SELECT prototypeId, p.level+1 FROM all_prototype_and_extension JOIN " - " prototypes AS p USING(typeId)) " - "SELECT typeId FROM prototypes ORDER BY level", - database}; - mutable ReadStatement<1, 1> selectPrototypeAndSelfIdsForTypeIdInOrderStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId, level) AS (" - " VALUES(?1, 0)" - " UNION ALL " - " SELECT prototypeId, tc.level+1 FROM all_prototype_and_extension JOIN " - " typeChain AS tc USING(typeId)) " - "SELECT typeId FROM typeChain ORDER BY level", - database}; - mutable ReadStatement<1, 1> selectPrototypeIdsStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId) AS (" - " SELECT prototypeId FROM all_prototype_and_extension WHERE typeId=?1 " - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeSelection " - " USING(typeId))" - "SELECT typeId FROM typeSelection", - database}; - WriteStatement<2> upsertPropertyEditorPathIdStatement{ - "INSERT INTO propertyEditorPaths(typeId, pathSourceId) VALUES(?1, ?2) ON CONFLICT DO " - "UPDATE SET pathSourceId=excluded.pathSourceId WHERE pathSourceId IS NOT " - "excluded.pathSourceId", - database}; - mutable ReadStatement<1, 1> selectPropertyEditorPathIdStatement{ - "SELECT pathSourceId FROM propertyEditorPaths WHERE typeId=?", database}; - mutable ReadStatement<3, 1> selectPropertyEditorPathsForForSourceIdsStatement{ - "SELECT typeId, pathSourceId, directoryId " - "FROM propertyEditorPaths " - "WHERE directoryId IN carray(?1) " - "ORDER BY typeId", - database}; - WriteStatement<3> insertPropertyEditorPathStatement{ - "INSERT INTO propertyEditorPaths(typeId, pathSourceId, directoryId) VALUES (?1, ?2, ?3)", - database}; - WriteStatement<3> updatePropertyEditorPathsStatement{"UPDATE propertyEditorPaths " - "SET pathSourceId=?2, directoryId=?3 " - "WHERE typeId=?1", - database}; - WriteStatement<1> deletePropertyEditorPathStatement{ - "DELETE FROM propertyEditorPaths WHERE typeId=?1", database}; - mutable ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{ - "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " - "sourceId IN carray(?1) ORDER BY typeId", - database}; - WriteStatement<5> insertTypeAnnotationStatement{ - "INSERT INTO typeAnnotations(typeId, sourceId, iconPath, itemLibrary, hints) VALUES(?1, " - "?2, ?3, ?4, ?5)", - database}; - WriteStatement<4> updateTypeAnnotationStatement{ - "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; - WriteStatement<1> deleteTypeAnnotationStatement{"DELETE FROM typeAnnotations WHERE typeId=?1", - database}; - mutable ReadStatement<1, 1> selectTypeIconPathStatement{ - "SELECT iconPath FROM typeAnnotations WHERE typeId=?1", database}; - mutable ReadStatement<2, 1> selectTypeHintsStatement{ - "SELECT hints.key, hints.value " - "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints " - "WHERE typeId=?1", - database}; - mutable ReadStatement<9> selectItemLibraryEntriesStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i", - database}; - mutable ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " - "WHERE typeId=?1", - database}; - mutable ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " - "WHERE typeId IN (SELECT DISTINCT typeId " - " FROM documentImports AS di JOIN exportedTypeNames USING(moduleId) " - " WHERE di.sourceId=?)", - database}; - mutable ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ - "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database}; - mutable ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{ - "SELECT p.value FROM json_each(?1) AS p", database}; - mutable ReadStatement<1, 1> selectTypeIdsByModuleIdStatement{ - "SELECT DISTINCT typeId FROM exportedTypeNames WHERE moduleId=?", database}; - mutable ReadStatement<1, 1> selectHeirTypeIdsStatement{ - "WITH RECURSIVE " - " typeSelection(typeId) AS (" - " SELECT typeId FROM types WHERE prototypeId=?1 OR extensionId=?1" - " UNION ALL " - " SELECT t.typeId " - " FROM types AS t JOIN typeSelection AS ts " - " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)" - "SELECT typeId FROM typeSelection", - database}; + std::unique_ptr<Statements> s; }; -extern template class ProjectStorage<Sqlite::Database>; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index efe9bc58f5..a5dc60c4fa 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -3,87 +3,186 @@ #include "projectstorageexceptions.h" +#include <tracing/qmldesignertracing.h> + namespace QmlDesigner { +using namespace NanotraceHR::Literals; +using NanotraceHR::keyValue; + +namespace { +auto &category() +{ + return ProjectStorageTracing::projectStorageCategory(); +} +} // namespace + +NoSourcePathForInvalidSourceId::NoSourcePathForInvalidSourceId() +{ + category().threadEvent("NoSourcePathForInvalidSourceId"_t); +} + const char *NoSourcePathForInvalidSourceId::what() const noexcept { return "You cannot get a file path for an invalid file path id!"; } +NoSourceContextPathForInvalidSourceContextId::NoSourceContextPathForInvalidSourceContextId() +{ + category().threadEvent("NoSourceContextPathForInvalidSourceContextId"_t); +} + const char *NoSourceContextPathForInvalidSourceContextId::what() const noexcept { return "You cannot get a directory path for an invalid directory path id!"; } +SourceContextIdDoesNotExists::SourceContextIdDoesNotExists() +{ + category().threadEvent("SourceContextIdDoesNotExists"_t); +} + const char *SourceContextIdDoesNotExists::what() const noexcept { return "The source context id does not exist in the database!"; } +SourceIdDoesNotExists::SourceIdDoesNotExists() +{ + category().threadEvent("SourceIdDoesNotExists"_t); +} + const char *SourceIdDoesNotExists::what() const noexcept { return "The source id does not exist in the database!"; } +TypeHasInvalidSourceId::TypeHasInvalidSourceId() +{ + category().threadEvent("TypeHasInvalidSourceId"_t); +} + const char *TypeHasInvalidSourceId::what() const noexcept { return "The source id is invalid!"; } +ModuleDoesNotExists::ModuleDoesNotExists() +{ + category().threadEvent("ModuleDoesNotExists"_t); +} + const char *ModuleDoesNotExists::what() const noexcept { return "The module does not exist!"; } +ModuleAlreadyExists::ModuleAlreadyExists() +{ + category().threadEvent("ModuleAlreadyExists"_t); +} + const char *ModuleAlreadyExists::what() const noexcept { return "The module does already exist!"; } -TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view errorMessage) - : ProjectStorageErrorWithMessage{"TypeNameDoesNotExists"sv, errorMessage} -{} +TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId) + : ProjectStorageErrorWithMessage{ + "TypeNameDoesNotExists"sv, + Utils::SmallString::join( + {"type: ", typeName, ", source id: ", Utils::SmallString::number(sourceId.internalId())})} +{ + category().threadEvent("TypeNameDoesNotExists"_t, + keyValue("type name", typeName), + keyValue("source id", sourceId)); +} + +PropertyNameDoesNotExists::PropertyNameDoesNotExists() +{ + category().threadEvent("PropertyNameDoesNotExists"_t); +} const char *PropertyNameDoesNotExists::what() const noexcept { return "The property name does not exist!"; } +PrototypeChainCycle::PrototypeChainCycle() +{ + category().threadEvent("PrototypeChainCycle"_t); +} + const char *PrototypeChainCycle::what() const noexcept { return "There is a prototype chain cycle!"; } +AliasChainCycle::AliasChainCycle() +{ + category().threadEvent("AliasChainCycle"_t); +} + const char *AliasChainCycle::what() const noexcept { return "There is a prototype chain cycle!"; } +CannotParseQmlTypesFile::CannotParseQmlTypesFile() +{ + category().threadEvent("CannotParseQmlTypesFile"_t); +} + const char *CannotParseQmlTypesFile::what() const noexcept { return "Cannot parse qml types file!"; } +CannotParseQmlDocumentFile::CannotParseQmlDocumentFile() +{ + category().threadEvent("CannotParseQmlDocumentFile"_t); +} + const char *CannotParseQmlDocumentFile::what() const noexcept { return "Cannot parse qml types file!"; } +ProjectDataHasInvalidProjectSourceId::ProjectDataHasInvalidProjectSourceId() +{ + category().threadEvent("ProjectDataHasInvalidProjectSourceId"_t); +} + const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept { return "The project source id is invalid!"; } +ProjectDataHasInvalidSourceId::ProjectDataHasInvalidSourceId() +{ + category().threadEvent("ProjectDataHasInvalidSourceId"_t); +} + const char *ProjectDataHasInvalidSourceId::what() const noexcept { return "The source id is invalid!"; } +ProjectDataHasInvalidModuleId::ProjectDataHasInvalidModuleId() +{ + category().threadEvent("ProjectDataHasInvalidModuleId"_t); +} + const char *ProjectDataHasInvalidModuleId::what() const noexcept { return "The module id is invalid!"; } +FileStatusHasInvalidSourceId::FileStatusHasInvalidSourceId() +{ + category().threadEvent("FileStatusHasInvalidSourceId"_t); +} + const char *FileStatusHasInvalidSourceId::what() const noexcept { return "The source id in file status is invalid!"; @@ -110,7 +209,14 @@ const char *ProjectStorageErrorWithMessage::what() const noexcept ExportedTypeCannotBeInserted::ExportedTypeCannotBeInserted(std::string_view errorMessage) : ProjectStorageErrorWithMessage{"ExportedTypeCannotBeInserted"sv, errorMessage} -{} +{ + category().threadEvent("ExportedTypeCannotBeInserted"_t, keyValue("error message", errorMessage)); +} + +TypeAnnotationHasInvalidSourceId::TypeAnnotationHasInvalidSourceId() +{ + category().threadEvent("TypeAnnotationHasInvalidSourceId"_t); +} const char *TypeAnnotationHasInvalidSourceId::what() const noexcept { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index 412dd4a9ff..d85f1f7f9e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -5,6 +5,8 @@ #include "../include/qmldesignercorelib_global.h" +#include "projectstorageids.h" + #include <exception> namespace QmlDesigner { @@ -13,15 +15,19 @@ using namespace std::literals::string_view_literals; class QMLDESIGNERCORE_EXPORT ProjectStorageError : public std::exception { +protected: + ProjectStorageError() = default; + public: const char *what() const noexcept override; }; class ProjectStorageErrorWithMessage : public ProjectStorageError { -public: +protected: ProjectStorageErrorWithMessage(std::string_view error, std::string_view errorMessage); +public: const char *what() const noexcept override; public: @@ -31,42 +37,49 @@ public: class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : public ProjectStorageError { public: + NoSourcePathForInvalidSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : public ProjectStorageError { public: + NoSourceContextPathForInvalidSourceContextId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : public ProjectStorageError { public: + SourceContextIdDoesNotExists(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : public ProjectStorageError { public: + SourceIdDoesNotExists(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : public ProjectStorageError { public: + TypeHasInvalidSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : public ProjectStorageError { public: + ModuleDoesNotExists(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : public ProjectStorageError { public: + ModuleAlreadyExists(); const char *what() const noexcept override; }; @@ -79,66 +92,76 @@ public: class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : public ProjectStorageErrorWithMessage { public: - TypeNameDoesNotExists(std::string_view errorMessage); + TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId = SourceId{}); }; class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : public ProjectStorageError { public: + PropertyNameDoesNotExists(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : public ProjectStorageError { public: + PrototypeChainCycle(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT AliasChainCycle : public ProjectStorageError { public: + AliasChainCycle(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : public ProjectStorageError { public: + CannotParseQmlTypesFile(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : public ProjectStorageError { public: + CannotParseQmlDocumentFile(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : public ProjectStorageError { public: + ProjectDataHasInvalidProjectSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError { public: + ProjectDataHasInvalidSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError { public: + ProjectDataHasInvalidModuleId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : public ProjectStorageError { public: + FileStatusHasInvalidSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT TypeAnnotationHasInvalidSourceId : public ProjectStorageError { public: + TypeAnnotationHasInvalidSourceId(); const char *what() const noexcept override; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h index b33c609509..cbb7d4265a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h @@ -11,7 +11,6 @@ namespace QmlDesigner { class ProjectStorageInterface; class SourcePathCacheInterface; -template<typename Database> class ProjectStorage; template<typename Type> diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 427c0ff8d6..9f0c134ed3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -24,6 +24,20 @@ constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumerat enum class FlagIs : unsigned int { False, Set, True }; +template<typename String> +void convertToString(String &string, const FlagIs &flagIs) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + + if (flagIs == FlagIs::False) + convertToString(string, false); + else if (flagIs == FlagIs::True) + convertToString(string, true); + else + convertToString(string, "is set"); +} + } // namespace QmlDesigner namespace QmlDesigner::Storage { @@ -46,6 +60,18 @@ constexpr bool operator&(PropertyDeclarationTraits first, PropertyDeclarationTra return static_cast<int>(first) & static_cast<int>(second); } +template<typename String> +void convertToString(String &string, const PropertyDeclarationTraits &traits) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("is read only", traits & PropertyDeclarationTraits::IsReadOnly), + keyValue("is pointer", traits & PropertyDeclarationTraits::IsPointer), + keyValue("is list", traits & PropertyDeclarationTraits::IsList)); + + convertToString(string, dict); +} + enum class TypeTraitsKind : unsigned int { None, Reference, @@ -53,6 +79,25 @@ enum class TypeTraitsKind : unsigned int { Sequence, }; +template<typename String> +void convertToString(String &string, const TypeTraitsKind &kind) +{ + switch (kind) { + case TypeTraitsKind::None: + convertToString(string, "None"); + break; + case TypeTraitsKind::Reference: + convertToString(string, "Reference"); + break; + case TypeTraitsKind::Value: + convertToString(string, "Value"); + break; + case TypeTraitsKind::Sequence: + convertToString(string, "Sequence"); + break; + } +} + struct TypeTraits { constexpr TypeTraits() @@ -100,6 +145,35 @@ struct TypeTraits return first.type == second.type && first.annotation == second.annotation; } + template<typename String> + friend void convertToString(String &string, const TypeTraits &typeTraits) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary( + keyValue("kind", typeTraits.kind), + keyValue("is enum", typeTraits.isEnum), + keyValue("is file component", typeTraits.isFileComponent), + keyValue("is project component", typeTraits.isProjectComponent), + keyValue("is in project module", typeTraits.isInProjectModule), + keyValue("uses custom parser", typeTraits.usesCustomParser), + keyValue("can be container", typeTraits.canBeContainer), + keyValue("force clip", typeTraits.forceClip), + keyValue("does layout children", typeTraits.doesLayoutChildren), + keyValue("can be dropped in form editor", typeTraits.canBeDroppedInFormEditor), + keyValue("can be dropped in navigator", typeTraits.canBeDroppedInNavigator), + keyValue("can be dropped in view 3D", typeTraits.canBeDroppedInView3D), + keyValue("is movable", typeTraits.isMovable), + keyValue("is resizable", typeTraits.isResizable), + keyValue("has form editor item", typeTraits.hasFormEditorItem), + keyValue("is stacked container", typeTraits.isStackedContainer), + keyValue("takes over rendering of children", typeTraits.takesOverRenderingOfChildren), + keyValue("visible in navigator", typeTraits.visibleInNavigator), + keyValue("visible in library", typeTraits.visibleInLibrary)); + + convertToString(string, dict); + } + union { struct { @@ -202,10 +276,22 @@ public: explicit operator bool() const { return major && minor; } + template<typename String> + friend void convertToString(String &string, const Version &version) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("major version", version.major.value), + keyValue("minor version", version.minor.value)); + + convertToString(string, dict); + } + public: VersionNumber major; VersionNumber minor; }; + } // namespace QmlDesigner::Storage namespace QmlDesigner::Storage::Info { @@ -217,6 +303,17 @@ struct TypeHint , expression{expression} {} + template<typename String> + friend void convertToString(String &string, const TypeHint &typeHint) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", typeHint.name), + keyValue("expression", typeHint.expression)); + + convertToString(string, dict); + } + Utils::SmallString name; Utils::PathString expression; }; @@ -231,6 +328,18 @@ struct ItemLibraryProperty , value{value} {} + template<typename String> + friend void convertToString(String &string, const ItemLibraryProperty &property) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", property.name), + keyValue("type", property.type), + keyValue("value", property.value)); + + convertToString(string, dict); + } + Utils::SmallString name; Utils::SmallString type; Sqlite::Value value; @@ -274,6 +383,24 @@ struct ItemLibraryEntry , properties{std::move(properties)} {} + template<typename String> + friend void convertToString(String &string, const ItemLibraryEntry &entry) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", entry.typeId), + keyValue("name", entry.name), + keyValue("icon path", entry.iconPath), + keyValue("category", entry.category), + keyValue("import", entry.import), + keyValue("tool tip", entry.toolTip), + keyValue("template path", entry.templatePath), + keyValue("properties", entry.properties), + keyValue("extra file paths", entry.extraFilePaths)); + + convertToString(string, dict); + } + TypeId typeId; Utils::SmallString name; Utils::PathString iconPath; @@ -321,6 +448,18 @@ public: < std::tie(second.moduleId, second.name, second.version); } + template<typename String> + friend void convertToString(String &string, const ExportedTypeName &exportedTypeName) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", exportedTypeName.name), + keyValue("version", exportedTypeName.version), + keyValue("module id", exportedTypeName.moduleId)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; Storage::Version version; @@ -342,6 +481,19 @@ public: , propertyTypeId{propertyTypeId} {} + template<typename String> + friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", propertyDeclaration.typeId), + keyValue("name", propertyDeclaration.name), + keyValue("traits", propertyDeclaration.traits), + keyValue("property type id", propertyDeclaration.propertyTypeId)); + + convertToString(string, dict); + } + TypeId typeId; ::Utils::SmallString name; PropertyDeclarationTraits traits; @@ -351,22 +503,26 @@ public: class Type { public: - Type(PropertyDeclarationId defaultPropertyId, - SourceId sourceId, - long long typeTraits, - long long typeAnnotationTraits) - : defaultPropertyId{defaultPropertyId} - , sourceId{sourceId} + Type(SourceId sourceId, long long typeTraits, long long typeAnnotationTraits) + : sourceId{sourceId} , traits{typeTraits, typeAnnotationTraits} {} - Type(PropertyDeclarationId defaultPropertyId, SourceId sourceId, TypeTraits traits) - : defaultPropertyId{defaultPropertyId} - , sourceId{sourceId} + Type(SourceId sourceId, TypeTraits traits) + : sourceId{sourceId} , traits{traits} {} - PropertyDeclarationId defaultPropertyId; + template<typename String> + friend void convertToString(String &string, const Type &type) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("source id", type.sourceId), keyValue("traits", type.traits)); + + convertToString(string, dict); + } + SourceId sourceId; TypeTraits traits; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 266c6ee7ca..971e635517 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -32,6 +32,7 @@ public: virtual void removeObserver(ProjectStorageObserver *observer) = 0; virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; + virtual Utils::SmallString moduleName(ModuleId moduleId) const = 0; virtual std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; virtual TypeId typeId(ModuleId moduleId, @@ -54,7 +55,10 @@ public: virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId, ::Utils::SmallStringView propertyName) const = 0; + virtual PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const = 0; virtual std::optional<Storage::Info::Type> type(TypeId typeId) const = 0; + virtual SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const = 0; + virtual SmallSourceIds<64> typeAnnotationDirectorySourceIds() const = 0; virtual Utils::PathString typeIconPath(TypeId typeId) const = 0; virtual Storage::Info::TypeHints typeHints(TypeId typeId) const = 0; virtual Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const = 0; @@ -64,9 +68,9 @@ public: virtual std::vector<::Utils::SmallString> functionDeclarationNames(TypeId typeId) const = 0; virtual std::optional<::Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const = 0; - virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0; - virtual TypeIds prototypeIds(TypeId type) const = 0; - virtual TypeIds heirIds(TypeId typeId) const = 0; + virtual SmallTypeIds<16> prototypeAndSelfIds(TypeId type) const = 0; + virtual SmallTypeIds<16> prototypeIds(TypeId type) const = 0; + virtual SmallTypeIds<64> heirIds(TypeId typeId) const = 0; virtual bool isBasedOn(TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0; @@ -80,7 +84,7 @@ public: virtual std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const = 0; virtual SourceId propertyEditorPathId(TypeId typeId) const = 0; - virtual const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const = 0; + virtual const Storage::Info::CommonTypeCache<ProjectStorageType> &commonTypeCache() const = 0; template<const char *moduleName, const char *typeName> TypeId commonTypeId() const diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h index a9185d91e8..04f11096bd 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h @@ -12,6 +12,28 @@ namespace QmlDesigner { enum class SourceType : int { Qml, QmlUi, QmlTypes, QmlDir, Directory }; +template<typename String> +void convertToString(String &string, SourceType sourceType) +{ + switch (sourceType) { + case SourceType::Qml: + convertToString(string, "Qml"); + break; + case SourceType::QmlUi: + convertToString(string, "QmlUi"); + break; + case SourceType::QmlTypes: + convertToString(string, "QmlTypes"); + break; + case SourceType::QmlDir: + convertToString(string, "QmlDir"); + break; + case SourceType::Directory: + convertToString(string, "Directory"); + break; + } +} + class ProjectChunkId { public: @@ -46,6 +68,17 @@ public: friend bool operator<(ProjectChunkId first, ProjectPartId second) { return first.id < second; } friend bool operator<(ProjectPartId first, ProjectChunkId second) { return first < second.id; } + + template<typename String> + friend void convertToString(String &string, const ProjectChunkId &id) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("project part id", id.id), + keyValue("source type", id.sourceType)); + + convertToString(string, dict); + } }; using ProjectChunkIds = std::vector<ProjectChunkId>; @@ -67,6 +100,16 @@ public: return first.id == second.id && first.sourceIds == second.sourceIds; } + template<typename String> + friend void convertToString(String &string, const IdPaths &idPaths) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("id", idPaths.id), keyValue("source ids", idPaths.sourceIds)); + + convertToString(string, dict); + } + public: ProjectChunkId id; SourceIds sourceIds; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 18c3931249..8d810d94bd 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -7,6 +7,7 @@ #include "projectstorageids.h" #include "projectstorageinfotypes.h" +#include <nanotrace/nanotracehr.h> #include <sqlite/sqlitevalue.h> #include <utils/smallstring.h> @@ -45,6 +46,17 @@ public: < std::tie(second.sourceId, second.moduleId, second.version); } + template<typename String> + friend void convertToString(String &string, const Import &import) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("module id", import.moduleId), + keyValue("source id", import.sourceId), + keyValue("version", import.version)); + convertToString(string, dict); + } + public: Storage::Version version; ModuleId moduleId; @@ -57,11 +69,50 @@ namespace Synchronization { enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; +template<typename String> +void convertToString(String &string, const TypeNameKind &kind) +{ + switch (kind) { + case TypeNameKind::Exported: + convertToString(string, "Exported"); + break; + case TypeNameKind::QualifiedExported: + convertToString(string, "QualifiedExported"); + break; + } +} + enum class FileType : char { QmlTypes, QmlDocument }; +template<typename String> +void convertToString(String &string, const FileType &type) +{ + switch (type) { + case FileType::QmlTypes: + convertToString(string, "QmlTypes"); + break; + case FileType::QmlDocument: + convertToString(string, "QmlDocument"); + break; + } +} + enum class IsQualified : int { No, Yes }; -inline int operator-(IsQualified first, IsQualified second) +template<typename String> +void convertToString(String &string, const IsQualified &isQualified) +{ + switch (isQualified) { + case IsQualified::No: + convertToString(string, "No"); + break; + case IsQualified::Yes: + convertToString(string, "Yes"); + break; + } +} + +inline int operator-(IsQualified first, const IsQualified &second) { return static_cast<int>(first) - static_cast<int>(second); } @@ -78,6 +129,25 @@ enum class ImportKind : char { ModuleExportedModuleDependency }; +template<typename String> +void convertToString(String &string, const ImportKind &kind) +{ + switch (kind) { + case ImportKind::Import: + convertToString(string, "Import"); + break; + case ImportKind::ModuleDependency: + convertToString(string, "ModuleDependency"); + break; + case ImportKind::ModuleExportedImport: + convertToString(string, "ModuleExportedImport"); + break; + case ImportKind::ModuleExportedModuleDependency: + convertToString(string, "ModuleExportedModuleDependency"); + break; + } +} + class ImportView { public: @@ -97,6 +167,19 @@ public: && first.version == second.version; } + template<typename String> + friend void convertToString(String &string, const ImportView &import) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("import id", import.importId), + keyValue("source id", import.sourceId), + keyValue("module id", import.moduleId), + keyValue("version", import.version)); + + convertToString(string, dict); + } + public: ImportId importId; SourceId sourceId; @@ -106,6 +189,19 @@ public: enum class IsAutoVersion : char { No, Yes }; +template<typename String> +void convertToString(String &string, const IsAutoVersion &isAutoVersion) +{ + switch (isAutoVersion) { + case IsAutoVersion::No: + convertToString(string, "No"); + break; + case IsAutoVersion::Yes: + convertToString(string, "Yes"); + break; + } +} + constexpr bool operator<(IsAutoVersion first, IsAutoVersion second) { return to_underlying(first) < to_underlying(second); @@ -137,6 +233,19 @@ public: < std::tie(second.moduleId, second.exportedModuleId, second.isAutoVersion, second.version); } + template<typename String> + friend void convertToString(String &string, const ModuleExportedImport &import) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("module id", import.moduleId), + keyValue("exported module id", import.exportedModuleId), + keyValue("version", import.version), + keyValue("is auto version", import.isAutoVersion)); + + convertToString(string, dict); + } + public: Storage::Version version; ModuleId moduleId; @@ -171,6 +280,20 @@ public: && first.version == second.version && first.isAutoVersion == second.isAutoVersion; } + template<typename String> + friend void convertToString(String &string, const ModuleExportedImportView &import) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("module exported import id", import.moduleExportedImportId), + keyValue("module id", import.moduleId), + keyValue("exported module id", import.exportedModuleId), + keyValue("version", import.version), + keyValue("is auto version", import.isAutoVersion)); + + convertToString(string, dict); + } + public: ModuleExportedImportId moduleExportedImportId; Storage::Version version; @@ -192,6 +315,16 @@ public: return first.name == second.name; } + template<typename String> + friend void convertToString(String &string, const ImportedType &importedType) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", importedType.name)); + + convertToString(string, dict); + } + public: TypeNameString name; }; @@ -210,6 +343,17 @@ public: return first.name == second.name && first.import == second.import; } + template<typename String> + friend void convertToString(String &string, const QualifiedImportedType &importedType) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", importedType.name), + keyValue("import", importedType.import)); + + convertToString(string, dict); + } + public: TypeNameString name; Import import; @@ -264,6 +408,19 @@ public: < std::tie(second.moduleId, second.name, second.version); } + template<typename String> + friend void convertToString(String &string, const ExportedType &exportedType) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", exportedType.name), + keyValue("module id", exportedType.moduleId), + keyValue("type id", exportedType.typeId), + keyValue("version", exportedType.version)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; Storage::Version version; @@ -295,6 +452,20 @@ public: , exportedTypeNameId{exportedTypeNameId} {} + template<typename String> + friend void convertToString(String &string, const ExportedTypeView &exportedType) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", exportedType.name), + keyValue("module id", exportedType.moduleId), + keyValue("type id", exportedType.typeId), + keyValue("version", exportedType.version), + keyValue("version", exportedType.exportedTypeNameId)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; Storage::Version version; @@ -305,6 +476,43 @@ public: using ImportedTypeName = std::variant<ImportedType, QualifiedImportedType>; +template<typename String> +void convertToString(String &string, const ImportedTypeName &typeName) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + + struct Dispatcher + { + static const QmlDesigner::Storage::Import &nullImport() + { + static QmlDesigner::Storage::Import import; + + return import; + } + + void operator()(const QmlDesigner::Storage::Synchronization::ImportedType &importedType) const + { + auto dict = dictonary(keyValue("name", importedType.name)); + + convertToString(string, dict); + } + + void operator()( + const QmlDesigner::Storage::Synchronization::QualifiedImportedType &qualifiedImportedType) const + { + auto dict = dictonary(keyValue("name", qualifiedImportedType.name), + keyValue("import", qualifiedImportedType.import)); + + convertToString(string, dict); + } + + String &string; + }; + + std::visit(Dispatcher{string}, typeName); +} + class EnumeratorDeclaration { public: @@ -325,6 +533,18 @@ public: && first.hasValue == second.hasValue; } + template<typename String> + friend void convertToString(String &string, const EnumeratorDeclaration &enumeratorDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", enumeratorDeclaration.name), + keyValue("value", enumeratorDeclaration.value), + keyValue("has value", enumeratorDeclaration.hasValue)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; long long value = 0; @@ -349,6 +569,18 @@ public: && first.enumeratorDeclarations == second.enumeratorDeclarations; } + template<typename String> + friend void convertToString(String &string, const EnumerationDeclaration &enumerationDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", enumerationDeclaration.name), + keyValue("enumerator declarations", + enumerationDeclaration.enumeratorDeclarations)); + + convertToString(string, dict); + } + public: TypeNameString name; EnumeratorDeclarations enumeratorDeclarations; @@ -368,6 +600,20 @@ public: , id{id} {} + template<typename String> + friend void convertToString(String &string, + const EnumerationDeclarationView &enumerationDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", enumerationDeclaration.name), + keyValue("enumerator declarations", + enumerationDeclaration.enumeratorDeclarations), + keyValue("id", enumerationDeclaration.id)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; ::Utils::SmallStringView enumeratorDeclarations; @@ -392,6 +638,18 @@ public: && first.traits == second.traits; } + template<typename String> + friend void convertToString(String &string, const ParameterDeclaration ¶meterDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", parameterDeclaration.name), + keyValue("type name", parameterDeclaration.typeName), + keyValue("traits", parameterDeclaration.traits)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; TypeNameString typeName; @@ -418,6 +676,17 @@ public: return first.name == second.name && first.parameters == second.parameters; } + template<typename String> + friend void convertToString(String &string, const SignalDeclaration &signalDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", signalDeclaration.name), + keyValue("parameters", signalDeclaration.parameters)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; ParameterDeclarations parameters; @@ -437,6 +706,18 @@ public: , id{id} {} + template<typename String> + friend void convertToString(String &string, const SignalDeclarationView &signalDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", signalDeclaration.name), + keyValue("signature", signalDeclaration.signature), + keyValue("id", signalDeclaration.id)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; ::Utils::SmallStringView signature; @@ -467,6 +748,18 @@ public: && first.parameters == second.parameters; } + template<typename String> + friend void convertToString(String &string, const FunctionDeclaration &functionDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", functionDeclaration.name), + keyValue("return type name", functionDeclaration.returnTypeName), + keyValue("parameters", functionDeclaration.parameters)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; TypeNameString returnTypeName; @@ -489,6 +782,19 @@ public: , id{id} {} + template<typename String> + friend void convertToString(String &string, const FunctionDeclarationView &functionDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", functionDeclaration.name), + keyValue("return type name", functionDeclaration.returnTypeName), + keyValue("signature", functionDeclaration.signature), + keyValue("id", functionDeclaration.id)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; ::Utils::SmallStringView returnTypeName; @@ -498,6 +804,19 @@ public: enum class PropertyKind { Property, Alias }; +template<typename String> +void convertToString(String &string, const PropertyKind &kind) +{ + switch (kind) { + case PropertyKind::Property: + convertToString(string, "Property"); + break; + case PropertyKind::Alias: + convertToString(string, "Alias"); + break; + } +} + class PropertyDeclaration { public: @@ -567,6 +886,24 @@ public: && first.traits == second.traits && first.kind == second.kind; } + template<typename String> + friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", propertyDeclaration.name), + keyValue("type name", propertyDeclaration.typeName), + keyValue("alias property name", propertyDeclaration.aliasPropertyName), + keyValue("alias property name tail", + propertyDeclaration.aliasPropertyNameTail), + keyValue("traits", propertyDeclaration.traits), + keyValue("type id", propertyDeclaration.typeId), + keyValue("property type id", propertyDeclaration.propertyTypeId), + keyValue("kind", propertyDeclaration.kind)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; ImportedTypeName typeName; @@ -597,6 +934,21 @@ public: , aliasId{aliasId} {} + template<typename String> + friend void convertToString(String &string, const PropertyDeclarationView &propertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", propertyDeclaration.name), + keyValue("traits", propertyDeclaration.traits), + keyValue("type id", propertyDeclaration.typeId), + keyValue("type name id", propertyDeclaration.typeNameId), + keyValue("id", propertyDeclaration.id), + keyValue("alias id", propertyDeclaration.aliasId)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; PropertyDeclarationTraits traits = {}; @@ -608,6 +960,22 @@ public: enum class ChangeLevel : char { Full, Minimal, ExcludeExportedTypes }; +template<typename String> +void convertToString(String &string, const ChangeLevel &changeLevel) +{ + switch (changeLevel) { + case ChangeLevel::Full: + convertToString(string, "Full"); + break; + case ChangeLevel::Minimal: + convertToString(string, "Minimal"); + break; + case ChangeLevel::ExcludeExportedTypes: + convertToString(string, "ExcludeExportedTypes"); + break; + } +} + class Type { public: @@ -717,6 +1085,27 @@ public: && first.sourceId == second.sourceId; } + template<typename String> + friend void convertToString(String &string, const Type &type) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type name", type.typeName), + keyValue("prototype", type.prototype), + keyValue("extension", type.extension), + keyValue("exported types", type.exportedTypes), + keyValue("property declarations", type.propertyDeclarations), + keyValue("function declarations", type.functionDeclarations), + keyValue("signal declarations", type.signalDeclarations), + keyValue("enumeration declarations", type.enumerationDeclarations), + keyValue("traits", type.traits), + keyValue("source id", type.sourceId), + keyValue("change level", type.changeLevel), + keyValue("default property name", type.defaultPropertyName)); + + convertToString(string, dict); + } + public: TypeNameString typeName; ::Utils::SmallString defaultPropertyName; @@ -747,6 +1136,20 @@ public: , moduleId{moduleId} {} + template<typename String> + friend void convertToString(String &string, const PropertyEditorQmlPath &propertyEditorQmlPath) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type name", propertyEditorQmlPath.typeName), + keyValue("type id", propertyEditorQmlPath.typeId), + keyValue("path id", propertyEditorQmlPath.pathId), + keyValue("directory id", propertyEditorQmlPath.directoryId), + keyValue("module id", propertyEditorQmlPath.moduleId)); + + convertToString(string, dict); + } + public: TypeNameString typeName; TypeId typeId; @@ -774,6 +1177,19 @@ public: && first.fileType == second.fileType; } + template<typename String> + friend void convertToString(String &string, const ProjectData &projectData) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("project source id", projectData.projectSourceId), + keyValue("source id", projectData.sourceId), + keyValue("module id", projectData.moduleId), + keyValue("file type", projectData.fileType)); + + convertToString(string, dict); + } + public: SourceId projectSourceId; SourceId sourceId; @@ -786,10 +1202,13 @@ using ProjectDatas = std::vector<ProjectData>; class TypeAnnotation { public: - TypeAnnotation(SourceId sourceId) + TypeAnnotation(SourceId sourceId, SourceId directorySourceId) : sourceId{sourceId} + , directorySourceId{directorySourceId} {} + TypeAnnotation(SourceId sourceId, + SourceId directorySourceId, Utils::SmallStringView typeName, ModuleId moduleId, Utils::SmallStringView iconPath, @@ -803,8 +1222,26 @@ public: , sourceId{sourceId} , moduleId{moduleId} , traits{traits} + , directorySourceId{directorySourceId} {} + template<typename String> + friend void convertToString(String &string, const TypeAnnotation &typeAnnotation) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type name", typeAnnotation.typeName), + keyValue("icon path", typeAnnotation.iconPath), + keyValue("item library json", typeAnnotation.itemLibraryJson), + keyValue("hints json", typeAnnotation.hintsJson), + keyValue("type id", typeAnnotation.typeId), + keyValue("source id", typeAnnotation.sourceId), + keyValue("module id", typeAnnotation.moduleId), + keyValue("traits", typeAnnotation.traits)); + + convertToString(string, dict); + } + public: TypeNameString typeName; Utils::PathString iconPath; @@ -814,6 +1251,7 @@ public: SourceId sourceId; ModuleId moduleId; TypeTraits traits; + SourceId directorySourceId; }; using TypeAnnotations = std::vector<TypeAnnotation>; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 62fcf310f6..761d6371ef 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -11,6 +11,9 @@ #include "qmltypesparserinterface.h" #include "sourcepath.h" #include "sourcepathcache.h" +#include "typeannotationreader.h" + +#include <tracing/qmldesignertracing.h> #include <sqlitedatabase.h> @@ -21,6 +24,26 @@ #include <functional> namespace QmlDesigner { +constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; +using NanotraceHR::keyValue; +using Tracer = ProjectStorageTracing::Category::TracerType; + +template<typename String> +void convertToString(String &string, const ProjectStorageUpdater::FileState &state) +{ + switch (state) { + case ProjectStorageUpdater::FileState::Changed: + convertToString(string, "Changed"); + break; + case ProjectStorageUpdater::FileState::NotChanged: + convertToString(string, "NotChanged"); + break; + case ProjectStorageUpdater::FileState::NotExists: + convertToString(string, "NotExists"); + break; + } +} + namespace { QStringList filterMultipleEntries(QStringList qmlTypes) @@ -110,10 +133,15 @@ SourceIds filterNotUpdatedSourceIds(SourceIds updatedSourceIds, SourceIds notUpd return filteredUpdatedSourceIds; } -void addSourceIds(SourceIds &sourceIds, const Storage::Synchronization::ProjectDatas &projectDatas) +void addSourceIds(SourceIds &sourceIds, + const Storage::Synchronization::ProjectDatas &projectDatas, + TracerLiteral message, + Tracer &tracer) { - for (const auto &projectData : projectDatas) + for (const auto &projectData : projectDatas) { + tracer.tick(message, keyValue("source id", projectData.sourceId)); sourceIds.push_back(projectData.sourceId); + } } Storage::Version convertVersion(LanguageUtils::ComponentVersion version) @@ -131,34 +159,71 @@ Storage::Synchronization::IsAutoVersion convertToIsAutoVersion(QmlDirParser::Imp void addDependencies(Storage::Imports &dependencies, SourceId sourceId, const QList<QmlDirParser::Import> &qmldirDependencies, - ProjectStorageInterface &projectStorage) + ProjectStorageInterface &projectStorage, + TracerLiteral message, + Tracer &tracer) { for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) { ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module} + "-cppnative"); - dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); + auto &import = dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); + tracer.tick(message, keyValue("import", import)); } } +void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &imports, + ModuleId moduleId, + ModuleId exportedModuleId, + Storage::Version version, + Storage::Synchronization::IsAutoVersion isAutoVersion, + std::string_view moduleName, + std::string_view exportedModuleName) +{ + NanotraceHR::Tracer tracer{"add module exported imports"_t, + category(), + keyValue("module id", moduleId), + keyValue("exported module id", exportedModuleId), + keyValue("version", version), + keyValue("is auto version", isAutoVersion), + keyValue("module name", moduleName), + keyValue("exported module name", exportedModuleName)}; + + imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion); +} + void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &imports, ModuleId moduleId, ModuleId cppModuleId, + std::string_view moduleName, + std::string_view cppModuleName, const QList<QmlDirParser::Import> &qmldirImports, ProjectStorageInterface &projectStorage) { - for (const QmlDirParser::Import &qmldirImport : qmldirImports) { - ModuleId exportedModuleId = projectStorage.moduleId(Utils::PathString{qmldirImport.module}); - imports.emplace_back(moduleId, - exportedModuleId, - convertVersion(qmldirImport.version), - convertToIsAutoVersion(qmldirImport.flags)); + NanotraceHR::Tracer tracer{"add module exported imports"_t, + category(), + keyValue("cpp module id", cppModuleId), + keyValue("module id", moduleId)}; - ModuleId exportedCppModuleId = projectStorage.moduleId( - Utils::PathString{qmldirImport.module} + "-cppnative"); - imports.emplace_back(cppModuleId, - exportedCppModuleId, - Storage::Version{}, - Storage::Synchronization::IsAutoVersion::No); + for (const QmlDirParser::Import &qmldirImport : qmldirImports) { + Utils::PathString exportedModuleName{qmldirImport.module}; + ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName); + addModuleExportedImport(imports, + moduleId, + exportedModuleId, + convertVersion(qmldirImport.version), + convertToIsAutoVersion(qmldirImport.flags), + moduleName, + exportedModuleName); + + exportedModuleName += "-cppnative"; + ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName); + addModuleExportedImport(imports, + cppModuleId, + exportedCppModuleId, + Storage::Version{}, + Storage::Synchronization::IsAutoVersion::No, + cppModuleName, + exportedModuleName); } } @@ -182,8 +247,14 @@ std::vector<IdPaths> createIdPaths(ProjectStorageUpdater::WatchedSourceIdsIds wa void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypesPaths, - const QString &propertyEditorResourcesPath) + const QString &propertyEditorResourcesPath, + const QStringList &typeAnnotationPaths) { + NanotraceHR::Tracer tracer{"update"_t, + category(), + keyValue("directories", directories), + keyValue("qml types paths", qmlTypesPaths)}; + Storage::Synchronization::SynchronizationPackage package; WatchedSourceIdsIds watchedSourceIds{Utils::span{directories}.size()}; NotUpdatedSourceIds notUpdatedSourceIds{Utils::span{directories}.size()}; @@ -191,6 +262,7 @@ void ProjectStorageUpdater::update(QStringList directories, updateDirectories(directories, package, notUpdatedSourceIds, watchedSourceIds); updateQmlTypes(qmlTypesPaths, package, notUpdatedSourceIds, watchedSourceIds); updatePropertyEditorPaths(propertyEditorResourcesPath, package, notUpdatedSourceIds); + updateTypeAnnotations(typeAnnotationPaths, package, notUpdatedSourceIds); package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds), std::move(notUpdatedSourceIds.sourceIds)); @@ -198,7 +270,11 @@ void ProjectStorageUpdater::update(QStringList directories, std::move(package.updatedFileStatusSourceIds), std::move(notUpdatedSourceIds.fileStatusSourceIds)); - m_projectStorage.synchronize(std::move(package)); + try { + m_projectStorage.synchronize(std::move(package)); + } catch (...) { + qWarning() << "Project storage could not been updated!"; + } m_pathWatcher.updateIdPaths(createIdPaths(watchedSourceIds, m_projectPartId)); } @@ -211,11 +287,16 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, if (qmlTypesPaths.empty()) return; + NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()}; + ModuleId moduleId = m_projectStorage.moduleId("QML-cppnative"); for (const QString &qmlTypesPath : qmlTypesPaths) { SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath}); watchedSourceIdsIds.qmltypesSourceIds.push_back(sourceId); + tracer.tick("append watched qml types source id"_t, + keyValue("source id", sourceId), + keyValue("qml types path", qmlTypesPath)); Storage::Synchronization::ProjectData projectData{sourceId, sourceId, @@ -228,7 +309,9 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, notUpdatedSourceIds); if (state == FileState::Changed) { + tracer.tick("append project data"_t, keyValue("project data", projectData)); package.projectDatas.push_back(std::move(projectData)); + tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId)); package.updatedProjectSourceIds.push_back(sourceId); } } @@ -246,13 +329,86 @@ ProjectStorageUpdater::FileState combineState(FileStates... fileStates) return ProjectStorageUpdater::FileState::NotExists; } + } // namespace +void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPath, + FileState qmldirState, + SourcePath qmldirSourcePath, + SourceId qmldirSourceId, + SourceId directorySourceId, + SourceContextId directoryId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, + Tracer &tracer) +{ + QmlDirParser parser; + if (qmldirState != FileState::NotExists) + parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath})); + + if (qmldirState != FileState::NotChanged) { + tracer.tick("append updated source id"_t, keyValue("module id", qmldirSourceId)); + package.updatedSourceIds.push_back(qmldirSourceId); + } + + Utils::PathString moduleName{parser.typeNamespace()}; + ModuleId moduleId = m_projectStorage.moduleId(moduleName); + Utils::PathString cppModuleName = moduleName + "-cppnative"; + ModuleId cppModuleId = m_projectStorage.moduleId(cppModuleName); + ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath); + + auto imports = filterMultipleEntries(parser.imports()); + + addModuleExportedImports(package.moduleExportedImports, + moduleId, + cppModuleId, + moduleName, + cppModuleName, + imports, + m_projectStorage); + tracer.tick("append updated module id"_t, keyValue("module id", moduleId)); + package.updatedModuleIds.push_back(moduleId); + + const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); + addSourceIds(package.updatedSourceIds, qmlProjectDatas, "append updated source id"_t, tracer); + addSourceIds(package.updatedFileStatusSourceIds, + qmlProjectDatas, + "append updated file status source id"_t, + tracer); + + auto qmlTypes = filterMultipleEntries(parser.typeInfos()); + + if (!qmlTypes.isEmpty()) { + parseTypeInfos(qmlTypes, + filterMultipleEntries(parser.dependencies()), + imports, + directorySourceId, + directoryPath, + cppModuleId, + package, + notUpdatedSourceIds, + watchedSourceIdsIds); + } + parseQmlComponents( + createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath), + directorySourceId, + directoryId, + package, + notUpdatedSourceIds, + watchedSourceIdsIds, + qmldirState); + tracer.tick("append updated project source id"_t, keyValue("module id", moduleId)); + package.updatedProjectSourceIds.push_back(directorySourceId); +} + void ProjectStorageUpdater::updateDirectories(const QStringList &directories, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) { + NanotraceHR::Tracer tracer{"update directories"_t, category()}; + for (const QString &directory : directories) updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds); } @@ -262,8 +418,10 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) { + NanotraceHR::Tracer tracer{"update directory"_t, category(), keyValue("directory", directoryPath)}; + SourcePath qmldirSourcePath{directoryPath + "/qmldir"}; - auto [directoryId, qmlDirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath); + auto [directoryId, qmldirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath); SourcePath directorySourcePath{directoryPath + "/."}; auto directorySourceId = m_pathCache.sourceId(directorySourcePath); @@ -271,62 +429,28 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa if (directoryState != FileState::NotExists) watchedSourceIdsIds.directorySourceIds.push_back(directorySourceId); - auto qmldirState = fileState(qmlDirSourceId, package, notUpdatedSourceIds); + auto qmldirState = fileState(qmldirSourceId, package, notUpdatedSourceIds); if (qmldirState != FileState::NotExists) - watchedSourceIdsIds.qmldirSourceIds.push_back(qmlDirSourceId); + watchedSourceIdsIds.qmldirSourceIds.push_back(qmldirSourceId); switch (combineState(directoryState, qmldirState)) { case FileState::Changed: { - QmlDirParser parser; - if (qmldirState != FileState::NotExists) - parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath})); - - if (qmldirState != FileState::NotChanged) - package.updatedSourceIds.push_back(qmlDirSourceId); - - Utils::PathString moduleName{parser.typeNamespace()}; - ModuleId moduleId = m_projectStorage.moduleId(moduleName); - ModuleId cppModuleId = m_projectStorage.moduleId(moduleName + "-cppnative"); - ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath); - - auto imports = filterMultipleEntries(parser.imports()); - - addModuleExportedImports(package.moduleExportedImports, - moduleId, - cppModuleId, - imports, - m_projectStorage); - package.updatedModuleIds.push_back(moduleId); - - const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); - addSourceIds(package.updatedSourceIds, qmlProjectDatas); - addSourceIds(package.updatedFileStatusSourceIds, qmlProjectDatas); - - auto qmlTypes = filterMultipleEntries(parser.typeInfos()); - - if (!qmlTypes.isEmpty()) { - parseTypeInfos(qmlTypes, - filterMultipleEntries(parser.dependencies()), - imports, - directorySourceId, - directoryPath, - cppModuleId, - package, - notUpdatedSourceIds, - watchedSourceIdsIds); - } - parseQmlComponents( - createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath), - directorySourceId, - directoryId, - package, - notUpdatedSourceIds, - watchedSourceIdsIds, - qmldirState); - package.updatedProjectSourceIds.push_back(directorySourceId); + tracer.tick("update directory changed"_t); + updateDirectoryChanged(directoryPath, + qmldirState, + qmldirSourcePath, + qmldirSourceId, + directorySourceId, + directoryId, + package, + notUpdatedSourceIds, + watchedSourceIdsIds, + tracer); break; } case FileState::NotChanged: { + tracer.tick("update directory not changed"_t); + parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), package, notUpdatedSourceIds, @@ -334,19 +458,32 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa break; } case FileState::NotExists: { + tracer.tick("update directory don't exits"_t); + package.updatedFileStatusSourceIds.push_back(directorySourceId); - package.updatedFileStatusSourceIds.push_back(qmlDirSourceId); + package.updatedFileStatusSourceIds.push_back(qmldirSourceId); package.updatedProjectSourceIds.push_back(directorySourceId); - package.updatedSourceIds.push_back(qmlDirSourceId); + package.updatedSourceIds.push_back(qmldirSourceId); auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) { + tracer.tick("append updated source id"_t, keyValue("source id", projectData.sourceId)); package.updatedSourceIds.push_back(projectData.sourceId); + tracer.tick("append updated file status source id"_t, + keyValue("source id", projectData.sourceId)); package.updatedFileStatusSourceIds.push_back(projectData.sourceId); } break; } } + + tracer.end(keyValue("qmldir source path", qmldirSourcePath), + keyValue("directory source path", directorySourcePath), + keyValue("directory id", directoryId), + keyValue("qmldir source id", qmldirSourceId), + keyValue("directory source source id", directorySourceId), + keyValue("qmldir state", qmldirState), + keyValue("directory state", directoryState)); } void ProjectStorageUpdater::updatePropertyEditorPaths( @@ -354,6 +491,10 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) { + NanotraceHR::Tracer tracer{"update property editor paths"_t, + category(), + keyValue("property editor resources path", propertyEditorResourcesPath)}; + if (propertyEditorResourcesPath.isEmpty()) return; @@ -373,12 +514,134 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( } } +namespace { + +template<typename SourceIds1, typename SourceIds2> +SmallSourceIds<16> mergedSourceIds(const SourceIds1 &sourceIds1, const SourceIds2 &sourceIds2) +{ + SmallSourceIds<16> mergedSourceIds; + + std::set_union(sourceIds1.begin(), + sourceIds1.end(), + sourceIds2.begin(), + sourceIds2.end(), + std::back_inserter(mergedSourceIds)); + + return mergedSourceIds; +} +} // namespace + +void ProjectStorageUpdater::updateTypeAnnotations(const QStringList &directoryPaths, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds) +{ + NanotraceHR::Tracer tracer("update type annotations"_t, category()); + + std::map<SourceId, SmallSourceIds<16>> updatedSourceIdsDictonary; + + for (SourceId directoryId : m_projectStorage.typeAnnotationDirectorySourceIds()) + updatedSourceIdsDictonary[directoryId] = {}; + + for (const auto &directoryPath : directoryPaths) + updateTypeAnnotations(directoryPath, package, notUpdatedSourceIds, updatedSourceIdsDictonary); + + updateTypeAnnotationDirectories(package, notUpdatedSourceIds, updatedSourceIdsDictonary); +} + void ProjectStorageUpdater::updateTypeAnnotations( - const QString & /*propertyEditorResourcesPath*/, - Storage::Synchronization::SynchronizationPackage & /*package*/, - NotUpdatedSourceIds & /*notUpdatedSourceIds*/) + const QString &rootDirectoryPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary) +{ + NanotraceHR::Tracer tracer("update type annotation directory"_t, + category(), + keyValue("path", rootDirectoryPath)); + + if (rootDirectoryPath.isEmpty()) + return; + + QDirIterator directoryIterator{rootDirectoryPath, + {"*.metainfo"}, + QDir::NoDotAndDotDot | QDir::Files, + QDirIterator::Subdirectories}; + + while (directoryIterator.hasNext()) { + auto fileInfo = directoryIterator.nextFileInfo(); + auto filePath = fileInfo.filePath(); + SourceId sourceId = m_pathCache.sourceId(SourcePath{filePath}); + + auto directoryPath = fileInfo.canonicalPath(); + + SourceId directorySourceId = m_pathCache.sourceId(SourcePath{directoryPath + "/."}); + + auto state = fileState(sourceId, package, notUpdatedSourceIds); + if (state == FileState::Changed) + updateTypeAnnotation(directoryPath, fileInfo.filePath(), sourceId, directorySourceId, package); + + if (state != FileState::NotChanged) + updatedSourceIdsDictonary[directorySourceId].push_back(sourceId); + } +} + +void ProjectStorageUpdater::updateTypeAnnotationDirectories( + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary) +{ + for (auto &[directorySourceId, updatedSourceIds] : updatedSourceIdsDictonary) { + auto directoryState = fileState(directorySourceId, package, notUpdatedSourceIds); + + if (directoryState != FileState::NotChanged) { + auto existingTypeAnnotationSourceIds = m_projectStorage.typeAnnotationSourceIds( + directorySourceId); + + std::sort(updatedSourceIds.begin(), updatedSourceIds.end()); + + auto changedSourceIds = mergedSourceIds(existingTypeAnnotationSourceIds, updatedSourceIds); + package.updatedTypeAnnotationSourceIds.insert(package.updatedTypeAnnotationSourceIds.end(), + changedSourceIds.begin(), + changedSourceIds.end()); + } else { + package.updatedTypeAnnotationSourceIds.insert(package.updatedTypeAnnotationSourceIds.end(), + updatedSourceIds.begin(), + updatedSourceIds.end()); + } + } +} + +namespace { +QString contentFromFile(const QString &path) +{ + QFile file{path}; + if (file.open(QIODevice::ReadOnly)) + return QString::fromUtf8(file.readAll()); + + return {}; +} +} // namespace + +void ProjectStorageUpdater::updateTypeAnnotation(const QString &directoryPath, + const QString &filePath, + SourceId sourceId, + SourceId directorySourceId, + Storage::Synchronization::SynchronizationPackage &package) { - // const auto typeAnnotations = dir.entryInfoList({"*.metainfo"}, QDir::Files); + NanotraceHR::Tracer tracer{"update type annotation path"_t, + category(), + keyValue("path", filePath), + keyValue("directory path", directoryPath)}; + + Storage::TypeAnnotationReader reader{m_projectStorage}; + + auto annotations = reader.parseTypeAnnotation(contentFromFile(filePath), + directoryPath, + sourceId, + directorySourceId); + auto &typeAnnotations = package.typeAnnotations; + package.typeAnnotations.insert(typeAnnotations.end(), + std::make_move_iterator(annotations.begin()), + std::make_move_iterator(annotations.end())); } void ProjectStorageUpdater::updatePropertyEditorPath( @@ -386,6 +649,13 @@ void ProjectStorageUpdater::updatePropertyEditorPath( Storage::Synchronization::SynchronizationPackage &package, SourceId directorySourceId) { + NanotraceHR::Tracer tracer{"update property editor path"_t, + category(), + keyValue("directory path", directoryPath), + keyValue("directory source id", directorySourceId)}; + + tracer.tick("append updated property editor qml path source id"_t, + keyValue("source id", directorySourceId)); package.updatedPropertyEditorQmlPathSourceIds.push_back(directorySourceId); auto dir = QDir{directoryPath}; const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files); @@ -398,6 +668,11 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath( Storage::Synchronization::SynchronizationPackage &package, SourceId directorySourceId) { + NanotraceHR::Tracer tracer{"update property editor file path"_t, + category(), + keyValue("directory path", path), + keyValue("directory source id", directorySourceId)}; + QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"}; auto match = regex.match(path); QString oldModuleName; @@ -410,7 +685,12 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath( } Storage::TypeNameString typeName{match.capturedView(2)}; SourceId pathId = m_pathCache.sourceId(SourcePath{path}); - package.propertyEditorQmlPaths.emplace_back(moduleId, typeName, pathId, directorySourceId); + const auto &paths = package.propertyEditorQmlPaths.emplace_back(moduleId, + typeName, + pathId, + directorySourceId); + tracer.tick("append property editor qml paths"_t, + keyValue("property editor qml paths", paths)); } } @@ -447,6 +727,10 @@ bool contains(const Container &container, Id id) void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &changedIdPaths) { + NanotraceHR::Tracer tracer{"paths with ids changed"_t, + category(), + keyValue("id paths", changedIdPaths)}; + m_changedIdPaths.insert(m_changedIdPaths.end(), changedIdPaths.begin(), changedIdPaths.end()); Storage::Synchronization::SynchronizationPackage package; @@ -539,21 +823,35 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIds) { + NanotraceHR::Tracer tracer{"parse type infos"_t, + category(), + keyValue("directory source id", directorySourceId), + keyValue("directory path", directoryPath), + keyValue("module id", moduleId)}; + for (const QString &typeInfo : typeInfos) { + NanotraceHR::Tracer tracer{"parse type info"_t, category(), keyValue("type info", typeInfo)}; + Utils::PathString qmltypesPath = Utils::PathString::join( {directoryPath, "/", Utils::SmallString{typeInfo}}); SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath}); + tracer.tick("append qmltypes source id"_t, keyValue("source id", sourceId)); watchedSourceIds.qmltypesSourceIds.push_back(sourceId); addDependencies(package.moduleDependencies, sourceId, joinImports(qmldirDependencies, qmldirImports), - m_projectStorage); + m_projectStorage, + "append module dependency"_t, + tracer); + + tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId)); package.updatedModuleDependencySourceIds.push_back(sourceId); auto projectData = package.projectDatas.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); + tracer.tick("append project data"_t, keyValue("source id", sourceId)); parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); } @@ -564,6 +862,8 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIds) { + NanotraceHR::Tracer tracer{"parse project datas"_t, category()}; + for (const Storage::Synchronization::ProjectData &projectData : projectDatas) { switch (projectData.fileType) { case Storage::Synchronization::FileType::QmlTypes: { @@ -577,6 +877,7 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId); parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); + break; } } } @@ -587,9 +888,14 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) -> FileState { + NanotraceHR::Tracer tracer{"parse type info"_t, + category(), + keyValue("qmltypes path", qmltypesPath)}; + auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds); switch (state) { case FileState::Changed: { + tracer.tick("append updated source ids"_t, keyValue("source id", projectData.sourceId)); package.updatedSourceIds.push_back(projectData.sourceId); const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath}); @@ -597,14 +903,16 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec break; } case FileState::NotChanged: { + tracer.tick("append not updated source ids"_t, keyValue("source id", projectData.sourceId)); notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId); break; } case FileState::NotExists: throw CannotParseQmlTypesFile{}; - break; } + tracer.end(keyValue("state", state)); + return state; } @@ -617,6 +925,14 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil WatchedSourceIdsIds &watchedSourceIds, FileState qmldirState) { + NanotraceHR::Tracer tracer{"parse qml component"_t, + category(), + keyValue("relative file path", relativeFilePath), + keyValue("directory path", directoryPath), + keyValue("exported types", exportedTypes), + keyValue("directory source id", directorySourceId), + keyValue("qmldir state", qmldirState)}; + if (std::find(relativeFilePath.begin(), relativeFilePath.end(), '+') != relativeFilePath.end()) return; @@ -626,16 +942,18 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil Storage::Synchronization::Type type; auto state = fileState(sourceId, package, notUpdatedSourceIds); + tracer.tick("append watched qml source id"_t, keyValue("source id", sourceId)); watchedSourceIds.qmlSourceIds.push_back(sourceId); switch (state) { case FileState::NotChanged: if (qmldirState == FileState::NotExists) { + tracer.tick("append not updated source id"_t, keyValue("source id", sourceId)); notUpdatedSourceIds.sourceIds.emplace_back(sourceId); - package.projectDatas.emplace_back(directorySourceId, - sourceId, - ModuleId{}, - Storage::Synchronization::FileType::QmlDocument); + + const auto &projectData = package.projectDatas.emplace_back( + directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); + tracer.tick("append project data"_t, keyValue("project data", projectData)); return; } @@ -649,11 +967,11 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil break; } - package.projectDatas.emplace_back(directorySourceId, - sourceId, - ModuleId{}, - Storage::Synchronization::FileType::QmlDocument); + const auto &projectData = package.projectDatas.emplace_back( + directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); + tracer.tick("append project data"_t, keyValue("project data", projectData)); + tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); type.typeName = SourcePath{qmlFilePath}.name(); @@ -661,6 +979,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil type.sourceId = sourceId; type.exportedTypes = std::move(exportedTypes); + tracer.end(keyValue("type", type)); + package.types.push_back(std::move(type)); } @@ -668,10 +988,13 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) { + NanotraceHR::Tracer tracer{"parse qml component"_t, category(), keyValue("source id", sourceId)}; + auto state = fileState(sourceId, package, notUpdatedSourceIds); if (state == FileState::NotChanged) return; + tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); if (state == FileState::NotExists) @@ -687,6 +1010,8 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, type.sourceId = sourceId; type.changeLevel = Storage::Synchronization::ChangeLevel::ExcludeExportedTypes; + tracer.end(keyValue("type", type)); + package.types.push_back(std::move(type)); } @@ -733,6 +1058,12 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState) { + NanotraceHR::Tracer tracer{"parse qml components"_t, + category(), + keyValue("directory source id", directorySourceId), + keyValue("directory id", directoryId), + keyValue("qmldir state", qmldirState)}; + std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) { return first.fileName < second.fileName; }); @@ -760,22 +1091,37 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) const { + NanotraceHR::Tracer tracer{"update property editor paths"_t, + category(), + keyValue("source id", sourceId)}; + auto currentFileStatus = m_fileStatusCache.find(sourceId); if (!currentFileStatus.isValid()) { + tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId)); package.updatedFileStatusSourceIds.push_back(sourceId); + + tracer.end(keyValue("state", FileState::NotExists)); return FileState::NotExists; } auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId); if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus) { + tracer.tick("append file status"_t, keyValue("file status", sourceId)); package.fileStatuses.push_back(currentFileStatus); + + tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId)); package.updatedFileStatusSourceIds.push_back(sourceId); + + tracer.end(keyValue("state", FileState::Changed)); return FileState::Changed; } + tracer.tick("append not updated file status source id"_t, keyValue("source id", sourceId)); notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId); + + tracer.end(keyValue("state", FileState::NotChanged)); return FileState::NotChanged; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 187a2219d0..640969fe99 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -11,10 +11,15 @@ #include "projectstoragetypes.h" #include "sourcepath.h" +#include <modelfwd.h> + +#include <tracing/qmldesignertracing.h> + #include <qmljs/parser/qmldirparser_p.h> #include <QStringList> +#include <map> #include <vector> namespace Sqlite { @@ -27,7 +32,6 @@ class ProjectStorageInterface; template<typename ProjectStorage, typename Mutex> class SourcePathCache; class FileStatusCache; -template<typename Database> class ProjectStorage; class QmlDocumentParserInterface; class QmlTypesParserInterface; @@ -35,10 +39,10 @@ class QmlTypesParserInterface; class ProjectStorageUpdater final : public ProjectStoragePathWatcherNotifierInterface { public: - using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>; + using PathCache = SourcePathCache<ProjectStorage, NonLockingMutex>; ProjectStorageUpdater(FileSystemInterface &fileSystem, - ProjectStorageInterface &projectStorage, + ProjectStorageType &projectStorage, FileStatusCache &fileStatusCache, PathCache &pathCache, QmlDocumentParserInterface &qmlDocumentParser, @@ -57,7 +61,8 @@ public: void update(QStringList directories, QStringList qmlTypesPaths, - const QString &propertyEditorResourcesPath); + const QString &propertyEditorResourcesPath, + const QStringList &typeAnnotationPaths); void pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) override; void pathsChanged(const SourceIds &filePathIds) override; @@ -141,13 +146,35 @@ private: Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); + void updateDirectoryChanged(std::string_view directoryPath, + FileState qmldirState, + SourcePath qmldirSourcePath, + SourceId qmldirSourceId, + SourceId directorySourceId, + SourceContextId directoryId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, + ProjectStorageTracing::Category::TracerType &tracer); void updatePropertyEditorPaths(const QString &propertyEditorResourcesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); - void updateTypeAnnotations(const QString &propertyEditorResourcesPath, + void updateTypeAnnotations(const QString &directoryPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary); + void updateTypeAnnotationDirectories(Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary); + void updateTypeAnnotations(const QStringList &directoryPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); + void updateTypeAnnotation(const QString &directoryPath, + const QString &filePath, + SourceId sourceId, + SourceId directorySourceId, + Storage::Synchronization::SynchronizationPackage &package); void updatePropertyEditorPath(const QString &path, Storage::Synchronization::SynchronizationPackage &package, SourceId directorySourceId); @@ -197,7 +224,7 @@ private: private: std::vector<IdPaths> m_changedIdPaths; FileSystemInterface &m_fileSystem; - ProjectStorageInterface &m_projectStorage; + ProjectStorageType &m_projectStorage; FileStatusCache &m_fileStatusCache; PathCache &m_pathCache; QmlDocumentParserInterface &m_qmlDocumentParser; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index f9eb8080f7..27efa8d530 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -10,6 +10,8 @@ #include <sqlitedatabase.h> +#include <tracing/qmldesignertracing.h> + #ifdef QDS_BUILD_QMLPARSER #include <private/qqmldomtop_p.h> #endif @@ -21,6 +23,10 @@ namespace QmlDesigner { #ifdef QDS_BUILD_QMLPARSER +constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; +using NanotraceHR::keyValue; +using Tracer = ProjectStorageTracing::Category::TracerType; + namespace QmlDom = QQmlJS::Dom; namespace Synchronization = Storage::Synchronization; @@ -84,6 +90,11 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) { + NanotraceHR::Tracer tracer{"create qualified imports"_t, + category(), + keyValue("sourceId", sourceId), + keyValue("directoryPath", directoryPath)}; + QualifiedImports qualifiedImports; for (const QmlDom::Import &qmlImport : qmlImports) { @@ -92,6 +103,8 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports, createImport(qmlImport, sourceId, directoryPath, storage)); } + tracer.end(keyValue("qualified imports", qualifiedImports)); + return qualifiedImports; } @@ -280,6 +293,11 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon SourceId sourceId, Utils::SmallStringView directoryPath) { + NanotraceHR::Tracer tracer{"qml document parser parse"_t, + category(), + keyValue("sourceId", sourceId), + keyValue("directoryPath", directoryPath)}; + Storage::Synchronization::Type type; using Option = QmlDom::DomEnvironment::Option; @@ -335,7 +353,7 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon m_storage); type.prototype = createImportedTypeName(qmlObject.name(), qualifiedImports); - + type.defaultPropertyName = qmlObject.localDefaultPropertyName(); addImports(imports, qmlFile->imports(), sourceId, directoryPath, m_storage); addPropertyDeclarations(type, qmlObject, qualifiedImports, file); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h index b8ab4ec4b1..1b494a2f69 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h @@ -15,7 +15,7 @@ class SourcePathCache; class QmlDocumentParser final : public QmlDocumentParserInterface { public: - using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>; + using ProjectStorage = QmlDesigner::ProjectStorage; using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>; #ifdef QDS_BUILD_QMLPARSER diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 3768535299..104338e514 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -5,8 +5,12 @@ #include "projectstorage.h" +#include <tracing/qmldesignertracing.h> + #include <sqlitedatabase.h> +#include <utils/span.h> + #ifdef QDS_BUILD_QMLPARSER #include <private/qqmldomtop_p.h> #include <private/qqmljstypedescriptionreader_p.h> @@ -21,6 +25,10 @@ namespace QmlDesigner { #ifdef QDS_BUILD_QMLPARSER +constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; +using NanotraceHR::keyValue; +using Tracer = ProjectStorageTracing::Category::TracerType; + namespace QmlDom = QQmlJS::Dom; namespace { @@ -31,6 +39,8 @@ using Storage::TypeNameString; ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQmlJSExportedScope> &objects) { + NanotraceHR::Tracer tracer{"parse qmltypes file"_t, category()}; + ComponentWithoutNamespaces componentWithoutNamespaces; for (const auto &object : objects) { @@ -46,13 +56,15 @@ ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQml name); } + tracer.end(keyValue("components without namespace", componentWithoutNamespaces)); + return componentWithoutNamespaces; } -void appendImports(Storage::Imports &imports, - const QString &dependency, - SourceId sourceId, - QmlTypesParser::ProjectStorage &storage) +const Storage::Import &appendImports(Storage::Imports &imports, + const QString &dependency, + SourceId sourceId, + QmlTypesParser::ProjectStorage &storage) { auto spaceFound = std::find_if(dependency.begin(), dependency.end(), [](QChar c) { return c.isSpace(); @@ -62,7 +74,7 @@ void appendImports(Storage::Imports &imports, moduleName.append("-cppnative"); ModuleId cppModuleId = storage.moduleId(moduleName); - imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); + return imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); } void addImports(Storage::Imports &imports, @@ -71,13 +83,25 @@ void addImports(Storage::Imports &imports, QmlTypesParser::ProjectStorage &storage, ModuleId cppModuleId) { - for (const QString &dependency : dependencies) - appendImports(imports, dependency, sourceId, storage); + NanotraceHR::Tracer tracer{ + "add imports"_t, + category(), + keyValue("source id", sourceId), + keyValue("module id", cppModuleId), + }; + + for (const QString &dependency : dependencies) { + const auto &import = appendImports(imports, dependency, sourceId, storage); + tracer.tick("append import"_t, keyValue("import", import), keyValue("dependency", dependency)); + } - imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); + const auto &import = imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); + tracer.tick("append import"_t, keyValue("import", import)); - if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) - imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); + if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) { + const auto &import = imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); + tracer.tick("append import"_t, keyValue("import", import)); + } } Storage::TypeTraits createAccessTypeTraits(QQmlJSScope::AccessSemantics accessSematics) @@ -412,6 +436,11 @@ void addType(Storage::Synchronization::Types &types, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespace) { + NanotraceHR::Tracer tracer{"add type"_t, + category(), + keyValue("source id", sourceId), + keyValue("module id", cppModuleId)}; + const auto &component = *exportScope.scope; auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals( @@ -421,7 +450,7 @@ void addType(Storage::Synchronization::Types &types, auto exports = exportScope.exports; auto enumerationTypes = addEnumerationTypes(types, typeName, sourceId, cppModuleId, enumerations); - types.emplace_back( + const auto &type = types.emplace_back( Utils::SmallStringView{typeName}, Storage::Synchronization::ImportedType{TypeNameString{component.baseTypeName()}}, Storage::Synchronization::ImportedType{TypeNameString{component.extensionTypeName()}}, @@ -431,7 +460,37 @@ void addType(Storage::Synchronization::Types &types, createProperties(component.ownProperties(), enumerationTypes, componentNameWithoutNamespace), std::move(functionsDeclarations), std::move(signalDeclarations), - createEnumeration(enumerations)); + createEnumeration(enumerations), + Storage::Synchronization::ChangeLevel::Full, + Utils::SmallString{component.ownDefaultPropertyName()}); + tracer.end(keyValue("type", type)); +} + +using namespace Qt::StringLiterals; + +constexpr auto skipLists = std::make_tuple( + std::pair{"QtQuick.Templates-cppnative"sv, std::array{"QQuickItem"_L1}}); + +Utils::span<const QLatin1StringView> getSkipList(std::string_view moduleName) +{ + static constexpr Utils::span<const QLatin1StringView> emptySkipList; + auto currentSkipList = emptySkipList; + + std::apply( + [&](const auto &entry) { + if (entry.first == moduleName) + currentSkipList = entry.second; + }, + skipLists); + + return currentSkipList; +} + +bool skipType(const QQmlJSExportedScope &object, Utils::span<const QLatin1StringView> skipList) +{ + return std::any_of(skipList.begin(), skipList.end(), [&](const QLatin1StringView skip) { + return object.scope->internalName() == skip; + }); } void addTypes(Storage::Synchronization::Types &types, @@ -440,15 +499,22 @@ void addTypes(Storage::Synchronization::Types &types, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespaces) { + NanotraceHR::Tracer tracer{"add types"_t, category()}; types.reserve(Utils::usize(objects) + types.size()); - for (const auto &object : objects) + const auto skipList = getSkipList(storage.moduleName(projectData.moduleId)); + + for (const auto &object : objects) { + if (skipType(object, skipList)) + continue; + addType(types, projectData.sourceId, projectData.moduleId, object, storage, componentNameWithoutNamespaces); + } } } // namespace @@ -458,6 +524,8 @@ void QmlTypesParser::parse(const QString &sourceContent, Storage::Synchronization::Types &types, const Storage::Synchronization::ProjectData &projectData) { + NanotraceHR::Tracer tracer{"qmltypes parser parse"_t, category()}; + QQmlJSTypeDescriptionReader reader({}, sourceContent); QList<QQmlJSExportedScope> components; QStringList dependencies; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 7c41925f30..4a6427501b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -4,6 +4,7 @@ #pragma once #include "nonlockingmutex.h" +#include "projectstoragefwd.h" #include "qmltypesparserinterface.h" namespace Sqlite { @@ -12,17 +13,13 @@ class Database; namespace QmlDesigner { -template<typename Database> -class ProjectStorage; - template<typename ProjectStorage, typename Mutex> class SourcePathCache; class QmlTypesParser final : public QmlTypesParserInterface { public: - using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>; - + using ProjectStorage = QmlDesigner::ProjectStorage; #ifdef QDS_BUILD_QMLPARSER QmlTypesParser(ProjectStorage &storage) : m_storage{storage} diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h index 837e58d48a..b655c5cc34 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h @@ -117,6 +117,12 @@ public: std::ptrdiff_t slashIndex() const { return m_slashIndex; } + template<typename String> + friend void convertToString(String &string, const SourcePath &path) + { + convertToString(string, path.toStringView()); + } + private: std::ptrdiff_t m_slashIndex = -1; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h index 5feaf30d00..1ef8ba7f21 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h @@ -125,4 +125,6 @@ public: SourceContextId sourceContextId; }; +using SourceNameAndSourceContextIds = std::vector<SourceNameAndSourceContextId>; + } // namespace QmlDesigner::Cache diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp index b829e9db36..67a63542bc 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp @@ -27,11 +27,11 @@ constexpr auto propertyElementName = "Property"_L1; constexpr auto extraFileElementName = "ExtraFile"_L1; } // namespace -Synchronization::TypeAnnotations TypeAnnotationReader::parseTypeAnnotation(const QString &content, - const QString &directoryPath, - SourceId sourceId) +Synchronization::TypeAnnotations TypeAnnotationReader::parseTypeAnnotation( + const QString &content, const QString &directoryPath, SourceId sourceId, SourceId directorySourceId) { m_sourceId = sourceId; + m_directorySourceId = directorySourceId; m_directoryPath = directoryPath; m_parserState = ParsingDocument; if (!SimpleAbstractStreamReader::readFromSource(content)) { @@ -178,7 +178,7 @@ TypeAnnotationReader::ParserSate TypeAnnotationReader::readDocument(const QStrin TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(const QString &name) { if (name == typeElementName) { - m_typeAnnotations.emplace_back(m_sourceId); + m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId); m_itemLibraryEntries = json::array(); return ParsingType; } else { @@ -277,7 +277,7 @@ void TypeAnnotationReader::readItemLibraryEntryProperty(QStringView name, const } else if (name == "category"_L1) { m_itemLibraryEntries.back()["category"] = value; } else if (name == "libraryIcon"_L1) { - m_itemLibraryEntries.back()["iconPath"] = value; + m_itemLibraryEntries.back()["iconPath"] = absoluteFilePathForDocument(variant.toString()); } else if (name == "version"_L1) { // setVersion(value.toString()); } else if (name == "requiredImport"_L1) { @@ -427,8 +427,8 @@ void TypeAnnotationReader::setVersion(const QString &versionNumber) int minor = 0; if (!versionNumber.isEmpty()) { - int val; - bool ok; + int val = -1; + bool ok = false; if (versionNumber.contains('.'_L1)) { val = versionNumber.split('.'_L1).constFirst().toInt(&ok); major = ok ? val : major; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h index 9332d5bed9..a320493ee2 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h @@ -49,7 +49,8 @@ public: Synchronization::TypeAnnotations parseTypeAnnotation(const QString &content, const QString &directoryPath, - SourceId sourceId); + SourceId sourceId, + SourceId directorySourceId); QStringList errors(); @@ -124,6 +125,7 @@ private: json m_itemLibraryEntries; Property m_currentProperty; SourceId m_sourceId; + SourceId m_directorySourceId; }; } // namespace QmlDesigner::Storage diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index d49c6156a6..b5798b713d 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -3,40 +3,48 @@ #include "qmldesignertracing.h" +#include <sqlitebasestatement.h> + namespace QmlDesigner { + +using namespace NanotraceHR::Literals; + namespace Tracing { namespace { using TraceFile = NanotraceHR::TraceFile<tracingStatus()>; -TraceFile &traceFile() +auto &traceFile() { - static TraceFile traceFile{"qml_designer.json"}; - return traceFile; + if constexpr (std::is_same_v<Sqlite::TraceFile, TraceFile>) { + return Sqlite::traceFile(); + } else { + static TraceFile traceFile{"tracing.json"}; + return traceFile; + } } } // namespace EventQueue &eventQueue() { - thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewTraceEvent, 10000, tracingStatus()> - stringViewEventQueueData(traceFile()); + thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewTraceEvent, tracingStatus()> + stringViewEventQueue(traceFile()); - return stringViewEventQueueData; + return stringViewEventQueue; } EventQueueWithStringArguments &eventQueueWithStringArguments() { - thread_local NanotraceHR:: - EventQueueData<NanotraceHR::StringViewWithStringArgumentsTraceEvent, 1000, tracingStatus()> - stringViewWithStringArgumentsEventQueueData(traceFile()); + thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewWithStringArgumentsTraceEvent, tracingStatus()> + stringViewWithStringArgumentsEventQueue(traceFile()); - return stringViewWithStringArgumentsEventQueueData; + return stringViewWithStringArgumentsEventQueue; } StringEventQueue &stringEventQueue() { - thread_local NanotraceHR::EventQueueData<NanotraceHR::StringTraceEvent, 1000, tracingStatus()> eventQueue( + thread_local NanotraceHR::EventQueue<NanotraceHR::StringTraceEvent, tracingStatus()> eventQueue( traceFile()); return eventQueue; @@ -46,7 +54,6 @@ StringEventQueue &stringEventQueue() namespace ModelTracing { namespace { -using namespace NanotraceHR::Literals; thread_local Category category_{"model"_t, Tracing::stringEventQueue(), category}; @@ -58,4 +65,27 @@ Category &category() } } // namespace ModelTracing + +namespace ProjectStorageTracing { + +Category &projectStorageCategory() +{ + thread_local Category category{"project storage"_t, + Tracing::eventQueueWithStringArguments(), + projectStorageCategory}; + + return category; +} + +Category &projectStorageUpdaterCategory() +{ + thread_local Category category{"project storage updater"_t, + Tracing::eventQueueWithStringArguments(), + projectStorageCategory}; + + return category; +} + +} // namespace ProjectStorageTracing + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 31058260d6..3a33834c70 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -44,4 +44,22 @@ using AsynchronousToken = Category::AsynchronousTokenType; [[gnu::pure]] QMLDESIGNERCORE_EXPORT Category &category(); } // namespace ModelTracing + +namespace ProjectStorageTracing { +constexpr NanotraceHR::Tracing projectStorageTracingStatus() +{ +#ifdef ENABLE_PROJECT_STORAGE_TRACING + return NanotraceHR::Tracing::IsEnabled; +#else + return NanotraceHR::Tracing::IsDisabled; +#endif +} + +using Category = NanotraceHR::StringViewWithStringArgumentsCategory<projectStorageTracingStatus()>; + +[[gnu::pure]] Category &projectStorageCategory(); + +[[gnu::pure]] Category &projectStorageUpdaterCategory(); + +} // namespace ProjectStorageTracing } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index fcaac762ba..92d80680a9 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -130,7 +130,7 @@ static void openComponentSourcePropertyOfLoader(const ModelNode &modelNode) } Core::EditorManager::openEditor(FilePath::fromString( - componentModelNode.metaInfo().componentFileName()), + ModelUtils::componentFilePath(componentModelNode)), Utils::Id(), Core::EditorManager::DoNotMakeVisible); } @@ -230,7 +230,9 @@ void DocumentManager::setCurrentDesignDocument(Core::IEditor *editor) auto found = m_designDocuments.find(editor); if (found == m_designDocuments.end()) { auto &inserted = m_designDocuments[editor] = std::make_unique<DesignDocument>( - m_projectManager.projectStorageDependencies(), m_externalDependencies); + editor->document()->filePath().toString(), + m_projectManager.projectStorageDependencies(), + m_externalDependencies); m_currentDesignDocument = inserted.get(); m_currentDesignDocument->setEditor(editor); } else { @@ -266,6 +268,11 @@ void DocumentManager::resetPossibleImports() } } +const GeneratedComponentUtils &DocumentManager::generatedComponentUtils() const +{ + return m_generatedComponentUtils; +} + bool DocumentManager::goIntoComponent(const ModelNode &modelNode) { QImage image = QmlDesignerPlugin::instance()->viewManager().takeFormEditorScreenshot(); @@ -537,6 +544,13 @@ Utils::FilePath DocumentManager::currentResourcePath() if (contentFilePath.exists()) return contentFilePath; + const auto project = ProjectManager::startupProject(); + const QString baseName = project->rootProjectDirectory().baseName() + "Content"; + + contentFilePath = resourcePath.pathAppended(baseName); + if (contentFilePath.exists()) + return contentFilePath; + return resourcePath; } diff --git a/src/plugins/qmldesigner/documentmanager.h b/src/plugins/qmldesigner/documentmanager.h index 090630e5fe..6447694339 100644 --- a/src/plugins/qmldesigner/documentmanager.h +++ b/src/plugins/qmldesigner/documentmanager.h @@ -5,6 +5,8 @@ #include "qmldesigner_global.h" +#include <generatedcomponentutils.h> + #include <QObject> #include <QList> #include <QLoggingCategory> @@ -31,6 +33,7 @@ public: ExternalDependenciesInterface &externalDependencies) : m_projectManager{projectManager} , m_externalDependencies{externalDependencies} + , m_generatedComponentUtils(externalDependencies) {} void setCurrentDesignDocument(Core::IEditor *editor); @@ -41,6 +44,8 @@ public: void resetPossibleImports(); + const GeneratedComponentUtils &generatedComponentUtils() const; + static bool goIntoComponent(const ModelNode &modelNode); static void goIntoComponent(const QString &fileName); @@ -64,6 +69,7 @@ private: QPointer<DesignDocument> m_currentDesignDocument; QmlDesignerProjectManager &m_projectManager; ExternalDependenciesInterface &m_externalDependencies; + GeneratedComponentUtils m_generatedComponentUtils; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/documentwarningwidget.cpp b/src/plugins/qmldesigner/documentwarningwidget.cpp index 9fb2b87635..e1b8346a66 100644 --- a/src/plugins/qmldesigner/documentwarningwidget.cpp +++ b/src/plugins/qmldesigner/documentwarningwidget.cpp @@ -39,8 +39,7 @@ DocumentWarningWidget::DocumentWarningWidget(QWidget *parent) m_messageLabel->setWordWrap(true); m_messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - m_ignoreWarningsCheckBox->setText(tr("Always ignore these warnings about features " - "not supported by Qt Quick Designer.")); + m_ignoreWarningsCheckBox->setText(tr("Turn off warnings about unsupported Qt Design Studio features.")); connect(m_navigateLabel, &QLabel::linkActivated, this, [this](const QString &link) { if (link == QLatin1String("goToCode")) { @@ -93,7 +92,7 @@ void DocumentWarningWidget::refreshContent() m_ignoreWarningsCheckBox->hide(); m_continueButton->setText(tr("OK")); } else { - m_headerLabel->setText(tr("This QML file contains features which are not supported by Qt Quick Designer at:")); + m_headerLabel->setText(tr("This QML file contains features which are not supported by Qt Design Studio at:")); { QSignalBlocker blocker(m_ignoreWarningsCheckBox); m_ignoreWarningsCheckBox->setChecked(!warningsEnabled()); diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index 6e8a2b6024..7819c6b49d 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -126,7 +126,7 @@ void PuppetEnvironmentBuilder::addRendering() const { m_environment.set("QML_BAD_GUI_RENDER_LOOP", "true"); m_environment.set("QML_PUPPET_MODE", "true"); - m_environment.set("QML_DISABLE_DISK_CACHE", "true"); + //m_environment.set("QML_DISABLE_DISK_CACHE", "true"); m_environment.set("QMLPUPPET_RENDER_EFFECTS", "true"); if (!m_environment.hasKey("QT_SCREEN_SCALE_FACTORS") && !m_environment.hasKey("QT_SCALE_FACTOR")) m_environment.set("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index db63d894fe..fb691097f7 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -78,15 +78,19 @@ inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; inline constexpr char EDIT3D_CAMERA_SPEED_CONFIG[] = "QmlDesigner.Editor3D.CameraSpeedConfig"; inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/"; -inline constexpr char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; +inline constexpr char COMPONENT_BUNDLES_TYPE[] = "ComponentBundles"; +inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "GeneratedComponents"; inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; -inline constexpr char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets"; +inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets"; +inline constexpr char QUICK_3D_COMPONENTS_FOLDER[] = "QtQuick3D"; inline constexpr char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon"; inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json"; inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options"; inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; -inline constexpr char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports"; -inline constexpr char DEFAULT_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects"; +inline constexpr char OLD_ASSET_IMPORT_FOLDER[] = "asset_imports"; +inline constexpr char OLD_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects"; +inline constexpr char OLD_EFFECTS_FOLDER[] = "Effects"; +inline constexpr char COMPOSED_EFFECTS_TYPE[] = "ComposedEffects"; inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__"; inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 7e28849fbb..9602bf050f 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -41,6 +41,7 @@ #include <QDirIterator> #include <QFileSystemWatcher> +#include <QLibraryInfo> #include <QQmlEngine> using namespace std::chrono; @@ -180,7 +181,7 @@ public: pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())} {} Sqlite::Database database; - ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()}; + ProjectStorage storage{database, database.isInitialized()}; PathCacheType pathCache{storage}; FileSystem fileSystem{pathCache}; FileStatusCache fileStatusCache{fileSystem}; @@ -281,7 +282,7 @@ AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache() } namespace { -[[maybe_unused]] ProjectStorage<Sqlite::Database> *dummyProjectStorage() +[[maybe_unused]] ProjectStorage *dummyProjectStorage() { return nullptr; } @@ -374,6 +375,10 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) { QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories}; + QString rootQmldirPath = path + "/qmldir"; + if (!skipPath(path) && QFileInfo::exists(rootQmldirPath)) + qmldirPaths.push_back(path); + while (dirIterator.hasNext()) { auto directoryPath = dirIterator.next(); @@ -401,17 +406,16 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) collectQmldirPaths(qmlPath(target).toString(), qmldirPaths); } -[[maybe_unused]] void qtQmldirPathsForLiteDesigner(::ProjectExplorer::Target *target, - QStringList &qmldirPaths) +[[maybe_unused]] void qtQmldirPathsForLiteDesigner(QStringList &qmldirPaths) { if constexpr (useProjectStorage()) { - auto qmlRootPath = qmlPath(target).toString(); + auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths); collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths); } } -QStringList directories(::ProjectExplorer::Target *target) +[[maybe_unused]] QStringList directories(::ProjectExplorer::Target *target) { if (!target) return {}; @@ -419,12 +423,8 @@ QStringList directories(::ProjectExplorer::Target *target) QStringList qmldirPaths; qmldirPaths.reserve(100); - if constexpr (isUsingQmlDesignerLite()) { - qtQmldirPathsForLiteDesigner(target, qmldirPaths); - } else { - qtQmldirPaths(target, qmldirPaths); - projectQmldirPaths(target, qmldirPaths); - } + qtQmldirPaths(target, qmldirPaths); + projectQmldirPaths(target, qmldirPaths); std::sort(qmldirPaths.begin(), qmldirPaths.end()); qmldirPaths.erase(std::unique(qmldirPaths.begin(), qmldirPaths.end()), qmldirPaths.end()); @@ -432,7 +432,20 @@ QStringList directories(::ProjectExplorer::Target *target) return qmldirPaths; } -QStringList qmlTypes(::ProjectExplorer::Target *target) +[[maybe_unused]] QStringList directoriesForLiteDesigner() +{ + QStringList qmldirPaths; + qmldirPaths.reserve(100); + + qtQmldirPathsForLiteDesigner(qmldirPaths); + + std::sort(qmldirPaths.begin(), qmldirPaths.end()); + qmldirPaths.erase(std::unique(qmldirPaths.begin(), qmldirPaths.end()), qmldirPaths.end()); + + return qmldirPaths; +} + +[[maybe_unused]] QStringList qmlTypes(::ProjectExplorer::Target *target) { if (!target) return {}; @@ -440,10 +453,26 @@ QStringList qmlTypes(::ProjectExplorer::Target *target) QStringList qmldirPaths; qmldirPaths.reserve(2); - const QString installDirectory = qmlPath(target).toString(); + const QString qmlRootPath = qmlPath(target).toString(); - qmldirPaths.append(installDirectory + "/builtins.qmltypes"); - qmldirPaths.append(installDirectory + "/jsroot.qmltypes"); + qmldirPaths.append(qmlRootPath + "/builtins.qmltypes"); + qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes"); + + qmldirPaths.append( + Core::ICore::resourcePath("qmldesigner/projectstorage/fake.qmltypes").toString()); + + return qmldirPaths; +} + +[[maybe_unused]] QStringList qmlTypesForLiteDesigner() +{ + QStringList qmldirPaths; + qmldirPaths.reserve(2); + + const auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); + + qmldirPaths.append(qmlRootPath + "/builtins.qmltypes"); + qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes"); qmldirPaths.append( Core::ICore::resourcePath("qmldesigner/projectstorage/fake.qmltypes").toString()); @@ -461,6 +490,11 @@ QString propertyEditorResourcesPath() return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); } +QString qtCreatorItemLibraryPath() +{ + return Core::ICore::resourcePath("qmldesigner/itemLibrary").toString(); +} + } // namespace void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project) @@ -594,9 +628,17 @@ void QmlDesignerProjectManager::update() if (!m_projectData || !m_projectData->projectStorageData) return; - m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), - qmlTypes(m_projectData->activeTarget), - propertyEditorResourcesPath()); + if constexpr (isUsingQmlDesignerLite()) { + m_projectData->projectStorageData->updater.update(directoriesForLiteDesigner(), + qmlTypesForLiteDesigner(), + propertyEditorResourcesPath(), + {qtCreatorItemLibraryPath()}); + } else { + m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), + qmlTypes(m_projectData->activeTarget), + propertyEditorResourcesPath(), + {qtCreatorItemLibraryPath()}); + } } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 3f3cdb7910..46cb42b60d 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -374,7 +374,6 @@ MetaInfo { name: "Keyframe" category: "none" version: "1.0" - requiredImport: "none" } } @@ -386,7 +385,6 @@ MetaInfo { name: "KeyframeGroup" category: "none" version: "1.0" - requiredImport: "none" } } diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 344e2700d9..17c75a0285 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -135,7 +135,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie m_useDefaultPuppetRadioButton = new QRadioButton(tr("Use fallback QML emulation layer")); m_useDefaultPuppetRadioButton->setToolTip( - tr("If you select this radio button, Qt Quick Designer always uses the " + tr("If you select this radio button, Qt Design Studio always uses the " "QML emulation layer (QML Puppet) located at the following path.")); m_useDefaultPuppetRadioButton->setChecked(true); @@ -167,7 +167,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie m_designerWarningsCheckBox = new QCheckBox( tr("Warn about unsupported features in .ui.qml files")); m_designerWarningsCheckBox->setToolTip( - tr("Warns about QML features that are not properly supported by the Qt Quick Designer.")); + tr("Warns about QML features that are not properly supported by the Qt Design Studio.")); m_designerWarningsUiQmlfiles = new QCheckBox( tr("Warn about using .qml files instead of .ui.qml files")); diff --git a/src/plugins/qmldesigner/utils/asset.cpp b/src/plugins/qmldesigner/utils/asset.cpp index 2984a4d890..ec1e4312e4 100644 --- a/src/plugins/qmldesigner/utils/asset.cpp +++ b/src/plugins/qmldesigner/utils/asset.cpp @@ -1,15 +1,19 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <QImageReader> - #include "asset.h" +#include "hdrimage.h" + +#include <QImageReader> +#include <QPixmap> + namespace QmlDesigner { Asset::Asset(const QString &filePath) : m_filePath(filePath) { + m_fileName = filePath.split('/').last(); const QStringList split = filePath.split('.'); if (split.size() > 1) m_suffix = "*." + split.last().toLower(); @@ -105,6 +109,19 @@ bool Asset::isSupported(const QString &path) return supportedSuffixes().contains(path); } +QPixmap Asset::pixmap(const QSize &size) const +{ + if (!isImage() && !isHdrFile()) + return {}; + + QPixmap icon = isHdrFile() ? HdrImage{m_filePath}.toPixmap() : QPixmap{m_filePath}; + + if (size.isValid()) + icon = icon.scaled(size, Qt::KeepAspectRatio); + + return icon; +} + Asset::Type Asset::type() const { return m_type; @@ -175,6 +192,11 @@ const QString Asset::id() const return m_filePath; } +const QString Asset::fileName() const +{ + return m_fileName; +} + bool Asset::isSupported() const { return m_type != Asset::Type::Unknown; diff --git a/src/plugins/qmldesigner/utils/asset.h b/src/plugins/qmldesigner/utils/asset.h index cb09f3a5ee..a5c5899f34 100644 --- a/src/plugins/qmldesigner/utils/asset.h +++ b/src/plugins/qmldesigner/utils/asset.h @@ -3,8 +3,11 @@ #pragma once +#include <QSize> #include <QString> +QT_FORWARD_DECLARE_CLASS(QPixmap) + namespace QmlDesigner { class Asset @@ -37,7 +40,9 @@ public: const QString suffix() const; const QString id() const; + const QString fileName() const; bool hasSuffix() const; + QPixmap pixmap(const QSize &size = {}) const; Type type() const; bool isImage() const; @@ -58,6 +63,7 @@ private: void resolveType(); QString m_filePath; + QString m_fileName; QString m_suffix; Type m_type = Unknown; }; diff --git a/src/plugins/qmldesigner/utils/imageutils.cpp b/src/plugins/qmldesigner/utils/imageutils.cpp index 8fa3131cd3..42df6184b9 100644 --- a/src/plugins/qmldesigner/utils/imageutils.cpp +++ b/src/plugins/qmldesigner/utils/imageutils.cpp @@ -11,7 +11,7 @@ namespace QmlDesigner { -QString ImageUtils::imageInfo(const QSize &dimensions, qint64 sizeInBytes) +QString ImageUtils::imageInfoString(const QSize &dimensions, qint64 sizeInBytes) { return QLatin1String("%1 x %2\n%3") .arg(QString::number(dimensions.width()), @@ -20,7 +20,7 @@ QString ImageUtils::imageInfo(const QSize &dimensions, qint64 sizeInBytes) sizeInBytes, 2, QLocale::DataSizeTraditionalFormat)); } -QString QmlDesigner::ImageUtils::imageInfo(const QString &path) +QPair<QSize, qint64> QmlDesigner::ImageUtils::imageInfo(const QString &path) { QFileInfo info(path); if (!info.exists()) @@ -52,7 +52,13 @@ QString QmlDesigner::ImageUtils::imageInfo(const QString &path) if (width <= 0 || height <= 0) return {}; - return imageInfo(QSize(width, height), info.size()); + return {QSize(width, height), info.size()}; +} + +QString ImageUtils::imageInfoString(const QString &path) +{ + QPair<QSize, qint64> info = imageInfo(path); + return imageInfoString(info.first, info.second); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/imageutils.h b/src/plugins/qmldesigner/utils/imageutils.h index a4036614a3..dae64423bb 100644 --- a/src/plugins/qmldesigner/utils/imageutils.h +++ b/src/plugins/qmldesigner/utils/imageutils.h @@ -12,8 +12,9 @@ class ImageUtils public: ImageUtils(); - static QString imageInfo(const QSize &dimensions, qint64 sizeInBytes); - static QString imageInfo(const QString &path); + static QPair<QSize, qint64> imageInfo(const QString &path); + static QString imageInfoString(const QString &path); + static QString imageInfoString(const QSize &dimensions, qint64 sizeInBytes); }; } // namespace QmlDesigner |