QmlDesigner: Add module scanner

For performance reason we want to get the qml modules directly from the
file system. When the project storage is finished we can get the modules
from there.

Task-number: QDS-9542
Change-Id: I26d4b028fbf5ebc541fcd8e34d285ded1fb14935
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Marco Bubke
2023-03-30 13:14:17 +02:00
committed by Thomas Hartmann
parent 79b5a9f03e
commit 6035ff939d
68 changed files with 540 additions and 329 deletions

View File

@@ -57,6 +57,7 @@ add_qtc_library(QmlDesignerCore STATIC
Utils
Qt::Widgets
Qt::Qml
Qt::QmlPrivate
Core
ProjectExplorer
QmakeProjectManager
@@ -92,7 +93,7 @@ extend_qtc_library(QmlDesignerCore
CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0
DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate
DEFINES QDS_HAS_QMLDOM
PUBLIC_DEFINES QDS_HAS_QMLPRIVATE
)
extend_qtc_library(QmlDesignerCore
@@ -400,6 +401,7 @@ extend_qtc_library(QmlDesignerCore
filesystem.cpp filesystem.h
filestatus.h
filestatuscache.cpp filestatuscache.h
modulescanner.cpp modulescanner.h
nonlockingmutex.h
projectstorageexceptions.cpp projectstorageexceptions.h
projectstorageinterface.h

View File

@@ -95,7 +95,7 @@ void DesignerActionManagerView::nodeOrderChanged(const NodeListProperty &)
setupContext(SelectionContext::UpdateMode::NodeHierachy);
}
void DesignerActionManagerView::importsChanged(const QList<Import> &, const QList<Import> &)
void DesignerActionManagerView::importsChanged(const Imports &, const Imports &)
{
setupContext();
}

View File

@@ -37,7 +37,7 @@ public:
void selectedNodesChanged(const QList<ModelNode> &,
const QList<ModelNode> &) override;
void nodeOrderChanged(const NodeListProperty &) override;
void importsChanged(const QList<Import> &, const QList<Import> &) override;
void importsChanged(const Imports &, const Imports &) override;
void signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty> &/*propertyList*/, PropertyChangeFlags /*propertyChange*/) override;
void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChangeFlag) override;
void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChangeFlag) override;

View File

@@ -183,7 +183,7 @@ void ConnectionView::auxiliaryDataChanged([[maybe_unused]] const ModelNode &node
selectionModel->clearSelection();
}
void ConnectionView::importsChanged(const QList<Import> & /*addedImports*/, const QList<Import> & /*removedImports*/)
void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/)
{
backendModel()->resetModel();
}

View File

@@ -50,7 +50,7 @@ public:
AuxiliaryDataKeyView key,
const QVariant &data) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void currentStateChanged(const ModelNode &node) override;

View File

