From 6e9cb8ad544c1468d7f8bdea1a8562a77749b767 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 10 Feb 2022 16:48:13 +0100 Subject: QmlDesigner: Support qualified prototypes in the qml document parser Task-number: QDS-6191 Change-Id: I459d09df4ed0ba901fd4f214dd3de81556ee7896 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Bot --- .../projectstorage/qmldocumentparser.cpp | 84 ++++++++++++++++++---- tests/unit/unittest/qmldocumentparser-test.cpp | 14 ++-- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index 81b01ddaba..8d0c904879 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -41,6 +41,8 @@ namespace QmlDom = QQmlJS::Dom; namespace { +using QualifiedImports = std::map; + int convertVersionNumber(qint32 versionNumber) { return versionNumber < 0 ? -1 : versionNumber; @@ -64,24 +66,49 @@ Utils::PathString convertUri(const QString &uri) return Utils::PathString{x.generic_string()}; } +Storage::Import createImport(const QmlDom::Import &qmlImport, + SourceId sourceId, + Utils::SmallStringView directoryPath, + QmlDocumentParser::ProjectStorage &storage) +{ + if (qmlImport.uri == u"file://.") { + auto moduleId = storage.moduleId(directoryPath); + return Storage::Import(moduleId, Storage::Version{}, sourceId); + } + + if (qmlImport.uri.startsWith(u"file://")) { + auto moduleId = storage.moduleId(convertUri(qmlImport.uri)); + return Storage::Import(moduleId, Storage::Version{}, sourceId); + } + + auto moduleId = storage.moduleId(Utils::SmallString{qmlImport.uri}); + return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); +} + +QualifiedImports filterQualifiedImports(const QList &qmlImports, + SourceId sourceId, + Utils::SmallStringView directoryPath, + QmlDocumentParser::ProjectStorage &storage) +{ + QualifiedImports qualifiedImports; + + for (const QmlDom::Import &qmlImport : qmlImports) { + if (!qmlImport.importId.isEmpty()) + qualifiedImports.try_emplace(qmlImport.importId, + createImport(qmlImport, sourceId, directoryPath, storage)); + } + + return qualifiedImports; +} + void addImports(Storage::Imports &imports, const QList &qmlImports, SourceId sourceId, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) { - for (const QmlDom::Import &qmlImport : qmlImports) { - if (qmlImport.uri == u"file://.") { - auto moduleId = storage.moduleId(directoryPath); - 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); - } - } + for (const QmlDom::Import &qmlImport : qmlImports) + imports.push_back(createImport(qmlImport, sourceId, directoryPath, storage)); auto end = imports.end(); auto begin = std::prev(end, qmlImports.size()); @@ -90,6 +117,30 @@ void addImports(Storage::Imports &imports, imports.erase(std::unique(begin, end), end); } +Storage::ImportedTypeName createImportedTypeName(const QStringView rawtypeName, + const QList &qmlImports, + SourceId sourceId, + Utils::SmallStringView directoryPath, + QmlDocumentParser::ProjectStorage &storage) +{ + if (!rawtypeName.contains('.')) { + return Storage::ImportedType{Utils::SmallString{rawtypeName}}; + } + + auto qualifiedImports = filterQualifiedImports(qmlImports, sourceId, directoryPath, storage); + + auto foundDot = std::find(rawtypeName.begin(), rawtypeName.end(), '.'); + + QStringView alias(rawtypeName.begin(), foundDot); + + auto foundImport = qualifiedImports.find(alias.toString()); + + QStringView typeName(std::next(foundDot), rawtypeName.end()); + + return Storage::QualifiedImportedType{Utils::SmallString{typeName.toString()}, + foundImport->second}; +} + void addPropertyDeclarations(Storage::Type &type, const QmlDom::QmlObject &rootObject) { for (const QmlDom::PropertyDefinition &propertyDeclaration : rootObject.propertyDefs()) { @@ -188,10 +239,15 @@ Storage::Type QmlDocumentParser::parse(const QString &sourceContent, const QmlDom::QmlObject &qmlObject = objects.front(); - type.prototype = Storage::ImportedType{Utils::SmallString{qmlObject.name()}}; - + const auto qmlImports = qmlFile->imports(); auto directoryPath{m_pathCache.sourceContextPath(m_pathCache.sourceContextId(sourceId))}; + type.prototype = createImportedTypeName(qmlObject.name(), + qmlImports, + sourceId, + directoryPath, + m_storage); + addImports(imports, qmlFile->imports(), sourceId, directoryPath, m_storage); addPropertyDeclarations(type, qmlObject); diff --git a/tests/unit/unittest/qmldocumentparser-test.cpp b/tests/unit/unittest/qmldocumentparser-test.cpp index ecf6897535..5e7312a45f 100644 --- a/tests/unit/unittest/qmldocumentparser-test.cpp +++ b/tests/unit/unittest/qmldocumentparser-test.cpp @@ -143,17 +143,19 @@ TEST_F(QmlDocumentParser, Prototype) ASSERT_THAT(type, HasPrototype(Storage::ImportedType("Example"))); } -TEST_F(QmlDocumentParser, DISABLED_QualifiedPrototype) +TEST_F(QmlDocumentParser, QualifiedPrototype) { auto exampleModuleId = storage.moduleId("Example"); - QString text = R"(import Example as Example - - Example.Item{})"; + QString text = R"(import Example 2.1 as Example + Example.Item{})"; + auto type = parser.parse(text, imports, qmlFileSourceId); ASSERT_THAT(type, - HasPrototype(Storage::QualifiedImportedType( - "Item", Storage::Import{exampleModuleId, Storage::Version{}, qmlFileSourceId}))); + HasPrototype(Storage::QualifiedImportedType("Item", + Storage::Import{exampleModuleId, + Storage::Version{2, 1}, + qmlFileSourceId}))); } TEST_F(QmlDocumentParser, Properties) -- cgit v1.2.3