From f9d9dedab59e1c542b5c9862302e62c9536ecce2 Mon Sep 17 00:00:00 2001 From: Fawzi Mohamed Date: Wed, 16 Jul 2014 16:34:30 +0200 Subject: [PATCH] qmldesigner: fix reparenting to Layout Using strings for everything creates issues, the whole thing should be cleaned up in master. cleverCompare is asymmetric, one side has the code model, but the other is a string. To keep it like that wherever we detect that the string is becoming too strange (normally for copied prototypes) we fall back to the cpp name if available whenever the import does not match the origin. Then cleverCompare can successfully compare the strings. Task-number:QTCREATORBUG-11905 Change-Id: I857f81b57d8ddd43263ce0e3bb085600ff6e8a43 Reviewed-by: Tim Jenssen --- .../designercore/metainfo/nodemetainfo.cpp | 155 +++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index e50d18383da..4cbe1cb9ce4 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -122,6 +122,157 @@ static TypeName resolveTypeName(const ASTPropertyReference *ref, const ContextPt return type; } +static QString qualifiedTypeNameForContext(const ObjectValue *objectValue, + const ViewerContext &vContext, + const ImportDependencies &dep) +{ + QString cppName; + QStringList packages; + if (const CppComponentValue *cppComponent = value_cast(objectValue)) { + QString className = cppComponent->className(); + foreach (const LanguageUtils::FakeMetaObject::Export &e, cppComponent->metaObject()->exports()) { + if (e.type == className) + packages << e.package; + if (e.package == QmlJS::CppQmlTypes::cppPackage) + cppName = e.type; + } + if (packages.size() == 1 && packages.at(0) == QmlJS::CppQmlTypes::cppPackage) + return packages.at(0) + QLatin1Char('.') + className; + } + // try to recover a "global context name" + QStringList possibleLibraries; + QStringList possibleQrcFiles; + QStringList possibleFiles; + bool hasQtQuick = false; + do { + if (objectValue->originId().isEmpty()) + break; + CoreImport cImport = dep.coreImport(objectValue->originId()); + if (!cImport.valid()) + break; + foreach (const Export &e, cImport.possibleExports) { + if (e.pathRequired.isEmpty() || vContext.paths.contains(e.pathRequired)) { + switch (e.exportName.type) { + case ImportType::Library: + { + QString typeName = objectValue->className(); + if (!e.typeName.isEmpty() && e.typeName != Export::LibraryTypeName) { + typeName = e.typeName; + if (typeName != objectValue->className()) + qCWarning(qmljsLog) << "Outdated classname " << objectValue->className() + << " vs " << typeName + << " for " << e.exportName.toString(); + } + if (packages.isEmpty() || packages.contains(e.exportName.libPath())) { + if (e.exportName.splitPath.value(0) == QLatin1String("QtQuick")) + hasQtQuick = true; + possibleLibraries.append(e.exportName.libPath() + '.' + typeName); + } + break; + } + case ImportType::File: + { + // remove the search path prefix. + // this means that the same relative path wrt. different import paths will clash + QString filePath = e.exportName.path(); + foreach (const QString &path, vContext.paths) { + if (filePath.startsWith(path) && filePath.size() > path.size() + && filePath.at(path.size()) == QLatin1Char('/')) + { + filePath = filePath.mid(path.size() + 1); + break; + } + } + + if (filePath.startsWith(QLatin1Char('/'))) + filePath = filePath.mid(1); + QFileInfo fileInfo(filePath); + QStringList splitName = fileInfo.path().split(QLatin1Char('/')); + QString typeName = fileInfo.baseName(); + if (!e.typeName.isEmpty()) { + if (e.typeName != fileInfo.baseName()) + qCWarning(qmljsLog) << "type renaming in file import " << e.typeName + << " for " << e.exportName.path(); + typeName = e.typeName; + } + if (typeName != objectValue->className()) + qCWarning(qmljsLog) << "Outdated classname " << objectValue->className() + << " vs " << typeName + << " for " << e.exportName.toString(); + splitName.append(typeName); + possibleFiles.append(splitName.join(QLatin1Char('.'))); + break; + } + case ImportType::QrcFile: + { + QString filePath = e.exportName.path(); + if (filePath.startsWith(QLatin1Char('/'))) + filePath = filePath.mid(1); + QFileInfo fileInfo(filePath); + QStringList splitName = fileInfo.path().split(QLatin1Char('/')); + QString typeName = fileInfo.baseName(); + if (!e.typeName.isEmpty()) { + if (e.typeName != fileInfo.baseName()) + qCWarning(qmljsLog) << "type renaming in file import " << e.typeName + << " for " << e.exportName.path(); + typeName = e.typeName; + } + if (typeName != objectValue->className()) + qCWarning(qmljsLog) << "Outdated classname " << objectValue->className() + << " vs " << typeName + << " for " << e.exportName.toString(); + splitName.append(typeName); + possibleQrcFiles.append(splitName.join(QLatin1Char('.'))); + break; + } + case ImportType::Invalid: + case ImportType::UnknownFile: + break; + case ImportType::Directory: + case ImportType::ImplicitDirectory: + case ImportType::QrcDirectory: + qCWarning(qmljsLog) << "unexpected import type in export " + << e.exportName.toString() << " of coreExport " + << objectValue->originId(); + break; + } + } + } + auto optimalName = [] (const QStringList &list) -> QString { + QString res = list.at(0); + for (int i = 1; i < list.size(); ++i) { + const QString &nameNow = list.at(i); + if (nameNow.size() < res.size() + || (nameNow.size() == res.size() && nameNow < res)) + res = nameNow; + } + return res; + }; + if (!possibleLibraries.isEmpty()) { + if (hasQtQuick) { + foreach (const QString &libImport, possibleLibraries) + if (!libImport.startsWith(QLatin1String("QtQuick"))) + possibleLibraries.removeAll(libImport); + } + return optimalName(possibleLibraries); + } + if (!possibleQrcFiles.isEmpty()) + return optimalName(possibleQrcFiles); + if (!possibleFiles.isEmpty()) + return optimalName(possibleFiles); + } while (false); + if (!cppName.isEmpty()) + return QmlJS::CppQmlTypes::cppPackage + QLatin1Char('.') + cppName; + if (const CppComponentValue *cppComponent = value_cast(objectValue)) { + if (cppComponent->moduleName().isEmpty()) + return cppComponent->className(); + else + return cppComponent->moduleName() + QLatin1Char('.') + cppComponent->className(); + } else { + return objectValue->className(); + } +} + class PropertyMemberProcessor : public MemberProcessor { public: @@ -144,8 +295,10 @@ public: } } } else { + if (const CppComponentValue * cppComponentValue = value_cast(value)) { - TypeName qualifiedTypeName = cppComponentValue->moduleName().isEmpty() ? cppComponentValue->className().toUtf8() : cppComponentValue->moduleName().toUtf8() + '.' + cppComponentValue->className().toUtf8(); + TypeName qualifiedTypeName = qualifiedTypeNameForContext(cppComponentValue, + m_context->vContext(), *m_context->snapshot().importDependencies()).toUtf8(); m_properties.append(qMakePair(propertyName, qualifiedTypeName)); } else { TypeId typeId;