@@ -144,7 +144,7 @@ void ContentLibraryView::modelAboutToBeDetached(Model *model)
AbstractView::modelAboutToBeDetached(model);
}
void ContentLibraryView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports)
void ContentLibraryView::importsChanged(const Imports &addedImports, const Imports &removedImports)
{
Q_UNUSED(addedImports)
Q_UNUSED(removedImports)

View File

@@ -31,7 +31,7 @@ public:
// AbstractView
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void active3DSceneChanged(qint32 sceneId) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;

View File

@@ -65,7 +65,7 @@ void DebugView::modelAboutToBeDetached(Model *model)
AbstractView::modelAboutToBeDetached(model);
}
void DebugView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports)
void DebugView::importsChanged(const Imports &addedImports, const Imports &removedImports)
{
if (isDebugViewEnabled()) {
QString message;

View File

@@ -24,7 +24,7 @@ public:
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void nodeCreated(const ModelNode &createdNode) override;
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;

View File

@@ -312,8 +312,8 @@ void Edit3DView::modelAboutToBeDetached(Model *model)
AbstractView::modelAboutToBeDetached(model);
}
void Edit3DView::importsChanged([[maybe_unused]] const QList<Import> &addedImports,
[[maybe_unused]] const QList<Import> &removedImports)
void Edit3DView::importsChanged([[maybe_unused]] const Imports &addedImports,
[[maybe_unused]] const Imports &removedImports)
{
checkImports();
}
@@ -920,7 +920,7 @@ void Edit3DView::addQuick3DImport()
{
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
if (document && !document->inFileComponentModelActive() && model()) {
const QList<Import> imports = model()->possibleImports();
const Imports imports = model()->possibleImports();
for (const auto &import : imports) {
if (import.url() == "QtQuick3D") {
if (!import.version().isEmpty() && import.majorVersion() >= 6) {

View File

@@ -44,7 +44,7 @@ public:
void updateActiveScene3D(const QVariantMap &sceneState) override;
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos3d) override;

View File

@@ -253,7 +253,7 @@ void FormEditorView::modelAboutToBeDetached(Model *model)
AbstractView::modelAboutToBeDetached(model);
}
void FormEditorView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/)
void FormEditorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/)
{
reset();
}

View File

@@ -48,7 +48,7 @@ public:
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void nodeCreated(const ModelNode &createdNode) override;
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;

View File

@@ -56,14 +56,14 @@ QHash<int, QByteArray> ItemLibraryAddImportModel::roleNames() const
return m_roleNames;
}
void ItemLibraryAddImportModel::update(const QList<Import> &possibleImports)
void ItemLibraryAddImportModel::update(const Imports &possibleImports)
{
beginResetModel();
m_importList.clear();
const DesignerMcuManager &mcuManager = DesignerMcuManager::instance();
const bool isQtForMCUs = mcuManager.isMCUProject();
QList<Import> filteredImports;
Imports filteredImports;
if (isQtForMCUs) {
const QStringList mcuAllowedList = mcuManager.allowedImports();
const QStringList mcuBannedList = mcuManager.bannedImports();

View File

@@ -24,7 +24,7 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
void update(const QList<Import> &possibleImports);
void update(const Imports &possibleImports);
void setSearchText(const QString &searchText);
Import getImportAt(int index) const;
@@ -33,7 +33,7 @@ public:
private:
QString m_searchText;
QList<Import> m_importList;
Imports m_importList;
QSet<QString> m_importFilterList;
QHash<int, QByteArray> m_roleNames;
QSet<QString> m_priorityImports;

View File

@@ -709,9 +709,9 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport()
model->rewriterView()->textModifier()->replace(0, 0, {});
} else if (counter < 100) {
try {
const QList<Import> posImports = model->possibleImports();
const QList<Import> currentImports = model->imports();
QList<Import> newImportsToAdd;
const Imports posImports = model->possibleImports();
const Imports currentImports = model->imports();
Imports newImportsToAdd;
for (auto &imp : std::as_const(m_requiredImports)) {
const bool isPos = Utils::contains(posImports, [imp](const Import &posImp) {

View File

@@ -107,7 +107,7 @@ private:
int m_currentImportId = 0;
QHash<int, ParseData> m_parseData;
QString m_progressTitle;
QList<Import> m_requiredImports;
Imports m_requiredImports;
QList<int> m_puppetQueue;
};
} // QmlDesigner

View File

@@ -343,7 +343,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
materialBundlePrefix.append(".MaterialBundle");
// create import sections
const QList<Import> usedImports = model->usedImports();
const Imports usedImports = model->usedImports();
QHash<QString, ItemLibraryImport *> importHash;
for (const Import &import : model->imports()) {
if (import.url() != projectName) {
@@ -550,7 +550,7 @@ ItemLibraryImport *ItemLibraryModel::importByUrl(const QString &importUrl) const
return nullptr;
}
void ItemLibraryModel::updateUsedImports(const QList<Import> &usedImports)
void ItemLibraryModel::updateUsedImports(const Imports &usedImports)
{
// imports in the excludeList are not marked used and thus can always be removed even when in use.
const QList<QString> excludeList = {"SimulinkConnector"};

View File

@@ -38,7 +38,7 @@ public:
ItemLibraryImport *importByUrl(const QString &importName) const;
void update(ItemLibraryInfo *itemLibraryInfo, Model *model);
void updateUsedImports(const QList<Import> &usedImports);
void updateUsedImports(const Imports &usedImports);
QMimeData *getMimeData(const ItemLibraryEntry &itemLibraryEntry);

View File

@@ -74,7 +74,7 @@ void ItemLibraryView::modelAboutToBeDetached(Model *model)
m_widget->setModel(nullptr);
}
void ItemLibraryView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports)
void ItemLibraryView::importsChanged(const Imports &addedImports, const Imports &removedImports)
{
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
for (const auto &import : addedImports)
@@ -111,7 +111,7 @@ void ItemLibraryView::importsChanged(const QList<Import> &addedImports, const QL
}
}
void ItemLibraryView::possibleImportsChanged(const QList<Import> &possibleImports)
void ItemLibraryView::possibleImportsChanged(const Imports &possibleImports)
{
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
for (const auto &import : possibleImports)
@@ -120,7 +120,7 @@ void ItemLibraryView::possibleImportsChanged(const QList<Import> &possibleImport
m_widget->updatePossibleImports(possibleImports);
}
void ItemLibraryView::usedImportsChanged(const QList<Import> &usedImports)
void ItemLibraryView::usedImportsChanged(const Imports &usedImports)
{
m_widget->updateUsedImports(usedImports);
}

View File

@@ -26,9 +26,9 @@ public:
// AbstractView
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void possibleImportsChanged(const QList<Import> &possibleImports) override;
void usedImportsChanged(const QList<Import> &usedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void possibleImportsChanged(const Imports &possibleImports) override;
void usedImportsChanged(const Imports &usedImports) override;
void documentMessagesChanged(const QList<DocumentMessage> &errors, const QList<DocumentMessage> &warnings) override;
void updateImport3DSupport(const QVariantMap &supportMap) override;
void customNotification(const AbstractView *view, const QString &identifier,

View File

@@ -86,7 +86,7 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event)
Import fileImport = Import::createFileImport(entry.requiredImport());
if (!m_model->hasImport(libImport, true, true)
&& !m_model->hasImport(fileImport, true, true)) {
const QList<Import> possImports = m_model->possibleImports();
const Imports possImports = m_model->possibleImports();
for (const auto &possImport : possImports) {
if ((!possImport.url().isEmpty() && possImport.url() == libImport.url())
|| (!possImport.file().isEmpty() && possImport.file() == fileImport.file())) {
@@ -246,7 +246,7 @@ void ItemLibraryWidget::handleAddImport(int index)
+ importStr);
}
QList<Import> imports;
Imports imports;
const QString dependency = getDependencyImport(import);
auto document = QmlDesignerPlugin::instance()->currentDesignDocument();
@@ -346,13 +346,13 @@ void ItemLibraryWidget::updateModel()
updateSearch();
}
void ItemLibraryWidget::updatePossibleImports(const QList<Import> &possibleImports)
void ItemLibraryWidget::updatePossibleImports(const Imports &possibleImports)
{
m_addModuleModel->update(possibleImports);
delayedUpdateModel();
}
void ItemLibraryWidget::updateUsedImports(const QList<Import> &usedImports)
void ItemLibraryWidget::updateUsedImports(const Imports &usedImports)
{
m_itemLibraryModel->updateUsedImports(usedImports);
}

View File

@@ -60,8 +60,8 @@ public:
void switchToComponentsView();
void delayedUpdateModel();
void updateModel();
void updatePossibleImports(const QList<Import> &possibleImports);
void updateUsedImports(const QList<Import> &usedImports);
void updatePossibleImports(const Imports &possibleImports);
void updateUsedImports(const Imports &usedImports);
void setModel(Model *model);
void setFlowMode(bool b);

View File

@@ -469,8 +469,8 @@ ModelNode MaterialBrowserView::getMaterialOfModel(const ModelNode &model, int id
return mat;
}
void MaterialBrowserView::importsChanged([[maybe_unused]] const QList<Import> &addedImports,
[[maybe_unused]] const QList<Import> &removedImports)
void MaterialBrowserView::importsChanged([[maybe_unused]] const Imports &addedImports,
[[maybe_unused]] const Imports &removedImports)
{
bool hasQuick3DImport = model()->hasImport("QtQuick3D");

View File

@@ -44,7 +44,7 @@ public:
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty,
PropertyChangeFlags propertyChange) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void instancesCompleted(const QVector<ModelNode> &completedNodeList) override;

View File

@@ -974,8 +974,8 @@ void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, co
m_qmlBackEnd->updateMaterialPreview(pixmap);
}
void MaterialEditorView::importsChanged([[maybe_unused]] const QList<Import> &addedImports,
[[maybe_unused]] const QList<Import> &removedImports)
void MaterialEditorView::importsChanged([[maybe_unused]] const Imports &addedImports,
[[maybe_unused]] const Imports &removedImports)
{
m_hasQuick3DImport = model()->hasImport("QtQuick3D");
m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);

View File

@@ -55,7 +55,7 @@ public:
void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override;
void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override;
void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent,

View File

@@ -901,7 +901,7 @@ void NavigatorTreeModel::addImport(const QString &importName)
{
Import import = Import::createLibraryImport(importName);
if (!m_view->model()->hasImport(import, true, true)) {
const QList<Import> possImports = m_view->model()->possibleImports();
const Imports possImports = m_view->model()->possibleImports();
for (const auto &possImport : possImports) {
if (possImport.url() == import.url()) {
import = possImport;

View File

@@ -228,7 +228,7 @@ void NavigatorView::modelAboutToBeDetached(Model *model)
AbstractView::modelAboutToBeDetached(model);
}
void NavigatorView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/)
void NavigatorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/)
{
treeWidget()->update();
}

View File

@@ -50,7 +50,7 @@ public:
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override;

View File

@@ -103,7 +103,7 @@ void TextEditorView::modelAboutToBeDetached(Model *model)
}
}
void TextEditorView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/)
void TextEditorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/)
{
}

View File

@@ -34,7 +34,7 @@ public:
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override;

View File

@@ -720,8 +720,8 @@ void TextureEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pro
m_locked = false;
}
void TextureEditorView::importsChanged([[maybe_unused]] const QList<Import> &addedImports,
[[maybe_unused]] const QList<Import> &removedImports)
void TextureEditorView::importsChanged([[maybe_unused]] const Imports &addedImports,
[[maybe_unused]] const Imports &removedImports)
{
m_hasQuick3DImport = model()->hasImport("QtQuick3D");
m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport);

View File

@@ -58,7 +58,7 @@ public:
void currentStateChanged(const ModelNode &node) override;
void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;

View File

@@ -195,9 +195,9 @@ public:
const ModelNode &movedNode,
int oldIndex);
virtual void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports);
virtual void possibleImportsChanged(const QList<Import> &possibleImports);
virtual void usedImportsChanged(const QList<Import> &usedImports);
virtual void importsChanged(const Imports &addedImports, const Imports &removedImports);
virtual void possibleImportsChanged(const Imports &possibleImports);
virtual void usedImportsChanged(const Imports &usedImports);
virtual void auxiliaryDataChanged(const ModelNode &node,
AuxiliaryDataKeyView type,

View File

@@ -40,6 +40,7 @@ public:
virtual PuppetStartData puppetStartData(const class Model &model) const = 0;
virtual bool instantQmlTextUpdate() const = 0;
virtual Utils::FilePath qmlPuppetPath() const = 0;
virtual QStringList modulePaths() const = 0;
};
} // namespace QmlDesigner

