From 703c410085b71217a7f242e7251d900185307366 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 27 Jun 2016 14:49:35 +0200 Subject: [PATCH] CMake: Only start parsing when creator gets focus again Rip out QFileSystemWatcher and use Qt Creators IDocument for file watching instead. The latter properly delays any action till creator gets focus again. Task-number: QTCREATORBUG-16354 Change-Id: Ibb71963416b09712a80ee95347425550453b7fd4 Reviewed-by: Tim Jenssen --- .../cmakeprojectmanager/builddirmanager.cpp | 67 ++++++++----------- .../cmakeprojectmanager/builddirmanager.h | 7 +- .../cmakebuildconfiguration.cpp | 5 ++ .../cmakebuildconfiguration.h | 2 + src/plugins/cmakeprojectmanager/cmakefile.cpp | 14 +++- src/plugins/cmakeprojectmanager/cmakefile.h | 6 +- .../cmakeprojectmanager/cmakeproject.cpp | 37 +++++++++- .../cmakeprojectmanager/cmakeproject.h | 5 ++ 8 files changed, 99 insertions(+), 44 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index 0a30e0d72e2..1064d308243 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -97,26 +96,14 @@ static QStringList toArguments(const CMakeConfig &config, const ProjectExplorer: // -------------------------------------------------------------------- BuildDirManager::BuildDirManager(CMakeBuildConfiguration *bc) : - m_buildConfiguration(bc), - m_watcher(new QFileSystemWatcher(this)) + m_buildConfiguration(bc) { QTC_ASSERT(bc, return); m_projectName = sourceDirectory().fileName(); m_reparseTimer.setSingleShot(true); - m_reparseTimer.setInterval(5000); + m_reparseTimer.setInterval(2000); connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::parse); - - connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this]() { - if (isParsing()) - return; - - const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); - if (!tool->isAutoRun()) - return; - - m_reparseTimer.start(); - }); } BuildDirManager::~BuildDirManager() @@ -163,6 +150,18 @@ bool BuildDirManager::isParsing() const return false; } +void BuildDirManager::cmakeFilesChanged() +{ + if (isParsing()) + return; + + const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); + if (!tool->isAutoRun()) + return; + + m_reparseTimer.start(); +} + void BuildDirManager::forceReparse() { if (m_buildConfiguration->target()->activeBuildConfiguration() != m_buildConfiguration) @@ -186,13 +185,8 @@ void BuildDirManager::resetData() m_cmakeCache.clear(); m_projectName.clear(); m_buildTargets.clear(); - m_watchedFiles.clear(); qDeleteAll(m_files); m_files.clear(); - - const QStringList watchedFiles = m_watcher->files(); - if (!watchedFiles.isEmpty()) - m_watcher->removePaths(watchedFiles); } bool BuildDirManager::persistCMakeState() @@ -229,8 +223,8 @@ void BuildDirManager::parse() return; } - const bool mustUpdate = m_watchedFiles.isEmpty() - || Utils::anyOf(m_watchedFiles, [&cbpFileFi](const Utils::FileName &f) { + const bool mustUpdate = m_cmakeFiles.isEmpty() + || Utils::anyOf(m_cmakeFiles, [&cbpFileFi](const Utils::FileName &f) { return f.toFileInfo().lastModified() > cbpFileFi.lastModified(); }); if (mustUpdate) { @@ -257,11 +251,6 @@ void BuildDirManager::clearCache() forceReparse(); } -bool BuildDirManager::isProjectFile(const Utils::FileName &fileName) const -{ - return m_watchedFiles.contains(fileName); -} - QString BuildDirManager::projectName() const { return m_projectName; @@ -277,6 +266,11 @@ QList BuildDirManager::files() return m_files; } +QSet BuildDirManager::cmakeFiles() +{ + return m_cmakeFiles; +} + void BuildDirManager::clearFiles() { m_files.clear(); @@ -361,15 +355,19 @@ void BuildDirManager::extractData() m_projectName = sourceDirectory().fileName(); m_files.append(new ProjectExplorer::FileNode(topCMake, ProjectExplorer::ProjectFileType, false)); - m_watchedFiles.insert(topCMake); + // Do not insert topCMake into m_cmakeFiles: The project already watches that! // Find cbp file QString cbpFile = CMakeManager::findCbpFile(workDirectory().toString()); if (cbpFile.isEmpty()) return; + m_cmakeFiles.insert(Utils::FileName::fromString(cbpFile)); - m_watcher->addPath(cbpFile); - m_watcher->addPath(workDirectory().toString() + QLatin1String("/CMakeCache.txt")); + // Add CMakeCache.txt file: + Utils::FileName cacheFile = workDirectory(); + cacheFile.appendPath(QLatin1String("CMakeCache.txt")); + if (cacheFile.toFileInfo().exists()) + m_cmakeFiles.insert(cacheFile); // setFolderName CMakeCbpParser cbpparser; @@ -380,21 +378,14 @@ void BuildDirManager::extractData() m_projectName = cbpparser.projectName(); m_files = cbpparser.fileList(); - QSet projectFiles; if (cbpparser.hasCMakeFiles()) { m_files.append(cbpparser.cmakeFileList()); foreach (const ProjectExplorer::FileNode *node, cbpparser.cmakeFileList()) - projectFiles.insert(node->filePath()); + m_cmakeFiles.insert(node->filePath()); } else { m_files.append(new ProjectExplorer::FileNode(topCMake, ProjectExplorer::ProjectFileType, false)); - projectFiles.insert(topCMake); } - m_watchedFiles = projectFiles; - const QStringList toWatch - = Utils::transform(m_watchedFiles.toList(), [](const Utils::FileName &fn) { return fn.toString(); }); - m_watcher->addPaths(toWatch); - m_buildTargets = cbpparser.buildTargets(); } diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.h b/src/plugins/cmakeprojectmanager/builddirmanager.h index 419a87c3d15..d830e8a8595 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.h +++ b/src/plugins/cmakeprojectmanager/builddirmanager.h @@ -73,6 +73,8 @@ public: const CMakeConfig intendedConfiguration() const; bool isParsing() const; + void cmakeFilesChanged(); + void parse(); void clearCache(); void forceReparse(); @@ -80,10 +82,10 @@ public: void resetData(); bool persistCMakeState(); - bool isProjectFile(const Utils::FileName &fileName) const; QString projectName() const; QList buildTargets() const; QList files(); + QSet cmakeFiles(); void clearFiles(); CMakeConfig parsedConfiguration() const; @@ -115,10 +117,9 @@ private: QTemporaryDir *m_tempDir = nullptr; mutable CMakeConfig m_cmakeCache; - QSet m_watchedFiles; + QSet m_cmakeFiles; QString m_projectName; QList m_buildTargets; - QFileSystemWatcher *m_watcher; QList m_files; // For error reporting: diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 8d9d46e4ba0..a013083c572 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -71,6 +71,11 @@ CMakeBuildConfiguration::~CMakeBuildConfiguration() m_buildDirManager->deleteLater(); // Do not block while waiting for cmake... } +void CMakeBuildConfiguration::cmakeFilesChanged() +{ + m_buildDirManager->cmakeFilesChanged(); +} + bool CMakeBuildConfiguration::isEnabled() const { return m_error.isEmpty(); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index 37ec1aa4275..d56965ffb6c 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -52,6 +52,8 @@ public: CMakeBuildConfiguration(ProjectExplorer::Target *parent); ~CMakeBuildConfiguration(); + void cmakeFilesChanged(); + bool isEnabled() const override; QString disabledReason() const override; diff --git a/src/plugins/cmakeprojectmanager/cmakefile.cpp b/src/plugins/cmakeprojectmanager/cmakefile.cpp index 0d338840166..30a1db0e153 100644 --- a/src/plugins/cmakeprojectmanager/cmakefile.cpp +++ b/src/plugins/cmakeprojectmanager/cmakefile.cpp @@ -27,6 +27,8 @@ #include "cmakeproject.h" #include "cmakeprojectconstants.h" +#include + #include using namespace Utils; @@ -34,7 +36,7 @@ using namespace Utils; namespace CMakeProjectManager { namespace Internal { -CMakeFile::CMakeFile(const FileName &fileName) +CMakeFile::CMakeFile(CMakeProject *project, const FileName &fileName) : m_project(project) { setId("Cmake.ProjectFile"); setMimeType(QLatin1String(Constants::CMAKEPROJECTMIMETYPE)); @@ -48,5 +50,15 @@ Core::IDocument::ReloadBehavior CMakeFile::reloadBehavior(ChangeTrigger state, C return BehaviorSilent; } +bool CMakeFile::reload(QString *errorString, Core::IDocument::ReloadFlag flag, Core::IDocument::ChangeType type) +{ + Q_UNUSED(errorString); + Q_UNUSED(flag); + + if (type != TypePermissions) + m_project->handleCmakeFileChanged(); + return true; +} + } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakefile.h b/src/plugins/cmakeprojectmanager/cmakefile.h index e5018ce7315..606012ea244 100644 --- a/src/plugins/cmakeprojectmanager/cmakefile.h +++ b/src/plugins/cmakeprojectmanager/cmakefile.h @@ -35,9 +35,13 @@ namespace Internal { class CMakeFile : public Core::IDocument { public: - CMakeFile(const Utils::FileName &fileName); + CMakeFile(CMakeProject *project, const Utils::FileName &fileName); ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override; + bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; + +private: + CMakeProject *m_project; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index e7c78ab01df..bb09ec627a5 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -36,6 +36,7 @@ #include "cmakeprojectmanager.h" #include +#include #include #include #include @@ -88,11 +89,13 @@ CMakeProject::CMakeProject(CMakeManager *manager, const FileName &fileName) { setId(Constants::CMAKEPROJECT_ID); setProjectManager(manager); - setDocument(new CMakeFile(fileName)); + setDocument(new Internal::CMakeFile(this, fileName)); + setRootProjectNode(new CMakeProjectNode(fileName)); setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX)); + Core::DocumentManager::addDocument(document()); rootProjectNode()->setDisplayName(fileName.parentDir().fileName()); connect(this, &CMakeProject::activeTargetChanged, this, &CMakeProject::handleActiveTargetChanged); @@ -217,6 +220,29 @@ void CMakeProject::parseCMakeOutput() rootProjectNode()->setDisplayName(bdm->projectName()); + // Delete no longer necessary file watcher: + const QSet currentWatched + = Utils::transform(m_watchedFiles, [](CMakeFile *cmf) { return cmf->filePath(); }); + const QSet toWatch = bdm->cmakeFiles(); + QSet toDelete = currentWatched; + toDelete.subtract(toWatch); + m_watchedFiles = Utils::filtered(m_watchedFiles, [&toDelete](Internal::CMakeFile *cmf) { + if (toDelete.contains(cmf->filePath())) { + delete cmf; + return false; + } + return true; + }); + + // Add new file watchers: + QSet toAdd = toWatch; + toAdd.subtract(currentWatched); + foreach (const Utils::FileName &fn, toAdd) { + CMakeFile *cm = new CMakeFile(this, fn); + Core::DocumentManager::addDocument(cm); + m_watchedFiles.insert(cm); + } + buildTree(static_cast(rootProjectNode()), bdm->files()); bdm->clearFiles(); // Some of the FileNodes in files() were deleted! @@ -512,6 +538,15 @@ bool CMakeProject::setupTarget(Target *t) return true; } +void CMakeProject::handleCmakeFileChanged() +{ + if (Target *t = activeTarget()) { + if (auto bc = qobject_cast(t->activeBuildConfiguration())) { + bc->cmakeFilesChanged(); + } + } +} + void CMakeProject::handleActiveTargetChanged() { if (m_connectedTarget) { diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index 38126f5f9aa..27e07d8f073 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -120,6 +120,8 @@ protected: bool setupTarget(ProjectExplorer::Target *t) override; private: + void handleCmakeFileChanged(); + void handleActiveTargetChanged(); void handleActiveBuildConfigurationChanged(); void handleParsingStarted(); @@ -144,7 +146,10 @@ private: QFuture m_codeModelFuture; QList m_extraCompilers; + QSet m_watchedFiles; + friend class Internal::CMakeBuildConfiguration; + friend class Internal::CMakeFile; }; } // namespace CMakeProjectManager