diff options
Diffstat (limited to 'src/plugins/qmldesigner')
62 files changed, 2117 insertions, 762 deletions
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index ce73af5161..0fe3867a7e 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -4,6 +4,7 @@ if (APPLE) endif() add_qtc_plugin(QmlDesigner + CONDITION TARGET Qt5::QuickWidgets DEPENDS QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem Qt5::QuickWidgets Qt5::CorePrivate Sqlite @@ -27,6 +28,7 @@ add_qtc_plugin(QmlDesigner documentmanager.cpp documentmanager.h documentwarningwidget.cpp documentwarningwidget.h generateresource.cpp generateresource.h + generatecmakelists.cpp generatecmakelists.h openuiqmlfiledialog.cpp openuiqmlfiledialog.h openuiqmlfiledialog.ui qmldesignerconstants.h qmldesignericons.h diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp index 872a004fcc..5b8a4934dd 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp @@ -46,7 +46,7 @@ AbstractEditorDialog::AbstractEditorDialog(QWidget *parent, const QString &title { setWindowFlag(Qt::Tool, true); setWindowTitle(defaultTitle()); - setModal(false); + setModal(true); setupJSEditor(); setupUIComponents(); @@ -111,11 +111,10 @@ void AbstractEditorDialog::setupJSEditor() { static BindingEditorFactory f; m_editor = qobject_cast<TextEditor::BaseTextEditor*>(f.createEditor()); - m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget()); + Q_ASSERT(m_editor); - Core::Context context = m_editor->context(); - context.prepend(BINDINGEDITOR_CONTEXT_ID); - m_editorWidget->m_context->setContext(context); + m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget()); + Q_ASSERT(m_editorWidget); auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor(); diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h index ed8cdd0a13..76de79b195 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h +++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h @@ -26,9 +26,10 @@ #ifndef ABSTRACTEDITORDIALOG_H #define ABSTRACTEDITORDIALOG_H -#include <bindingeditor/bindingeditorwidget.h> #include <qmldesignercorelib_global.h> + #include <texteditor/texteditor.h> +#include <bindingeditor/bindingeditorwidget.h> #include <QDialog> diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index 89364c5116..1f2a8224d3 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -286,10 +286,13 @@ void ActionEditor::prepareConnections() m_dialog->setAllConnections(connections, singletons, states); } -void ActionEditor::updateWindowName() +void ActionEditor::updateWindowName(const QString &targetName) { if (!m_dialog.isNull()) { - m_dialog->setWindowTitle(m_dialog->defaultTitle()); + if (targetName.isEmpty()) + m_dialog->setWindowTitle(m_dialog->defaultTitle()); + else + m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + targetName + "]"); m_dialog->raise(); } } diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h index c0356e81c4..09597bc8d1 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -64,7 +64,7 @@ public: void prepareConnections(); - Q_INVOKABLE void updateWindowName(); + Q_INVOKABLE void updateWindowName(const QString &targetName = {}); signals: void accepted(); diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index d28457e1f5..eff90366a0 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -42,8 +42,6 @@ namespace QmlDesigner { -static BindingEditor *s_lastBindingEditor = nullptr; - BindingEditor::BindingEditor(QObject *) { } @@ -62,11 +60,6 @@ void BindingEditor::prepareDialog() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_BINDINGEDITOR_OPENED); - if (s_lastBindingEditor) - s_lastBindingEditor->hideWidget(); - - s_lastBindingEditor = this; - m_dialog = new BindingEditorDialog(Core::ICore::dialogParent()); QObject::connect(m_dialog, &AbstractEditorDialog::accepted, @@ -91,9 +84,6 @@ void BindingEditor::showWidget(int x, int y) void BindingEditor::hideWidget() { - if (s_lastBindingEditor == this) - s_lastBindingEditor = nullptr; - if (m_dialog) { m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override m_dialog->close(); @@ -125,6 +115,12 @@ void BindingEditor::setBackendValue(const QVariant &backendValue) if (node.isValid()) { m_backendValueTypeName = node.metaInfo().propertyTypeName(propertyEditorValue->name()); + QString nodeId = node.id(); + if (nodeId.isEmpty()) + nodeId = node.simplifiedTypeName(); + + m_targetName = nodeId + "." + propertyEditorValue->name(); + if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown") if (QmlObjectNode::isValidQmlObjectNode(node)) m_backendValueTypeName = QmlObjectNode(node).instanceType(propertyEditorValue->name()); @@ -164,6 +160,12 @@ void BindingEditor::setStateModelNode(const QVariant &stateModelNode) } } +void BindingEditor::setStateName(const QString &name) +{ + m_targetName = name; + m_targetName += ".when"; +} + void BindingEditor::setModelNode(const ModelNode &modelNode) { if (modelNode.isValid()) @@ -177,6 +179,11 @@ void BindingEditor::setBackendValueTypeName(const TypeName &backendValueTypeName emit backendValueChanged(); } +void BindingEditor::setTargetName(const QString &target) +{ + m_targetName = target; +} + void BindingEditor::prepareBindings() { if (!m_modelNode.isValid() || m_backendValueTypeName.isEmpty()) @@ -279,8 +286,26 @@ void BindingEditor::prepareBindings() void BindingEditor::updateWindowName() { - if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty()) - m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + m_backendValueTypeName + "]"); + if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty()) { + const QString targetString = " [" + + (m_targetName.isEmpty() ? QString() : (m_targetName + ": ")) + + QString::fromUtf8(m_backendValueTypeName) + "]"; + + m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString); + } +} + +QString BindingEditor::targetName() const +{ + return m_targetName; +} + +QString BindingEditor::stateName() const +{ + if (m_targetName.endsWith(".when")) + return m_targetName.chopped(5); + + return {}; } QVariant BindingEditor::backendValue() const diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h index 495462128f..738d9c7101 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h @@ -44,6 +44,7 @@ class BindingEditor : public QObject Q_PROPERTY(QVariant backendValueProperty READ backendValue WRITE setBackendValue NOTIFY backendValueChanged) Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged) Q_PROPERTY(QVariant stateModelNodeProperty READ stateModelNode WRITE setStateModelNode NOTIFY stateModelNodeChanged) + Q_PROPERTY(QString stateNameProperty READ stateName WRITE setStateName) public: BindingEditor(QObject *parent = nullptr); @@ -64,15 +65,21 @@ public: void setModelNodeBackend(const QVariant &modelNodeBackend); //2. modelnode (this one also sets backend value type name to bool) + //State Name is not mandatory, but used in bindingEditor dialog name void setStateModelNode(const QVariant &stateModelNode); + void setStateName(const QString &name); - //3. modelnode + backend value type name + //3. modelnode + backend value type name + optional target name void setModelNode(const ModelNode &modelNode); void setBackendValueTypeName(const TypeName &backendValueTypeName); + void setTargetName(const QString &target); Q_INVOKABLE void prepareBindings(); Q_INVOKABLE void updateWindowName(); + QString targetName() const; + QString stateName() const; + signals: void accepted(); void rejected(); @@ -93,6 +100,7 @@ private: QVariant m_stateModelNode; QmlDesigner::ModelNode m_modelNode; TypeName m_backendValueTypeName; + QString m_targetName; }; } diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp index 6cd810df37..5b9c54b4d4 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp @@ -35,6 +35,10 @@ #include <qmljseditor/qmljseditordocument.h> #include <qmljseditor/qmljssemantichighlighter.h> #include <qmljstools/qmljsindenter.h> +#include <qmljstools/qmljstoolsconstants.h> + +#include <projectexplorer/projectexplorerconstants.h> +#include <utils/fancylineedit.h> #include <QAction> @@ -43,17 +47,19 @@ namespace QmlDesigner { BindingEditorWidget::BindingEditorWidget() : m_context(new Core::IContext(this)) { + Core::Context context(BINDINGEDITOR_CONTEXT_ID, + ProjectExplorer::Constants::QMLJS_LANGUAGE_ID); + m_context->setWidget(this); + m_context->setContext(context); Core::ICore::addContextObject(m_context); - const Core::Context context(BINDINGEDITOR_CONTEXT_ID); - /* * We have to register our own active auto completion shortcut, because the original short cut will * use the cursor position of the original editor in the editor manager. */ - m_completionAction = new QAction(tr("Trigger Completion"), this); + Core::Command *command = Core::ActionManager::registerAction( m_completionAction, TextEditor::Constants::COMPLETE_THIS, context); command->setDefaultKeySequence(QKeySequence( @@ -84,11 +90,9 @@ bool BindingEditorWidget::event(QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); - if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { + if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && !keyEvent->modifiers()) { emit returnKeyClicked(); return true; - } else { - return QmlJSEditor::QmlJSEditorWidget::event(event); } } return QmlJSEditor::QmlJSEditorWidget::event(event); @@ -133,8 +137,12 @@ void BindingDocument::triggerPendingUpdates() BindingEditorFactory::BindingEditorFactory() { setId(BINDINGEDITOR_CONTEXT_ID); - setDisplayName(QCoreApplication::translate("OpenWith::Editors", QmlDesigner::BINDINGEDITOR_CONTEXT_ID)); + setDisplayName(QCoreApplication::translate("OpenWith::Editors", BINDINGEDITOR_CONTEXT_ID)); setEditorActionHandlers(0); + addMimeType(BINDINGEDITOR_CONTEXT_ID); + addMimeType(QmlJSTools::Constants::QML_MIMETYPE); + addMimeType(QmlJSTools::Constants::QMLTYPES_MIMETYPE); + addMimeType(QmlJSTools::Constants::JS_MIMETYPE); setDocumentCreator([]() { return new BindingDocument; }); setEditorWidgetCreator([]() { return new BindingEditorWidget; }); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 8ca977e742..d9a67a8400 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -290,8 +290,8 @@ QHash<QString, QStringList> DesignerActionManager::handleExternalAssetsDrop(cons for (const QString &category : categories) { AddResourceOperation operation = categoryOperation.value(category); QStringList files = categoryFiles.value(category); - bool success = operation(files, {}); - if (success) + AddFilesResult result = operation(files, {}); + if (result == AddFilesResult::Succeeded) addedCategoryFiles.insert(category, files); } diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index 60e2c7562f..e86e441b14 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -28,6 +28,7 @@ #include <qmldesignercorelib_global.h> #include "actioninterface.h" #include "modelnode.h" +#include "modelnodeoperations.h" #include <coreplugin/actionmanager/command.h> #include <utils/styledbar.h> @@ -45,7 +46,7 @@ namespace QmlDesigner { class DesignerActionManagerView; -using AddResourceOperation = std::function<bool (const QStringList &, const QString &)>; +using AddResourceOperation = std::function<AddFilesResult (const QStringList &, const QString &)>; using ModelNodePreviewImageOperation = std::function<QVariant (const ModelNode &)>; struct AddResourceHandler diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index ab2853354c..b486740495 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -992,13 +992,15 @@ Utils::FilePath projectFilePath() return Utils::FilePath(); } -static bool addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory) +static AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory) { QString directory = AddImagesDialog::getDirectory(fileNames, defaultDirectory); if (directory.isEmpty()) - return false; + return AddFilesResult::Cancelled; + + DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); + QTC_ASSERT(document, return AddFilesResult::Failed); - bool allSuccessful = true; QList<QPair<QString, QString>> copyList; QStringList removeList; for (const QString &fileName : fileNames) { @@ -1021,26 +1023,21 @@ static bool addFilesToProject(const QStringList &fileNames, const QString &defau // unnecessarily refreshing file models multiple times during the operation for (const auto &file : qAsConst(removeList)) QFile::remove(file); + for (const auto &filePair : qAsConst(copyList)) { const bool success = QFile::copy(filePair.first, filePair.second); - - auto document = QmlDesignerPlugin::instance()->currentDesignDocument(); - - QTC_ASSERT(document, return false); - - if (success) { - ProjectExplorer::Node *node = ProjectExplorer::ProjectTree::nodeForFile(document->fileName()); - if (node) { - ProjectExplorer::FolderNode *containingFolder = node->parentFolderNode(); - if (containingFolder) - containingFolder->addFiles({Utils::FilePath::fromString(filePair.second)}); - } - } else { - allSuccessful = false; + if (!success) + return AddFilesResult::Failed; + + ProjectExplorer::Node *node = ProjectExplorer::ProjectTree::nodeForFile(document->fileName()); + if (node) { + ProjectExplorer::FolderNode *containingFolder = node->parentFolderNode(); + if (containingFolder) + containingFolder->addFiles({Utils::FilePath::fromString(filePair.second)}); } } - return allSuccessful; + return AddFilesResult::Succeeded; } static QString getAssetDefaultDirectory(const QString &assetDir, const QString &defaultDirectory) @@ -1060,22 +1057,22 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString & return adjustedDefaultDirectory; } -bool addFontToProject(const QStringList &fileNames, const QString &defaultDirectory) +AddFilesResult addFontToProject(const QStringList &fileNames, const QString &defaultDirectory) { return addFilesToProject(fileNames, getAssetDefaultDirectory("fonts", defaultDirectory)); } -bool addSoundToProject(const QStringList &fileNames, const QString &defaultDirectory) +AddFilesResult addSoundToProject(const QStringList &fileNames, const QString &defaultDirectory) { return addFilesToProject(fileNames, getAssetDefaultDirectory("sounds", defaultDirectory)); } -bool addShaderToProject(const QStringList &fileNames, const QString &defaultDirectory) +AddFilesResult addShaderToProject(const QStringList &fileNames, const QString &defaultDirectory) { return addFilesToProject(fileNames, getAssetDefaultDirectory("shaders", defaultDirectory)); } -bool addImageToProject(const QStringList &fileNames, const QString &defaultDirectory) +AddFilesResult addImageToProject(const QStringList &fileNames, const QString &defaultDirectory) { return addFilesToProject(fileNames, getAssetDefaultDirectory("images", defaultDirectory)); } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 0e302a3c65..feb7faa556 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -28,6 +28,9 @@ #include "selectioncontext.h" namespace QmlDesigner { + +enum class AddFilesResult { Succeeded, Failed, Cancelled }; + namespace ModelNodeOperations { bool goIntoComponent(const ModelNode &modelNode); @@ -73,10 +76,10 @@ void addItemToStackedContainer(const SelectionContext &selectionContext); void increaseIndexOfStackedContainer(const SelectionContext &selectionContext); void decreaseIndexOfStackedContainer(const SelectionContext &selectionContext); void addTabBarToStackedContainer(const SelectionContext &selectionContext); -bool addImageToProject(const QStringList &fileNames, const QString &directory); -bool addFontToProject(const QStringList &fileNames, const QString &directory); -bool addSoundToProject(const QStringList &fileNames, const QString &directory); -bool addShaderToProject(const QStringList &fileNames, const QString &directory); +AddFilesResult addImageToProject(const QStringList &fileNames, const QString &directory); +AddFilesResult addFontToProject(const QStringList &fileNames, const QString &directory); +AddFilesResult addSoundToProject(const QStringList &fileNames, const QString &directory); +AddFilesResult addShaderToProject(const QStringList &fileNames, const QString &directory); void createFlowActionArea(const SelectionContext &selectionContext); void addTransition(const SelectionContext &selectionState); void addFlowEffect(const SelectionContext &selectionState, const TypeName &typeName); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp index 11bfebe5e7..095b8b6262 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp @@ -159,17 +159,20 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) QMenu menu(this); - menu.addAction(tr("Open Connection Editor"), [&]() { + menu.addAction(tr("Open Connection Editor"), this, [&]() { auto *connectionModel = qobject_cast<ConnectionModel *>(targetView->model()); const SignalHandlerProperty property = connectionModel->signalHandlerPropertyForRow(index.row()); const ModelNode node = property.parentModelNode(); + const QString targetName = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data().toString() + + "." + property.name(); + m_connectionEditor->showWidget(); m_connectionEditor->setConnectionValue(index.data().toString()); m_connectionEditor->setModelIndex(index); m_connectionEditor->setModelNode(node); m_connectionEditor->prepareConnections(); - m_connectionEditor->updateWindowName(); + m_connectionEditor->updateWindowName(targetName); }); QMap<QString, QVariant> data; @@ -179,7 +182,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) const auto actions = designerActionManager.actionsForTargetView( ActionInterface::TargetView::ConnectionEditor); - for (auto actionInterface : actions) { + for (const auto &actionInterface : actions) { auto *action = actionInterface->action(); action->setData(data); menu.addAction(action); @@ -198,7 +201,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) QMenu menu(this); - menu.addAction(tr("Open Binding Editor"), [&]() { + menu.addAction(tr("Open Binding Editor"), this, [&]() { BindingModel *bindingModel = qobject_cast<BindingModel*>(targetView->model()); const BindingProperty property = bindingModel->bindingPropertyForRow(index.row()); @@ -209,10 +212,13 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) const TypeName typeName = property.isDynamic() ? property.dynamicTypeName() : node.metaInfo().propertyTypeName(property.name()); + const QString targetName = node.displayName() + "." + property.name(); + m_bindingEditor->showWidget(); m_bindingEditor->setBindingValue(property.expression()); m_bindingEditor->setModelNode(node); m_bindingEditor->setBackendValueTypeName(typeName); + m_bindingEditor->setTargetName(targetName); m_bindingEditor->prepareBindings(); m_bindingEditor->updateWindowName(); @@ -232,7 +238,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) DynamicPropertiesModel *propertiesModel = qobject_cast<DynamicPropertiesModel *>(targetView->model()); QMenu menu(this); - menu.addAction(tr("Open Binding Editor"), [&]() { + menu.addAction(tr("Open Binding Editor"), this, [&]() { AbstractProperty abstractProperty = propertiesModel->abstractPropertyForRow(index.row()); if (!abstractProperty.isValid()) return; @@ -247,17 +253,20 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) else return; + const QString targetName = node.displayName() + "." + abstractProperty.name(); + m_dynamicEditor->showWidget(); m_dynamicEditor->setBindingValue(newExpression); m_dynamicEditor->setModelNode(node); m_dynamicEditor->setBackendValueTypeName(abstractProperty.dynamicTypeName()); + m_dynamicEditor->setTargetName(targetName); m_dynamicEditor->prepareBindings(); m_dynamicEditor->updateWindowName(); m_dynamicIndex = index; }); - menu.addAction(tr("Reset Property"), [&]() { + menu.addAction(tr("Reset Property"), this, [&]() { propertiesModel->resetProperty(propertiesModel->abstractPropertyForRow(index.row()).name()); }); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 10e1373dde..187bc4ca17 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -339,26 +339,13 @@ static inline bool hasNodeSourceParent(const ModelNode &node) void FormEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/) { - // If node is not connected to scene root, don't do anything yet to avoid duplicated effort, - // as any removal or addition will remove or add descendants as well. - if (!node.isInHierarchy()) - return; + addOrRemoveFormEditorItem(node); +} - QmlItemNode itemNode(node); - if (hasNodeSourceParent(node)) { - if (FormEditorItem *item = m_scene->itemForQmlItemNode(itemNode)) { - QList<FormEditorItem *> removed = scene()->itemsForQmlItemNodes(itemNode.allSubModelNodes()); - removed.append(item); - m_currentTool->itemsAboutToRemoved(removed); - removeNodeFromScene(itemNode); - } - } else if (itemNode.isValid() && node.nodeSourceType() == ModelNode::NodeWithoutSource) { - if (!m_scene->itemForQmlItemNode(itemNode)) { - setupFormEditorItemTree(itemNode); - // Simulate selection change to refresh tools - selectedNodesChanged(selectedModelNodes(), {}); - } - } +void FormEditorView::nodeSourceChanged(const ModelNode &node, const QString &newNodeSource) +{ + Q_UNUSED(newNodeSource) + addOrRemoveFormEditorItem(node); } WidgetInfo FormEditorView::widgetInfo() @@ -863,6 +850,38 @@ void FormEditorView::resetNodeInstanceView() resetPuppet(); } +void FormEditorView::addOrRemoveFormEditorItem(const ModelNode &node) +{ + // If node is not connected to scene root, don't do anything yet to avoid duplicated effort, + // as any removal or addition will remove or add descendants as well. + if (!node.isInHierarchy()) + return; + + QmlItemNode itemNode(node); + + auto removeItemFromScene = [this, &itemNode]() { + if (FormEditorItem *item = m_scene->itemForQmlItemNode(itemNode)) { + QList<FormEditorItem *> removed = scene()->itemsForQmlItemNodes(itemNode.allSubModelNodes()); + removed.append(item); + m_currentTool->itemsAboutToRemoved(removed); + removeNodeFromScene(itemNode); + } + }; + if (hasNodeSourceParent(node)) { + removeItemFromScene(); + } else if (itemNode.isValid()) { + if (node.nodeSourceType() == ModelNode::NodeWithoutSource) { + if (!m_scene->itemForQmlItemNode(itemNode)) { + setupFormEditorItemTree(itemNode); + // Simulate selection change to refresh tools + selectedNodesChanged(selectedModelNodes(), {}); + } + } else { + removeItemFromScene(); + } + } +} + void FormEditorView::reset() { QTimer::singleShot(200, this, &FormEditorView::delayedReset); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h index 04b7d1e83e..ca3fb72bb0 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -70,6 +70,7 @@ public: void nodeCreated(const ModelNode &createdNode) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; + void nodeSourceChanged(const ModelNode &node, const QString &newNodeSource) override; void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override; void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; @@ -147,6 +148,7 @@ private: void createFormEditorWidget(); void temporaryBlockView(int duration = 1000); void resetNodeInstanceView(); + void addOrRemoveFormEditorItem(const ModelNode &node); QPointer<FormEditorWidget> m_formEditorWidget; QPointer<FormEditorScene> m_scene; diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 9374e61776..0b7d7fa2c9 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -634,8 +634,11 @@ void DesignDocument::setEditor(Core::IEditor *editor) connect(Core::EditorManager::instance(), &Core::EditorManager::aboutToSave, this, [this](Core::IDocument *document) { if (m_textEditor && m_textEditor->document() == document) { - if (m_documentModel && m_documentModel->rewriterView()) + if (m_documentModel && m_documentModel->rewriterView()) { + if (fileName().completeSuffix() == "ui.qml") + m_documentModel->rewriterView()->sanitizeModel(); m_documentModel->rewriterView()->writeAuxiliaryData(); + } } }); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 04bc51284b..135eaaf5aa 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -788,7 +788,10 @@ void ItemLibraryAssetImportDialog::onClose() addInfo(tr("Canceling import.")); m_importer.cancelImport(); } else { - reject(); + if (ui->progressBar->value() == 100) // import done successfully + accept(); + else + reject(); close(); deleteLater(); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp index d351232e36..4962f5d6b4 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp @@ -147,20 +147,19 @@ void ItemLibraryCategoriesModel::resetModel() bool ItemLibraryCategoriesModel::isAllCategoriesHidden() const { for (const auto &category : std::as_const(m_categoryList)) { - // ignore "All Other Components" as its categoryVisible is always true - if (category->isCategoryVisible() && category->categoryName() != "All Other Components") + if (category->isCategoryVisible()) return false; } return true; } -void ItemLibraryCategoriesModel::showAllCategories(bool show) +void ItemLibraryCategoriesModel::showAllCategories() { for (const auto &category : std::as_const(m_categoryList)) { - if (category->isCategoryVisible() != show) { - category->setCategoryVisible(show); - ItemLibraryModel::saveCategoryVisibleState(show, category->categoryName(), + if (!category->isCategoryVisible()) { + category->setCategoryVisible(true); + ItemLibraryModel::saveCategoryVisibleState(true, category->categoryName(), category->ownerImport()->importName()); } } @@ -168,37 +167,56 @@ void ItemLibraryCategoriesModel::showAllCategories(bool show) emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categoryVisible")}); } -QObject *ItemLibraryCategoriesModel::selectFirstVisibleCategory() +void ItemLibraryCategoriesModel::hideCategory(const QString &categoryName) +{ + for (int i = 0; i < m_categoryList.size(); ++i) { + const auto category = m_categoryList.at(i); + if (category->categoryName() == categoryName) { + category->setCategoryVisible(false); + ItemLibraryModel::saveCategoryVisibleState(false, category->categoryName(), + category->ownerImport()->importName()); + emit dataChanged(index(i), index(i), {m_roleNames.key("categoryVisible")}); + break; + } + } +} + +int ItemLibraryCategoriesModel::selectFirstVisibleCategory() { for (int i = 0; i < m_categoryList.length(); ++i) { const auto category = m_categoryList.at(i); if (category->isCategoryVisible()) { category->setCategorySelected(true); - emit dataChanged(index(i),index(i), {m_roleNames.key("categorySelected")}); - return category; + emit dataChanged(index(i), index(i), {m_roleNames.key("categorySelected")}); + return i; } } - return nullptr; + return -1; } -void ItemLibraryCategoriesModel::clearSelectedCategories() +void ItemLibraryCategoriesModel::clearSelectedCategory(int categoryIndex) { - for (const auto &category : std::as_const(m_categoryList)) - category->setCategorySelected(false); + if (categoryIndex == -1 || m_categoryList.isEmpty()) + return; - emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categorySelected")}); + m_categoryList.at(categoryIndex)->setCategorySelected(false); + emit dataChanged(index(categoryIndex), index(categoryIndex), {m_roleNames.key("categorySelected")}); } -void ItemLibraryCategoriesModel::selectCategory(int categoryIndex) +QPointer<ItemLibraryCategory> ItemLibraryCategoriesModel::selectCategory(int categoryIndex) { - const auto category = m_categoryList.at(categoryIndex); + if (categoryIndex == -1 || m_categoryList.isEmpty()) + return nullptr; + + const QPointer<ItemLibraryCategory> category = m_categoryList.at(categoryIndex); if (!category->categorySelected()) { - clearSelectedCategories(); category->setCategorySelected(true); emit dataChanged(index(categoryIndex),index(categoryIndex), {m_roleNames.key("categorySelected")}); } + + return category; } void ItemLibraryCategoriesModel::addRoleNames() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h index 9433af804f..36437a0ed6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h @@ -55,10 +55,11 @@ public: bool isAllCategoriesHidden() const; void sortCategorySections(); void resetModel(); - void showAllCategories(bool show = true); - void clearSelectedCategories(); - QObject *selectFirstVisibleCategory(); - void selectCategory(int categoryIndex); + void showAllCategories(); + void hideCategory(const QString &categoryName); + void clearSelectedCategory(int categoryIndex); + int selectFirstVisibleCategory(); + QPointer<ItemLibraryCategory> selectCategory(int categoryIndex); private: void addRoleNames(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp index 0744b7b2b3..ce04b298fe 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp @@ -137,6 +137,7 @@ void ItemLibraryCategory::setExpanded(bool expanded) void ItemLibraryCategory::setCategorySelected(bool selected) { m_categorySelected = selected; + emit categorySelectedChanged(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp index 7e696e43ef..ebf13d0fe5 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp @@ -133,28 +133,38 @@ bool ItemLibraryImport::updateCategoryVisibility(const QString &searchText, bool return hasVisibleCategories; } -void ItemLibraryImport::showAllCategories(bool show) +void ItemLibraryImport::showAllCategories() { - m_categoryModel.showAllCategories(show); + m_categoryModel.showAllCategories(); + setAllCategoriesVisible(true); } -void ItemLibraryImport::selectCategory(int categoryIndex) +void ItemLibraryImport::hideCategory(const QString &categoryName) { - m_categoryModel.selectCategory(categoryIndex); + m_categoryModel.hideCategory(categoryName); + setAllCategoriesVisible(false); } -QObject *ItemLibraryImport::selectFirstVisibleCategory() +ItemLibraryCategory *ItemLibraryImport::selectCategory(int categoryIndex) +{ + return m_categoryModel.selectCategory(categoryIndex); +} + +int ItemLibraryImport::selectFirstVisibleCategory() { return m_categoryModel.selectFirstVisibleCategory(); } -void ItemLibraryImport::clearSelectedCategories() +void ItemLibraryImport::clearSelectedCategory(int categoryIndex) { - m_categoryModel.clearSelectedCategories(); + m_categoryModel.clearSelectedCategory(categoryIndex); } bool ItemLibraryImport::isAllCategoriesHidden() const { + if (!m_isVisible) + return true; + return m_categoryModel.isAllCategoriesHidden(); } @@ -221,7 +231,7 @@ void ItemLibraryImport::setImportExpanded(bool expanded) } } -ItemLibraryCategory *ItemLibraryImport::getCategorySection(const QString &categoryName) const +ItemLibraryCategory *ItemLibraryImport::getCategoryByName(const QString &categoryName) const { for (ItemLibraryCategory *catSec : std::as_const(m_categoryModel.categorySections())) { if (catSec->categoryName() == categoryName) @@ -231,6 +241,16 @@ ItemLibraryCategory *ItemLibraryImport::getCategorySection(const QString &catego return nullptr; } +ItemLibraryCategory *ItemLibraryImport::getCategoryAt(int categoryIndex) const +{ + const QList<QPointer<ItemLibraryCategory>> categories = m_categoryModel.categorySections(); + + if (categoryIndex != -1 && !categories.isEmpty()) + return categories.at(categoryIndex); + + return nullptr; +} + // static QString ItemLibraryImport::userComponentsTitle() { @@ -264,22 +284,14 @@ void ItemLibraryImport::updateRemovable() } } -// returns true if all categories are visible, otherwise false -bool ItemLibraryImport::importCatVisibleState() const +bool ItemLibraryImport::allCategoriesVisible() const { - if (m_categoryModel.rowCount() > 0) { - for (ItemLibraryCategory *cat : m_categoryModel.categorySections()) { - if (!cat->isCategoryVisible()) - return false; - } - } - - return true; + return m_allCategoriesVisible; } -void ItemLibraryImport::setImportCatVisibleState(bool show) +void ItemLibraryImport::setAllCategoriesVisible(bool visible) { - m_categoryModel.showAllCategories(show); + m_allCategoriesVisible = visible; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h index f5a1cd0279..89aaca8e02 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h @@ -43,7 +43,7 @@ class ItemLibraryImport : public QObject Q_PROPERTY(bool importExpanded READ importExpanded WRITE setImportExpanded NOTIFY importExpandChanged FINAL) Q_PROPERTY(bool importRemovable READ importRemovable NOTIFY importRemovableChanged FINAL) Q_PROPERTY(bool importUnimported READ importUnimported FINAL) - Q_PROPERTY(bool importCatVisibleState READ importCatVisibleState WRITE setImportCatVisibleState NOTIFY importCatVisibleStateChanged FINAL) + Q_PROPERTY(bool allCategoriesVisible READ allCategoriesVisible WRITE setAllCategoriesVisible NOTIFY allCategoriesVisibleChanged FINAL) Q_PROPERTY(QObject *categoryModel READ categoryModel NOTIFY categoryModelChanged FINAL) public: @@ -65,11 +65,12 @@ public: bool importVisible() const; bool importUsed() const; bool importRemovable() const; - bool importCatVisibleState() const; + bool allCategoriesVisible() const; bool hasCategories() const; bool hasSingleCategory() const; bool isAllCategoriesHidden() const; - ItemLibraryCategory *getCategorySection(const QString &categoryName) const; + ItemLibraryCategory *getCategoryByName(const QString &categoryName) const; + ItemLibraryCategory *getCategoryAt(int categoryIndex) const; void addCategory(ItemLibraryCategory *category); QObject *categoryModel(); @@ -78,12 +79,14 @@ public: void setImportUsed(bool importUsed); void sortCategorySections(); void setImportExpanded(bool expanded = true); - void setImportCatVisibleState(bool show); + void setAllCategoriesVisible(bool visible); void expandCategories(bool expand = true); - void showAllCategories(bool show = true); - void selectCategory(int categoryIndex); - QObject *selectFirstVisibleCategory(); - void clearSelectedCategories(); + void showAllCategories(); + void hideCategory(const QString &categoryName); + ItemLibraryCategory *selectCategory(int categoryIndex); + int selectFirstVisibleCategory(); + void clearSelectedCategory(int categoryIndex); + bool importUnimported() const { return m_sectionType == SectionType::Unimported; } static QString userComponentsTitle(); static QString quick3DAssetsTitle(); @@ -97,17 +100,17 @@ signals: void importUsedChanged(); void importExpandChanged(); void importRemovableChanged(); - void importCatVisibleStateChanged(); + void allCategoriesVisibleChanged(); private: void updateRemovable(); - bool importUnimported() const { return m_sectionType == SectionType::Unimported; } Import m_import; bool m_importExpanded = true; bool m_isVisible = true; bool m_importUsed = false; bool m_importRemovable = false; + bool m_allCategoriesVisible = true; SectionType m_sectionType = SectionType::Default; ItemLibraryCategoriesModel m_categoryModel; }; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index ebef71f63f..b571cc4981 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -59,8 +59,8 @@ bool ItemLibraryModel::loadExpandedState(const QString §ionName) return expandedStateHash.value(sectionName, true); } -void ItemLibraryModel::saveCategoryVisibleState(bool isVisible, const QString &categoryName, const - QString &importName) +void ItemLibraryModel::saveCategoryVisibleState(bool isVisible, const QString &categoryName, + const QString &importName) { categoryVisibleStateHash.insert(categoryName + '_' + importName, isVisible); } @@ -70,58 +70,61 @@ bool ItemLibraryModel::loadCategoryVisibleState(const QString &categoryName, con return categoryVisibleStateHash.value(categoryName + '_' + importName, true); } -void ItemLibraryModel::showHiddenCategories() +void ItemLibraryModel::selectImportCategory(const QString &importUrl, int categoryIndex) { - for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) { - if (import->hasCategories()) - import->showAllCategories(true); - } - - categoryVisibleStateHash.clear(); -} + clearSelectedCategory(); -bool ItemLibraryModel::getIsAnyCategoryHidden() const -{ - for (const bool &catState : std::as_const(categoryVisibleStateHash)) { - if (!catState) - return true; - } + m_selectedImportUrl = importUrl; + m_selectedCategoryIndex = categoryIndex; - return false; + updateSelection(); } -void ItemLibraryModel::selectImportCategory(const QString importUrl, int categoryIndex) +void ItemLibraryModel::clearSelectedCategory() { - ItemLibraryImport *selectedCategoryImport = importByUrl(importUrl); - - for (int i = 0; i < m_importList.length(); ++i) { - const auto importToSelect = m_importList.at(i); - - if (selectedCategoryImport == importToSelect) - importToSelect->selectCategory(categoryIndex); - else - importToSelect->clearSelectedCategories(); + if (m_selectedCategoryIndex != -1) { + ItemLibraryImport *selectedImport = importByUrl(m_selectedImportUrl); + if (selectedImport) + selectedImport->clearSelectedCategory(m_selectedCategoryIndex); } } -bool ItemLibraryModel::isAllCategoriesHidden() const +void ItemLibraryModel::selectImportFirstVisibleCategory() { - for (int i = 0; i < m_importList.length(); ++i) { - if (!m_importList.at(i)->isAllCategoriesHidden()) - return false; - } + if (m_selectedCategoryIndex != -1) { + ItemLibraryImport *selectedImport = importByUrl(m_selectedImportUrl); + if (selectedImport) { + ItemLibraryCategory *selectedCategory = selectedImport->getCategoryAt(m_selectedCategoryIndex); + if (selectedCategory) { + bool isUnimported = selectedImport->sectionType() == ItemLibraryImport::SectionType::Unimported; + // unimported category is always visible so checking its Import visibility instead + bool isVisible = isUnimported ? selectedImport->importVisible() + : selectedCategory->isCategoryVisible(); + if (isVisible) + return; // there is already a selected visible category - return true; -} + clearSelectedCategory(); + } + } + } -QObject *ItemLibraryModel::selectImportFirstVisibleCategory() -{ for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) { - if (!import->isAllCategoriesHidden()) - return import->selectFirstVisibleCategory(); + if (!import->isAllCategoriesHidden()) { + m_selectedImportUrl = import->importUrl(); + m_selectedCategoryIndex = import->selectFirstVisibleCategory(); + + ItemLibraryCategory *selectedCategory = import->getCategoryAt(m_selectedCategoryIndex); + if (selectedCategory) { + setItemsModel(selectedCategory->itemModel()); + setImportUnimportedSelected(import->importUnimported()); + return; + } + } } - return nullptr; + m_selectedImportUrl.clear(); + m_selectedCategoryIndex = -1; + setItemsModel(nullptr); } bool ItemLibraryModel::isAnyCategoryHidden() const @@ -137,6 +140,30 @@ void ItemLibraryModel::setIsAnyCategoryHidden(bool state) } } +bool ItemLibraryModel::importUnimportedSelected() const +{ + return m_importUnimportedSelected; +} + +void ItemLibraryModel::setImportUnimportedSelected(bool state) +{ + if (state != m_importUnimportedSelected) { + m_importUnimportedSelected = state; + emit importUnimportedSelectedChanged(); + } +} + +QObject *ItemLibraryModel::itemsModel() const +{ + return m_itemsModel; +} + +void ItemLibraryModel::setItemsModel(QObject *model) +{ + m_itemsModel = model; + emit itemsModelChanged(); +} + void ItemLibraryModel::expandAll() { int i = 0; @@ -164,6 +191,46 @@ void ItemLibraryModel::collapseAll() } } +void ItemLibraryModel::hideCategory(const QString &importUrl, const QString &categoryName) +{ + ItemLibraryImport *import = importByUrl(importUrl); + if (!import) + return; + + import->hideCategory(categoryName); + + selectImportFirstVisibleCategory(); + setIsAnyCategoryHidden(true); +} + +void ItemLibraryModel::showImportHiddenCategories(const QString &importUrl) +{ + ItemLibraryImport *targetImport = nullptr; + bool hiddenCatsExist = false; + for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) { + if (import->importUrl() == importUrl) + targetImport = import; + else + hiddenCatsExist |= !import->allCategoriesVisible(); + } + + if (targetImport) { + targetImport->showAllCategories(); + updateSelection(); // useful when all categories are hidden + setIsAnyCategoryHidden(hiddenCatsExist); + } +} + +void ItemLibraryModel::showAllHiddenCategories() +{ + for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) + import->showAllCategories(); + + updateSelection(); // useful when all categories are hidden + setIsAnyCategoryHidden(false); + categoryVisibleStateHash.clear(); +} + void ItemLibraryModel::setFlowMode(bool b) { m_flowMode = b; @@ -242,6 +309,8 @@ void ItemLibraryModel::setSearchText(const QString &searchText) bool changed = false; updateVisibility(&changed); + + selectImportFirstVisibleCategory(); } } @@ -411,7 +480,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) } // get or create category section - ItemLibraryCategory *categorySection = importSection->getCategorySection(catName); + ItemLibraryCategory *categorySection = importSection->getCategoryByName(catName); if (!categorySection) { categorySection = new ItemLibraryCategory(catName, importSection); importSection->addCategory(categorySection); @@ -428,8 +497,11 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) } sortSections(); + bool changed = false; updateVisibility(&changed); + + updateSelection(); endResetModel(); } @@ -453,6 +525,23 @@ void ItemLibraryModel::clearSections() m_importList.clear(); } +void ItemLibraryModel::updateSelection() +{ + if (m_selectedCategoryIndex != -1) { + ItemLibraryImport *selectedImport = importByUrl(m_selectedImportUrl); + if (selectedImport) { + ItemLibraryCategory *selectedCategory = selectedImport->selectCategory(m_selectedCategoryIndex); + if (selectedCategory) { + setItemsModel(selectedCategory->itemModel()); + setImportUnimportedSelected(selectedImport->importUnimported()); + return; + } + } + } + + selectImportFirstVisibleCategory(); +} + void ItemLibraryModel::registerQmlTypes() { qmlRegisterAnonymousType<QmlDesigner::ItemLibraryModel>("ItemLibraryModel", 1); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h index ad9803821f..7852999edd 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h @@ -33,15 +33,19 @@ QT_FORWARD_DECLARE_CLASS(QMimeData) namespace QmlDesigner { -class ItemLibraryInfo; +class ItemLibraryCategory; class ItemLibraryEntry; -class Model; class ItemLibraryImport; +class ItemLibraryInfo; +class Model; class ItemLibraryModel : public QAbstractListModel { Q_OBJECT + Q_PROPERTY(bool isAnyCategoryHidden READ isAnyCategoryHidden WRITE setIsAnyCategoryHidden NOTIFY isAnyCategoryHiddenChanged FINAL) + Q_PROPERTY(QObject *itemsModel READ itemsModel WRITE setItemsModel NOTIFY itemsModelChanged) + Q_PROPERTY(bool importUnimportedSelected READ importUnimportedSelected WRITE setImportUnimportedSelected NOTIFY importUnimportedSelectedChanged) public: explicit ItemLibraryModel(QObject *parent = nullptr); @@ -66,20 +70,26 @@ public: bool isAnyCategoryHidden() const; void setIsAnyCategoryHidden(bool state); + bool importUnimportedSelected() const; + void setImportUnimportedSelected(bool state); + + QObject *itemsModel() const; + void setItemsModel(QObject *model); + static void registerQmlTypes(); static void saveExpandedState(bool expanded, const QString §ionName); static bool loadExpandedState(const QString §ionName); static void saveCategoryVisibleState(bool isVisible, const QString &categoryName, const QString &importName); static bool loadCategoryVisibleState(const QString &categoryName, const QString &importName); + void selectImportFirstVisibleCategory(); Q_INVOKABLE void expandAll(); Q_INVOKABLE void collapseAll(); - Q_INVOKABLE void showHiddenCategories(); - Q_INVOKABLE bool getIsAnyCategoryHidden() const; - Q_INVOKABLE void selectImportCategory(const QString importUrl, int categoryIndex); - Q_INVOKABLE QObject *selectImportFirstVisibleCategory(); - Q_INVOKABLE bool isAllCategoriesHidden() const; + Q_INVOKABLE void hideCategory(const QString &importUrl, const QString &categoryName); + Q_INVOKABLE void showImportHiddenCategories(const QString &importUrl); + Q_INVOKABLE void showAllHiddenCategories(); + Q_INVOKABLE void selectImportCategory(const QString &importUrl, int categoryIndex); Import entryToImport(const ItemLibraryEntry &entry); @@ -87,12 +97,18 @@ public: signals: void isAnyCategoryHiddenChanged(); + void importUnimportedSelectedChanged(); + void selectedCategoryChanged(); + void selectedImportUrlChanged(); + void itemsModelChanged(); private: void updateVisibility(bool *changed); void addRoleNames(); void sortSections(); void clearSections(); + void updateSelection(); + void clearSelectedCategory(); QList<QPointer<ItemLibraryImport>> m_importList; QHash<int, QByteArray> m_roleNames; @@ -100,6 +116,10 @@ private: QString m_searchText; bool m_flowMode = false; bool m_isAnyCategoryHidden = false; + bool m_importUnimportedSelected = false; + QString m_selectedImportUrl; + int m_selectedCategoryIndex = -1; + QObject *m_itemsModel = nullptr; // items model for the horizontal layout inline static QHash<QString, bool> expandedStateHash; inline static QHash<QString, bool> categoryVisibleStateHash; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 06b84d83f8..9c8b4e09f9 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -245,13 +245,15 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap) m_importableExtensions3DMap = extMap; - auto import3DModelOperation = [this](const QStringList &fileNames, const QString &defaultDir) -> bool { + AddResourceOperation import3DModelOperation = [this](const QStringList &fileNames, + const QString &defaultDir) -> AddFilesResult { auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir, m_importableExtensions3DMap, m_importOptions3DMap, {}, {}, Core::ICore::mainWindow()); - importDlg->exec(); - return true; + int result = importDlg->exec(); + + return result == QDialog::Accepted ? AddFilesResult::Succeeded : AddFilesResult::Cancelled; }; auto add3DHandler = [&](const QString &group, const QString &ext) { diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index de15fde7cf..ef1e85287a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -38,6 +38,7 @@ #include <itemlibrarymodel.h> #include <itemlibraryaddimportmodel.h> #include "itemlibraryassetsiconprovider.h" +#include "modelnodeoperations.h" #include <metainfo.h> #include <model.h> #include <rewritingexception.h> @@ -560,45 +561,31 @@ void ItemLibraryWidget::addImportForItem(const QString &importUrl) void ItemLibraryWidget::addResources(const QStringList &files) { - auto document = QmlDesignerPlugin::instance()->currentDesignDocument(); + DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); QTC_ASSERT(document, return); - QList<AddResourceHandler> handlers = QmlDesignerPlugin::instance()->viewManager().designerActionManager().addResourceHandler(); - - QMultiMap<QString, QString> map; - for (const AddResourceHandler &handler : handlers) { - map.insert(handler.category, handler.filter); - } - - QMap<QString, QString> reverseMap; - for (const AddResourceHandler &handler : handlers) { - reverseMap.insert(handler.filter, handler.category); - } - - QMap<QString, int> priorities; - for (const AddResourceHandler &handler : handlers) { - priorities.insert(handler.category, handler.piority); - } - - QStringList sortedKeys = map.uniqueKeys(); - Utils::sort(sortedKeys, [&priorities](const QString &first, - const QString &second){ - return priorities.value(first) < priorities.value(second); - }); + const QList<AddResourceHandler> handlers = QmlDesignerPlugin::instance()->viewManager() + .designerActionManager().addResourceHandler(); QStringList fileNames = files; - if (fileNames.isEmpty()) { - QStringList filters; - - for (const QString &key : qAsConst(sortedKeys)) { - QString str = key + " ("; - str.append(map.values(key).join(" ")); - str.append(")"); - filters.append(str); + if (fileNames.isEmpty()) { // if no files, show the "add assets" dialog + QMultiMap<QString, QString> map; + QHash<QString, int> priorities; + for (const AddResourceHandler &handler : handlers) { + map.insert(handler.category, handler.filter); + priorities.insert(handler.category, handler.piority); } - filters.prepend(tr("All Files (%1)").arg(map.values().join(" "))); + QStringList sortedKeys = map.uniqueKeys(); + Utils::sort(sortedKeys, [&priorities](const QString &first, const QString &second) { + return priorities.value(first) < priorities.value(second); + }); + + QStringList filters { tr("All Files (%1)").arg(map.values().join(' ')) }; + QString filterTemplate = "%1 (%2)"; + for (const QString &key : qAsConst(sortedKeys)) + filters.append(filterTemplate.arg(key, map.values(key).join(' '))); static QString lastDir; const QString currentDir = lastDir.isEmpty() ? document->fileName().parentDir().toString() : lastDir; @@ -616,24 +603,30 @@ void ItemLibraryWidget::addResources(const QStringList &files) } } - QMultiMap<QString, QString> partitionedFileNames; + QHash<QString, QString> filterToCategory; + QHash<QString, AddResourceOperation> categoryToOperation; + for (const AddResourceHandler &handler : handlers) { + filterToCategory.insert(handler.filter, handler.category); + categoryToOperation.insert(handler.category, handler.operation); + } + + QMultiMap<QString, QString> categoryFileNames; // filenames grouped by category for (const QString &fileName : qAsConst(fileNames)) { const QString suffix = "*." + QFileInfo(fileName).suffix().toLower(); - const QString category = reverseMap.value(suffix); - partitionedFileNames.insert(category, fileName); + const QString category = filterToCategory.value(suffix); + categoryFileNames.insert(category, fileName); } - for (const QString &category : partitionedFileNames.uniqueKeys()) { - for (const AddResourceHandler &handler : handlers) { - QStringList fileNames = partitionedFileNames.values(category); - if (handler.category == category) { - QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_RESOURCE_IMPORTED + category); - if (!handler.operation(fileNames, document->fileName().parentDir().toString())) - Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"), tr("Could not add %1 to project.").arg(fileNames.join(" "))); - break; - } - } + for (const QString &category : categoryFileNames.uniqueKeys()) { + QStringList fileNames = categoryFileNames.values(category); + AddResourceOperation operation = categoryToOperation.value(category); + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_RESOURCE_IMPORTED + category); + AddFilesResult result = operation(fileNames, document->fileName().parentDir().toString()); + if (result == AddFilesResult::Failed) { + Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"), + tr("Could not add %1 to project.").arg(fileNames.join(' '))); + } } } diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index 99612fa2ad..c797a19ae4 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -25,6 +25,8 @@ #pragma once +#include <utils/porting.h> + #include <QString> #include <QStringList> #include <QMetaType> @@ -74,7 +76,7 @@ private: QStringList m_importPathList; }; -QMLDESIGNERCORE_EXPORT uint qHash(const Import &import); +QMLDESIGNERCORE_EXPORT Utils::QHashValueType qHash(const Import &import); } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index 76f547eeb1..765f2c9c65 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -37,7 +37,7 @@ public: constexpr explicit BasicId() = default; - BasicId(const char *) = delete; + constexpr BasicId(const char *) = delete; constexpr explicit BasicId(InternalIntergerType id) : id{id} diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index ac251ef064..44e804f25d 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -177,6 +177,8 @@ public: ModelNode getNodeForCanonicalIndex(int index); + void sanitizeModel(); + signals: void modelInterfaceProjectUpdated(); diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp index 28d8e02826..46ad1270e7 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp @@ -89,6 +89,7 @@ void MetaInfoReader::setQualifcation(const TypeName &qualification) void MetaInfoReader::elementStart(const QString &name, const QmlJS::SourceLocation &nameLocation) { + Q_UNUSED(nameLocation) switch (parserState()) { case ParsingDocument: setParserState(readDocument(name)); break; case ParsingMetaInfo: setParserState(readMetaInfoRootElement(name)); break; @@ -133,6 +134,8 @@ void MetaInfoReader::propertyDefinition(const QString &name, const QVariant &value, const QmlJS::SourceLocation &valueLocation) { + Q_UNUSED(nameLocation) + Q_UNUSED(valueLocation) switch (parserState()) { case ParsingType: readTypeProperty(name, value); break; case ParsingImports: readImportsProperty(name, value); break; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 33693210a9..97ff30850f 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1505,6 +1505,8 @@ QVariant NodeMetaInfo::propertyCastedValue(const PropertyName &propertyName, con return variant.toFloat(); } else if (typeName == "<cpp>.int") { return variant.toInt(); + } else if (typeName == "<cpp>.bool") { + return variant.toBool(); } else if (copyVariant.convert(typeId)) { return copyVariant; } diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index d895b07764..1c217bf47b 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -109,7 +109,7 @@ int Import::majorFromVersion(const QString &version) return version.split('.').first().toInt(); } -uint qHash(const Import &import) +Utils::QHashValueType qHash(const Import &import) { return ::qHash(import.url()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias()); } diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp index 55305c72d1..2445042fbb 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp @@ -332,7 +332,7 @@ static void removeStateOperationsForChildren(const QmlObjectNode &node) stateOperation.modelNode().destroy(); //remove of belonging StatesOperations } - for (const QmlObjectNode &childNode : node.modelNode().directSubModelNodes()) { + for (const QmlObjectNode childNode : node.modelNode().directSubModelNodes()) { removeStateOperationsForChildren(childNode); } } diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp index 85eb51de44..924a4e0ee6 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp @@ -310,7 +310,7 @@ QList<QmlTimelineKeyframeGroup> QmlTimelineKeyframeGroup::allInvalidTimelineKeyf QTC_ASSERT(view->rootModelNode().isValid(), return ret); const auto groups = view->rootModelNode().subModelNodesOfType("QtQuick.Timeline.KeyframeGroup"); - for (const QmlTimelineKeyframeGroup &group : groups) { + for (const QmlTimelineKeyframeGroup group : groups) { if (group.isDangling()) ret.append(group); } diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index af066b81c2..e33c82b124 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -39,6 +39,8 @@ #include <modelnodepositionstorage.h> #include <modelnode.h> #include <nodeproperty.h> +#include <qmlobjectnode.h> +#include <qmltimelinekeyframegroup.h> #ifndef QMLDESIGNER_TEST #include <qmldesignerplugin.h> @@ -57,6 +59,7 @@ #include <utility> #include <vector> +#include <algorithm> using namespace QmlDesigner::Internal; @@ -658,6 +661,36 @@ ModelNode RewriterView::getNodeForCanonicalIndex(int index) return m_canonicalIntModelNode.value(index); } +void RewriterView::sanitizeModel() +{ + if (inErrorState()) + return; + + QmlObjectNode root = rootModelNode(); + + QTC_ASSERT(root.isValid(), return); + + QList<ModelNode> danglingNodes; + + const auto danglingStates = root.allInvalidStateOperations(); + const auto danglingKeyframeGroups = QmlTimelineKeyframeGroup::allInvalidTimelineKeyframeGroups(this); + + std::transform(danglingStates.begin(), + danglingStates.end(), + std::back_inserter(danglingNodes), + [](const auto &node) { return node.modelNode(); }); + + std::transform(danglingKeyframeGroups.begin(), + danglingKeyframeGroups.end(), + std::back_inserter(danglingNodes), + [](const auto &node) { return node.modelNode(); }); + + executeInTransaction("RewriterView::sanitizeModel", [&]() { + for (auto node : std::as_const(danglingNodes)) + node.destroy(); + }); +} + Internal::ModelNodePositionStorage *RewriterView::positionStorage() const { return m_positionStorage.data(); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 2a428c6533..5df23b7385 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -28,6 +28,7 @@ #include "projectstorageexceptions.h" #include "projectstorageinterface.h" #include "sourcepathcachetypes.h" +#include "storagecache.h" #include <sqlitealgorithms.h> #include <sqlitetable.h> @@ -35,7 +36,9 @@ #include <utils/algorithm.h> #include <utils/optional.h> +#include <utils/set_algorithm.h> +#include <algorithm> #include <tuple> namespace QmlDesigner { @@ -53,65 +56,73 @@ public: ProjectStorage(Database &database, bool isInitialized) : database{database} , initializer{database, isInitialized} - {} + { + moduleCache.populate(); + } - void synchronize(Storage::Modules modules, - Storage::Imports imports, - Storage::Types types, - SourceIds sourceIds, - FileStatuses fileStatuses) override + void synchronize(Storage::SynchronizationPackage package) override { Sqlite::ImmediateTransaction transaction{database}; - std::vector<AliasPropertyDeclaration> insertedAliasPropertyDeclarations; - std::vector<AliasPropertyDeclaration> updatedAliasPropertyDeclarations; + AliasPropertyDeclarations insertedAliasPropertyDeclarations; + AliasPropertyDeclarations updatedAliasPropertyDeclarations; + + AliasPropertyDeclarations relinkableAliasPropertyDeclarations; + PropertyDeclarations relinkablePropertyDeclarations; + Prototypes relinkablePrototypes; + TypeIds deletedTypeIds; TypeIds updatedTypeIds; - updatedTypeIds.reserve(types.size()); + updatedTypeIds.reserve(package.types.size()); TypeIds typeIdsToBeDeleted; - auto sourceIdValues = Utils::transform<std::vector>(sourceIds, [](SourceId sourceId) { + auto sourceIdValues = Utils::transform<std::vector>(package.sourceIds, [](SourceId sourceId) { return &sourceId; }); std::sort(sourceIdValues.begin(), sourceIdValues.end()); - synchronizeFileStatuses(fileStatuses, sourceIdValues); - synchronizeModules(modules, typeIdsToBeDeleted, sourceIdValues); - synchronizeImports(imports, sourceIdValues); - synchronizeTypes(types, + synchronizeFileStatuses(package.fileStatuses, sourceIdValues); + synchronizeImports(package.imports, sourceIdValues); + synchronizeTypes(package.types, updatedTypeIds, insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations); - - deleteNotUpdatedTypes(updatedTypeIds, sourceIdValues, typeIdsToBeDeleted); + updatedAliasPropertyDeclarations, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + sourceIdValues); + + deleteNotUpdatedTypes(updatedTypeIds, + sourceIdValues, + typeIdsToBeDeleted, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + deletedTypeIds); + + relink(relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + deletedTypeIds); linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); transaction.commit(); } - ModuleId fetchModuleId(Utils::SmallStringView moduleName) + ModuleId moduleId(Utils::SmallStringView moduleName) override { - Sqlite::DeferredTransaction transaction{database}; - - ModuleId moduleId = fetchModuleIdUnguarded(moduleName); - - transaction.commit(); - - return moduleId; + return moduleCache.id(moduleName); } - ModuleIds fetchModuleIds(const Storage::Modules &modules) + Utils::SmallString moduleName(ModuleId moduleId) { - Sqlite::DeferredTransaction transaction{database}; - - ModuleIds moduleIds = fetchModuleIdsUnguarded(modules); - - transaction.commit(); + if (!moduleId) + throw ModuleDoesNotExists{}; - return moduleIds; + return moduleCache.value(moduleId); } PropertyDeclarationId fetchPropertyDeclarationByTypeIdAndName(TypeId typeId, @@ -132,9 +143,9 @@ public: static_cast<void *>(moduleIds.data()), static_cast<long long>(moduleIds.size()), name); } - TypeId fetchTypeIdByName(ModuleId moduleId, Utils::SmallStringView name) + TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name) { - return selectTypeIdByModuleIdAndNameStatement.template valueWithTransaction<TypeId>(&moduleId, + return selectTypeIdBySourceIdAndNameStatement.template valueWithTransaction<TypeId>(&sourceId, name); } @@ -283,11 +294,6 @@ public: return writeSourceId(sourceContextId, sourceName); } - auto fetchAllModules() const - { - return selectAllModulesStatement.template valuesWithTransaction<Storage::Module>(128); - } - auto fetchAllFileStatuses() const { return selectAllFileStatusesStatement.template rangeWithTransaction<FileStatus>(); @@ -299,9 +305,79 @@ public: &sourceId); } - SourceIds fetchSourceDependencieIds(SourceId sourceId) const override { return {}; } + Storage::ProjectDatas fetchProjectDatas(SourceId sourceId) const override + { + return Storage::ProjectDatas{}; + } private: + class ModuleStorageAdapter + { + public: + auto fetchId(const Utils::SmallStringView name) { return storage.fetchModuleId(name); } + + auto fetchValue(ModuleId id) { return storage.fetchModuleName(id); } + + auto fetchAll() { return storage.fetchAllModules(); } + + ProjectStorage &storage; + }; + + class Module : public StorageCacheEntry<Utils::PathString, Utils::SmallStringView, ModuleId> + { + using Base = StorageCacheEntry<Utils::PathString, Utils::SmallStringView, ModuleId>; + + public: + using Base::Base; + + friend bool operator==(const Module &first, const Module &second) + { + return first.id == second.id && first.value == second.value; + } + }; + + friend ModuleStorageAdapter; + + static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept + { + return Utils::reverseCompare(first, second) < 0; + } + + using ModuleCache = StorageCache<Utils::PathString, + Utils::SmallStringView, + ModuleId, + ModuleStorageAdapter, + NonLockingMutex, + moduleNameLess, + Module>; + + ModuleId fetchModuleId(Utils::SmallStringView moduleName) + { + Sqlite::DeferredTransaction transaction{database}; + + ModuleId moduleId = fetchModuleIdUnguarded(moduleName); + + transaction.commit(); + + return moduleId; + } + + auto fetchModuleName(ModuleId id) + { + Sqlite::DeferredTransaction transaction{database}; + + auto moduleName = fetchModuleNameUnguarded(id); + + transaction.commit(); + + return moduleName; + } + + auto fetchAllModules() const + { + return selectAllModulesStatement.template valuesWithTransaction<Module>(128); + } + class AliasPropertyDeclaration { public: @@ -318,6 +394,13 @@ private: , aliasPropertyDeclarationId{aliasPropertyDeclarationId} {} + friend bool operator<(const AliasPropertyDeclaration &first, + const AliasPropertyDeclaration &second) + { + return std::tie(first.typeId, first.propertyDeclarationId) + < std::tie(second.typeId, second.propertyDeclarationId); + } + public: TypeId typeId; PropertyDeclarationId propertyDeclarationId; @@ -326,6 +409,8 @@ private: PropertyDeclarationId aliasPropertyDeclarationId; }; + using AliasPropertyDeclarations = std::vector<AliasPropertyDeclaration>; + class PropertyDeclaration { public: @@ -345,12 +430,20 @@ private: , importedTypeNameId{importedTypeNameId} {} + friend bool operator<(const PropertyDeclaration &first, const PropertyDeclaration &second) + { + return std::tie(first.typeId, first.propertyDeclarationId) + < std::tie(second.typeId, second.propertyDeclarationId); + } + public: TypeId typeId; PropertyDeclarationId propertyDeclarationId; ImportedTypeNameId importedTypeNameId; }; + using PropertyDeclarations = std::vector<PropertyDeclaration>; + class Prototype { public: @@ -359,15 +452,58 @@ private: , prototypeNameId{std::move(prototypeNameId)} {} + friend bool operator<(Prototype first, Prototype second) + { + return first.typeId < second.typeId; + } + public: TypeId typeId; ImportedTypeNameId prototypeNameId; }; + using Prototypes = std::vector<Prototype>; + + template<typename Type> + struct TypeCompare + { + bool operator()(const Type &type, TypeId typeId) { return type.typeId < typeId; }; + + bool operator()(TypeId typeId, const Type &type) { return typeId < type.typeId; }; + + bool operator()(const Type &first, const Type &second) + { + return first.typeId < second.typeId; + }; + }; + + template<typename Property> + struct PropertyCompare + { + bool operator()(const Property &property, PropertyDeclarationId id) + { + return property.propertyDeclarationId < id; + }; + + bool operator()(PropertyDeclarationId id, const Property &property) + { + return id < property.propertyDeclarationId; + }; + + bool operator()(const Property &first, const Property &second) + { + return first.propertyDeclarationId < second.propertyDeclarationId; + }; + }; + void synchronizeTypes(Storage::Types &types, TypeIds &updatedTypeIds, - std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations, - std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations) + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + const std::vector<int> &sourceIdValues) { Storage::ExportedTypes exportedTypes; exportedTypes.reserve(types.size() * 3); @@ -381,16 +517,19 @@ private: extractExportedTypes(typeId, type, exportedTypes); } - synchronizeExportedTypes(updatedTypeIds, exportedTypes); + synchronizeExportedTypes(sourceIdValues, + updatedTypeIds, + exportedTypes, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes); - for (auto &&type : types) - syncPrototypes(type); - - for (auto &&type : types) - resetRemovedAliasPropertyDeclarationsToNull(type.typeId, type.propertyDeclarations); - - for (auto &&type : types) - syncDeclarations(type, insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); + syncPrototypes(types, relinkablePrototypes); + resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations); + syncDeclarations(types, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + relinkablePropertyDeclarations); } void synchronizeFileStatuses(FileStatuses &fileStatuses, const std::vector<int> &sourceIdValues) @@ -428,43 +567,10 @@ private: Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove); } - void synchronizeModules(Storage::Modules &modules, - TypeIds &typeIdsToBeDeleted, - const std::vector<int> &moduleIdValues) - { - auto compareKey = [](auto &&first, auto &&second) { - return first.sourceId.id - second.sourceId.id; - }; - - std::sort(modules.begin(), modules.end(), [&](auto &&first, auto &&second) { - return compareKey(first, second) < 0; - }); - - auto range = selectModulesForIdsStatement.template range<Storage::ModuleView>( - Utils::span(moduleIdValues)); - - auto insert = [&](Storage::Module &module) { - insertModuleStatement.write(module.name, &module.sourceId); - }; - - auto update = [&](const Storage::ModuleView &moduleView, Storage::Module &module) { - if (moduleView.name != module.name) - updateModuleStatement.write(&moduleView.sourceId, module.name); - }; - - auto remove = [&](const Storage::ModuleView &moduleView) { - deleteModuleStatement.write(&moduleView.sourceId); - selectTypeIdsForModuleIdStatement.readTo(typeIdsToBeDeleted, &moduleView.sourceId); - }; - - Sqlite::insertUpdateDelete(range, modules, compareKey, insert, update, remove); - } - void synchronizeImports(Storage::Imports &imports, std::vector<int> &sourceIdValues) { deleteDocumentImportsForDeletedDocuments(imports, sourceIdValues); - addModuleIdToImports(imports); synchronizeDocumentImports(imports, sourceIdValues); } @@ -487,50 +593,28 @@ private: deleteDocumentImportsWithSourceIdsStatement.write(Utils::span{documentSourceIdsToBeDeleted}); } - void synchronizeModulesAndUpdatesModuleIds(Storage::Modules &modules, - TypeIds &typeIdsToBeDeleted, - const std::vector<int> &moduleIds) + ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const { - auto compareKey = [](auto &&first, auto &&second) { - return first.sourceId.id - second.sourceId.id; - }; - - std::sort(modules.begin(), modules.end(), [&](auto &&first, auto &&second) { - return compareKey(first, second) < 0; - }); - - auto range = selectModulesForIdsStatement.template range<Storage::ModuleView>( - Utils::span(moduleIds)); - - auto insert = [&](Storage::Module &module) { - insertModuleStatement.write(module.name, &module.sourceId); - }; - - auto update = [&](const Storage::ModuleView &moduleView, Storage::Module &module) { - if (moduleView.name != module.name) - updateModuleStatement.write(&moduleView.sourceId, module.name); - }; + auto moduleId = selectModuleIdByNameStatement.template value<ModuleId>(name); - auto remove = [&](const Storage::ModuleView &moduleView) { - deleteModuleStatement.write(&moduleView.sourceId); - selectTypeIdsForModuleIdStatement.readTo(typeIdsToBeDeleted, &moduleView.sourceId); - }; + if (moduleId) + return moduleId; - Sqlite::insertUpdateDelete(range, modules, compareKey, insert, update, remove); + return insertModuleNameStatement.template value<ModuleId>(name); } - ModuleId fetchModuleIdUnguarded(const Storage::Module &module) const + auto fetchModuleNameUnguarded(ModuleId id) const { - return fetchModuleIdUnguarded(module.name); - } + auto moduleName = selectModuleNameStatement.template value<Utils::PathString>(&id); - ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const - { - return selectModuleIdByNameStatement.template value<ModuleId>(name); + if (moduleName.empty()) + throw ModuleDoesNotExists{}; + + return moduleName; } void handleAliasPropertyDeclarationsWithPropertyType( - TypeId typeId, std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations) + TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { auto callback = [&](long long typeId, long long propertyDeclarationId, @@ -555,8 +639,7 @@ private: } void prepareLinkingOfAliasPropertiesDeclarationsWithAliasId( - PropertyDeclarationId aliasId, - std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations) + PropertyDeclarationId aliasId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { auto callback = [&](long long propertyDeclarationId, long long propertyImportedTypeNameId, @@ -578,14 +661,14 @@ private: &aliasId); } - void handlePropertyDeclarationWithPropertyType( - TypeId typeId, std::vector<PropertyDeclaration> &relinkablePropertyDeclarations) + void handlePropertyDeclarationWithPropertyType(TypeId typeId, + PropertyDeclarations &relinkablePropertyDeclarations) { updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, &typeId); } - void handlePrototypes(TypeId prototypeId, std::vector<Prototype> &relinkablePrototypes) + void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) { auto callback = [&](long long typeId, long long prototypeNameId) { relinkablePrototypes.emplace_back(TypeId{typeId}, ImportedTypeNameId{prototypeNameId}); @@ -597,9 +680,9 @@ private: } void deleteType(TypeId typeId, - std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations, - std::vector<PropertyDeclaration> &relinkablePropertyDeclarations, - std::vector<Prototype> &relinkablePrototypes) + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes) { handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations); @@ -612,72 +695,85 @@ private: deleteTypeStatement.write(&typeId); } - void relinkAliasPropertyDeclarations( - const std::vector<AliasPropertyDeclaration> &aliasPropertyDeclarations, - const TypeIds &deletedTypeIds) + void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, + const TypeIds &deletedTypeIds) { - for (const AliasPropertyDeclaration &alias : aliasPropertyDeclarations) { - if (std::binary_search(deletedTypeIds.begin(), deletedTypeIds.end(), alias.typeId)) - continue; + std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); - auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); + Utils::set_greedy_difference( + aliasPropertyDeclarations.cbegin(), + aliasPropertyDeclarations.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const AliasPropertyDeclaration &alias) { + auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); - if (!typeId) - throw TypeNameDoesNotExists{}; + if (!typeId) + throw TypeNameDoesNotExists{}; - auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( - typeId, alias.aliasPropertyName); + auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( + typeId, alias.aliasPropertyName); - updatePropertyDeclarationWithAliasAndTypeStatement.write(&alias.propertyDeclarationId, - &propertyTypeId, - propertyTraits, - &alias.aliasImportedTypeNameId, - &aliasId); - } + updatePropertyDeclarationWithAliasAndTypeStatement.write(&alias.propertyDeclarationId, + &propertyTypeId, + propertyTraits, + &alias.aliasImportedTypeNameId, + &aliasId); + }, + TypeCompare<AliasPropertyDeclaration>{}); } - void relinkPropertyDeclarations(const std::vector<PropertyDeclaration> &relinkablePropertyDeclaration, + void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, const TypeIds &deletedTypeIds) { - for (const PropertyDeclaration &property : relinkablePropertyDeclaration) { - if (std::binary_search(deletedTypeIds.begin(), deletedTypeIds.end(), property.typeId)) - continue; + std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); - TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); + Utils::set_greedy_difference( + relinkablePropertyDeclaration.cbegin(), + relinkablePropertyDeclaration.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const PropertyDeclaration &property) { + TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); - if (!propertyTypeId) - throw TypeNameDoesNotExists{}; + if (!propertyTypeId) + throw TypeNameDoesNotExists{}; - updatePropertyDeclarationTypeStatement.write(&property.propertyDeclarationId, - &propertyTypeId); - } + updatePropertyDeclarationTypeStatement.write(&property.propertyDeclarationId, + &propertyTypeId); + }, + TypeCompare<PropertyDeclaration>{}); } - void relinkPrototypes(std::vector<Prototype> relinkablePrototypes, const TypeIds &deletedTypeIds) + void relinkPrototypes(Prototypes &relinkablePrototypes, const TypeIds &deletedTypeIds) { - for (const Prototype &prototype : relinkablePrototypes) { - if (std::binary_search(deletedTypeIds.begin(), deletedTypeIds.end(), prototype.typeId)) - continue; + std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); - TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); + Utils::set_greedy_difference( + relinkablePrototypes.cbegin(), + relinkablePrototypes.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const Prototype &prototype) { + TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); - if (!prototypeId) - throw TypeNameDoesNotExists{}; + if (!prototypeId) + throw TypeNameDoesNotExists{}; - updateTypePrototypeStatement.write(&prototype.typeId, &prototypeId); - checkForPrototypeChainCycle(prototype.typeId); - } + updateTypePrototypeStatement.write(&prototype.typeId, &prototypeId); + checkForPrototypeChainCycle(prototype.typeId); + }, + TypeCompare<Prototype>{}); } void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const std::vector<int> &sourceIdValues, - const TypeIds &typeIdsToBeDeleted) + const TypeIds &typeIdsToBeDeleted, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + TypeIds &deletedTypeIds) { - std::vector<AliasPropertyDeclaration> relinkableAliasPropertyDeclarations; - std::vector<PropertyDeclaration> relinkablePropertyDeclarations; - std::vector<Prototype> relinkablePrototypes; - TypeIds deletedTypeIds; - auto updatedTypeIdValues = Utils::transform<std::vector>(updatedTypeIds, [](TypeId typeId) { return &typeId; }); @@ -696,7 +792,13 @@ private: Utils::span(updatedTypeIdValues)); for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted) callback(&typeIdToBeDeleted); + } + void relink(AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + TypeIds &deletedTypeIds) + { std::sort(deletedTypeIds.begin(), deletedTypeIds.end()); relinkPrototypes(relinkablePrototypes, deletedTypeIds); @@ -704,7 +806,7 @@ private: relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds); } - void linkAliasPropertyDeclarationAliasIds(const std::vector<AliasPropertyDeclaration> &aliasDeclarations) + void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations) { for (const auto &aliasDeclaration : aliasDeclarations) { auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId); @@ -722,7 +824,7 @@ private: } } - void updateAliasPropertyDeclarationValues(const std::vector<AliasPropertyDeclaration> &aliasDeclarations) + void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations) { for (const auto &aliasDeclaration : aliasDeclarations) { updatetPropertiesDeclarationValuesOfAliasStatement.write( @@ -732,14 +834,14 @@ private: } } - void checkAliasPropertyDeclarationCycles(const std::vector<AliasPropertyDeclaration> &aliasDeclarations) + void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations) { for (const auto &aliasDeclaration : aliasDeclarations) checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId); } - void linkAliases(const std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations, - const std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations) + void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) { linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations); @@ -751,16 +853,25 @@ private: updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); } - void synchronizeExportedTypes(const TypeIds &typeIds, Storage::ExportedTypes &exportedTypes) + void synchronizeExportedTypes(const std::vector<int> &sourceIdValues, + const TypeIds &updatedTypeIds, + Storage::ExportedTypes &exportedTypes, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes) { std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { return std::tie(first.moduleId, first.name, first.version) < std::tie(second.moduleId, second.name, second.version); }); - auto range = selectExportedTypesForTypeIdStatement.template range<Storage::ExportedTypeView>( - const_cast<void *>(static_cast<const void *>(typeIds.data())), - static_cast<long long>(typeIds.size())); + Utils::span typeIdValues{static_cast<const TypeIds::value_type::DatabaseType *>( + &updatedTypeIds.data()->id), + updatedTypeIds.size()}; + + auto range = selectExportedTypesForSourceIdsStatement + .template range<Storage::ExportedTypeView>(Utils::span{sourceIdValues}, + typeIdValues); auto compareKey = [](const Storage::ExportedTypeView &view, const Storage::ExportedType &type) -> long long { @@ -780,53 +891,55 @@ private: }; auto insert = [&](const Storage::ExportedType &type) { - if (type.version) { - upsertExportedTypeNamesWithVersionStatement.write(&type.moduleId, - type.name, - static_cast<long long>( - Storage::TypeNameKind::Exported), - type.version.major.value, - type.version.minor.value, - &type.typeId); - - } else if (type.version.major) { - upsertExportedTypeNamesWithMajorVersionStatement - .write(&type.moduleId, - type.name, - static_cast<long long>(Storage::TypeNameKind::Exported), - type.version.major.value, - &type.typeId); - } else { - upsertExportedTypeNamesWithoutVersionStatement - .write(&type.moduleId, - type.name, - static_cast<long long>(Storage::TypeNameKind::Exported), - &type.typeId); + 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::ModuleDoesNotExists{}; } }; auto update = [&](const Storage::ExportedTypeView &view, const Storage::ExportedType &type) { - if (view.typeId != type.typeId) + if (view.typeId != type.typeId) { + handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithPropertyType(view.typeId, + relinkableAliasPropertyDeclarations); + handlePrototypes(view.typeId, relinkablePrototypes); updateExportedTypeNameTypeIdStatement.write(&view.exportedTypeNameId, &type.typeId); + } }; auto remove = [&](const Storage::ExportedTypeView &view) { + handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithPropertyType(view.typeId, + relinkableAliasPropertyDeclarations); + handlePrototypes(view.typeId, relinkablePrototypes); deleteExportedTypeNameStatement.write(&view.exportedTypeNameId); }; Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove); } - void upsertNativeType(ModuleId moduleId, Utils::SmallStringView name, TypeId typeId) - { - upsertExportedTypeNameStatement.write(&moduleId, - name, - static_cast<long long>(Storage::TypeNameKind::Native), - &typeId); - } - void synchronizePropertyDeclarationsInsertAlias( - std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, const Storage::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) @@ -872,7 +985,7 @@ private: } void synchronizePropertyDeclarationsUpdateAlias( - std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, const Storage::PropertyDeclarationView &view, const Storage::PropertyDeclaration &value, SourceId sourceId) @@ -887,7 +1000,8 @@ private: void synchronizePropertyDeclarationsUpdateProperty(const Storage::PropertyDeclarationView &view, const Storage::PropertyDeclaration &value, - SourceId sourceId) + SourceId sourceId, + PropertyDeclarationIds &propertyDeclarationIds) { auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); @@ -906,14 +1020,15 @@ private: &propertyImportedTypeNameId); updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement .write(&view.id, &propertyTypeId, static_cast<int>(value.traits)); + propertyDeclarationIds.push_back(view.id); } - void synchronizePropertyDeclarations( - TypeId typeId, - Storage::PropertyDeclarations &propertyDeclarations, - SourceId sourceId, - std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations, - std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations) + void synchronizePropertyDeclarations(TypeId typeId, + Storage::PropertyDeclarations &propertyDeclarations, + SourceId sourceId, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarationIds &propertyDeclarationIds) { std::sort(propertyDeclarations.begin(), propertyDeclarations.end(), @@ -947,8 +1062,12 @@ private: view, value, sourceId); + propertyDeclarationIds.push_back(view.id); } else { - synchronizePropertyDeclarationsUpdateProperty(view, value, sourceId); + synchronizePropertyDeclarationsUpdateProperty(view, + value, + sourceId, + propertyDeclarationIds); } }; @@ -962,14 +1081,20 @@ private: } deletePropertyDeclarationStatement.write(&view.id); + propertyDeclarationIds.push_back(view.id); }; Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove); } - void resetRemovedAliasPropertyDeclarationsToNull(TypeId typeId, - Storage::PropertyDeclarations &aliasDeclarations) + void resetRemovedAliasPropertyDeclarationsToNull(Storage::Type &type, + PropertyDeclarationIds &propertyDeclarationIds) { + if (type.changeLevel == Storage::ChangeLevel::Minimal) + return; + + Storage::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations; + class AliasPropertyDeclarationView { public: @@ -992,7 +1117,7 @@ private: }); auto range = selectPropertyDeclarationsWithAliasForTypeIdStatement - .template range<AliasPropertyDeclarationView>(&typeId); + .template range<AliasPropertyDeclarationView>(&type.typeId); auto compareKey = [](const AliasPropertyDeclarationView &view, const Storage::PropertyDeclaration &value) { @@ -1006,29 +1131,24 @@ private: auto remove = [&](const AliasPropertyDeclarationView &view) { updatePropertyDeclarationAliasIdToNullStatement.write(&view.id); + propertyDeclarationIds.push_back(view.id); }; Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove); } - ModuleIds fetchModuleIdsUnguarded(const Storage::Modules &modules) + void resetRemovedAliasPropertyDeclarationsToNull( + Storage::Types &types, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { - ModuleIds moduleIds; - moduleIds.reserve(moduleIds.size()); + PropertyDeclarationIds propertyDeclarationIds; + propertyDeclarationIds.reserve(types.size()); - for (auto &&module : modules) - moduleIds.push_back(fetchModuleIdUnguarded(module)); + for (auto &&type : types) + resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds); - return moduleIds; - } - - void addModuleIdToImports(Storage::Imports &imports) - { - for (Storage::Import &import : imports) { - import.moduleId = fetchModuleIdUnguarded(import.name); - if (!import.moduleId) - throw ModuleDoesNotExists{}; - } + removeRelinkableEntries(relinkableAliasPropertyDeclarations, + propertyDeclarationIds, + PropertyCompare<AliasPropertyDeclaration>{}); } void synchronizeDocumentImports(Storage::Imports &imports, const std::vector<int> &sourceIdValues) @@ -1096,7 +1216,7 @@ private: json.append(parameter.name); json.append("\",\"tn\":\""); json.append(parameter.typeName); - if (parameter.traits == Storage::PropertyDeclarationTraits::Non) { + if (parameter.traits == Storage::PropertyDeclarationTraits::None) { json.append("\"}"); } else { json.append("\",\"tr\":"); @@ -1257,60 +1377,88 @@ private: Storage::ExportedTypes &exportedTypes) { for (const auto &exportedType : type.exportedTypes) - exportedTypes.emplace_back(exportedType.name, exportedType.version, typeId, type.moduleId); + exportedTypes.emplace_back(exportedType.name, + exportedType.version, + typeId, + exportedType.moduleId); } - struct ModuleAndTypeId - { - ModuleAndTypeId() = default; - ModuleAndTypeId(int moduleId, long long typeId) - : moduleId{moduleId} - , typeId{typeId} - {} - - ModuleId moduleId; - TypeId typeId; - }; - TypeId declareType(Storage::Type &type) { - if (!type.moduleId && type.typeName.isEmpty()) { - auto [moduleId, typeId] = selectModuleAndTypeIdBySourceIdStatement - .template value<ModuleAndTypeId>(&type.sourceId); - type.typeId = typeId; - type.moduleId = moduleId; + if (type.typeName.isEmpty()) { + type.typeId = selectTypeIdBySourceIdStatement.template value<TypeId>(&type.sourceId); + return type.typeId; } - if (!type.moduleId) - throw ModuleDoesNotExists{}; - - type.typeId = upsertTypeStatement.template value<TypeId>(&type.moduleId, + type.typeId = upsertTypeStatement.template value<TypeId>(&type.sourceId, type.typeName, - static_cast<int>(type.accessSemantics), - &type.sourceId); + static_cast<int>( + type.accessSemantics)); if (!type.typeId) - type.typeId = selectTypeIdByModuleIdAndNameStatement.template value<TypeId>(&type.moduleId, + type.typeId = selectTypeIdBySourceIdAndNameStatement.template value<TypeId>(&type.sourceId, type.typeName); - upsertNativeType(type.moduleId, type.typeName, type.typeId); return type.typeId; } void syncDeclarations(Storage::Type &type, - std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations, - std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations) + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarationIds &propertyDeclarationIds) { - auto typeId = type.typeId; - synchronizePropertyDeclarations(typeId, + if (type.changeLevel == Storage::ChangeLevel::Minimal) + return; + + synchronizePropertyDeclarations(type.typeId, type.propertyDeclarations, type.sourceId, insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations); - synchronizeFunctionDeclarations(typeId, type.functionDeclarations); - synchronizeSignalDeclarations(typeId, type.signalDeclarations); - synchronizeEnumerationDeclarations(typeId, type.enumerationDeclarations); + updatedAliasPropertyDeclarations, + propertyDeclarationIds); + synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations); + synchronizeSignalDeclarations(type.typeId, type.signalDeclarations); + synchronizeEnumerationDeclarations(type.typeId, type.enumerationDeclarations); + } + + template<typename Relinkable, typename Ids, typename Compare> + void removeRelinkableEntries(std::vector<Relinkable> &relinkables, Ids &ids, Compare compare) + { + std::vector<Relinkable> newRelinkables; + newRelinkables.reserve(relinkables.size()); + + std::sort(ids.begin(), ids.end()); + std::sort(relinkables.begin(), relinkables.end(), compare); + + Utils::set_greedy_difference( + relinkables.begin(), + relinkables.end(), + ids.cbegin(), + ids.cend(), + [&](Relinkable &entry) { newRelinkables.push_back(std::move(entry)); }, + compare); + + relinkables = std::move(newRelinkables); + } + + void syncDeclarations(Storage::Types &types, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations) + { + PropertyDeclarationIds propertyDeclarationIds; + propertyDeclarationIds.reserve(types.size() * 10); + + for (auto &&type : types) + syncDeclarations(type, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + propertyDeclarationIds); + + removeRelinkableEntries(relinkablePropertyDeclarations, + propertyDeclarationIds, + PropertyCompare<PropertyDeclaration>{}); } void checkForPrototypeChainCycle(TypeId typeId) const @@ -1338,7 +1486,7 @@ private: &propertyDeclarationId); } - void syncPrototypes(Storage::Type &type) + void syncPrototype(Storage::Type &type, TypeIds &typeIds) { if (type.changeLevel == Storage::ChangeLevel::Minimal) return; @@ -1358,22 +1506,35 @@ private: updatePrototypeStatement.write(&type.typeId, &prototypeId, &prototypeTypeNameId); checkForPrototypeChainCycle(type.typeId); } + + typeIds.push_back(type.typeId); + } + + void syncPrototypes(Storage::Types &types, Prototypes &relinkablePrototypes) + { + TypeIds typeIds; + typeIds.reserve(types.size()); + + for (auto &type : types) + syncPrototype(type, typeIds); + + removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare<Prototype>{}); } ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const { if (import.version) { - return selectImportIdBySourceIdAndImportNameAndVersionStatement.template value<ImportId>( - &sourceId, import.name, import.version.major.value, import.version.minor.value); + return selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value<ImportId>( + &sourceId, &import.moduleId, import.version.major.value, import.version.minor.value); } if (import.version.major) { - return selectImportIdBySourceIdAndImportNameAndMajorVersionStatement - .template value<ImportId>(&sourceId, import.name, import.version.major.value); + return selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement + .template value<ImportId>(&sourceId, &import.moduleId, import.version.major.value); } - return selectImportIdBySourceIdAndImportNameStatement.template value<ImportId>(&sourceId, - import.name); + return selectImportIdBySourceIdAndModuleIdStatement.template value<ImportId>(&sourceId, + &import.moduleId); } ImportedTypeNameId fetchImportedTypeNameId(const Storage::ImportedTypeName &name, SourceId sourceId) @@ -1440,9 +1601,6 @@ private: &typeNameId); } - if (kind == Storage::TypeNameKind::Native) - return selectTypeIdForNativeTypeNameNamesStatement.template value<TypeId>(&typeNameId); - return selectTypeIdForImportedTypeNameNamesStatement.template value<TypeId>(&typeNameId); } @@ -1649,21 +1807,16 @@ private: typesTable.setUseIfNotExists(true); typesTable.setName("types"); typesTable.addColumn("typeId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}}); - auto &moduleIdColumn = typesTable.addForeignKeyColumn("moduleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::Enforment::Deferred); + auto &sourceIdColumn = typesTable.addColumn("sourceId"); auto &typesNameColumn = typesTable.addColumn("name"); typesTable.addColumn("accessSemantics"); - typesTable.addColumn("sourceId"); typesTable.addForeignKeyColumn("prototypeId", typesTable, Sqlite::ForeignKeyAction::NoAction, Sqlite::ForeignKeyAction::Restrict); typesTable.addColumn("prototypeNameId"); - typesTable.addUniqueIndex({moduleIdColumn, typesNameColumn}); + typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); typesTable.initialize(database); @@ -1706,23 +1859,20 @@ private: auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", foreignModuleIdColumn, Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::Enforment::Deferred); + Sqlite::ForeignKeyAction::NoAction); auto &nameColumn = table.addColumn("name"); - auto &kindColumn = table.addColumn("kind"); auto &typeIdColumn = table.addColumn("typeId"); auto &majorVersionColumn = table.addColumn("majorVersion"); auto &minorVersionColumn = table.addColumn("minorVersion"); - table.addUniqueIndex({moduleIdColumn, nameColumn, kindColumn}, + table.addUniqueIndex({moduleIdColumn, nameColumn}, "majorVersion IS NULL AND minorVersion IS NULL"); - table.addUniqueIndex({moduleIdColumn, nameColumn, kindColumn, majorVersionColumn}, + table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn}, "majorVersion IS NOT NULL AND minorVersion IS NULL"); - table.addUniqueIndex( - {moduleIdColumn, nameColumn, kindColumn, majorVersionColumn, minorVersionColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); + table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn, minorVersionColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); - table.addIndex({typeIdColumn}, "kind=1"); + table.addIndex({typeIdColumn}); table.initialize(database); } @@ -1823,6 +1973,7 @@ private: Sqlite::Enforment::Deferred); auto &majorVersionColumn = table.addColumn("majorVersion"); auto &minorVersionColumn = table.addColumn("minorVersion"); + table.addColumn("kind"); table.addUniqueIndex({sourceIdColumn, moduleIdColumn}, "majorVersion IS NULL AND minorVersion IS NULL"); @@ -1856,18 +2007,18 @@ private: public: Database &database; Initializer initializer; + ModuleCache moduleCache{ModuleStorageAdapter{*this}}; ReadWriteStatement<1> upsertTypeStatement{ - "INSERT INTO types(moduleId, name, accessSemantics, sourceId) VALUES(?1, ?2, " - "?3, nullif(?4, -1)) ON CONFLICT DO UPDATE SET accessSemantics=excluded.accessSemantics, " - "sourceId=excluded.sourceId WHERE accessSemantics IS NOT excluded.accessSemantics OR " - "sourceId IS NOT excluded.sourceId RETURNING typeId", + "INSERT INTO types(sourceId, name, accessSemantics) VALUES(?1, ?2, ?3) ON CONFLICT DO " + "UPDATE SET accessSemantics=excluded.accessSemantics WHERE accessSemantics IS NOT " + "excluded.accessSemantics RETURNING typeId", database}; WriteStatement updatePrototypeStatement{ "UPDATE types SET prototypeId=?2, prototypeNameId=?3 WHERE typeId=?1 AND (prototypeId IS " "NOT ?2 OR prototypeNameId IS NOT ?3)", database}; mutable ReadStatement<1> selectTypeIdByExportedNameStatement{ - "SELECT typeId FROM exportedTypeNames WHERE name=?1 AND kind=1", database}; + "SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; mutable ReadStatement<1> selectPrototypeIdStatement{ "WITH RECURSIVE " " typeSelection(typeId) AS (" @@ -1925,18 +2076,14 @@ public: "INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database}; mutable ReadStatement<3> selectAllSourcesStatement{ "SELECT sourceName, sourceContextId, sourceId FROM sources", database}; - mutable ReadStatement<5> selectTypeByTypeIdStatement{ - "SELECT moduleId, name, (SELECT name FROM types WHERE typeId=outerTypes.prototypeId), " - "accessSemantics, ifnull(sourceId, -1) FROM types AS outerTypes WHERE typeId=?", + mutable ReadStatement<4> selectTypeByTypeIdStatement{ + "SELECT sourceId, name, prototypeId, accessSemantics FROM types WHERE typeId=?", database}; + mutable ReadStatement<4> selectExportedTypesByTypeIdStatement{ + "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM " + "exportedTypeNames WHERE typeId=?", database}; - mutable ReadStatement<3> selectExportedTypesByTypeIdStatement{ - "SELECT name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM exportedTypeNames " - "WHERE typeId=? AND kind=1", - database}; - mutable ReadStatement<6> selectTypesStatement{ - "SELECT moduleId, name, typeId, (SELECT name FROM types WHERE " - "typeId=t.prototypeId), accessSemantics, ifnull(sourceId, -1) FROM types AS " - "t", + mutable ReadStatement<5> selectTypesStatement{ + "SELECT sourceId, name, typeId, ifnull(prototypeId, -1), accessSemantics FROM types", database}; ReadStatement<1> selectNotUpdatedTypesInSourcesStatement{ "SELECT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN carray(?2))", @@ -1953,10 +2100,9 @@ public: "DELETE FROM signalDeclarations WHERE typeId=?", database}; WriteStatement deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database}; mutable ReadStatement<4> selectPropertyDeclarationsByTypeIdStatement{ - "SELECT name, (SELECT name FROM types WHERE typeId=pd.propertyTypeId), propertyTraits, " - "(SELECT name FROM propertyDeclarations WHERE " - "propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM propertyDeclarations AS pd " - "WHERE typeId=?", + "SELECT name, nullif(propertyTypeId, -1), propertyTraits, (SELECT name FROM " + "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM " + "propertyDeclarations AS pd WHERE typeId=?", database}; ReadStatement<6> selectPropertyDeclarationsForTypeIdStatement{ "SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, " @@ -2081,25 +2227,18 @@ public: database}; WriteStatement deleteEnumerationDeclarationStatement{ "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database}; - WriteStatement insertModuleStatement{"INSERT INTO modules(name, moduleId) VALUES(?1, ?2)", - database}; - WriteStatement updateModuleStatement{"UPDATE modules SET name=?2 WHERE moduleId=?1", database}; - WriteStatement deleteModuleStatement{"DELETE FROM modules WHERE moduleId=?", database}; mutable ReadStatement<1> selectModuleIdByNameStatement{ "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database}; - mutable ReadStatement<2> selectModulesForIdsStatement{ - "SELECT name, moduleId FROM modules WHERE moduleId IN carray(?1) ORDER BY " - "moduleId", - database}; - mutable ReadStatement<2> selectAllModulesStatement{ - "SELECT name, moduleId FROM modules ORDER BY moduleId", database}; - mutable ReadStatement<1> selectTypeIdsForModuleIdStatement{ - "SELECT typeId FROM types WHERE moduleId=?", database}; - mutable ReadStatement<1> selectTypeIdByModuleIdAndNameStatement{ - "SELECT typeId FROM types WHERE moduleId=?1 and name=?2", database}; + mutable ReadWriteStatement<1> insertModuleNameStatement{ + "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database}; + mutable ReadStatement<1> selectModuleNameStatement{ + "SELECT name FROM modules WHERE moduleId =?1", database}; + mutable ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", database}; + mutable ReadStatement<1> selectTypeIdBySourceIdAndNameStatement{ + "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database}; mutable ReadStatement<1> selectTypeIdByModuleIdsAndExportedNameStatement{ "SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND " - "name=?3 AND kind=1", + "name=?3", database}; mutable ReadStatement<5> selectDocumentImportForSourceIdStatement{ "SELECT importId, sourceId, moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) " @@ -2241,8 +2380,8 @@ public: WriteStatement deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1", database}; WriteStatement updateFileStatusStatement{ "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database}; - ReadStatement<2> selectModuleAndTypeIdBySourceIdStatement{ - "SELECT moduleId, typeId FROM types WHERE sourceId=?", database}; + ReadStatement<1> selectTypeIdBySourceIdStatement{"SELECT typeId FROM types WHERE sourceId=?", + database}; mutable ReadStatement<1> selectImportedTypeNameIdStatement{ "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 " "AND name=?3 LIMIT 1", @@ -2251,24 +2390,24 @@ public: "INSERT INTO importedTypeNames(kind, importOrSourceId, name) VALUES (?1, ?2, ?3) " "RETURNING importedTypeNameId", database}; - mutable ReadStatement<1> selectImportIdBySourceIdAndImportNameStatement{ - "SELECT importId FROM documentImports JOIN modules AS m USING(moduleId) WHERE sourceId=?1 " - "AND m.name=?2 AND majorVersion IS NULL AND minorVersion IS NULL LIMIT 1", + mutable ReadStatement<1> 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> selectImportIdBySourceIdAndImportNameAndMajorVersionStatement{ - "SELECT importId FROM documentImports JOIN modules AS m USING(moduleId) WHERE sourceId=?1 " - "AND m.name=?2 AND majorVersion=?3 AND minorVersion IS NULL LIMIT 1", + mutable ReadStatement<1> selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement{ + "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " + "majorVersion=?3 AND minorVersion IS NULL LIMIT 1", database}; - mutable ReadStatement<1> selectImportIdBySourceIdAndImportNameAndVersionStatement{ - "SELECT importId FROM documentImports JOIN modules AS m USING(moduleId) WHERE sourceId=?1 " - "AND m.name=?2 AND majorVersion=?3 AND minorVersion=?4 LIMIT 1", + mutable ReadStatement<1> selectImportIdBySourceIdAndModuleIdAndVersionStatement{ + "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " + "majorVersion=?3 AND minorVersion=?4 LIMIT 1", database}; mutable ReadStatement<1> selectKindFromImportedTypeNamesStatement{ "SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database}; mutable ReadStatement<1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{ "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " "importOrSourceId=importId JOIN exportedTypeNames AS etn USING(moduleId) WHERE " - "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND etn.kind=1 AND " + "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", @@ -2276,39 +2415,29 @@ public: mutable ReadStatement<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 etn.kind=1 AND " + "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 etn.majorVersion DESC NULLS FIRST, " "etn.minorVersion DESC NULLS FIRST LIMIT 1", database}; - mutable ReadStatement<1> selectTypeIdForNativeTypeNameNamesStatement{ - "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " - "importOrSourceId=sourceId JOIN exportedTypeNames AS etn USING(moduleId) WHERE itn.kind=0 " - "AND importedTypeNameId=?1 AND itn.name=etn.name AND etn.kind=0 LIMIT 1", - database}; WriteStatement deleteAllSourcesStatement{"DELETE FROM sources", database}; WriteStatement deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database}; - mutable ReadStatement<6> selectExportedTypesForTypeIdStatement{ - "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, " - "exportedTypeNameId FROM exportedTypeNames WHERE typeId IN carray(?1, ?2, 'int64') AND " - "kind=1 ORDER BY moduleId, name, majorVersion, minorVersion", - database}; - WriteStatement upsertExportedTypeNamesWithVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, kind, majorVersion, minorVersion, typeId) " - "VALUES(?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT DO UPDATE SET typeId=excluded.typeId", - database}; - WriteStatement upsertExportedTypeNamesWithMajorVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, kind, majorVersion, typeId) " - "VALUES(?1, ?2, ?3, ?4, ?5) ON CONFLICT DO UPDATE SET typeId=excluded.typeId", - database}; - WriteStatement upsertExportedTypeNamesWithoutVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, kind, typeId) VALUES(?1, ?2, ?3, ?4) ON " - "CONFLICT DO UPDATE SET typeId=excluded.typeId", - database}; - WriteStatement upsertExportedTypeNameStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, kind, typeId) VALUES(?1, ?2, ?3, ?4) ON " - "CONFLICT DO UPDATE SET typeId=excluded.typeId WHERE typeId IS NOT excluded.typeId", - database}; + mutable ReadStatement<6> selectExportedTypesForSourceIdsStatement{ + "SELECT moduleId, etn.name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, " + "exportedTypeNameId FROM exportedTypeNames AS etn JOIN types USING(typeId) WHERE sourceId " + "IN carray(?1) OR typeId in carray(?2) ORDER BY moduleId, etn.name, majorVersion, " + "minorVersion", + database}; + WriteStatement insertExportedTypeNamesWithVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, minorVersion, typeId) " + "VALUES(?1, ?2, ?3, ?4, ?5)", + database}; + WriteStatement insertExportedTypeNamesWithMajorVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, typeId) " + "VALUES(?1, ?2, ?3, ?4)", + database}; + WriteStatement insertExportedTypeNamesWithoutVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, typeId) VALUES(?1, ?2, ?3)", database}; WriteStatement deleteExportedTypeNameStatement{ "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; WriteStatement updateExportedTypeNameTypeIdStatement{ diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index 0d23e11605..42790bded6 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -101,4 +101,10 @@ public: const char *what() const noexcept override { return "There is a prototype chain cycle!"; } }; +class CannotParseQmlTypesFile : std::exception +{ +public: + const char *what() const noexcept override { return "Cannot parse qml types file!"; } +}; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 302a1801a2..7d27bd22f1 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -33,15 +33,12 @@ namespace QmlDesigner { class ProjectStorageInterface { public: - virtual void synchronize(Storage::Modules modules, - Storage::Imports imports, - Storage::Types types, - SourceIds sourceIds, - FileStatuses fileStatuses) - = 0; + virtual void synchronize(Storage::SynchronizationPackage package) = 0; + + virtual ModuleId moduleId(Utils::SmallStringView name) = 0; virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0; - virtual SourceIds fetchSourceDependencieIds(SourceId sourceId) const = 0; + virtual Storage::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0; protected: ~ProjectStorageInterface() = default; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 50db7c4853..ef05539076 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -25,6 +25,7 @@ #pragma once +#include "filestatus.h" #include "projectstorageids.h" #include <utils/smallstring.h> @@ -35,10 +36,10 @@ namespace QmlDesigner::Storage { -enum class TypeAccessSemantics : int { Invalid, Reference, Value, Sequence, IsEnum = 1 << 8 }; +enum class TypeAccessSemantics : int { None, Reference, Value, Sequence, IsEnum = 1 << 8 }; enum class PropertyDeclarationTraits : unsigned int { - Non = 0, + None = 0, IsReadOnly = 1 << 0, IsPointer = 1 << 1, IsList = 1 << 2 @@ -121,38 +122,6 @@ public: VersionNumber minor; }; -class Module -{ -public: - explicit Module() = default; - - explicit Module(Utils::SmallStringView name, SourceId sourceId = SourceId{}) - : name{name} - , sourceId{sourceId} - {} - - explicit Module(QStringView name, SourceId sourceId = SourceId{}) - : name{name} - , sourceId{sourceId} - {} - - explicit Module(Utils::SmallStringView name, int sourceId) - : name{name} - , sourceId{sourceId} - {} - - friend bool operator==(const Module &first, const Module &second) - { - return first.name == second.name; - } - -public: - Utils::PathString name; - SourceId sourceId; -}; - -using Modules = std::vector<Module>; - enum class IsQualified : int { No, Yes }; inline int operator-(IsQualified first, IsQualified second) @@ -165,34 +134,36 @@ inline int operator<(IsQualified first, IsQualified second) return static_cast<int>(first) < static_cast<int>(second); } +enum class ImportKind : char { Module, Directory, QmlTypesDependency }; + class Import { public: explicit Import() = default; - explicit Import(Utils::SmallStringView name, Version version, SourceId sourceId) - : name{name} - , version{version} + explicit Import(ModuleId moduleId, Version version, SourceId sourceId) + : version{version} + , moduleId{moduleId} , sourceId{sourceId} {} - explicit Import(Utils::SmallStringView name, int majorVersion, int minorVersion, int sourceId) - : name{name} + explicit Import(int moduleId, int majorVersion, int minorVersion, int sourceId) + : moduleId{moduleId} , version{majorVersion, minorVersion} , sourceId{sourceId} {} friend bool operator==(const Import &first, const Import &second) { - return first.name == second.name && first.version == second.version + return first.moduleId == second.moduleId && first.version == second.version && first.sourceId == second.sourceId; } public: - Utils::PathString name; Version version; ModuleId moduleId; SourceId sourceId; + Utils::SmallString aliasName; }; using Imports = std::vector<Import>; @@ -269,6 +240,12 @@ public: , version{version} {} + explicit ExportedType(ModuleId moduleId, Utils::SmallStringView name, Version version = Version{}) + : name{name} + , version{version} + , moduleId{moduleId} + {} + explicit ExportedType(Utils::SmallStringView name, Version version, TypeId typeId, ModuleId moduleId) : name{name} , version{version} @@ -276,9 +253,10 @@ public: , moduleId{moduleId} {} - explicit ExportedType(Utils::SmallStringView name, int majorVersion, int minorVersion) + explicit ExportedType(int moduleId, Utils::SmallStringView name, int majorVersion, int minorVersion) : name{name} , version{majorVersion, minorVersion} + , moduleId{moduleId} {} friend bool operator==(const ExportedType &first, const ExportedType &second) @@ -299,6 +277,11 @@ class ExportedTypeView { public: explicit ExportedTypeView() = default; + explicit ExportedTypeView(ModuleId moduleId, Utils::SmallStringView name, Storage::Version version) + : name{name} + , version{version} + , moduleId{moduleId} + {} explicit ExportedTypeView(int moduleId, Utils::SmallStringView name, int majorVersion, @@ -552,6 +535,15 @@ public: {} explicit PropertyDeclaration(Utils::SmallStringView name, + TypeId propertyTypeId, + PropertyDeclarationTraits traits) + : name{name} + , traits{traits} + , propertyTypeId{propertyTypeId} + , kind{PropertyKind::Property} + {} + + explicit PropertyDeclaration(Utils::SmallStringView name, ImportedTypeName typeName, PropertyDeclarationTraits traits, Utils::SmallStringView aliasPropertyName) @@ -563,13 +555,24 @@ public: {} explicit PropertyDeclaration(Utils::SmallStringView name, - Utils::SmallStringView typeName, + TypeId propetyTypeId, + PropertyDeclarationTraits traits, + Utils::SmallStringView aliasPropertyName) + : name{name} + , aliasPropertyName{aliasPropertyName} + , traits{traits} + , propertyTypeId{propertyTypeId} + , kind{PropertyKind::Property} + {} + + explicit PropertyDeclaration(Utils::SmallStringView name, + long long propertyTypeId, int traits, Utils::SmallStringView aliasPropertyName) : name{name} - , typeName{NativeType{typeName}} , aliasPropertyName{aliasPropertyName} , traits{static_cast<PropertyDeclarationTraits>(traits)} + , propertyTypeId{propertyTypeId} , kind{PropertyKind::Property} {} @@ -594,6 +597,7 @@ public: ImportedTypeName typeName; Utils::SmallString aliasPropertyName; PropertyDeclarationTraits traits = {}; + TypeId propertyTypeId; TypeId typeId; PropertyKind kind = PropertyKind::Property; }; @@ -632,8 +636,7 @@ class Type { public: explicit Type() = default; - explicit Type(ModuleId moduleId, - Utils::SmallStringView typeName, + explicit Type(Utils::SmallStringView typeName, ImportedTypeName prototype, TypeAccessSemantics accessSemantics, SourceId sourceId, @@ -650,37 +653,42 @@ public: , functionDeclarations{std::move(functionDeclarations)} , signalDeclarations{std::move(signalDeclarations)} , enumerationDeclarations{std::move(enumerationDeclarations)} - , moduleId{moduleId} , accessSemantics{accessSemantics} , sourceId{sourceId} , changeLevel{changeLevel} {} - explicit Type(ModuleId moduleId, - Utils::SmallStringView typeName, + explicit Type(Utils::SmallStringView typeName, + TypeId prototypeId, + TypeAccessSemantics accessSemantics, + SourceId sourceId) + : typeName{typeName} + , accessSemantics{accessSemantics} + , sourceId{sourceId} + , prototypeId{prototypeId} + {} + + explicit Type(Utils::SmallStringView typeName, Utils::SmallStringView prototype, int accessSemantics, int sourceId) : typeName{typeName} , prototype{NativeType{prototype}} - , moduleId{moduleId} , accessSemantics{static_cast<TypeAccessSemantics>(accessSemantics)} , sourceId{sourceId} {} - explicit Type(int moduleId, + explicit Type(int sourceId, Utils::SmallStringView typeName, long long typeId, - Utils::SmallStringView prototype, - int accessSemantics, - int sourceId) + long long prototypeId, + int accessSemantics) : typeName{typeName} - , prototype{NativeType{prototype}} - , moduleId{moduleId} , accessSemantics{static_cast<TypeAccessSemantics>(accessSemantics)} , sourceId{sourceId} , typeId{typeId} + , prototypeId{prototypeId} {} friend bool operator==(const Type &first, const Type &second) noexcept @@ -690,7 +698,6 @@ public: && first.propertyDeclarations == second.propertyDeclarations && first.functionDeclarations == second.functionDeclarations && first.signalDeclarations == second.signalDeclarations - && first.moduleId == second.moduleId && first.sourceId == second.sourceId && first.sourceId == second.sourceId; } @@ -702,31 +709,52 @@ public: FunctionDeclarations functionDeclarations; SignalDeclarations signalDeclarations; EnumerationDeclarations enumerationDeclarations; - TypeAccessSemantics accessSemantics = TypeAccessSemantics::Invalid; + TypeAccessSemantics accessSemantics = TypeAccessSemantics::None; SourceId sourceId; TypeId typeId; - ModuleId moduleId; + TypeId prototypeId; ChangeLevel changeLevel = ChangeLevel::Full; }; using Types = std::vector<Type>; -class ModuleView +class ProjectData { public: - explicit ModuleView(Utils::SmallStringView name, int sourceId) - : name{name} - , sourceId{sourceId} + ModuleId extraModuleId; + SourceId sourceId; +}; + +using ProjectDatas = std::vector<ProjectData>; + +class SynchronizationPackage +{ +public: + SynchronizationPackage() = default; + SynchronizationPackage(Imports imports, Types types, SourceIds sourceIds) + : imports{std::move(imports)} + , types{std::move(types)} + , sourceIds(std::move(sourceIds)) {} - friend bool operator==(const ModuleView &first, const ModuleView &second) - { - return first.name == second.name && first.sourceId == second.sourceId; - } + SynchronizationPackage(Types types) + : types{std::move(types)} + {} + + SynchronizationPackage(SourceIds sourceIds) + : sourceIds(std::move(sourceIds)) + {} + + SynchronizationPackage(SourceIds sourceIds, FileStatuses fileStatuses) + : sourceIds(std::move(sourceIds)) + , fileStatuses(std::move(fileStatuses)) + {} public: - Utils::SmallStringView name; - SourceId sourceId; + Imports imports; + Types types; + SourceIds sourceIds; + FileStatuses fileStatuses; }; } // namespace QmlDesigner::Storage diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index b5a7945181..cc91a06fae 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -52,40 +52,34 @@ ComponentReferences createComponentReferences(const QMultiHash<QString, QmlDirPa void ProjectUpdater::update() { - Storage::Modules modules; - Storage::Imports imports; - Storage::Types types; - SourceIds sourceIds; - FileStatuses fileStatuses; + Storage::SynchronizationPackage package; for (const QString &qmldirPath : m_projectManager.qtQmlDirs()) { SourcePath qmldirSourcePath{qmldirPath}; SourceId qmlDirSourceId = m_pathCache.sourceId(qmldirSourcePath); - switch (fileState(qmlDirSourceId, fileStatuses)) { + switch (fileState(qmlDirSourceId, package.fileStatuses)) { case FileState::Changed: { QmlDirParser parser; parser.parse(m_fileSystem.contentAsQString(qmldirPath)); - modules.emplace_back(parser.typeNamespace(), qmlDirSourceId); - - sourceIds.push_back(qmlDirSourceId); + package.sourceIds.push_back(qmlDirSourceId); SourceContextId directoryId = m_pathCache.sourceContextId(qmlDirSourceId); - parseTypeInfos(parser.typeInfos(), directoryId, imports, types, sourceIds, fileStatuses); + Utils::PathString moduleName{parser.typeNamespace()}; + ModuleId moduleId = m_projectStorage.moduleId(moduleName); + + parseTypeInfos(parser.typeInfos(), directoryId, package); parseQmlComponents(createComponentReferences(parser.components()), directoryId, - ModuleId{&qmlDirSourceId}, - imports, - types, - sourceIds, - fileStatuses); + moduleId, + package); break; } case FileState::NotChanged: { - SourceIds qmltypesSourceIds = m_projectStorage.fetchSourceDependencieIds(qmlDirSourceId); - parseTypeInfos(qmltypesSourceIds, imports, types, sourceIds, fileStatuses); + auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(qmlDirSourceId); + parseTypeInfos(qmlProjectDatas, package); break; } case FileState::NotExists: { @@ -95,21 +89,14 @@ void ProjectUpdater::update() } } - m_projectStorage.synchronize(std::move(modules), - std::move(imports), - std::move(types), - std::move(sourceIds), - std::move(fileStatuses)); + m_projectStorage.synchronize(std::move(package)); } void ProjectUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) {} void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos, SourceContextId directoryId, - Storage::Imports &imports, - Storage::Types &types, - SourceIds &sourceIds, - FileStatuses &fileStatuses) + Storage::SynchronizationPackage &package) { QString directory{m_pathCache.sourceContextPath(directoryId)}; @@ -117,44 +104,37 @@ void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos, SourceId sourceId = m_pathCache.sourceId(directoryId, Utils::SmallString{typeInfo}); QString qmltypesPath = directory + "/" + typeInfo; - parseTypeInfo(sourceId, qmltypesPath, imports, types, sourceIds, fileStatuses); + Storage::ProjectData projectData{ModuleId{}, sourceId}; + + parseTypeInfo(projectData, qmltypesPath, package); } } -void ProjectUpdater::parseTypeInfos(const SourceIds &qmltypesSourceIds, - Storage::Imports &imports, - Storage::Types &types, - SourceIds &sourceIds, - FileStatuses &fileStatuses) +void ProjectUpdater::parseTypeInfos(const Storage::ProjectDatas &projectDatas, + Storage::SynchronizationPackage &package) { - for (SourceId sourceId : qmltypesSourceIds) { - QString qmltypesPath = m_pathCache.sourcePath(sourceId).toQString(); + for (const Storage::ProjectData &projectData : projectDatas) { + QString qmltypesPath = m_pathCache.sourcePath(projectData.sourceId).toQString(); - parseTypeInfo(sourceId, qmltypesPath, imports, types, sourceIds, fileStatuses); + parseTypeInfo(projectData, qmltypesPath, package); } } -void ProjectUpdater::parseTypeInfo(SourceId sourceId, +void ProjectUpdater::parseTypeInfo(const Storage::ProjectData &projectData, const QString &qmltypesPath, - Storage::Imports &imports, - Storage::Types &types, - SourceIds &sourceIds, - FileStatuses &fileStatuses) + Storage::SynchronizationPackage &package) { - if (fileState(sourceId, fileStatuses) == FileState::Changed) { - sourceIds.push_back(sourceId); + if (fileState(projectData.sourceId, package.fileStatuses) == FileState::Changed) { + package.sourceIds.push_back(projectData.sourceId); const auto content = m_fileSystem.contentAsQString(qmltypesPath); - m_qmlTypesParser.parse(content, imports, types, sourceIds); + m_qmlTypesParser.parse(content, package.imports, package.types, projectData); } } void ProjectUpdater::parseQmlComponents(ComponentReferences components, SourceContextId directoryId, ModuleId moduleId, - Storage::Imports &imports, - Storage::Types &types, - SourceIds &sourceIds, - FileStatuses &fileStatuses) + Storage::SynchronizationPackage &package) { std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) { return std::tie(first.get().typeName, first.get().majorVersion, first.get().minorVersion) @@ -174,23 +154,23 @@ void ProjectUpdater::parseQmlComponents(ComponentReferences components, Utils::SmallString fileName{component.fileName}; SourceId sourceId = m_pathCache.sourceId(directoryId, fileName); - if (fileState(sourceId, fileStatuses) != FileState::Changed) + if (fileState(sourceId, package.fileStatuses) != FileState::Changed) continue; - sourceIds.push_back(sourceId); + package.sourceIds.push_back(sourceId); const auto content = m_fileSystem.contentAsQString(directory + "/" + component.fileName); - auto type = m_qmlDocumentParser.parse(content, imports); + auto type = m_qmlDocumentParser.parse(content, package.imports); type.typeName = fileName; - type.moduleId = moduleId; type.accessSemantics = Storage::TypeAccessSemantics::Reference; type.sourceId = sourceId; type.exportedTypes.push_back( - Storage::ExportedType{Utils::SmallString{component.typeName}, + Storage::ExportedType{moduleId, + Utils::SmallString{component.typeName}, Storage::Version{component.majorVersion, component.minorVersion}}); - types.push_back(std::move(type)); + package.types.push_back(std::move(type)); } } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index dfe8ce05c4..e3323d7609 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -88,28 +88,16 @@ private: void parseTypeInfos(const QStringList &typeInfos, SourceContextId directoryId, - Storage::Imports &imports, - Storage::Types &types, - SourceIds &sourceIds, - FileStatuses &fileStatuses); - void parseTypeInfos(const SourceIds &qmltypesSourceIds, - Storage::Imports &imports, - Storage::Types &types, - SourceIds &sourceIds, - FileStatuses &fileStatuses); - void parseTypeInfo(SourceId sourceId, + Storage::SynchronizationPackage &package); + void parseTypeInfos(const Storage::ProjectDatas &projectDatas, + Storage::SynchronizationPackage &package); + void parseTypeInfo(const Storage::ProjectData &projectData, const QString &qmltypesPath, - Storage::Imports &imports, - Storage::Types &types, - SourceIds &sourceIds, - FileStatuses &fileStatuses); + Storage::SynchronizationPackage &package); void parseQmlComponents(ComponentReferences components, SourceContextId directoryId, ModuleId moduleId, - Storage::Imports &imports, - Storage::Types &types, - SourceIds &sourceIds, - FileStatuses &fileStatuses); + Storage::SynchronizationPackage &package); FileState fileState(SourceId sourceId, FileStatuses &fileStatuses) const; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp new file mode 100644 index 0000000000..c4a2bae617 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "qmldocumentparser.h" + +#include "projectstorage.h" +#include "sourcepathcache.h" + +#include <sqlitedatabase.h> + +#include <qmldom/qqmldomtop_p.h> + +#include <QDateTime> + +namespace QmlDesigner { + +namespace QmlDom = QQmlJS::Dom; + +namespace { + +int convertVersionNumber(qint32 versionNumber) +{ + return versionNumber < 0 ? -1 : versionNumber; +} + +Storage::Version convertVersion(QmlDom::Version version) +{ + return Storage::Version{convertVersionNumber(version.majorVersion), + convertVersionNumber(version.minorVersion)}; +} + +Utils::PathString convertUri(const QString &uri) +{ + Utils::PathString path{QStringView{uri.begin() + 7, uri.end()}}; + if (path.endsWith("/.")) + return path; + if (path.endsWith("/")) { + path += "."; + return path; + } + + path += "/."; + return path; +} + +void addImports(Storage::Imports &imports, + const QList<QmlDom::Import> &qmlImports, + SourceId sourceId, + SourceContextId sourceContextId, + QmlDocumentParser::PathCache &pathCache, + QmlDocumentParser::ProjectStorage &storage) +{ + for (const QmlDom::Import &qmlImport : qmlImports) { + if (qmlImport.uri == u"file://.") { + auto moduleId = storage.moduleId(pathCache.sourceContextPath(sourceContextId)); + imports.emplace_back(moduleId, Storage::Version{}, sourceId); + } else if (qmlImport.uri.startsWith(u"file://")) { + auto moduleId = storage.moduleId(convertUri(qmlImport.uri)); + imports.emplace_back(moduleId, Storage::Version{}, sourceId); + } else { + auto moduleId = storage.moduleId(Utils::SmallString{qmlImport.uri}); + imports.emplace_back(moduleId, convertVersion(qmlImport.version), sourceId); + } + } +} + +void addPropertyDeclarations(Storage::Type &type, const QmlDom::QmlObject &rootObject) +{ + for (const QmlDom::PropertyDefinition &propertyDeclaration : rootObject.propertyDefs()) { + type.propertyDeclarations.emplace_back(Utils::SmallString{propertyDeclaration.name}, + Storage::ImportedType{ + Utils::SmallString{propertyDeclaration.typeName}}, + Storage::PropertyDeclarationTraits::None); + } +} + +void addParameterDeclaration(Storage::ParameterDeclarations ¶meterDeclarations, + const QList<QmlDom::MethodParameter> ¶meters) +{ + for (const QmlDom::MethodParameter ¶meter : parameters) { + parameterDeclarations.emplace_back(Utils::SmallString{parameter.name}, + Utils::SmallString{parameter.typeName}); + } +} + +void addFunctionAndSignalDeclarations(Storage::Type &type, const QmlDom::QmlObject &rootObject) +{ + for (const QmlDom::MethodInfo &methodInfo : rootObject.methods()) { + if (methodInfo.methodType == QmlDom::MethodInfo::Method) { + auto &functionDeclaration = type.functionDeclarations.emplace_back( + Utils::SmallString{methodInfo.name}, "", Storage::ParameterDeclarations{}); + addParameterDeclaration(functionDeclaration.parameters, methodInfo.parameters); + } else { + auto &signalDeclaration = type.signalDeclarations.emplace_back( + Utils::SmallString{methodInfo.name}); + addParameterDeclaration(signalDeclaration.parameters, methodInfo.parameters); + } + } +} + +Storage::EnumeratorDeclarations createEnumerators(const QmlDom::EnumDecl &enumeration) +{ + Storage::EnumeratorDeclarations enumeratorDeclarations; + for (const QmlDom::EnumItem &enumerator : enumeration.values()) { + enumeratorDeclarations.emplace_back(Utils::SmallString{enumerator.name()}, + static_cast<long long>(enumerator.value())); + } + return enumeratorDeclarations; +} + +void addEnumeraton(Storage::Type &type, const QmlDom::Component &component) +{ + for (const QmlDom::EnumDecl &enumeration : component.enumerations()) { + Storage::EnumeratorDeclarations enumeratorDeclarations = createEnumerators(enumeration); + type.enumerationDeclarations.emplace_back(Utils::SmallString{enumeration.name()}, + std::move(enumeratorDeclarations)); + } +} + +} // namespace + +Storage::Type QmlDocumentParser::parse(const QString &sourceContent, + Storage::Imports &imports, + SourceId sourceId, + SourceContextId sourceContextId) +{ + Storage::Type type; + + QmlDom::DomItem environment = QmlDom::DomEnvironment::create( + {}, + QmlDom::DomEnvironment::Option::SingleThreaded + | QmlDom::DomEnvironment::Option::NoDependencies); + + QmlDom::DomItem items; + + environment.loadFile( + {}, + {}, + sourceContent, + QDateTime{}, + [&](QmlDom::Path, const QmlDom::DomItem &, const QmlDom::DomItem &newItems) { + items = newItems; + }, + QmlDom::LoadOption::DefaultLoad, + QmlDom::DomType::QmlFile); + + environment.loadPendingDependencies(); + + QmlDom::DomItem file = items.field(QmlDom::Fields::currentItem); + const QmlDom::QmlFile *qmlFile = file.as<QmlDom::QmlFile>(); + const auto &components = qmlFile->components(); + + if (components.empty()) + return type; + + const auto &component = components.first(); + const auto &objects = component.objects(); + + if (objects.empty()) + return type; + + const QmlDom::QmlObject &qmlObject = objects.front(); + + type.prototype = Storage::ImportedType{Utils::SmallString{qmlObject.name()}}; + + addImports(imports, qmlFile->imports(), sourceId, sourceContextId, m_pathCache, m_storage); + + addPropertyDeclarations(type, qmlObject); + addFunctionAndSignalDeclarations(type, qmlObject); + addEnumeraton(type, component); + + return type; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h new file mode 100644 index 0000000000..e2dd243405 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "nonlockingmutex.h" +#include "qmldocumentparserinterface.h" + +namespace Sqlite { +class Database; +} + +namespace QmlDesigner { + +template<typename Database> +class ProjectStorage; + +template<typename ProjectStorage, typename Mutex> +class SourcePathCache; + +class QmlDocumentParser +{ +public: + using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>; + using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>; + + QmlDocumentParser(PathCache &pathCache, ProjectStorage &storage) + : m_pathCache{pathCache} + , m_storage{storage} + {} + + virtual Storage::Type parse(const QString &sourceContent, + Storage::Imports &imports, + SourceId sourceId, + SourceContextId sourceContextId); + +private: + PathCache &m_pathCache; + ProjectStorage &m_storage; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp new file mode 100644 index 0000000000..f5d09409aa --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "qmltypesparser.h" + +#include "projectstorage.h" +#include "sourcepathcache.h" + +#include <sqlitedatabase.h> + +#include <qmlcompiler/qqmljstypedescriptionreader_p.h> +#include <qmldom/qqmldomtop_p.h> + +#include <QDateTime> + +#include <algorithm> +#include <tuple> + +namespace QmlDesigner { + +namespace QmlDom = QQmlJS::Dom; + +namespace { + +void 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(); + }); + + Utils::PathString moduleName{QStringView(dependency.begin(), spaceFound)}; + moduleName.append("-cppnative"); + ModuleId cppModuleId = storage.moduleId(moduleName); + + auto majorVersionFound = std::find_if(spaceFound, dependency.end(), [](QChar c) { + return c.isDigit(); + }); + auto majorVersionEnd = std::find_if(majorVersionFound, dependency.end(), [](QChar c) { + return !c.isDigit(); + }); + + Storage::Version version; + + QStringView majorVersionString(majorVersionFound, majorVersionEnd); + if (!majorVersionString.isEmpty()) { + version.major.value = majorVersionString.toInt(); + + auto minorVersionFound = std::find_if(majorVersionEnd, dependency.end(), [](QChar c) { + return c.isDigit(); + }); + auto minorVersionEnd = std::find_if(minorVersionFound, dependency.end(), [](QChar c) { + return !c.isDigit(); + }); + QStringView minorVersionString(minorVersionFound, minorVersionEnd); + if (!minorVersionString.isEmpty()) + version.minor.value = QStringView(minorVersionFound, minorVersionEnd).toInt(); + } + + imports.emplace_back(cppModuleId, version, sourceId); +} + +void addImports(Storage::Imports &imports, + SourceId sourceId, + const QStringList &dependencies, + QmlTypesParser::ProjectStorage &storage) +{ + for (const QString &dependency : dependencies) + appendImports(imports, dependency, sourceId, storage); + + imports.emplace_back(storage.moduleId("QML"), Storage::Version{}, sourceId); + imports.emplace_back(storage.moduleId("QtQml-cppnative"), Storage::Version{}, sourceId); +} + +Storage::TypeAccessSemantics createTypeAccessSemantics(QQmlJSScope::AccessSemantics accessSematics) +{ + switch (accessSematics) { + case QQmlJSScope::AccessSemantics::Reference: + return Storage::TypeAccessSemantics::Reference; + case QQmlJSScope::AccessSemantics::Value: + return Storage::TypeAccessSemantics::Value; + case QQmlJSScope::AccessSemantics::None: + return Storage::TypeAccessSemantics::None; + case QQmlJSScope::AccessSemantics::Sequence: + return Storage::TypeAccessSemantics::Sequence; + } + + return Storage::TypeAccessSemantics::None; +} + +Storage::Version createVersion(QTypeRevision qmlVersion) +{ + return Storage::Version{qmlVersion.majorVersion(), qmlVersion.minorVersion()}; +} + +Storage::ExportedTypes createExports(const QList<QQmlJSScope::Export> &qmlExports, + const QQmlJSScope &component, + QmlTypesParser::ProjectStorage &storage, + ModuleId cppModuleId) +{ + Storage::ExportedTypes exportedTypes; + exportedTypes.reserve(Utils::usize(qmlExports)); + + for (const QQmlJSScope::Export &qmlExport : qmlExports) { + exportedTypes.emplace_back(storage.moduleId(Utils::SmallString{qmlExport.package()}), + Utils::SmallString{qmlExport.type()}, + createVersion(qmlExport.version())); + } + + exportedTypes.emplace_back(cppModuleId, Utils::SmallString{component.internalName()}); + + return exportedTypes; +} + +Storage::PropertyDeclarationTraits createPropertyDeclarationTraits(const QQmlJSMetaProperty &qmlProperty) +{ + Storage::PropertyDeclarationTraits traits{}; + + if (qmlProperty.isList()) + traits = traits | Storage::PropertyDeclarationTraits::IsList; + + if (qmlProperty.isPointer()) + traits = traits | Storage::PropertyDeclarationTraits::IsPointer; + + if (!qmlProperty.isWritable()) + traits = traits | Storage::PropertyDeclarationTraits::IsReadOnly; + + return traits; +} + +Storage::PropertyDeclarations createProperties(const QHash<QString, QQmlJSMetaProperty> &qmlProperties) +{ + Storage::PropertyDeclarations propertyDeclarations; + propertyDeclarations.reserve(Utils::usize(qmlProperties)); + + for (const QQmlJSMetaProperty &qmlProperty : qmlProperties) { + propertyDeclarations.emplace_back(Utils::SmallString{qmlProperty.propertyName()}, + Storage::NativeType{ + Utils::SmallString{qmlProperty.typeName()}}, + createPropertyDeclarationTraits(qmlProperty)); + } + + return propertyDeclarations; +} + +Storage::ParameterDeclarations createParameters(const QQmlJSMetaMethod &qmlMethod) +{ + Storage::ParameterDeclarations parameterDeclarations; + + const QStringList ¶meterNames = qmlMethod.parameterNames(); + const QStringList ¶meterTypeNames = qmlMethod.parameterTypeNames(); + auto currentName = parameterNames.begin(); + auto currentType = parameterTypeNames.begin(); + auto nameEnd = parameterNames.end(); + auto typeEnd = parameterTypeNames.end(); + + for (; currentName != nameEnd && currentType != typeEnd; ++currentName, ++currentType) { + parameterDeclarations.emplace_back(Utils::SmallString{*currentName}, + Utils::SmallString{*currentType}); + } + + return parameterDeclarations; +} + +std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> createFunctionAndSignals( + const QMultiHash<QString, QQmlJSMetaMethod> &qmlMethods) +{ + std::tuple<Storage::FunctionDeclarations, Storage::SignalDeclarations> functionAndSignalDeclarations; + Storage::FunctionDeclarations &functionsDeclarations{std::get<0>(functionAndSignalDeclarations)}; + functionsDeclarations.reserve(Utils::usize(qmlMethods)); + Storage::SignalDeclarations &signalDeclarations{std::get<1>(functionAndSignalDeclarations)}; + signalDeclarations.reserve(Utils::usize(qmlMethods)); + + for (const QQmlJSMetaMethod &qmlMethod : qmlMethods) { + if (qmlMethod.methodType() != QQmlJSMetaMethod::Type::Signal) { + functionsDeclarations.emplace_back(Utils::SmallString{qmlMethod.methodName()}, + Utils::SmallString{qmlMethod.returnTypeName()}, + createParameters(qmlMethod)); + } else { + signalDeclarations.emplace_back(Utils::SmallString{qmlMethod.methodName()}, + createParameters(qmlMethod)); + } + } + + return functionAndSignalDeclarations; +} + +Storage::EnumeratorDeclarations createEnumeratorsWithValues(const QQmlJSMetaEnum &qmlEnumeration) +{ + Storage::EnumeratorDeclarations enumeratorDeclarations; + + const QStringList &keys = qmlEnumeration.keys(); + const QList<int> &values = qmlEnumeration.values(); + auto currentKey = keys.begin(); + auto currentValue = values.begin(); + auto keyEnd = keys.end(); + auto valueEnd = values.end(); + + for (; currentKey != keyEnd && currentValue != valueEnd; ++currentKey, ++currentValue) + enumeratorDeclarations.emplace_back(Utils::SmallString{*currentKey}, *currentValue); + + return enumeratorDeclarations; +} + +Storage::EnumeratorDeclarations createEnumeratorsWithoutValues(const QQmlJSMetaEnum &qmlEnumeration) +{ + Storage::EnumeratorDeclarations enumeratorDeclarations; + + for (const QString &key : qmlEnumeration.keys()) + enumeratorDeclarations.emplace_back(Utils::SmallString{key}); + + return enumeratorDeclarations; +} + +Storage::EnumeratorDeclarations createEnumerators(const QQmlJSMetaEnum &qmlEnumeration) +{ + if (qmlEnumeration.hasValues()) + return createEnumeratorsWithValues(qmlEnumeration); + + return createEnumeratorsWithoutValues(qmlEnumeration); +} + +Storage::EnumerationDeclarations createEnumeration(const QHash<QString, QQmlJSMetaEnum> &qmlEnumerations) +{ + Storage::EnumerationDeclarations enumerationDeclarations; + enumerationDeclarations.reserve(Utils::usize(qmlEnumerations)); + + for (const QQmlJSMetaEnum &qmlEnumeration : qmlEnumerations) { + enumerationDeclarations.emplace_back(Utils::SmallString{qmlEnumeration.name()}, + createEnumerators(qmlEnumeration)); + } + + return enumerationDeclarations; +} + +void addType(Storage::Types &types, + SourceId sourceId, + ModuleId cppModuleId, + const QQmlJSScope &component, + QmlTypesParser::ProjectStorage &storage) +{ + auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals(component.ownMethods()); + types.emplace_back(Utils::SmallString{component.internalName()}, + Storage::NativeType{Utils::SmallString{component.baseTypeName()}}, + createTypeAccessSemantics(component.accessSemantics()), + sourceId, + createExports(component.exports(), component, storage, cppModuleId), + createProperties(component.ownProperties()), + std::move(functionsDeclarations), + std::move(signalDeclarations), + createEnumeration(component.ownEnumerations())); +} + +void addTypes(Storage::Types &types, + const Storage::ProjectData &projectData, + const QHash<QString, QQmlJSScope::Ptr> &objects, + QmlTypesParser::ProjectStorage &storage) +{ + types.reserve(Utils::usize(objects) + types.size()); + + for (const auto &object : objects) + addType(types, projectData.sourceId, projectData.extraModuleId, *object.get(), storage); +} + +} // namespace + +void QmlTypesParser::parse(const QString &sourceContent, + Storage::Imports &imports, + Storage::Types &types, + const Storage::ProjectData &projectData) +{ + QQmlJSTypeDescriptionReader reader({}, sourceContent); + QHash<QString, QQmlJSScope::Ptr> components; + QStringList dependencies; + bool isValid = reader(&components, &dependencies); + if (!isValid) + throw CannotParseQmlTypesFile{}; + + addImports(imports, projectData.sourceId, dependencies, m_storage); + addTypes(types, projectData, components, m_storage); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h new file mode 100644 index 0000000000..40f88d240d --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "nonlockingmutex.h" +#include "qmltypesparserinterface.h" + +namespace Sqlite { +class Database; +} + +namespace QmlDesigner { + +template<typename Database> +class ProjectStorage; + +template<typename ProjectStorage, typename Mutex> +class SourcePathCache; + +class QmlTypesParser : public QmlTypesParserInterface +{ +public: + using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>; + using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>; + + QmlTypesParser(PathCache &pathCache, ProjectStorage &storage) + : m_pathCache{pathCache} + , m_storage{storage} + {} + + void parse(const QString &sourceContent, + Storage::Imports &imports, + Storage::Types &types, + const Storage::ProjectData &projectData) override; + +private: + PathCache &m_pathCache; + ProjectStorage &m_storage; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h index 40c9883835..3255c0b5aa 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h @@ -37,7 +37,7 @@ public: virtual void parse(const QString &sourceContent, Storage::Imports &imports, Storage::Types &types, - SourceIds &sourceIds) + const Storage::ProjectData &projectData) = 0; protected: diff --git a/src/plugins/qmldesigner/generatecmakelists.cpp b/src/plugins/qmldesigner/generatecmakelists.cpp new file mode 100644 index 0000000000..8e96c0ec95 --- /dev/null +++ b/src/plugins/qmldesigner/generatecmakelists.cpp @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "generatecmakelists.h" + +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/actionmanager/actioncontainer.h> + +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/project.h> +#include <projectexplorer/session.h> + +#include <qmlprojectmanager/qmlprojectmanagerconstants.h> + +#include <utils/fileutils.h> + +#include <QAction> +#include <QRegularExpression> +#include <QStringList> +#include <QTextStream> + +using namespace Utils; + +namespace QmlDesigner { +namespace GenerateCmakeLists { + +const QDir::Filters FILES_ONLY = QDir::Files; +const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot; + +const char CMAKEFILENAME[] = "CMakeLists.txt"; +const char QMLDIRFILENAME[] = "qmldir"; + +void generateMenuEntry() +{ + Core::ActionContainer *buildMenu = + Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT); + const Core::Context projectCntext(QmlProjectManager::Constants::QML_PROJECT_ID); + auto action = new QAction("Generate CMakeLists.txt files"); + QObject::connect(action, &QAction::triggered, GenerateCmakeLists::onGenerateCmakeLists); + Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists"); + buildMenu->addAction(cmd, ProjectExplorer::Constants::G_BUILD_RUN); + + action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr); + QObject::connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, [action]() { + action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr); + }); +} + +void onGenerateCmakeLists() +{ + generateMainCmake(ProjectExplorer::SessionManager::startupProject()->projectDirectory()); +} + +QStringList processDirectory(const FilePath &dir) +{ + QStringList moduleNames; + + FilePaths files = dir.dirEntries(FILES_ONLY); + for (FilePath &file : files) { + if (!file.fileName().compare(CMAKEFILENAME)) + files.removeAll(file); + } + + if (files.isEmpty()) { + generateSubdirCmake(dir); + FilePaths subDirs = dir.dirEntries(DIRS_ONLY); + for (FilePath &subDir : subDirs) { + QStringList subDirModules = processDirectory(subDir); + moduleNames.append(subDirModules); + } + } + else { + QString moduleName = generateModuleCmake(dir); + if (!moduleName.isEmpty()) { + moduleNames.append(moduleName); + } + } + + return moduleNames; +} + +const char MAINFILE_REQUIRED_VERSION[] = "cmake_minimum_required(VERSION 3.18)\n\n"; +const char MAINFILE_PROJECT[] = "project(%1 LANGUAGES CXX)\n\n"; +const char MAINFILE_CMAKE_OPTIONS[] = "set(CMAKE_INCLUDE_CURRENT_DIR ON)\nset(CMAKE_AUTOMOC ON)\n\n"; +const char MAINFILE_PACKAGES[] = "find_package(Qt6 COMPONENTS Gui Qml Quick)\n"; +const char MAINFILE_LIBRARIES[] = "set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)\n\n"; +const char MAINFILE_CPP[] = "add_executable(%1 main.cpp)\n\n"; +const char MAINFILE_MAINMODULE[] = "qt6_add_qml_module(%1\n\tURI \"Main\"\n\tVERSION 1.0\n\tNO_PLUGIN\n\tQML_FILES main.qml\n)\n\n"; +const char MAINFILE_LINK_LIBRARIES[] = "target_link_libraries(%1 PRIVATE\n\tQt${QT_VERSION_MAJOR}::Core\n\tQt${QT_VERSION_MAJOR}::Gui\n\tQt${QT_VERSION_MAJOR}::Quick\n\tQt${QT_VERSION_MAJOR}::Qml\n)\n\n"; + +const char ADD_SUBDIR[] = "add_subdirectory(%1)\n"; + +void generateMainCmake(const FilePath &rootDir) +{ + //TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all. + QString projectName = ProjectExplorer::SessionManager::startupProject()->displayName(); + + FilePaths subDirs = rootDir.dirEntries(DIRS_ONLY); + + QString fileContent; + fileContent.append(MAINFILE_REQUIRED_VERSION); + fileContent.append(QString(MAINFILE_PROJECT).arg(projectName)); + fileContent.append(MAINFILE_CMAKE_OPTIONS); + fileContent.append(MAINFILE_PACKAGES); + fileContent.append(QString(MAINFILE_CPP).arg(projectName)); + fileContent.append(QString(MAINFILE_MAINMODULE).arg(projectName)); + fileContent.append(MAINFILE_LIBRARIES); + + for (FilePath &subDir : subDirs) { + QStringList subDirModules = processDirectory(subDir); + if (!subDirModules.isEmpty()) + fileContent.append(QString(ADD_SUBDIR).arg(subDir.fileName())); + } + fileContent.append("\n"); + + fileContent.append(QString(MAINFILE_LINK_LIBRARIES).arg(projectName)); + + createCmakeFile(rootDir, fileContent); +} + +const char MODULEFILE_PROPERTY_SINGLETON[] = "QT_QML_SINGLETON_TYPE"; +const char MODULEFILE_PROPERTY_SET[] = "set_source_files_properties(%1\n\tPROPERTIES\n\t\t%2 %3\n)\n\n"; +const char MODULEFILE_CREATE_MODULE[] = "qt6_add_qml_module(%1\n\tURI \"%1\"\n\tVERSION 1.0\n%2)\n\n"; + + +QString generateModuleCmake(const FilePath &dir) +{ + QString fileContent; + const QStringList qmlFilesOnly("*.qml"); + const QStringList qmldirFilesOnly(QMLDIRFILENAME); + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + + FilePaths qmldirFileList = dir.dirEntries(qmldirFilesOnly, FILES_ONLY); + if (!qmldirFileList.isEmpty()) { + QStringList singletons = getSingletonsFromQmldirFile(qmldirFileList.first()); + for (QString &singleton : singletons) { + fileContent.append(QString(MODULEFILE_PROPERTY_SET).arg(singleton).arg(MODULEFILE_PROPERTY_SINGLETON).arg("true")); + } + } + + FilePaths qmlFileList = dir.dirEntries(qmlFilesOnly, FILES_ONLY); + QString qmlFiles; + for (FilePath &qmlFile : qmlFileList) { + if (project->isKnownFile(qmlFile)) + qmlFiles.append(QString("\t\t%1\n").arg(qmlFile.fileName())); + } + + QStringList resourceFileList = getDirectoryTreeResources(dir); + QString resourceFiles; + for (QString &resourceFile : resourceFileList) { + resourceFiles.append(QString("\t\t%1\n").arg(resourceFile)); + } + + QString moduleContent; + if (!qmlFiles.isEmpty()) { + moduleContent.append(QString("\tQML_FILES\n%1").arg(qmlFiles)); + } + if (!resourceFiles.isEmpty()) { + moduleContent.append(QString("\tRESOURCES\n%1").arg(resourceFiles)); + } + + QString moduleName = dir.fileName(); + + fileContent.append(QString(MODULEFILE_CREATE_MODULE).arg(moduleName).arg(moduleContent)); + + createCmakeFile(dir, fileContent); + + return moduleName; +} + +void generateSubdirCmake(const FilePath &dir) +{ + QString fileContent; + FilePaths subDirs = dir.dirEntries(DIRS_ONLY); + + for (FilePath &subDir : subDirs) { + fileContent.append(QString(ADD_SUBDIR).arg(subDir.fileName())); + } + + createCmakeFile(dir, fileContent); +} + +QStringList getSingletonsFromQmldirFile(const FilePath &filePath) +{ + QStringList singletons; + QFile f(filePath.toString()); + f.open(QIODevice::ReadOnly); + QTextStream stream(&f); + + while (!stream.atEnd()) { + QString line = stream.readLine(); + if (line.startsWith("singleton", Qt::CaseInsensitive)) { + QStringList tokenizedLine = line.split(QRegularExpression("\\s+")); + QString fileName = tokenizedLine.last(); + if (fileName.endsWith(".qml", Qt::CaseInsensitive)) { + singletons.append(fileName); + } + } + } + + f.close(); + + return singletons; +} + +QStringList getDirectoryTreeResources(const FilePath &dir) +{ + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + QStringList resourceFileList; + + FilePaths thisDirFiles = dir.dirEntries(FILES_ONLY); + for (FilePath &file : thisDirFiles) { + if (!isFileBlacklisted(file.fileName()) && + !file.fileName().endsWith(".qml", Qt::CaseInsensitive) && + project->isKnownFile(file)) { + resourceFileList.append(file.fileName()); + } + } + + FilePaths subDirsList = dir.dirEntries(DIRS_ONLY); + for (FilePath &subDir : subDirsList) { + QStringList subDirResources = getDirectoryTreeResources(subDir); + for (QString &resource : subDirResources) { + resourceFileList.append(subDir.fileName().append('/').append(resource)); + } + + } + + return resourceFileList; +} + +void createCmakeFile(const FilePath &dir, const QString &content) +{ + FilePath filePath = dir.pathAppended(CMAKEFILENAME); + QFile cmakeFile(filePath.toString()); + cmakeFile.open(QIODevice::WriteOnly); + QTextStream stream(&cmakeFile); + stream << content; + cmakeFile.close(); +} + +bool isFileBlacklisted(const QString &fileName) +{ + return (!fileName.compare(QMLDIRFILENAME) || + !fileName.compare(CMAKEFILENAME)); +} + +} +} diff --git a/src/plugins/qmldesigner/generatecmakelists.h b/src/plugins/qmldesigner/generatecmakelists.h new file mode 100644 index 0000000000..55b0f6958d --- /dev/null +++ b/src/plugins/qmldesigner/generatecmakelists.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <projectexplorer/project.h> + +#include <utils/fileutils.h> + +namespace QmlDesigner { +namespace GenerateCmakeLists { +void generateMenuEntry(); +void onGenerateCmakeLists(); +void generateMainCmake(const Utils::FilePath &rootDir); +void generateSubdirCmake(const Utils::FilePath &dir); +QString generateModuleCmake(const Utils::FilePath &dir); +QStringList processDirectory(const Utils::FilePath &dir); +QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath); +QStringList getDirectoryTreeResources(const Utils::FilePath &dir); +void createCmakeFile(const Utils::FilePath &filePath, const QString &content); +bool isFileBlacklisted(const QString &fileName); +} +} diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 77c4715f17..014c79b59d 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -31,6 +31,7 @@ #include "designmodecontext.h" #include "openuiqmlfiledialog.h" #include "generateresource.h" +#include "generatecmakelists.h" #include "nodeinstanceview.h" #include "gestures.h" @@ -222,6 +223,8 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool()) GenerateResource::generateMenuEntry(); + GenerateCmakeLists::generateMenuEntry(); + const QString fontPath = Core::ICore::resourcePath( "qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf") diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pri b/src/plugins/qmldesigner/qmldesignerplugin.pri index 58fb55788f..4cf7edf4ba 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pri +++ b/src/plugins/qmldesigner/qmldesignerplugin.pri @@ -5,6 +5,7 @@ HEADERS += $$PWD/qmldesignerconstants.h \ $$PWD/designersettings.h \ $$PWD/editorproxy.h \ $$PWD/generateresource.h \ + $$PWD/generatecmakelists.h \ $$PWD/settingspage.h \ $$PWD/designmodecontext.h \ $$PWD/documentmanager.h \ @@ -20,6 +21,7 @@ SOURCES += $$PWD/qmldesignerplugin.cpp \ $$PWD/designersettings.cpp \ $$PWD/editorproxy.cpp \ $$PWD/generateresource.cpp \ + $$PWD/generatecmakelists.cpp \ $$PWD/settingspage.cpp \ $$PWD/designmodecontext.cpp \ $$PWD/documentmanager.cpp \ diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index a1cf95c0e8..a3b70ec970 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -1007,6 +1007,8 @@ Project { files: [ "generateresource.cpp", "generateresource.h", + "generatecmakelists.cpp", + "generatecmakelists.h", "designersettings.cpp", "designersettings.h", "designmodecontext.cpp", diff --git a/src/plugins/qmldesigner/qtquickplugin/images/component-icon.png b/src/plugins/qmldesigner/qtquickplugin/images/component-icon.png Binary files differnew file mode 100644 index 0000000000..9c7df42bc7 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/component-icon.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/component-icon16.png b/src/plugins/qmldesigner/qtquickplugin/images/component-icon16.png Binary files differnew file mode 100644 index 0000000000..99941541c6 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/component-icon16.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/component-icon@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/component-icon@2x.png Binary files differnew file mode 100644 index 0000000000..f66349a63b --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/component-icon@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/loader-icon.png b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon.png Binary files differnew file mode 100644 index 0000000000..29082eacf1 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/loader-icon16.png b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon16.png Binary files differnew file mode 100644 index 0000000000..4a2b093259 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon16.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/loader-icon@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon@2x.png Binary files differnew file mode 100644 index 0000000000..750b13bd02 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/loader-icon@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon.png b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon.png Binary files differnew file mode 100644 index 0000000000..efe3ca80b4 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon16.png b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon16.png Binary files differnew file mode 100644 index 0000000000..775a57a38c --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon16.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon@2x.png Binary files differnew file mode 100644 index 0000000000..bb541b6711 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/repeater-icon@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc index be1770378b..8adfb84baf 100644 --- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc +++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc @@ -72,5 +72,14 @@ <file>images/animated-image-icon.png</file> <file>images/animated-image-icon@2x.png</file> <file>images/animated-image-icon16.png</file> + <file>images/component-icon.png</file> + <file>images/component-icon@2x.png</file> + <file>images/component-icon16.png</file> + <file>images/repeater-icon.png</file> + <file>images/repeater-icon@2x.png</file> + <file>images/repeater-icon16.png</file> + <file>images/loader-icon.png</file> + <file>images/loader-icon@2x.png</file> + <file>images/loader-icon16.png</file> </qresource> </RCC> diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 019be09973..851d457cdc 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -434,7 +434,7 @@ MetaInfo { Type { name: "QtQml.Component" - icon: ":/qtquickplugin/images/item-icon16.png" + icon: ":/qtquickplugin/images/component-icon16.png" Hints { canBeDroppedInNavigator: true @@ -444,19 +444,19 @@ MetaInfo { ItemLibraryEntry { name: "Component" category: "e.Qt Quick - Component" - libraryIcon: ":/qtquickplugin/images/item-icon.png" + libraryIcon: ":/qtquickplugin/images/component-icon.png" version: "2.0" } } Type { name: "QtQuick.Loader" - icon: ":/qtquickplugin/images/item-icon16.png" + icon: ":/qtquickplugin/images/loader-icon16.png" ItemLibraryEntry { name: "Loader" category: "e.Qt Quick - Component" - libraryIcon: ":/qtquickplugin/images/item-icon.png" + libraryIcon: ":/qtquickplugin/images/loader-icon.png" version: "2.0" Property { name: "width"; type: "int"; value: 200; } Property { name: "height"; type: "int"; value: 200; } @@ -465,7 +465,7 @@ MetaInfo { Type { name: "QtQuick.Repeater" - icon: ":/qtquickplugin/images/item-icon16.png" + icon: ":/qtquickplugin/images/repeater-icon16.png" Hints { canBeDroppedInFormEditor: false @@ -475,7 +475,7 @@ MetaInfo { ItemLibraryEntry { name: "Repeater" category: "e.Qt Quick - Component" - libraryIcon: ":/qtquickplugin/images/item-icon.png" + libraryIcon: ":/qtquickplugin/images/repeater-icon.png" version: "2.0" } } |