View File

@@ -44,7 +44,7 @@ public:
void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) override;
void nodeOrderChanged(const NodeListProperty &listProperty) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override;
@@ -197,7 +197,7 @@ void ForwardView<ViewType>::nodeOrderChanged(const NodeListProperty &listPropert
}
template <class ViewType>
void ForwardView<ViewType>::importChanged(const QList<Import> &addedImports, const QList<Import> &removedImports)
void ForwardView<ViewType>::importChanged(const Imports &addedImports, const Imports &removedImports)
{
for (const ViewTypePointer &view : std::as_const(m_targetViewList))
view->importChanged(addedImport, removedImport);

View File

@@ -56,6 +56,8 @@ private:
QMLDESIGNERCORE_EXPORT size_t qHash(const Import &import);
using Imports = QList<Import>;
} // namespace QmlDesigner
Q_DECLARE_METATYPE(QmlDesigner::Import)

View File

@@ -115,12 +115,12 @@ public:
// Editing sub-components:
// Imports:
const QList<Import> &imports() const;
const QList<Import> &possibleImports() const;
const QList<Import> &usedImports() const;
void changeImports(const QList<Import> &importsToBeAdded, const QList<Import> &importsToBeRemoved);
void setPossibleImports(const QList<Import> &possibleImports);
void setUsedImports(const QList<Import> &usedImports);
const Imports &imports() const;
const Imports &possibleImports() const;
const Imports &usedImports() const;
void changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved);
void setPossibleImports(const Imports &possibleImports);
void setUsedImports(const Imports &usedImports);
bool hasImport(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const;
bool isImportPossible(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const;
QString pathForImport(const Import &import);

View File

@@ -85,7 +85,7 @@ public:
void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) override;
void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override;
void nodeOrderChanged(const NodeListProperty &listProperty) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void auxiliaryDataChanged(const ModelNode &node,
AuxiliaryDataKeyView key,
const QVariant &data) override;

View File

@@ -84,7 +84,7 @@ public:
void rewriterBeginTransaction() override;
void rewriterEndTransaction() override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
TextModifier *textModifier() const;
void setTextModifier(TextModifier *textModifier);

View File

@@ -27,7 +27,7 @@ public:
explicit SubComponentManager(Model *model,
class ExternalDependenciesInterface &externalDependencies);
void update(const QUrl &fileUrl, const QList<Import> &imports);
void update(const QUrl &fileUrl, const Imports &imports);
void addAndParseImport(const Import &import);
QStringList qmlFiles() const;
@@ -53,7 +53,7 @@ private: // functions
private: // variables
QFileSystemWatcher m_watcher;
QList<Import> m_imports;
Imports m_imports;
// key: canonical directory path
QMultiHash<QString,QString> m_dirToQualifier;
QUrl m_filePath;

View File

@@ -625,7 +625,7 @@ void NodeInstanceView::nodeOrderChanged(const NodeListProperty &listProperty)
m_nodeInstanceServer->reparentInstances(ReparentInstancesCommand(containerList));
}
void NodeInstanceView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/)
void NodeInstanceView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/)
{
restartProcess();
}

View File

