forked from qt-creator/qt-creator
CppTools: Rework handling of ProjectInfo changes
(a) The code model manager figures out by itself which files were added or removed from the project. If this was done successfully, check also the timestamp of the common files and reindex if necessary. (b) A full reindexing is only triggered if the project configuration changes (defines, includes, framework paths). (c) If project files were removed, the garbage collector is called. Task-number: QTCREATORBUG-9730 Change-Id: Ib855614b070880576233a3525813617c967a72f3 Reviewed-by: Fawzi Mohamed <fawzi.mohamed@digia.com>
This commit is contained in:
@@ -172,12 +172,11 @@ static const char pp_configuration[] =
|
|||||||
"#define __inline inline\n"
|
"#define __inline inline\n"
|
||||||
"#define __forceinline inline\n";
|
"#define __forceinline inline\n";
|
||||||
|
|
||||||
void CppModelManager::updateModifiedSourceFiles()
|
QStringList CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> documentsToCheck)
|
||||||
{
|
{
|
||||||
const Snapshot snapshot = this->snapshot();
|
|
||||||
QStringList sourceFiles;
|
QStringList sourceFiles;
|
||||||
|
|
||||||
foreach (const Document::Ptr doc, snapshot) {
|
foreach (const Document::Ptr doc, documentsToCheck) {
|
||||||
const QDateTime lastModified = doc->lastModified();
|
const QDateTime lastModified = doc->lastModified();
|
||||||
|
|
||||||
if (!lastModified.isNull()) {
|
if (!lastModified.isNull()) {
|
||||||
@@ -188,7 +187,18 @@ void CppModelManager::updateModifiedSourceFiles()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSourceFiles(sourceFiles);
|
return sourceFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CppModelManager::updateModifiedSourceFiles()
|
||||||
|
{
|
||||||
|
const Snapshot snapshot = this->snapshot();
|
||||||
|
QList<Document::Ptr> documentsToCheck;
|
||||||
|
foreach (const Document::Ptr document, snapshot)
|
||||||
|
documentsToCheck << document;
|
||||||
|
|
||||||
|
const QStringList filesToUpdate = timeStampModifiedFiles(documentsToCheck);
|
||||||
|
updateSourceFiles(filesToUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -584,39 +594,155 @@ CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Proje
|
|||||||
return m_projectToProjectsInfo.value(project, ProjectInfo(project));
|
return m_projectToProjectsInfo.value(project, ProjectInfo(project));
|
||||||
}
|
}
|
||||||
|
|
||||||
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &pinfo)
|
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
|
||||||
|
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
|
||||||
{
|
{
|
||||||
if (!pinfo.isValid())
|
if (!projectInfo.isValid())
|
||||||
return QFuture<void>();
|
return;
|
||||||
|
|
||||||
{ // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
|
QMutexLocker snapshotLocker(&m_snapshotMutex);
|
||||||
QMutexLocker locker(&m_projectMutex);
|
foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
|
||||||
|
|
||||||
ProjectExplorer::Project *project = pinfo.project().data();
|
|
||||||
ProjectInfo oldProjectInfo = m_projectToProjectsInfo.value(project);
|
|
||||||
if (oldProjectInfo.isValid()) {
|
|
||||||
if (pinfo.defines() == oldProjectInfo.defines()
|
|
||||||
&& pinfo.includePaths() == oldProjectInfo.includePaths()
|
|
||||||
&& pinfo.frameworkPaths() == oldProjectInfo.frameworkPaths()
|
|
||||||
&& pinfo.sourceFiles() == oldProjectInfo.sourceFiles()) {
|
|
||||||
return QFuture<void>();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (const ProjectPart::Ptr &projectPart, oldProjectInfo.projectParts()) {
|
|
||||||
foreach (const ProjectFile &cxxFile, projectPart->files) {
|
foreach (const ProjectFile &cxxFile, projectPart->files) {
|
||||||
foreach (const QString &fileName,
|
foreach (const QString &fileName, m_snapshot.allIncludesForDocument(cxxFile.path))
|
||||||
m_snapshot.allIncludesForDocument(cxxFile.path)) {
|
|
||||||
m_snapshot.remove(fileName);
|
m_snapshot.remove(fileName);
|
||||||
}
|
|
||||||
m_snapshot.remove(cxxFile.path);
|
m_snapshot.remove(cxxFile.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Remove all given files from the snapshot.
|
||||||
|
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
|
||||||
|
{
|
||||||
|
QMutexLocker snapshotLocker(&m_snapshotMutex);
|
||||||
|
QSetIterator<QString> i(filesToRemove);
|
||||||
|
while (i.hasNext())
|
||||||
|
m_snapshot.remove(i.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProjectInfoComparer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProjectInfoComparer(const CppModelManager::ProjectInfo &oldProjectInfo,
|
||||||
|
const CppModelManager::ProjectInfo &newProjectInfo)
|
||||||
|
: m_old(oldProjectInfo)
|
||||||
|
, m_oldSourceFiles(oldProjectInfo.sourceFiles().toSet())
|
||||||
|
, m_new(newProjectInfo)
|
||||||
|
, m_newSourceFiles(newProjectInfo.sourceFiles().toSet())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool definesChanged() const
|
||||||
|
{
|
||||||
|
return m_new.defines() != m_old.defines();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool configurationChanged() const
|
||||||
|
{
|
||||||
|
return definesChanged()
|
||||||
|
|| m_new.includePaths() != m_old.includePaths()
|
||||||
|
|| m_new.frameworkPaths() != m_old.frameworkPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nothingChanged() const
|
||||||
|
{
|
||||||
|
return !configurationChanged() && m_new.sourceFiles() == m_old.sourceFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> addedFiles() const
|
||||||
|
{
|
||||||
|
QSet<QString> addedFilesSet = m_newSourceFiles;
|
||||||
|
addedFilesSet.subtract(m_oldSourceFiles);
|
||||||
|
return addedFilesSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> removedFiles() const
|
||||||
|
{
|
||||||
|
QSet<QString> removedFilesSet = m_oldSourceFiles;
|
||||||
|
removedFilesSet.subtract(m_newSourceFiles);
|
||||||
|
return removedFilesSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of common files that have a changed timestamp.
|
||||||
|
QSet<QString> timeStampModifiedFiles(const Snapshot &snapshot) const
|
||||||
|
{
|
||||||
|
QSet<QString> commonSourceFiles = m_newSourceFiles;
|
||||||
|
commonSourceFiles.intersect(m_oldSourceFiles);
|
||||||
|
|
||||||
|
QList<Document::Ptr> documentsToCheck;
|
||||||
|
QSetIterator<QString> i(commonSourceFiles);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
const QString file = i.next();
|
||||||
|
if (Document::Ptr document = snapshot.document(file))
|
||||||
|
documentsToCheck << document;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CppModelManager::timeStampModifiedFiles(documentsToCheck).toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const CppModelManager::ProjectInfo &m_old;
|
||||||
|
const QSet<QString> m_oldSourceFiles;
|
||||||
|
|
||||||
|
const CppModelManager::ProjectInfo &m_new;
|
||||||
|
const QSet<QString> m_newSourceFiles;
|
||||||
|
};
|
||||||
|
|
||||||
|
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
|
||||||
|
{
|
||||||
|
if (!newProjectInfo.isValid())
|
||||||
|
return QFuture<void>();
|
||||||
|
|
||||||
|
QStringList filesToReindex;
|
||||||
|
bool filesRemoved = false;
|
||||||
|
|
||||||
|
{ // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
|
||||||
|
QMutexLocker projectLocker(&m_projectMutex);
|
||||||
|
|
||||||
|
ProjectExplorer::Project *project = newProjectInfo.project().data();
|
||||||
|
const QStringList newSourceFiles = newProjectInfo.sourceFiles();
|
||||||
|
|
||||||
|
// Check if we can avoid a full reindexing
|
||||||
|
ProjectInfo oldProjectInfo = m_projectToProjectsInfo.value(project);
|
||||||
|
if (oldProjectInfo.isValid()) {
|
||||||
|
ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
|
||||||
|
if (comparer.nothingChanged())
|
||||||
|
return QFuture<void>();
|
||||||
|
|
||||||
|
// If the project configuration changed, do a full reindexing
|
||||||
|
if (comparer.configurationChanged()) {
|
||||||
|
removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
|
||||||
|
filesToReindex << newSourceFiles;
|
||||||
|
|
||||||
|
// The "configuration file" includes all defines and therefore should be updated
|
||||||
|
if (comparer.definesChanged()) {
|
||||||
|
QMutexLocker snapshotLocker(&m_snapshotMutex);
|
||||||
m_snapshot.remove(configurationFileName());
|
m_snapshot.remove(configurationFileName());
|
||||||
|
}
|
||||||
|
|
||||||
m_projectToProjectsInfo.insert(project, pinfo);
|
// Otherwise check for added and modified files
|
||||||
|
} else {
|
||||||
|
const QSet<QString> addedFiles = comparer.addedFiles();
|
||||||
|
filesToReindex << addedFiles.toList();
|
||||||
|
|
||||||
|
const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
|
||||||
|
filesToReindex << modifiedFiles.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Announce and purge the removed files from the snapshot
|
||||||
|
const QSet<QString> removedFiles = comparer.removedFiles();
|
||||||
|
if (!removedFiles.isEmpty()) {
|
||||||
|
filesRemoved = true;
|
||||||
|
emit aboutToRemoveFiles(removedFiles.toList());
|
||||||
|
removeFilesFromSnapshot(removedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A new project was opened/created, do a full indexing
|
||||||
|
} else {
|
||||||
|
filesToReindex << newSourceFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Project/ProjectInfo and File/ProjectPart table
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
m_projectToProjectsInfo.insert(project, newProjectInfo);
|
||||||
m_fileToProjectParts.clear();
|
m_fileToProjectParts.clear();
|
||||||
foreach (const ProjectInfo &projectInfo, m_projectToProjectsInfo) {
|
foreach (const ProjectInfo &projectInfo, m_projectToProjectsInfo) {
|
||||||
foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
|
foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
|
||||||
@@ -625,14 +751,21 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &pinfo)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
} // Mutex scope
|
||||||
|
|
||||||
|
// If requested, dump everything we got
|
||||||
if (!qgetenv("QTCREATOR_DUMP_PROJECT_INFO").isEmpty())
|
if (!qgetenv("QTCREATOR_DUMP_PROJECT_INFO").isEmpty())
|
||||||
dumpModelManagerConfiguration();
|
dumpModelManagerConfiguration();
|
||||||
|
|
||||||
emit projectPartsUpdated(pinfo.project().data());
|
// Remove files from snapshot that are not reachable any more
|
||||||
|
if (filesRemoved)
|
||||||
|
GC();
|
||||||
|
|
||||||
return updateSourceFiles(pinfo.sourceFiles(), ForcedProgressNotification);
|
emit projectPartsUpdated(newProjectInfo.project().data());
|
||||||
|
|
||||||
|
// Trigger reindexing
|
||||||
|
return updateSourceFiles(filesToReindex, ForcedProgressNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) const
|
QList<ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) const
|
||||||
|
@@ -72,7 +72,7 @@ public:
|
|||||||
|
|
||||||
virtual QList<ProjectInfo> projectInfos() const;
|
virtual QList<ProjectInfo> projectInfos() const;
|
||||||
virtual ProjectInfo projectInfo(ProjectExplorer::Project *project) const;
|
virtual ProjectInfo projectInfo(ProjectExplorer::Project *project) const;
|
||||||
virtual QFuture<void> updateProjectInfo(const ProjectInfo &pinfo);
|
virtual QFuture<void> updateProjectInfo(const ProjectInfo &newProjectInfo);
|
||||||
virtual QList<CppTools::ProjectPart::Ptr> projectPart(const QString &fileName) const;
|
virtual QList<CppTools::ProjectPart::Ptr> projectPart(const QString &fileName) const;
|
||||||
|
|
||||||
virtual CPlusPlus::Snapshot snapshot() const;
|
virtual CPlusPlus::Snapshot snapshot() const;
|
||||||
@@ -141,6 +141,8 @@ public:
|
|||||||
return m_definedMacros;
|
return m_definedMacros;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QStringList timeStampModifiedFiles(const QList<Document::Ptr> documentsToCheck);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void aboutToRemoveFiles(const QStringList &files);
|
void aboutToRemoveFiles(const QStringList &files);
|
||||||
void gcFinished(); // Needed for tests.
|
void gcFinished(); // Needed for tests.
|
||||||
@@ -160,6 +162,9 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
void delayedGC();
|
void delayedGC();
|
||||||
void replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot);
|
void replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot);
|
||||||
|
void removeFilesFromSnapshot(const QSet<QString> &removedFiles);
|
||||||
|
void removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo);
|
||||||
|
|
||||||
WorkingCopy buildWorkingCopyList();
|
WorkingCopy buildWorkingCopyList();
|
||||||
|
|
||||||
void ensureUpdated();
|
void ensureUpdated();
|
||||||
|
@@ -48,6 +48,8 @@ typedef CppTools::ProjectPart ProjectPart;
|
|||||||
typedef CppTools::ProjectFile ProjectFile;
|
typedef CppTools::ProjectFile ProjectFile;
|
||||||
typedef ProjectExplorer::Project Project;
|
typedef ProjectExplorer::Project Project;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(ProjectFile)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class TestDataDirectory
|
class TestDataDirectory
|
||||||
@@ -176,6 +178,52 @@ private:
|
|||||||
QString m_fileToRemove;
|
QString m_fileToRemove;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Changes a file on the disk and restores its original contents on destruction
|
||||||
|
class FileChangerAndRestorer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileChangerAndRestorer(const QString &filePath)
|
||||||
|
: m_filePath(filePath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~FileChangerAndRestorer()
|
||||||
|
{
|
||||||
|
restoreContents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saves the contents also internally so it can be restored on destruction
|
||||||
|
bool readContents(QByteArray *contents)
|
||||||
|
{
|
||||||
|
Utils::FileReader fileReader;
|
||||||
|
const bool isFetchOk = fileReader.fetch(m_filePath);
|
||||||
|
if (isFetchOk) {
|
||||||
|
m_originalFileContents = fileReader.data();
|
||||||
|
if (contents)
|
||||||
|
*contents = m_originalFileContents;
|
||||||
|
}
|
||||||
|
return isFetchOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeContents(const QByteArray &contents) const
|
||||||
|
{
|
||||||
|
Utils::FileSaver fileSaver(m_filePath);
|
||||||
|
fileSaver.write(contents);
|
||||||
|
fileSaver.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void restoreContents() const
|
||||||
|
{
|
||||||
|
Utils::FileSaver fileSaver(m_filePath);
|
||||||
|
fileSaver.write(m_originalFileContents);
|
||||||
|
fileSaver.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray m_originalFileContents;
|
||||||
|
const QString &m_filePath;
|
||||||
|
};
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
/// Check: The preprocessor cleans include and framework paths.
|
/// Check: The preprocessor cleans include and framework paths.
|
||||||
@@ -232,8 +280,7 @@ void CppToolsPlugin::test_modelmanager_framework_headers()
|
|||||||
part->files << ProjectFile(source, ProjectFile::CXXSource);
|
part->files << ProjectFile(source, ProjectFile::CXXSource);
|
||||||
pi.appendProjectPart(part);
|
pi.appendProjectPart(part);
|
||||||
|
|
||||||
mm->updateProjectInfo(pi);
|
mm->updateProjectInfo(pi).waitForFinished();
|
||||||
mm->updateSourceFiles(QStringList(source)).waitForFinished();
|
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
QVERIFY(mm->snapshot().contains(source));
|
QVERIFY(mm->snapshot().contains(source));
|
||||||
@@ -280,7 +327,6 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files()
|
|||||||
part->includePaths = QStringList() << testDataDir.includeDir(false);
|
part->includePaths = QStringList() << testDataDir.includeDir(false);
|
||||||
part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource));
|
part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource));
|
||||||
pi.appendProjectPart(part);
|
pi.appendProjectPart(part);
|
||||||
|
|
||||||
mm->updateProjectInfo(pi);
|
mm->updateProjectInfo(pi);
|
||||||
|
|
||||||
QStringList refreshedFiles = helper.waitForRefreshedSourceFiles();
|
QStringList refreshedFiles = helper.waitForRefreshedSourceFiles();
|
||||||
@@ -301,6 +347,7 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files()
|
|||||||
pi.clearProjectParts();
|
pi.clearProjectParts();
|
||||||
pi.appendProjectPart(part);
|
pi.appendProjectPart(part);
|
||||||
mm->updateProjectInfo(pi);
|
mm->updateProjectInfo(pi);
|
||||||
|
|
||||||
refreshedFiles = helper.waitForRefreshedSourceFiles();
|
refreshedFiles = helper.waitForRefreshedSourceFiles();
|
||||||
|
|
||||||
QCOMPARE(refreshedFiles.size(), 1);
|
QCOMPARE(refreshedFiles.size(), 1);
|
||||||
@@ -342,7 +389,6 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times()
|
|||||||
part->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader));
|
part->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader));
|
||||||
part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource));
|
part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource));
|
||||||
pi.appendProjectPart(part);
|
pi.appendProjectPart(part);
|
||||||
|
|
||||||
mm->updateProjectInfo(pi);
|
mm->updateProjectInfo(pi);
|
||||||
|
|
||||||
CPlusPlus::Snapshot snapshot;
|
CPlusPlus::Snapshot snapshot;
|
||||||
@@ -364,6 +410,7 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times()
|
|||||||
pi.appendProjectPart(part);
|
pi.appendProjectPart(part);
|
||||||
|
|
||||||
mm->updateProjectInfo(pi);
|
mm->updateProjectInfo(pi);
|
||||||
|
|
||||||
refreshedFiles = helper.waitForRefreshedSourceFiles();
|
refreshedFiles = helper.waitForRefreshedSourceFiles();
|
||||||
|
|
||||||
QCOMPARE(refreshedFiles.size(), 3);
|
QCOMPARE(refreshedFiles.size(), 3);
|
||||||
@@ -420,6 +467,165 @@ void CppToolsPlugin::test_modelmanager_refresh_test_for_changes()
|
|||||||
QVERIFY(subsequentFuture.isCanceled() && subsequentFuture.isFinished());
|
QVERIFY(subsequentFuture.isCanceled() && subsequentFuture.isFinished());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check: (1) Added project files are recognized and parsed.
|
||||||
|
/// Check: (2) Removed project files are recognized and purged from the snapshot.
|
||||||
|
void CppToolsPlugin::test_modelmanager_refresh_added_and_purge_removed()
|
||||||
|
{
|
||||||
|
ModelManagerTestHelper helper;
|
||||||
|
CppModelManager *mm = CppModelManager::instance();
|
||||||
|
|
||||||
|
const TestDataDirectory testDataDir(QLatin1String("testdata_refresh"));
|
||||||
|
|
||||||
|
const QString testHeader1(testDataDir.file(QLatin1String("header.h")));
|
||||||
|
const QString testHeader2(testDataDir.file(QLatin1String("defines.h")));
|
||||||
|
const QString testCpp(testDataDir.file(QLatin1String("source.cpp")));
|
||||||
|
|
||||||
|
Project *project = helper.createProject(QLatin1String("test_modelmanager_refresh_3"));
|
||||||
|
ProjectInfo pi = mm->projectInfo(project);
|
||||||
|
QCOMPARE(pi.project().data(), project);
|
||||||
|
|
||||||
|
ProjectPart::Ptr part(new ProjectPart);
|
||||||
|
part->cxxVersion = ProjectPart::CXX98;
|
||||||
|
part->qtVersion = ProjectPart::Qt5;
|
||||||
|
part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource));
|
||||||
|
part->files.append(ProjectFile(testHeader1, ProjectFile::CXXHeader));
|
||||||
|
pi.appendProjectPart(part);
|
||||||
|
|
||||||
|
CPlusPlus::Snapshot snapshot;
|
||||||
|
QStringList refreshedFiles;
|
||||||
|
|
||||||
|
mm->updateProjectInfo(pi);
|
||||||
|
refreshedFiles = helper.waitForRefreshedSourceFiles();
|
||||||
|
|
||||||
|
QCOMPARE(refreshedFiles.size(), 2);
|
||||||
|
QVERIFY(refreshedFiles.contains(testHeader1));
|
||||||
|
QVERIFY(refreshedFiles.contains(testCpp));
|
||||||
|
|
||||||
|
snapshot = mm->snapshot();
|
||||||
|
QVERIFY(snapshot.contains(testHeader1));
|
||||||
|
QVERIFY(snapshot.contains(testCpp));
|
||||||
|
|
||||||
|
// Now add testHeader2 and remove testHeader1
|
||||||
|
pi.clearProjectParts();
|
||||||
|
ProjectPart::Ptr newPart(new ProjectPart);
|
||||||
|
newPart->cxxVersion = ProjectPart::CXX98;
|
||||||
|
newPart->qtVersion = ProjectPart::Qt5;
|
||||||
|
newPart->files.append(ProjectFile(testCpp, ProjectFile::CXXSource));
|
||||||
|
newPart->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader));
|
||||||
|
pi.appendProjectPart(newPart);
|
||||||
|
|
||||||
|
mm->updateProjectInfo(pi);
|
||||||
|
refreshedFiles = helper.waitForRefreshedSourceFiles();
|
||||||
|
|
||||||
|
// Only the added project file was reparsed
|
||||||
|
QCOMPARE(refreshedFiles.size(), 1);
|
||||||
|
QVERIFY(refreshedFiles.contains(testHeader2));
|
||||||
|
|
||||||
|
snapshot = mm->snapshot();
|
||||||
|
QVERIFY(snapshot.contains(testHeader2));
|
||||||
|
QVERIFY(snapshot.contains(testCpp));
|
||||||
|
// The removed project file is not anymore in the snapshot
|
||||||
|
QVERIFY(!snapshot.contains(testHeader1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check: Timestamp modified files are reparsed if project files are added or removed
|
||||||
|
/// while the project configuration stays the same
|
||||||
|
void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_change()
|
||||||
|
{
|
||||||
|
QFETCH(QString, fileToChange);
|
||||||
|
QFETCH(QList<ProjectFile>, initialProjectFiles);
|
||||||
|
QFETCH(QList<ProjectFile>, finalProjectFiles);
|
||||||
|
|
||||||
|
ModelManagerTestHelper helper;
|
||||||
|
CppModelManager *mm = CppModelManager::instance();
|
||||||
|
|
||||||
|
Project *project = helper.createProject(
|
||||||
|
QLatin1String("test_modelmanager_refresh_timeStampModified"));
|
||||||
|
ProjectInfo pi = mm->projectInfo(project);
|
||||||
|
QCOMPARE(pi.project().data(), project);
|
||||||
|
|
||||||
|
ProjectPart::Ptr part(new ProjectPart);
|
||||||
|
part->cxxVersion = ProjectPart::CXX98;
|
||||||
|
part->qtVersion = ProjectPart::Qt5;
|
||||||
|
foreach (const ProjectFile &file, initialProjectFiles)
|
||||||
|
part->files.append(file);
|
||||||
|
pi.appendProjectPart(part);
|
||||||
|
|
||||||
|
Document::Ptr document;
|
||||||
|
CPlusPlus::Snapshot snapshot;
|
||||||
|
QStringList refreshedFiles;
|
||||||
|
|
||||||
|
mm->updateProjectInfo(pi);
|
||||||
|
refreshedFiles = helper.waitForRefreshedSourceFiles();
|
||||||
|
|
||||||
|
QCOMPARE(refreshedFiles.size(), initialProjectFiles.size());
|
||||||
|
snapshot = mm->snapshot();
|
||||||
|
foreach (const ProjectFile &file, initialProjectFiles) {
|
||||||
|
QVERIFY(refreshedFiles.contains(file.path));
|
||||||
|
QVERIFY(snapshot.contains(file.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
document = snapshot.document(fileToChange);
|
||||||
|
const QDateTime lastModifiedBefore = document->lastModified();
|
||||||
|
QCOMPARE(document->globalSymbolCount(), 1U);
|
||||||
|
QCOMPARE(document->globalSymbolAt(0)->name()->identifier()->chars(), "someGlobal");
|
||||||
|
|
||||||
|
// Modify the file
|
||||||
|
QTest::qSleep(1000); // Make sure the timestamp is different
|
||||||
|
FileChangerAndRestorer fileChangerAndRestorer(fileToChange);
|
||||||
|
QByteArray originalContents;
|
||||||
|
QVERIFY(fileChangerAndRestorer.readContents(&originalContents));
|
||||||
|
const QByteArray newFileContentes = originalContents + "\nint addedOtherGlobal;";
|
||||||
|
fileChangerAndRestorer.writeContents(newFileContentes);
|
||||||
|
|
||||||
|
// Add or remove source file. The configuration stays the same.
|
||||||
|
part->files.clear();
|
||||||
|
foreach (const ProjectFile &file, finalProjectFiles)
|
||||||
|
part->files.append(file);
|
||||||
|
pi.clearProjectParts();
|
||||||
|
pi.appendProjectPart(part);
|
||||||
|
|
||||||
|
mm->updateProjectInfo(pi);
|
||||||
|
refreshedFiles = helper.waitForRefreshedSourceFiles();
|
||||||
|
|
||||||
|
QCOMPARE(refreshedFiles.size(), finalProjectFiles.size());
|
||||||
|
snapshot = mm->snapshot();
|
||||||
|
foreach (const ProjectFile &file, finalProjectFiles) {
|
||||||
|
QVERIFY(refreshedFiles.contains(file.path));
|
||||||
|
QVERIFY(snapshot.contains(file.path));
|
||||||
|
}
|
||||||
|
document = snapshot.document(fileToChange);
|
||||||
|
const QDateTime lastModifiedAfter = document->lastModified();
|
||||||
|
QVERIFY(lastModifiedAfter > lastModifiedBefore);
|
||||||
|
QCOMPARE(document->globalSymbolCount(), 2U);
|
||||||
|
QCOMPARE(document->globalSymbolAt(0)->name()->identifier()->chars(), "someGlobal");
|
||||||
|
QCOMPARE(document->globalSymbolAt(1)->name()->identifier()->chars(), "addedOtherGlobal");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_change_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("fileToChange");
|
||||||
|
QTest::addColumn<QList<ProjectFile> >("initialProjectFiles");
|
||||||
|
QTest::addColumn<QList<ProjectFile> >("finalProjectFiles");
|
||||||
|
|
||||||
|
const TestDataDirectory testDataDir(QLatin1String("testdata_refresh2"));
|
||||||
|
const QString testCpp(testDataDir.file(QLatin1String("source.cpp")));
|
||||||
|
const QString testCpp2(testDataDir.file(QLatin1String("source2.cpp")));
|
||||||
|
|
||||||
|
const QString fileToChange = testCpp;
|
||||||
|
QList<ProjectFile> projectFiles1 = QList<ProjectFile>()
|
||||||
|
<< ProjectFile(testCpp, ProjectFile::CXXSource);
|
||||||
|
QList<ProjectFile> projectFiles2 = QList<ProjectFile>()
|
||||||
|
<< ProjectFile(testCpp, ProjectFile::CXXSource)
|
||||||
|
<< ProjectFile(testCpp2, ProjectFile::CXXSource);
|
||||||
|
|
||||||
|
// Add a file
|
||||||
|
QTest::newRow("case: add project file") << fileToChange << projectFiles1 << projectFiles2;
|
||||||
|
|
||||||
|
// Remove a file
|
||||||
|
QTest::newRow("case: remove project file") << fileToChange << projectFiles2 << projectFiles1;
|
||||||
|
}
|
||||||
|
|
||||||
/// Check: If a second project is opened, the code model is still aware of
|
/// Check: If a second project is opened, the code model is still aware of
|
||||||
/// files of the first project.
|
/// files of the first project.
|
||||||
void CppToolsPlugin::test_modelmanager_snapshot_after_two_projects()
|
void CppToolsPlugin::test_modelmanager_snapshot_after_two_projects()
|
||||||
|
@@ -68,6 +68,37 @@ using namespace ProjectExplorer;
|
|||||||
Parser may enable tricks for Qt v5.x
|
Parser may enable tricks for Qt v5.x
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn virtual QFuture<void> updateProjectInfo(const ProjectInfo &pinfo) = 0;
|
||||||
|
\param pinfo Updated ProjectInfo.
|
||||||
|
\return A future that reports progress and allows to cancel the reparsing operation.
|
||||||
|
|
||||||
|
This function is expected to be called by the project managers to update the
|
||||||
|
code model with new project information.
|
||||||
|
|
||||||
|
In particular, the function should be called in case:
|
||||||
|
1. A new project is opened/created
|
||||||
|
2. The project configuration changed. This includes
|
||||||
|
2.1 Changes of defines, includes, framework paths
|
||||||
|
2.2 Addition/Removal of project files
|
||||||
|
|
||||||
|
\sa CppTools::CppModelManagerInterface::updateSourceFiles()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn virtual QFuture<void> updateSourceFiles(const QStringList &sourceFiles, ProgressNotificationMode mode = ReservedProgressNotification) = 0;
|
||||||
|
\param sourceFiles List of source file to update. The items are absolute paths.
|
||||||
|
\param mode The progress modification mode.
|
||||||
|
\return A future that reports progress and allows to cancel the reparsing operation.
|
||||||
|
|
||||||
|
Trigger an asynchronous reparsing of the given source files.
|
||||||
|
|
||||||
|
This function is not meant to be called by the project managers.
|
||||||
|
|
||||||
|
\sa CppTools::CppModelManagerInterface::ProgressNotificationMode
|
||||||
|
\sa CppTools::CppModelManagerInterface::updateProjectInfo()
|
||||||
|
*/
|
||||||
|
|
||||||
ProjectPart::ProjectPart()
|
ProjectPart::ProjectPart()
|
||||||
: cVersion(C89)
|
: cVersion(C89)
|
||||||
, cxxVersion(CXX11)
|
, cxxVersion(CXX11)
|
||||||
|
@@ -249,6 +249,9 @@ public:
|
|||||||
virtual CppTools::CppIndexingSupport *indexingSupport() = 0;
|
virtual CppTools::CppIndexingSupport *indexingSupport() = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
/// Project data might be locked while this is emitted.
|
||||||
|
void aboutToRemoveFiles(const QStringList &files);
|
||||||
|
|
||||||
void documentUpdated(CPlusPlus::Document::Ptr doc);
|
void documentUpdated(CPlusPlus::Document::Ptr doc);
|
||||||
void sourceFilesRefreshed(const QStringList &files);
|
void sourceFilesRefreshed(const QStringList &files);
|
||||||
|
|
||||||
@@ -258,9 +261,11 @@ signals:
|
|||||||
void projectPartsUpdated(ProjectExplorer::Project *project);
|
void projectPartsUpdated(ProjectExplorer::Project *project);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void updateModifiedSourceFiles() = 0;
|
// Documented in source file.
|
||||||
virtual QFuture<void> updateSourceFiles(const QStringList &sourceFiles,
|
virtual QFuture<void> updateSourceFiles(const QStringList &sourceFiles,
|
||||||
ProgressNotificationMode mode = ReservedProgressNotification) = 0;
|
ProgressNotificationMode mode = ReservedProgressNotification) = 0;
|
||||||
|
|
||||||
|
virtual void updateModifiedSourceFiles() = 0;
|
||||||
virtual void GC() = 0;
|
virtual void GC() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -192,6 +192,9 @@ private slots:
|
|||||||
void test_modelmanager_refresh_also_includes_of_project_files();
|
void test_modelmanager_refresh_also_includes_of_project_files();
|
||||||
void test_modelmanager_refresh_several_times();
|
void test_modelmanager_refresh_several_times();
|
||||||
void test_modelmanager_refresh_test_for_changes();
|
void test_modelmanager_refresh_test_for_changes();
|
||||||
|
void test_modelmanager_refresh_added_and_purge_removed();
|
||||||
|
void test_modelmanager_refresh_timeStampModified_if_sourcefiles_change();
|
||||||
|
void test_modelmanager_refresh_timeStampModified_if_sourcefiles_change_data();
|
||||||
void test_modelmanager_snapshot_after_two_projects();
|
void test_modelmanager_snapshot_after_two_projects();
|
||||||
void test_modelmanager_extraeditorsupport_uiFiles();
|
void test_modelmanager_extraeditorsupport_uiFiles();
|
||||||
void test_modelmanager_gc_if_last_cppeditor_closed();
|
void test_modelmanager_gc_if_last_cppeditor_closed();
|
||||||
|
1
tests/cppmodelmanager/testdata_refresh2/source.cpp
Normal file
1
tests/cppmodelmanager/testdata_refresh2/source.cpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
int someGlobal;
|
1
tests/cppmodelmanager/testdata_refresh2/source2.cpp
Normal file
1
tests/cppmodelmanager/testdata_refresh2/source2.cpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
int f() {}
|
Reference in New Issue
Block a user