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 <tim.jenssen@theqtcompany.com>
This commit is contained in:
Tobias Hunger
2016-06-27 14:49:35 +02:00
parent d8ed91c44f
commit 703c410085
8 changed files with 99 additions and 44 deletions

View File

@@ -49,7 +49,6 @@
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
#include <QFileSystemWatcher>
#include <QMessageBox>
#include <QRegularExpression>
#include <QSet>
@@ -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<ProjectExplorer::FileNode *> BuildDirManager::files()
return m_files;
}
QSet<Utils::FileName> 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<Utils::FileName> 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();
}

View File

@@ -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<CMakeBuildTarget> buildTargets() const;
QList<ProjectExplorer::FileNode *> files();
QSet<Utils::FileName> cmakeFiles();
void clearFiles();
CMakeConfig parsedConfiguration() const;
@@ -115,10 +117,9 @@ private:
QTemporaryDir *m_tempDir = nullptr;
mutable CMakeConfig m_cmakeCache;
QSet<Utils::FileName> m_watchedFiles;
QSet<Utils::FileName> m_cmakeFiles;
QString m_projectName;
QList<CMakeBuildTarget> m_buildTargets;
QFileSystemWatcher *m_watcher;
QList<ProjectExplorer::FileNode *> m_files;
// For error reporting:

View File

@@ -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();

View File

@@ -52,6 +52,8 @@ public:
CMakeBuildConfiguration(ProjectExplorer::Target *parent);
~CMakeBuildConfiguration();
void cmakeFilesChanged();
bool isEnabled() const override;
QString disabledReason() const override;

View File

@@ -27,6 +27,8 @@
#include "cmakeproject.h"
#include "cmakeprojectconstants.h"
#include <projectexplorer/target.h>
#include <utils/fileutils.h>
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

View File

@@ -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

View File

@@ -36,6 +36,7 @@
#include "cmakeprojectmanager.h"
#include <coreplugin/icore.h>
#include <coreplugin/documentmanager.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/projectinfo.h>
#include <cpptools/projectpartbuilder.h>
@@ -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<Utils::FileName> currentWatched
= Utils::transform(m_watchedFiles, [](CMakeFile *cmf) { return cmf->filePath(); });
const QSet<Utils::FileName> toWatch = bdm->cmakeFiles();
QSet<Utils::FileName> 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<Utils::FileName> 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<CMakeProjectNode *>(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<CMakeBuildConfiguration *>(t->activeBuildConfiguration())) {
bc->cmakeFilesChanged();
}
}
}
void CMakeProject::handleActiveTargetChanged()
{
if (m_connectedTarget) {

View File

@@ -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<void> m_codeModelFuture;
QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers;
QSet<Internal::CMakeFile *> m_watchedFiles;
friend class Internal::CMakeBuildConfiguration;
friend class Internal::CMakeFile;
};
} // namespace CMakeProjectManager