@@ -768,7 +768,7 @@ NodeMetaInfoPrivate::NodeMetaInfoPrivate(Model *model, TypeName type, int maj, i
}
} else {
m_isFileComponent = true;
const Imports *imports = context()->imports(document());
const auto *imports = context()->imports(document());
const ImportInfo importInfo = imports->info(lookupNameComponent().constLast(),
context().data());
@@ -791,7 +791,7 @@ NodeMetaInfoPrivate::NodeMetaInfoPrivate(Model *model, TypeName type, int maj, i
} else {
// Special case for aliased types for the rewriter
const Imports *imports = context()->imports(document());
const auto *imports = context()->imports(document());
const ImportInfo importInfo = imports->info(QString::fromUtf8(m_qualfiedTypeName),
context().data());
if (importInfo.isValid()) {
@@ -1198,7 +1198,7 @@ QString NodeMetaInfoPrivate::importDirectoryPath() const
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
if (isValid()) {
const Imports *imports = context()->imports(document());
const auto *imports = context()->imports(document());
ImportInfo importInfo = imports->info(lookupNameComponent().constLast(), context().data());
if (importInfo.type() == ImportType::Directory) {
@@ -1333,7 +1333,7 @@ void NodeMetaInfoPrivate::setupPrototypes()
m_prototypes.append(description);
} else {
if (context()->lookupType(document(), {ov->className()})) {
const Imports *allImports = context()->imports(document());
const auto *allImports = context()->imports(document());
ImportInfo importInfo = allImports->info(description.className, context().data());
if (importInfo.isValid()) {

View File

@@ -491,7 +491,7 @@ QStringList SubComponentManager::qmlFiles() const
return m_watcher.files();
}
void SubComponentManager::update(const QUrl &filePath, const QList<Import> &imports)
void SubComponentManager::update(const QUrl &filePath, const Imports &imports)
{
if (debug)
qDebug() << Q_FUNC_INFO << filePath << imports.size();

View File

@@ -315,15 +315,15 @@ void AbstractView::nodeTypeChanged(const ModelNode & /*node*/, const TypeName &
}
void AbstractView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/)
void AbstractView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/)
{
}
void AbstractView::possibleImportsChanged(const QList<Import> &/*possibleImports*/)
void AbstractView::possibleImportsChanged(const Imports &/*possibleImports*/)
{
}
void AbstractView::usedImportsChanged(const QList<Import> &/*usedImports*/)
void AbstractView::usedImportsChanged(const Imports &/*usedImports*/)
{
}
@@ -893,7 +893,7 @@ QmlTimeline AbstractView::currentTimeline() const
static int getMinorVersionFromImport(const Model *model)
{
const QList<Import> imports = model->imports();
const Imports imports = model->imports();
for (const Import &import : imports) {
if (import.isLibraryImport() && import.url() == "QtQuick") {
const QString versionString = import.version();
@@ -909,7 +909,7 @@ static int getMinorVersionFromImport(const Model *model)
static int getMajorVersionFromImport(const Model *model)
{
const QList<Import> imports = model->imports();
const Imports imports = model->imports();
for (const Import &import : imports) {
if (import.isLibraryImport() && import.url() == QStringLiteral("QtQuick")) {
const QString versionString = import.version();

View File

@@ -108,10 +108,10 @@ void ModelPrivate::detachAllViews()
}
}
void ModelPrivate::changeImports(const QList<Import> &toBeAddedImportList,
const QList<Import> &toBeRemovedImportList)
void ModelPrivate::changeImports(const Imports &toBeAddedImportList,
const Imports &toBeRemovedImportList)
{
QList<Import> removedImportList;
Imports removedImportList;
for (const Import &import : toBeRemovedImportList) {
if (m_imports.contains(import)) {
removedImportList.append(import);
@@ -119,7 +119,7 @@ void ModelPrivate::changeImports(const QList<Import> &toBeAddedImportList,
}
}
QList<Import> addedImportList;
Imports addedImportList;
for (const Import &import : toBeAddedImportList) {
if (!m_imports.contains(import)) {
addedImportList.append(import);
@@ -131,8 +131,7 @@ void ModelPrivate::changeImports(const QList<Import> &toBeAddedImportList,
notifyImportsChanged(addedImportList, removedImportList);
}
void ModelPrivate::notifyImportsChanged(const QList<Import> &addedImports,
const QList<Import> &removedImports)
void ModelPrivate::notifyImportsChanged(const Imports &addedImports, const Imports &removedImports)
{
bool resetModel = false;
QString description;
@@ -157,7 +156,7 @@ void ModelPrivate::notifyImportsChanged(const QList<Import> &addedImports,
resetModelByRewriter(description);
}
void ModelPrivate::notifyPossibleImportsChanged(const QList<Import> &possibleImports)
void ModelPrivate::notifyPossibleImportsChanged(const Imports &possibleImports)
{
for (const QPointer<AbstractView> &view : enabledViews()) {
Q_ASSERT(view != nullptr);
@@ -165,7 +164,7 @@ void ModelPrivate::notifyPossibleImportsChanged(const QList<Import> &possibleImp
}
}
void ModelPrivate::notifyUsedImportsChanged(const QList<Import> &usedImports)
void ModelPrivate::notifyUsedImportsChanged(const Imports &usedImports)
{
for (const QPointer<AbstractView> &view : enabledViews()) {
Q_ASSERT(view != nullptr);
@@ -1400,28 +1399,27 @@ Model::Model(const TypeName &typeName, int major, int minor, Model *metaInfoProx
Model::~Model() = default;
const QList<Import> &Model::imports() const
const Imports &Model::imports() const
{
return d->imports();
}
const QList<Import> &Model::possibleImports() const
const Imports &Model::possibleImports() const
{
return d->m_possibleImportList;
}
const QList<Import> &Model::usedImports() const
const Imports &Model::usedImports() const
{
return d->m_usedImportList;
}
void Model::changeImports(const QList<Import> &importsToBeAdded,
const QList<Import> &importsToBeRemoved)
void Model::changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved)
{
d->changeImports(importsToBeAdded, importsToBeRemoved);
}
void Model::setPossibleImports(const QList<Import> &possibleImports)
void Model::setPossibleImports(const Imports &possibleImports)
{
if (d->m_possibleImportList != possibleImports) {
d->m_possibleImportList = possibleImports;
@@ -1429,7 +1427,7 @@ void Model::setPossibleImports(const QList<Import> &possibleImports)
}
}
void Model::setUsedImports(const QList<Import> &usedImports)
void Model::setUsedImports(const Imports &usedImports)
{
if (d->m_usedImportList != usedImports) {
d->m_usedImportList = usedImports;

View File

@@ -202,13 +202,13 @@ public:
void resetModelByRewriter(const QString &description);
// Imports:
const QList<Import> &imports() const { return m_imports; }
const Imports &imports() const { return m_imports; }
void addImport(const Import &import);
void removeImport(const Import &import);
void changeImports(const QList<Import> &importsToBeAdded, const QList<Import> &importToBeRemoved);
void notifyImportsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports);
void notifyPossibleImportsChanged(const QList<Import> &possibleImports);
void notifyUsedImportsChanged(const QList<Import> &usedImportsChanged);
void changeImports(const Imports &importsToBeAdded, const Imports &importToBeRemoved);
void notifyImportsChanged(const Imports &addedImports, const Imports &removedImports);
void notifyPossibleImportsChanged(const Imports &possibleImports);
void notifyUsedImportsChanged(const Imports &usedImportsChanged);
//node state property manipulation
void addProperty(const InternalNodePointer &node, const PropertyName &name);
@@ -269,9 +269,9 @@ private:
private:
Model *m_model = nullptr;
MetaInfo m_metaInfo;
QList<Import> m_imports;
QList<Import> m_possibleImportList;
QList<Import> m_usedImportList;
Imports m_imports;
Imports m_possibleImportList;
Imports m_usedImportList;
QList<QPointer<AbstractView>> m_viewList;
QList<QPointer<AbstractView>> m_enabledViewList;
QList<InternalNodePointer> m_selectedInternalNodeList;

View File

@@ -173,7 +173,7 @@ ModelNode ModelMerger::insertModel(const ModelNode &modelNode, const MergePredic
return {};
RewriterTransaction transaction(view()->beginRewriterTransaction(QByteArrayLiteral("ModelMerger::insertModel")));
QList<Import> newImports;
Imports newImports;
for (const Import &import : modelNode.model()->imports()) {
if (!view()->model()->hasImport(import, true, true))

View File

@@ -261,7 +261,7 @@ void RewriterView::nodeReparented(const ModelNode &node, const NodeAbstractPrope
applyChanges();
}
void RewriterView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports)
void RewriterView::importsChanged(const Imports &addedImports, const Imports &removedImports)
{
for (const Import &import : addedImports)
importAdded(import);

View File

@@ -19,6 +19,7 @@
#include "signalhandlerproperty.h"
#include "variantproperty.h"
#include <externaldependenciesinterface.h>
#include <projectstorage/modulescanner.h>
#include <rewritingexception.h>
#include <enumeration.h>
@@ -44,6 +45,7 @@
#include <QSet>
#include <memory>
#include <tuple>
using namespace LanguageUtils;
using namespace QmlJS;
@@ -716,7 +718,7 @@ bool TextToModelMerger::isActive() const
void TextToModelMerger::setupImports(const Document::Ptr &doc,
DifferenceHandler &differenceHandler)
{
QList<Import> existingImports = m_rewriterView->model()->imports();
Imports existingImports = m_rewriterView->model()->imports();
m_hasVersionlessImport = false;
@@ -756,162 +758,125 @@ void TextToModelMerger::setupImports(const Document::Ptr &doc,
differenceHandler.importAbsentInQMl(import);
}
static bool isLatestImportVersion(const ImportKey &importKey, const QHash<QString, ImportKey> &filteredPossibleImportKeys)
namespace {
bool skipByMetaInfo(QStringView moduleName, const QStringList &skipModuleNames)
{
return !filteredPossibleImportKeys.contains(importKey.path())
|| filteredPossibleImportKeys.value(importKey.path()).majorVersion < importKey.majorVersion
|| (filteredPossibleImportKeys.value(importKey.path()).majorVersion == importKey.majorVersion
&& filteredPossibleImportKeys.value(importKey.path()).minorVersion < importKey.minorVersion);
return std::any_of(skipModuleNames.begin(),
skipModuleNames.end(),
[&](const QString &skipModuleName) {
return moduleName.contains(skipModuleName);
});
}
static bool filterByMetaInfo(const ImportKey &importKey, Model *model)
class StartsWith : public QStringView
{
if (model) {
for (const QString &filter : model->metaInfo().itemLibraryInfo()->blacklistImports()) {
if (importKey.libraryQualifiedPath().contains(filter))
return true;
public:
using QStringView::QStringView;
bool operator()(QStringView moduleName) const { return moduleName.startsWith(*this); }
};
class EndsWith : public QStringView
{
public:
using QStringView::QStringView;
bool operator()(QStringView moduleName) const { return moduleName.endsWith(*this); }
};
class StartsAndEndsWith : public std::pair<QStringView, QStringView>
{
public:
using Base = std::pair<QStringView, QStringView>;
using Base::Base;
bool operator()(QStringView moduleName) const
{
return moduleName.startsWith(first) && moduleName.endsWith(second);
}
};
class Equals : public QStringView
{
public:
using QStringView::QStringView;
bool operator()(QStringView moduleName) const { return moduleName == *this; }
};
constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"),
StartsWith(u"QML"),
StartsWith(u"QtQml"),
StartsAndEndsWith(u"QtQuick", u".PrivateWidgets"),
EndsWith(u".private"),
EndsWith(u".Private"),
Equals(u"QtQuick.Particles"),
Equals(u"QtQuick.Dialogs"),
Equals(u"QtQuick.Controls.Styles"),
Equals(u"QtNfc"),
Equals(u"Qt.WebSockets"),
Equals(u"QtWebkit"),
Equals(u"QtLocation"),
Equals(u"QtWebChannel"),
Equals(u"QtWinExtras"),
Equals(u"QtPurchasing"),
Equals(u"QtBluetooth"),
Equals(u"Enginio"));
bool skipModule(QStringView moduleName)
{
return std::apply([=](const auto &...skipModule) { return (skipModule(moduleName) || ...); },
skipModules);
}
bool skipModule(QStringView moduleName, const QStringList &skipModuleNames)
{
return skipModule(moduleName) || skipByMetaInfo(moduleName, skipModuleNames);
}
void collectPossibleFileImports(const QString &checkPath,
QSet<QString> usedImportsSet,
QList<QmlDesigner::Import> &possibleImports)
{
const QStringList qmlList("*.qml");
const QStringList qmldirList("qmldir");
const QChar delimeter('/');
if (QFileInfo(checkPath).isRoot())
return;
const QStringList entries = QDir(checkPath).entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
const QString checkPathDelim = checkPath + delimeter;
for (const QString &entry : entries) {
QDir dir(checkPathDelim + entry);
const QString dirPath = dir.path();
if (!dir.entryInfoList(qmlList, QDir::Files).isEmpty()
&& dir.entryInfoList(qmldirList, QDir::Files).isEmpty()
&& !usedImportsSet.contains(dirPath)) {
const QString importName = dir.path().mid(checkPath.size() + 1);
QmlDesigner::Import import = QmlDesigner::Import::createFileImport(importName);
possibleImports.append(import);
}
collectPossibleFileImports(dirPath, usedImportsSet, possibleImports);
}
return false;
}
static bool isBlacklistImport(const ImportKey &importKey, Model *model)
{
const QString &importPathFirst = importKey.splitPath.constFirst();
const QString &importPathLast = importKey.splitPath.constLast();
return importPathFirst == QStringLiteral("<cpp>")
|| importPathFirst == QStringLiteral("QML")
|| importPathFirst == QStringLiteral("QtQml")
|| (importPathFirst == QStringLiteral("QtQuick") && importPathLast == QStringLiteral("PrivateWidgets"))
|| importPathLast == QStringLiteral("Private")
|| importPathLast == QStringLiteral("private")
|| importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Particles") //Unsupported
|| importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Dialogs") //Unsupported
|| importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Controls.Styles") //Unsupported
|| importKey.libraryQualifiedPath() == QStringLiteral("QtNfc") //Unsupported
|| importKey.libraryQualifiedPath() == QStringLiteral("Qt.WebSockets")
|| importKey.libraryQualifiedPath() == QStringLiteral("QtWebkit")
|| importKey.libraryQualifiedPath() == QStringLiteral("QtLocation")
|| importKey.libraryQualifiedPath() == QStringLiteral("QtWebChannel")
|| importKey.libraryQualifiedPath() == QStringLiteral("QtWinExtras")
|| importKey.libraryQualifiedPath() == QStringLiteral("QtPurchasing")
|| importKey.libraryQualifiedPath() == QStringLiteral("QtBluetooth")
|| importKey.libraryQualifiedPath() == QStringLiteral("Enginio")
|| filterByMetaInfo(importKey, model);
}
static QHash<QString, ImportKey> filterPossibleImportKeys(const QSet<ImportKey> &possibleImportKeys, Model *model)
{
QHash<QString, ImportKey> filteredPossibleImportKeys;
for (const ImportKey &importKey : possibleImportKeys) {
if (isLatestImportVersion(importKey, filteredPossibleImportKeys) && !isBlacklistImport(importKey, model))
filteredPossibleImportKeys.insert(importKey.path(), importKey);
}
return filteredPossibleImportKeys;
}
static void removeUsedImports(QHash<QString, ImportKey> &filteredPossibleImportKeys, const QList<QmlJS::Import> &usedImports)
{
for (const QmlJS::Import &import : usedImports)
filteredPossibleImportKeys.remove(import.info.path());
}
static QList<QmlDesigner::Import> generatePossibleFileImports(const QString &path,
const QList<QmlJS::Import> &usedImports)
QList<QmlDesigner::Import> generatePossibleFileImports(const QString &path,
const QList<QmlJS::Import> &usedImports)
{
QSet<QString> usedImportsSet;
for (const QmlJS::Import &i : usedImports)
usedImportsSet.insert(i.info.path());
QList<QmlDesigner::Import> possibleImports;
const QStringList qmlList("*.qml");
const QStringList qmldirList("qmldir");
QStringList fileImportPaths;
const QChar delimeter('/');
std::function<void(const QString &)> checkDir;
checkDir = [&](const QString &checkPath) {
if (QFileInfo(checkPath).isRoot())
return;
const QStringList entries = QDir(checkPath).entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
const QString checkPathDelim = checkPath + delimeter;
for (const QString &entry : entries) {
QDir dir(checkPathDelim + entry);
const QString dirPath = dir.path();
if (!dir.entryInfoList(qmlList, QDir::Files).isEmpty()
&& dir.entryInfoList(qmldirList, QDir::Files).isEmpty()
&& !usedImportsSet.contains(dirPath)) {
const QString importName = dir.path().mid(path.size() + 1);
QmlDesigner::Import import = QmlDesigner::Import::createFileImport(importName);
possibleImports.append(import);
}
checkDir(dirPath);
}
};
checkDir(path);
collectPossibleFileImports(path, usedImportsSet, possibleImports);
return possibleImports;
}
static QList<QmlDesigner::Import> generatePossibleLibraryImports(const QHash<QString, ImportKey> &filteredPossibleImportKeys)
{
QList<QmlDesigner::Import> possibleImports;
QSet<QString> controlsImplVersions;
bool hasVersionedControls = false;
bool hasVersionlessControls = false;
const QString controlsName = "QtQuick.Controls";
const QString controlsImplName = "QtQuick.Controls.impl";
} // namespace
for (const ImportKey &importKey : filteredPossibleImportKeys) {
QString libraryName = importKey.splitPath.join(QLatin1Char('.'));
int majorVersion = importKey.majorVersion;
if (majorVersion >= 0) {
int minorVersion = (importKey.minorVersion == LanguageUtils::ComponentVersion::NoVersion) ? 0 : importKey.minorVersion;
if (libraryName.contains("QtQuick.Studio")) {
majorVersion = 1;
minorVersion = 0;
}
QString version = QStringLiteral("%1.%2").arg(majorVersion).arg(minorVersion);
if (!libraryName.endsWith(".impl"))
possibleImports.append(QmlDesigner::Import::createLibraryImport(libraryName, version));
// In Qt6, QtQuick.Controls itself doesn't have any version as it has no types,
// so it never gets added normally to possible imports.
// We work around this by injecting corresponding QtQuick.Controls version for each
// found impl version, if no valid QtQuick.Controls versions are found.
if (!hasVersionedControls) {
if (libraryName == controlsImplName)
controlsImplVersions.insert(version);
else if (libraryName == controlsName)
hasVersionedControls = true;
}
} else if (!hasVersionlessControls && libraryName == controlsName) {
// If QtQuick.Controls module is not included even in non-versioned, it means
// QtQuick.Controls is either in use or not available at all,
// so we shouldn't inject it.
hasVersionlessControls = true;
}
}
if (hasVersionlessControls && !hasVersionedControls && !controlsImplVersions.isEmpty()) {
for (const auto &version : std::as_const(controlsImplVersions))
possibleImports.append(QmlDesigner::Import::createLibraryImport(controlsName, version));
}
return possibleImports;
}
void TextToModelMerger::setupPossibleImports(const QmlJS::Snapshot &snapshot, const QmlJS::ViewerContext &viewContext)
void TextToModelMerger::setupPossibleImports()
{
if (!m_rewriterView->possibleImportsEnabled())
return;
@@ -919,27 +884,25 @@ void TextToModelMerger::setupPossibleImports(const QmlJS::Snapshot &snapshot, co
static QUrl lastProjectUrl;
auto &externalDependencies = m_rewriterView->externalDependencies();
auto projectUrl = externalDependencies.projectUrl();
auto allUsedImports = m_scopeChain->context()->imports(m_document.data())->all();
if (m_possibleImportKeys.isEmpty() || projectUrl != lastProjectUrl)
m_possibleImportKeys = snapshot.importDependencies()->libraryImports(viewContext);
if (m_possibleModules.isEmpty() || projectUrl != lastProjectUrl) {
const auto skipModuleNames = m_rewriterView->model()->metaInfo().itemLibraryInfo()->blacklistImports();
ModuleScanner moduleScanner{
[&](QStringView moduleName) { return skipModule(moduleName, skipModuleNames); }};
moduleScanner.scan(m_rewriterView->externalDependencies().modulePaths());
m_possibleModules = moduleScanner.modules();
}
lastProjectUrl = projectUrl;
QHash<QString, ImportKey> filteredPossibleImportKeys = filterPossibleImportKeys(
m_possibleImportKeys, m_rewriterView->model());
const QmlJS::Imports *imports = m_scopeChain->context()->imports(m_document.data());
if (imports)
removeUsedImports(filteredPossibleImportKeys, imports->all());
QList<QmlDesigner::Import> possibleImports = generatePossibleLibraryImports(filteredPossibleImportKeys);
auto modules = m_possibleModules;
if (document()->fileName() != "<internal>")
possibleImports.append(
generatePossibleFileImports(document()->path().toString(), imports->all()));
modules.append(generatePossibleFileImports(document()->path().toString(), allUsedImports));
if (m_rewriterView->isAttached())
m_rewriterView->model()->setPossibleImports(possibleImports);
m_rewriterView->model()->setPossibleImports(modules);
}
void TextToModelMerger::setupUsedImports()
@@ -951,7 +914,7 @@ void TextToModelMerger::setupUsedImports()
const QList<QmlJS::Import> allImports = imports->all();
QSet<QString> usedImportsSet;
QList<Import> usedImports;
Imports usedImports;
// populate usedImportsSet from current model nodes
const QList<ModelNode> allModelNodes = m_rewriterView->allModelNodes();
@@ -964,9 +927,6 @@ void TextToModelMerger::setupUsedImports()
for (const QmlJS::Import &import : allImports) {
QString version = import.info.version().toString();
if (!import.info.version().isValid())
version = getHighestPossibleImport(import.info.name());
if (!import.info.name().isEmpty() && usedImportsSet.contains(import.info.name())) {
if (import.info.type() == ImportType::Library)
usedImports.append(
@@ -1077,7 +1037,7 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH
collectLinkErrors(&errors, ctxt);
}
setupPossibleImports(snapshot, m_vContext);
setupPossibleImports();
qCInfo(rewriterBenchmark) << "possible imports:" << time.elapsed();
@@ -1115,12 +1075,6 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH
setActive(false);
// Clear possible imports cache if code model hasn't settled yet
const int importKeysSize = m_possibleImportKeys.size();
if (m_previousPossibleImportsSize != importKeysSize)
m_possibleImportKeys.clear();
m_previousPossibleImportsSize = importKeysSize;
return true;
} catch (Exception &e) {
DocumentMessage error(&e);
@@ -2387,8 +2341,8 @@ QList<QmlTypeData> TextToModelMerger::getQMLSingletons() const
void TextToModelMerger::clearPossibleImportKeys()
{
m_possibleImportKeys.clear();
m_previousPossibleImportsSize = -1;
m_possibleModules.clear();
m_previousPossibleModulesSize = -1;
}
QString TextToModelMerger::textAt(const Document::Ptr &doc,
@@ -2403,19 +2357,3 @@ QString TextToModelMerger::textAt(const Document::Ptr &doc,
{
return doc->source().mid(from.offset, to.end() - from.begin());
}
QString TextToModelMerger::getHighestPossibleImport(const QString &importName) const
{
QString version = "2.15";
int maj = -1;
const auto imports = m_possibleImportKeys.values();
for (const ImportKey &import : imports) {
if (importName == import.libraryQualifiedPath()) {
if (import.majorVersion > maj) {
version = QString("%1.%2").arg(import.majorVersion).arg(import.minorVersion);
maj = import.majorVersion;
}
}
}
return version;
}

View File

@@ -37,7 +37,7 @@ public:
bool isActive() const;
void setupImports(const QmlJS::Document::Ptr &doc, DifferenceHandler &differenceHandler);
void setupPossibleImports(const QmlJS::Snapshot &snapshot, const QmlJS::ViewerContext &viewContext);
void setupPossibleImports();
void setupUsedImports();
bool load(const QString &data, DifferenceHandler &differenceHandler);
@@ -137,8 +137,6 @@ private:
const QmlJS::SourceLocation &from,
const QmlJS::SourceLocation &to);
QString getHighestPossibleImport(const QString &importName) const;
private:
RewriterView *m_rewriterView;
bool m_isActive;
@@ -150,8 +148,8 @@ private:
QSet<ModelNode> m_clearImplicitComponentList;
QmlJS::ViewerContext m_vContext;
QSet<QPair<QString, QString> > m_qrcMapping;
QSet<QmlJS::ImportKey> m_possibleImportKeys;
int m_previousPossibleImportsSize = -1;
Imports m_possibleModules;
int m_previousPossibleModulesSize = -1;
bool m_hasVersionlessImport = false;
};

View File

@@ -0,0 +1,73 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "modulescanner.h"
#ifdef QDS_HAS_QMLPRIVATE
#include <private/qqmldirparser_p.h>
#endif
#include <QFile>
#include <filesystem>
namespace QmlDesigner {
namespace {
std::optional<QString> contentAsQString(const QString &filePath)
{
QFile file{filePath};
if (file.open(QIODevice::ReadOnly))
return {QString::fromUtf8(file.readAll())};
return {};
}
} // namespace
void ModuleScanner::scan(const QStringList &modulePaths)
{
for (const QString &modulePath : modulePaths)
scan(modulePath.toStdString());
}
void ModuleScanner::scan(std::string_view modulePath)
{
#ifdef QDS_HAS_QMLPRIVATE
try {
const std::filesystem::path installDirectoryPath{modulePath};
auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath};
auto end = std::filesystem::end(current);
for (; current != end; ++current) {
const auto &entry = *current;
auto path = entry.path();
if (path.filename() == "qmldir") {
QQmlDirParser parser;
auto content = contentAsQString(QString::fromStdU16String(path.u16string()));
if (!content)
continue;
bool hasError = parser.parse(*content);
if (hasError)
continue;
auto moduleName = parser.typeNamespace();
if (moduleName.isEmpty() || m_skip(moduleName))
continue;
m_modules.push_back(Import::createLibraryImport(moduleName));
}
}
} catch (const std::filesystem::filesystem_error &) {
return;
}
#endif
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,37 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <qmldesignercorelib_global.h>
#include <import.h>
#include <optional>
namespace QmlDesigner {
class QMLDESIGNERCORE_EXPORT ModuleScanner
{
public:
using SkipFunction = std::function<bool(QStringView)>;
ModuleScanner(SkipFunction skip)
: m_skip{std::move(skip)}
{
m_modules.reserve(128);
}
void scan(const QStringList &modulePaths);
const Imports &modules() const { return m_modules; }
private:
void scan(std::string_view modulePaths);
private:
SkipFunction m_skip;
Imports m_modules;
};
} // namespace QmlDesigner

View File

@@ -10,7 +10,7 @@
#include <sqlitedatabase.h>
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
#include <private/qqmldomtop_p.h>
#endif
@@ -19,7 +19,7 @@
namespace QmlDesigner {
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
namespace QmlDom = QQmlJS::Dom;

View File

@@ -18,7 +18,7 @@ public:
using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>;
using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>;
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
QmlDocumentParser(ProjectStorage &storage, PathCache &pathCache)
: m_storage{storage}
, m_pathCache{pathCache}
@@ -35,7 +35,7 @@ public:
private:
// m_pathCache and m_storage are only used when compiled for QDS
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
ProjectStorage &m_storage;
PathCache &m_pathCache;
#endif

View File

@@ -8,7 +8,7 @@
#include <sqlitedatabase.h>
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
#include <private/qqmldomtop_p.h>
#include <private/qqmljstypedescriptionreader_p.h>
#endif
@@ -20,7 +20,7 @@
namespace QmlDesigner {
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
namespace QmlDom = QQmlJS::Dom;

View File

@@ -24,7 +24,7 @@ public:
using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>;
using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>;
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
QmlTypesParser(PathCache &pathCache, ProjectStorage &storage)
: m_pathCache{pathCache}
, m_storage{storage}
@@ -41,7 +41,7 @@ public:
private:
// m_pathCache and m_storage are only used when compiled for QDS
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
PathCache &m_pathCache;
ProjectStorage &m_storage;
#endif

View File

@@ -9,9 +9,11 @@
#include <edit3d/edit3dviewconfig.h>
#include <itemlibraryimport.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <puppetenvironmentbuilder.h>
#include <qmlprojectmanager/qmlproject.h>
#include <qmlpuppetpaths.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
@@ -186,4 +188,50 @@ Utils::FilePath ExternalDependencies::qmlPuppetPath() const
return puppetPath;
}
namespace {
QString qmlPath(ProjectExplorer::Target *target)
{
auto kit = target->kit();
if (!kit)
return {};
auto qtVersion = QtSupport::QtKitAspect::qtVersion(kit);
if (!qtVersion)
return {};
return qtVersion->qmlPath().toString();
}
} // namespace
QStringList ExternalDependencies::modulePaths() const
{
QStringList modulePaths;
auto project = ProjectExplorer::SessionManager::startupProject();
if (!project)
return modulePaths;
auto target = project->activeTarget();
if (!target)
return modulePaths;
if (auto path = qmlPath(target); !path.isEmpty())
modulePaths.push_back(path);
const auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
target->buildSystem());
if (!qmlBuildSystem)
return modulePaths;
for (const QString &modulePath : qmlBuildSystem->customImportPaths())
modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString());
return modulePaths;
}
} // namespace QmlDesigner

View File

@@ -36,6 +36,7 @@ public:
PuppetStartData puppetStartData(const class Model &model) const override;
bool instantQmlTextUpdate() const override;
Utils::FilePath qmlPuppetPath() const override;
QStringList modulePaths() const override;
private:
const DesignerSettings &m_designerSettings;

View File

@@ -342,7 +342,7 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa
qmldirPaths.push_back(QDir::cleanPath(pojectDirectory.absoluteFilePath(importPath))
+ "/qmldir");
}
#ifdef QDS_HAS_QMLDOM
#ifdef QDS_HAS_QMLPRIVATE
bool skipPath(const std::filesystem::path &path)
{
auto directory = path.filename();
@@ -359,25 +359,27 @@ bool skipPath(const std::filesystem::path &path)
}
#endif
void qtQmldirPaths([[maybe_unused]] ::ProjectExplorer::Target *target,
[[maybe_unused]] QStringList &qmldirPaths)
void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths)
{
#ifdef QDS_HAS_QMLDOM
const QString installDirectory = qmlPath(target).toString();
#ifdef QDS_HAS_QMLPRIVATE
const std::filesystem::path installDirectoryPath{installDirectory.toStdString()};
if (useProjectStorage()) {
const QString installDirectory = qmlPath(target).toString();
auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath};
auto end = std::filesystem::end(current);
for (; current != end; ++current) {
const auto &entry = *current;
auto path = entry.path();
if (current.depth() < 3 && !current->is_regular_file() && skipPath(path)) {
current.disable_recursion_pending();
continue;
}
if (path.filename() == "qmldir") {
qmldirPaths.push_back(QString::fromStdU16String(path.generic_u16string()));
const std::filesystem::path installDirectoryPath{installDirectory.toStdString()};
auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath};
auto end = std::filesystem::end(current);
for (; current != end; ++current) {
const auto &entry = *current;
auto path = entry.path();
if (current.depth() < 3 && !current->is_regular_file() && skipPath(path)) {
current.disable_recursion_pending();
continue;
}
if (path.filename() == "qmldir") {
qmldirPaths.push_back(QString::fromStdU16String(path.generic_u16string()));
}
}
}
#endif

View File

@@ -160,6 +160,7 @@ public:
PuppetStartData puppetStartData(const class Model &) const override { return {}; }
bool instantQmlTextUpdate() const override { return true; }
Utils::FilePath qmlPuppetPath() const override { return {}; }
QStringList modulePaths() const override { return {}; }
public:
QSettings qsettings;
@@ -1074,10 +1075,10 @@ void tst_TestCore::testRewriterChangeImports()
//
Import webkitImport = Import::createLibraryImport("QtWebKit", "1.0");
QList<Import> importList;
Imports importList;
importList << webkitImport;
model->changeImports(importList, QList<Import>());
model->changeImports(importList, Imports());
const QLatin1String qmlWithImport("\n"
"import QtQuick 2.1\n"
@@ -1086,7 +1087,7 @@ void tst_TestCore::testRewriterChangeImports()
"Rectangle {}\n");
QCOMPARE(textEdit.toPlainText(), qmlWithImport);
model->changeImports(QList<Import>(), importList);
model->changeImports(Imports(), importList);
QCOMPARE(model->imports().size(), 1);
QCOMPARE(model->imports().first(), Import::createLibraryImport("QtQuick", "2.1"));
@@ -1100,7 +1101,7 @@ void tst_TestCore::testRewriterChangeImports()
Import webkitImportAlias = Import::createLibraryImport("QtWebKit", "1.0", "Web");
model->changeImports(QList<Import>() << webkitImportAlias, QList<Import>() << webkitImport);
model->changeImports(Imports() << webkitImportAlias, Imports() << webkitImport);
const QLatin1String qmlWithAliasImport("\n"
"import QtQuick 2.1\n"
@@ -1109,7 +1110,7 @@ void tst_TestCore::testRewriterChangeImports()
"Rectangle {}\n");
QCOMPARE(textEdit.toPlainText(), qmlWithAliasImport);
model->changeImports(QList<Import>(), QList<Import>() << webkitImportAlias);
model->changeImports(Imports(), Imports() << webkitImportAlias);
QCOMPARE(model->imports().first(), Import::createLibraryImport("QtQuick", "2.1"));
QCOMPARE(textEdit.toPlainText(), qmlString);

View File

@@ -25,7 +25,7 @@ add_qtc_test(unittest GTEST
BEFORE "../mockup/qmldesigner/designercore/include"
DEPENDS
Qt::Core Qt::Network Qt::Widgets
Qt::Xml Qt::Concurrent Qt::Qml Qt::Gui
Qt::Xml Qt::Concurrent Qt::QmlPrivate Qt::Gui
Qt6Core5Compat QmlJS Sqlite SqliteC
Googletest
DEFINES
@@ -36,6 +36,7 @@ add_qtc_test(unittest GTEST
QTC_RESOURCE_DIR="${CMAKE_CURRENT_LIST_DIR}/../../../share/qtcreator"
TESTDATA_DIR="${CMAKE_CURRENT_BINARY_DIR}/data"
TEST_RELATIVE_LIBEXEC_PATH="${TEST_RELATIVE_LIBEXEC_PATH}"
QT6_INSTALL_PREFIX="${QT6_INSTALL_PREFIX}"
SOURCES
abstractviewmock.h
compare-operators.h
@@ -97,6 +98,7 @@ add_qtc_test(unittest GTEST
mockimagecachestorage.h
asynchronousexplicitimagecache-test.cpp
asynchronousimagefactory-test.cpp
modulescanner-test.cpp
)
if (NOT TARGET unittest)
@@ -250,6 +252,7 @@ extend_qtc_test(unittest
projectstorage/filesystem.cpp projectstorage/filesystem.h
projectstorage/filestatus.h
projectstorage/filestatuscache.cpp projectstorage/filestatuscache.h
projectstorage/modulescanner.cpp projectstorage/modulescanner.h
projectstorage/nonlockingmutex.h
projectstorage/projectstorageexceptions.cpp projectstorage/projectstorageexceptions.h
projectstorage/projectstorageinterface.h
@@ -335,7 +338,7 @@ extend_qtc_test(unittest
CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0
SOURCES_PREFIX "${QmlDesignerDir}/designercore"
DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate
DEFINES QDS_HAS_QMLDOM
DEFINES QDS_HAS_QMLPRIVATE
SOURCES
projectstorage/qmldocumentparser.cpp projectstorage/qmldocumentparser.h
projectstorage/qmltypesparser.cpp projectstorage/qmltypesparser.h

View File

@@ -5,7 +5,6 @@
#include <gmock/gmock.h>
using testing::_;
using testing::A;
using testing::AllOf;
@@ -21,6 +20,7 @@ using testing::ByRef;
using testing::ContainerEq;
using testing::Contains;
using testing::ElementsAre;
using testing::EndsWith;
using testing::Eq;
using testing::Exactly;
using testing::Field;
@@ -29,7 +29,6 @@ using testing::Gt;
using testing::HasSubstr;
using testing::InSequence;
using testing::Invoke;
using testing::IsEmpty;
using testing::IsNull;
using testing::Le;
using testing::Lt;

View File

@@ -0,0 +1,53 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "googletest.h"
#include <projectstorage/modulescanner.h>
#include <QDebug>
namespace {
template<typename Matcher>
auto UrlProperty(const Matcher &matcher)
{
return Property(&QmlDesigner::Import::url, matcher);
}
class ModuleScanner : public testing::Test
{
protected:
QmlDesigner::ModuleScanner scanner{
[](QStringView moduleName) { return moduleName.endsWith(u"impl"); }};
};
TEST_F(ModuleScanner, ReturnEmptyOptionalForWrongPath)
{
scanner.scan(QStringList{""});
ASSERT_THAT(scanner.modules(), IsEmpty());
}
TEST_F(ModuleScanner, GetQtQuick)
{
scanner.scan(QStringList{QT6_INSTALL_PREFIX});
ASSERT_THAT(scanner.modules(), Contains(UrlProperty("QtQuick")));
}
TEST_F(ModuleScanner, SkipEmptyModules)
{
scanner.scan(QStringList{QT6_INSTALL_PREFIX});
ASSERT_THAT(scanner.modules(), Not(Contains(UrlProperty(IsEmpty()))));
}
TEST_F(ModuleScanner, UseSkipFunction)
{
scanner.scan(QStringList{QT6_INSTALL_PREFIX});
ASSERT_THAT(scanner.modules(), Not(Contains(UrlProperty(EndsWith(QStringView{u"impl"})))));
}
} // namespace

View File

@@ -9,8 +9,6 @@
#include <utils/smallstringio.h>
#include <utils/smallstringvector.h>
using namespace ::testing;
using Utils::PathString;
using Utils::SmallString;
using Utils::SmallStringLiteral;

View File

@@ -7,7 +7,7 @@
#include <utils/smallstringio.h>
namespace UnitTests {
namespace Internal {
template <typename StringType>
class EndsWithMatcher
@@ -46,10 +46,68 @@ private:
const StringType m_suffix;
};
inline
testing::PolymorphicMatcher<EndsWithMatcher<Utils::SmallString> >
EndsWith(const Utils::SmallString &suffix)
class QStringEndsWithMatcher
{
return testing::MakePolymorphicMatcher(EndsWithMatcher<Utils::SmallString>(suffix));
public:
explicit QStringEndsWithMatcher(const QString &suffix)
: m_suffix(suffix)
{}
template<typename MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType &s, testing::MatchResultListener * /* listener */) const
{
return s.endsWith(m_suffix);
}
void DescribeTo(::std::ostream *os) const
{
*os << "ends with " << testing::PrintToString(m_suffix);
}
void DescribeNegationTo(::std::ostream *os) const
{
*os << "doesn't end with " << testing::PrintToString(m_suffix);
}
private:
const QString m_suffix;
};
class IsEmptyMatcher : public testing::internal::IsEmptyMatcher
{
public:
using Base = testing::internal::IsEmptyMatcher;
using Base::MatchAndExplain;
bool MatchAndExplain(const QString &s, testing::MatchResultListener *listener) const
{
if (s.isEmpty()) {
return true;
}
*listener << "whose size is " << s.size();
return false;
}
void DescribeTo(std::ostream *os) const { *os << "is empty"; }
void DescribeNegationTo(std::ostream *os) const { *os << "isn't empty"; }
};
} // namespace Internal
inline auto EndsWith(const Utils::SmallString &suffix)
{
return Internal::EndsWithMatcher(suffix);
}
inline auto EndsWith(const QStringView &suffix)
{
return ::testing::PolymorphicMatcher(Internal::QStringEndsWithMatcher(suffix.toString()));
}
inline auto IsEmpty()
{
return ::testing::PolymorphicMatcher(Internal::IsEmptyMatcher());
}

View File

@@ -15,10 +15,9 @@ bool operator==(const QString &first, const char *second)
namespace UnitTest {
inline
Utils::PathString temporaryDirPath()
inline ::Utils::PathString temporaryDirPath()
{
return Utils::PathString::fromQString(Utils::TemporaryDirectory::masterDirectoryPath());
return ::Utils::PathString::fromQString(Utils::TemporaryDirectory::masterDirectoryPath());
}
} // namespace UnitTest