forked from qt-creator/qt-creator
QmlJS: Reparse cpp types when component headers change
Previously the code model would only get updated when the document that contains the qmlRegisterType changed. If the actual component exported to QML lives in a different file, any updates to that component would be lost. With this change we keep track of the header files the components are declared in and rescan if any of them changes. This is still not the greatest way to do it as there are a number of ways to introduce dependencies the system cannot detect. It's better than before, though. Change-Id: Ic077c516dca3ac720f78973c84e5e6e91b6a5c07 Reviewed-by: Marco Benelli <marco.benelli@qt.io>
This commit is contained in:
@@ -817,16 +817,17 @@ FindExportedCppTypes::FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
QStringList FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
||||||
{
|
{
|
||||||
m_contextProperties.clear();
|
m_contextProperties.clear();
|
||||||
m_exportedTypes.clear();
|
m_exportedTypes.clear();
|
||||||
|
QStringList fileNames;
|
||||||
|
|
||||||
// this check only guards against some input errors, if document's source and AST has not
|
// this check only guards against some input errors, if document's source and AST has not
|
||||||
// been guarded properly the source and AST may still become empty/null while this function is running
|
// been guarded properly the source and AST may still become empty/null while this function is running
|
||||||
if (document->utf8Source().isEmpty()
|
if (document->utf8Source().isEmpty()
|
||||||
|| !document->translationUnit()->ast())
|
|| !document->translationUnit()->ast())
|
||||||
return;
|
return fileNames;
|
||||||
|
|
||||||
FindExportsVisitor finder(document);
|
FindExportsVisitor finder(document);
|
||||||
finder();
|
finder();
|
||||||
@@ -838,7 +839,7 @@ void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
|||||||
const QList<ContextProperty> contextPropertyDescriptions = finder.contextProperties();
|
const QList<ContextProperty> contextPropertyDescriptions = finder.contextProperties();
|
||||||
const QList<ExportedQmlType> exports = finder.exportedTypes();
|
const QList<ExportedQmlType> exports = finder.exportedTypes();
|
||||||
if (exports.isEmpty() && contextPropertyDescriptions.isEmpty())
|
if (exports.isEmpty() && contextPropertyDescriptions.isEmpty())
|
||||||
return;
|
return fileNames;
|
||||||
|
|
||||||
// context properties need lookup inside function scope, and thus require a full check
|
// context properties need lookup inside function scope, and thus require a full check
|
||||||
CPlusPlus::Document::Ptr localDoc = document;
|
CPlusPlus::Document::Ptr localDoc = document;
|
||||||
@@ -863,14 +864,19 @@ void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
|
|||||||
|
|
||||||
// convert to list of FakeMetaObject::ConstPtr
|
// convert to list of FakeMetaObject::ConstPtr
|
||||||
m_exportedTypes.reserve(fakeMetaObjects.size() + extraFakeMetaObjects.size());
|
m_exportedTypes.reserve(fakeMetaObjects.size() + extraFakeMetaObjects.size());
|
||||||
foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, fakeMetaObjects) {
|
fileNames.reserve(fakeMetaObjects.size());
|
||||||
fmo->updateFingerprint();
|
for (auto it = fakeMetaObjects.constBegin(), end = fakeMetaObjects.constEnd(); it != end;
|
||||||
m_exportedTypes += fmo;
|
++it) {
|
||||||
|
it.value()->updateFingerprint();
|
||||||
|
m_exportedTypes += it.value();
|
||||||
|
fileNames += QLatin1String(it.key()->fileName());
|
||||||
}
|
}
|
||||||
foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, extraFakeMetaObjects) {
|
foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, extraFakeMetaObjects) {
|
||||||
fmo->updateFingerprint();
|
fmo->updateFingerprint();
|
||||||
m_exportedTypes += fmo;
|
m_exportedTypes += fmo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fileNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::exportedTypes() const
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::exportedTypes() const
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public:
|
|||||||
FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot);
|
FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot);
|
||||||
|
|
||||||
// document must have a valid source and ast for the duration of the call
|
// document must have a valid source and ast for the duration of the call
|
||||||
void operator()(const CPlusPlus::Document::Ptr &document);
|
QStringList operator()(const CPlusPlus::Document::Ptr &document);
|
||||||
|
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedTypes() const;
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedTypes() const;
|
||||||
QHash<QString, QString> contextProperties() const;
|
QHash<QString, QString> contextProperties() const;
|
||||||
|
|||||||
@@ -1259,6 +1259,37 @@ void ModelManagerInterface::asyncReset()
|
|||||||
m_asyncResetTimer->start();
|
m_asyncResetTimer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rescanExports(const QString &fileName, FindExportedCppTypes &finder,
|
||||||
|
ModelManagerInterface::CppDataHash &newData)
|
||||||
|
{
|
||||||
|
bool hasNewInfo = false;
|
||||||
|
|
||||||
|
QList<LanguageUtils::FakeMetaObject::ConstPtr> exported = finder.exportedTypes();
|
||||||
|
QHash<QString, QString> contextProperties = finder.contextProperties();
|
||||||
|
if (exported.isEmpty() && contextProperties.isEmpty()) {
|
||||||
|
hasNewInfo = hasNewInfo || newData.remove(fileName) > 0;
|
||||||
|
} else {
|
||||||
|
ModelManagerInterface::CppData &data = newData[fileName];
|
||||||
|
if (!hasNewInfo && (data.exportedTypes.size() != exported.size()
|
||||||
|
|| data.contextProperties != contextProperties))
|
||||||
|
hasNewInfo = true;
|
||||||
|
if (!hasNewInfo) {
|
||||||
|
QHash<QString, QByteArray> newFingerprints;
|
||||||
|
foreach (LanguageUtils::FakeMetaObject::ConstPtr newType, exported)
|
||||||
|
newFingerprints[newType->className()]=newType->fingerprint();
|
||||||
|
foreach (LanguageUtils::FakeMetaObject::ConstPtr oldType, data.exportedTypes) {
|
||||||
|
if (newFingerprints.value(oldType->className()) != oldType->fingerprint()) {
|
||||||
|
hasNewInfo = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.exportedTypes = exported;
|
||||||
|
data.contextProperties = contextProperties;
|
||||||
|
}
|
||||||
|
return hasNewInfo;
|
||||||
|
}
|
||||||
|
|
||||||
void ModelManagerInterface::updateCppQmlTypes(QFutureInterface<void> &interface,
|
void ModelManagerInterface::updateCppQmlTypes(QFutureInterface<void> &interface,
|
||||||
ModelManagerInterface *qmlModelManager,
|
ModelManagerInterface *qmlModelManager,
|
||||||
CPlusPlus::Snapshot snapshot,
|
CPlusPlus::Snapshot snapshot,
|
||||||
@@ -1266,7 +1297,14 @@ void ModelManagerInterface::updateCppQmlTypes(QFutureInterface<void> &interface,
|
|||||||
{
|
{
|
||||||
interface.setProgressRange(0, documents.size());
|
interface.setProgressRange(0, documents.size());
|
||||||
interface.setProgressValue(0);
|
interface.setProgressValue(0);
|
||||||
CppDataHash newData = qmlModelManager->cppData();
|
|
||||||
|
CppDataHash newData;
|
||||||
|
QHash<QString, QStringList> newDeclarations;
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&qmlModelManager->m_cppDataMutex);
|
||||||
|
newData = qmlModelManager->m_cppDataHash;
|
||||||
|
newDeclarations = qmlModelManager->m_cppDeclarationFiles;
|
||||||
|
}
|
||||||
|
|
||||||
FindExportedCppTypes finder(snapshot);
|
FindExportedCppTypes finder(snapshot);
|
||||||
|
|
||||||
@@ -1281,41 +1319,37 @@ void ModelManagerInterface::updateCppQmlTypes(QFutureInterface<void> &interface,
|
|||||||
const bool scan = pair.second;
|
const bool scan = pair.second;
|
||||||
const QString fileName = doc->fileName();
|
const QString fileName = doc->fileName();
|
||||||
if (!scan) {
|
if (!scan) {
|
||||||
hasNewInfo = hasNewInfo || newData.remove(fileName) > 0;
|
hasNewInfo = newData.remove(fileName) > 0 || hasNewInfo;
|
||||||
|
foreach (const QString &file, newDeclarations[fileName]) {
|
||||||
|
finder(snapshot.document(file));
|
||||||
|
hasNewInfo = rescanExports(file, finder, newData) || hasNewInfo;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
finder(doc);
|
for (auto it = newDeclarations.begin(), end = newDeclarations.end(); it != end;) {
|
||||||
|
if (it->removeOne(fileName)) {
|
||||||
QList<LanguageUtils::FakeMetaObject::ConstPtr> exported = finder.exportedTypes();
|
doc->releaseSourceAndAST();
|
||||||
QHash<QString, QString> contextProperties = finder.contextProperties();
|
if (it->isEmpty()) {
|
||||||
if (exported.isEmpty() && contextProperties.isEmpty()) {
|
it = newDeclarations.erase(it);
|
||||||
hasNewInfo = hasNewInfo || newData.remove(fileName) > 0;
|
continue;
|
||||||
} else {
|
|
||||||
CppData &data = newData[fileName];
|
|
||||||
if (!hasNewInfo && (data.exportedTypes.size() != exported.size()
|
|
||||||
|| data.contextProperties != contextProperties))
|
|
||||||
hasNewInfo = true;
|
|
||||||
if (!hasNewInfo) {
|
|
||||||
QHash<QString, QByteArray> newFingerprints;
|
|
||||||
foreach (LanguageUtils::FakeMetaObject::ConstPtr newType, exported)
|
|
||||||
newFingerprints[newType->className()]=newType->fingerprint();
|
|
||||||
foreach (LanguageUtils::FakeMetaObject::ConstPtr oldType, data.exportedTypes) {
|
|
||||||
if (newFingerprints.value(oldType->className()) != oldType->fingerprint()) {
|
|
||||||
hasNewInfo = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.exportedTypes = exported;
|
++it;
|
||||||
data.contextProperties = contextProperties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (const QString &declarationFile, finder(doc)) {
|
||||||
|
newDeclarations[declarationFile].append(fileName);
|
||||||
|
doc->keepSourceAndAST(); // keep for later reparsing when dependent doc changes
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNewInfo = rescanExports(doc->fileName(), finder, newData) || hasNewInfo;
|
||||||
doc->releaseSourceAndAST();
|
doc->releaseSourceAndAST();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(&qmlModelManager->m_cppDataMutex);
|
QMutexLocker locker(&qmlModelManager->m_cppDataMutex);
|
||||||
qmlModelManager->m_cppDataHash = newData;
|
qmlModelManager->m_cppDataHash = newData;
|
||||||
|
qmlModelManager->m_cppDeclarationFiles = newDeclarations;
|
||||||
if (hasNewInfo)
|
if (hasNewInfo)
|
||||||
// one could get away with re-linking the cpp types...
|
// one could get away with re-linking the cpp types...
|
||||||
QMetaObject::invokeMethod(qmlModelManager, "asyncReset");
|
QMetaObject::invokeMethod(qmlModelManager, "asyncReset");
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ private:
|
|||||||
QrcCache m_qrcCache;
|
QrcCache m_qrcCache;
|
||||||
|
|
||||||
CppDataHash m_cppDataHash;
|
CppDataHash m_cppDataHash;
|
||||||
|
QHash<QString, QStringList> m_cppDeclarationFiles;
|
||||||
mutable QMutex m_cppDataMutex;
|
mutable QMutex m_cppDataMutex;
|
||||||
|
|
||||||
// project integration
|
// project integration
|
||||||
|
|||||||
Reference in New Issue
Block a user