forked from qt-creator/qt-creator
Fixes: Updating the completion for ui files
Details: That is we update all generated headers (which are new or modfied) in the C++ Engine. Big if, this only works if we find the correct path for the ui header files. Which is known to sometimes not work, I'll fix that next.
This commit is contained in:
@@ -44,165 +44,6 @@ enum { debugWatcher = 0 };
|
||||
namespace Qt4ProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
int DirectoryWatcher::m_objectCount = 0;
|
||||
QHash<QString,int> DirectoryWatcher::m_directoryCount;
|
||||
QFileSystemWatcher *DirectoryWatcher::m_watcher = 0;
|
||||
|
||||
/*
|
||||
\class DirectoryWatcher
|
||||
|
||||
A wrapper for QFileSystemWatcher that collects
|
||||
consecutive changes to a registered directory and emits directoryChanged() and fileChanged().
|
||||
|
||||
Note that files added are only monitored if the parent directory is added, too.
|
||||
|
||||
All instances of DirectoryWatcher share one QFileSystemWatcher object.
|
||||
That's because every QFileSystemWatcher object consumes a file descriptor,
|
||||
even if no files are watched.
|
||||
*/
|
||||
DirectoryWatcher::DirectoryWatcher(QObject *parent) :
|
||||
QObject(parent),
|
||||
m_timer(0)
|
||||
{
|
||||
if (!m_watcher)
|
||||
m_watcher = new QFileSystemWatcher();
|
||||
++m_objectCount;
|
||||
connect(m_watcher, SIGNAL(directoryChanged(QString)),
|
||||
this, SLOT(slotDirectoryChanged(QString)));
|
||||
}
|
||||
|
||||
DirectoryWatcher::~DirectoryWatcher()
|
||||
{
|
||||
foreach (const QString &dir, m_directories)
|
||||
removeDirectory(dir);
|
||||
if (--m_objectCount == 0) {
|
||||
delete m_watcher;
|
||||
m_watcher = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList DirectoryWatcher::directories() const
|
||||
{
|
||||
if (debugWatcher)
|
||||
qDebug() << Q_FUNC_INFO << m_directories;
|
||||
return m_directories;
|
||||
}
|
||||
|
||||
void DirectoryWatcher::addDirectory(const QString &dir)
|
||||
{
|
||||
if (debugWatcher)
|
||||
qDebug() << Q_FUNC_INFO << dir;
|
||||
if (m_directories.contains(dir))
|
||||
return;
|
||||
m_directories += dir;
|
||||
if (m_directoryCount[dir] == 0)
|
||||
m_watcher->addPath(dir);
|
||||
m_directoryCount[dir] += 1;
|
||||
}
|
||||
|
||||
void DirectoryWatcher::removeDirectory(const QString &dir)
|
||||
{
|
||||
if (debugWatcher)
|
||||
qDebug() << Q_FUNC_INFO << dir;
|
||||
m_directories.removeOne(dir);
|
||||
m_directoryCount[dir] -= 1;
|
||||
if (m_directoryCount[dir] == 0)
|
||||
m_watcher->removePath(dir);
|
||||
}
|
||||
|
||||
QStringList DirectoryWatcher::files() const
|
||||
{
|
||||
if (debugWatcher)
|
||||
qDebug() << Q_FUNC_INFO << m_files.keys();
|
||||
return m_files.keys();
|
||||
}
|
||||
|
||||
void DirectoryWatcher::addFile(const QString &filePath)
|
||||
{
|
||||
addFiles(QStringList() << filePath);
|
||||
}
|
||||
|
||||
void DirectoryWatcher::addFiles(const QStringList &filePaths)
|
||||
{
|
||||
foreach (const QString filePath, filePaths) {
|
||||
QFileInfo file(filePath);
|
||||
m_files.insert(file.absoluteFilePath(),file.lastModified());
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryWatcher::removeFile(const QString &filePath)
|
||||
{
|
||||
m_files.remove(filePath);
|
||||
}
|
||||
|
||||
void DirectoryWatcher::slotDirectoryChanged(const QString &path)
|
||||
{
|
||||
if (debugWatcher)
|
||||
qDebug() << Q_FUNC_INFO << path;
|
||||
if (!m_directories.contains(path)
|
||||
|| m_pendingDirectories.contains(path))
|
||||
return;
|
||||
|
||||
if (!m_timer) {
|
||||
m_timer = new QTimer(this);
|
||||
m_timer->setSingleShot(true);
|
||||
m_timer->setInterval(500); // delay for 0.5 sec
|
||||
connect(m_timer, SIGNAL(timeout()), this, SLOT(slotDelayedDirectoriesChanged()));
|
||||
}
|
||||
if (!m_timer->isActive())
|
||||
m_timer->start();
|
||||
m_pendingDirectories.push_back(path);
|
||||
}
|
||||
|
||||
void DirectoryWatcher::slotDelayedDirectoriesChanged()
|
||||
{
|
||||
if (debugWatcher)
|
||||
qDebug() << Q_FUNC_INFO << " emitting " << m_pendingDirectories;
|
||||
const QStringList::const_iterator cend = m_pendingDirectories.constEnd();
|
||||
for (QStringList::const_iterator it = m_pendingDirectories.constBegin(); it != cend; ++it) {
|
||||
const QString dir = *it;
|
||||
if (!QFileInfo(dir).exists())
|
||||
removeDirectory(*it);
|
||||
emit directoryChanged(*it);
|
||||
updateFileList(*it);
|
||||
}
|
||||
m_pendingDirectories.clear();
|
||||
}
|
||||
|
||||
void DirectoryWatcher::updateFileList(const QString &dir)
|
||||
{
|
||||
const QStringList monitoredFiles = m_files.keys();
|
||||
QStringList removedFiles = monitoredFiles;
|
||||
if (QFileInfo(dir).exists()) {
|
||||
// Compare directory contents and emit signals
|
||||
QFileInfoList entryInfoList
|
||||
= QDir(dir).entryInfoList(QDir::Files|QDir::CaseSensitive);
|
||||
// Loop over directory creating the new map of file->time, removing
|
||||
// the existing entries from the old map
|
||||
const QFileInfoList::const_iterator cend = entryInfoList.constEnd();
|
||||
for (QFileInfoList::const_iterator filIt = entryInfoList.constBegin();
|
||||
filIt != cend; ++filIt) {
|
||||
const QString path = filIt->absoluteFilePath();
|
||||
FileModificationTimeMap::iterator mapIt = m_files.find(path);
|
||||
if (mapIt != m_files.end()) {
|
||||
const QDateTime lastModified = filIt->lastModified();
|
||||
if (lastModified > mapIt.value()) {
|
||||
if (debugWatcher)
|
||||
qDebug() << Q_FUNC_INFO << "emitting file changed" << path;
|
||||
emit fileChanged(path);
|
||||
m_files[path] = lastModified;
|
||||
}
|
||||
removedFiles.removeOne(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!removedFiles.isEmpty()) {
|
||||
foreach (const QString &file, removedFiles)
|
||||
removeFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
int FileWatcher::m_objectCount = 0;
|
||||
QHash<QString,int> FileWatcher::m_fileCount;
|
||||
QFileSystemWatcher *FileWatcher::m_watcher = 0;
|
||||
@@ -256,7 +97,5 @@ void FileWatcher::removeFile(const QString &file)
|
||||
m_watcher->removePath(file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Qt4ProjectManager
|
||||
|
@@ -47,46 +47,6 @@ QT_END_NAMESPACE
|
||||
namespace Qt4ProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class DirectoryWatcher : public QObject
|
||||
{
|
||||
Q_DISABLE_COPY(DirectoryWatcher)
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DirectoryWatcher(QObject *parent = 0);
|
||||
virtual ~DirectoryWatcher();
|
||||
|
||||
QStringList directories() const;
|
||||
void addDirectory(const QString &dir);
|
||||
void removeDirectory(const QString &dir);
|
||||
|
||||
QStringList files() const;
|
||||
void addFile(const QString &filePath);
|
||||
void addFiles(const QStringList &filePaths);
|
||||
void removeFile(const QString &filePath);
|
||||
|
||||
signals:
|
||||
void directoryChanged(const QString &path);
|
||||
void fileChanged(const QString &path);
|
||||
|
||||
private slots:
|
||||
void slotDirectoryChanged(const QString &);
|
||||
void slotDelayedDirectoriesChanged();
|
||||
|
||||
private:
|
||||
void updateFileList(const QString &dir);
|
||||
|
||||
static int m_objectCount;
|
||||
static QHash<QString,int> m_directoryCount;
|
||||
static QFileSystemWatcher *m_watcher;
|
||||
|
||||
QTimer *m_timer;
|
||||
QStringList m_directories;
|
||||
QStringList m_pendingDirectories;
|
||||
|
||||
typedef QHash<QString, QDateTime> FileModificationTimeMap;
|
||||
FileModificationTimeMap m_files;
|
||||
};
|
||||
|
||||
class FileWatcher : public QObject
|
||||
{
|
||||
Q_DISABLE_COPY(FileWatcher)
|
||||
|
@@ -49,6 +49,7 @@
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
|
||||
#include <cpptools/cppmodelmanagerinterface.h>
|
||||
#include <cplusplus/CppDocument.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
@@ -488,6 +489,9 @@ QStringList Qt4PriFileNode::varNames(FileType type)
|
||||
return vars;
|
||||
}
|
||||
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/buildmanager.h>
|
||||
|
||||
/*!
|
||||
\class Qt4ProFileNode
|
||||
Implements abstract ProjectNode class
|
||||
@@ -498,8 +502,7 @@ Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
|
||||
: Qt4PriFileNode(project, this, filePath),
|
||||
// own stuff
|
||||
m_projectType(InvalidProject),
|
||||
m_isQBuildProject(false),
|
||||
m_dirWatcher(new DirectoryWatcher(this))
|
||||
m_isQBuildProject(false)
|
||||
{
|
||||
if (parent)
|
||||
setParent(parent);
|
||||
@@ -507,14 +510,13 @@ Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
|
||||
m_updateTimer.setInterval(100);
|
||||
m_updateTimer.setSingleShot(true);
|
||||
|
||||
connect(m_dirWatcher, SIGNAL(directoryChanged(const QString&)),
|
||||
this, SLOT(updateGeneratedFiles()));
|
||||
connect(m_dirWatcher, SIGNAL(fileChanged(const QString&)),
|
||||
this, SLOT(fileChanged(const QString&)));
|
||||
connect(m_project, SIGNAL(activeBuildConfigurationChanged()),
|
||||
this, SLOT(update()));
|
||||
connect(&m_updateTimer, SIGNAL(timeout()),
|
||||
this, SLOT(update()));
|
||||
|
||||
connect(ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
|
||||
this, SLOT(buildStateChanged(ProjectExplorer::Project*)));
|
||||
}
|
||||
|
||||
Qt4ProFileNode::~Qt4ProFileNode()
|
||||
@@ -522,6 +524,12 @@ Qt4ProFileNode::~Qt4ProFileNode()
|
||||
|
||||
}
|
||||
|
||||
void Qt4ProFileNode::buildStateChanged(ProjectExplorer::Project *project)
|
||||
{
|
||||
if (project == m_project && !ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager()->isBuilding(m_project))
|
||||
updateUiFiles();
|
||||
}
|
||||
|
||||
bool Qt4ProFileNode::hasTargets() const
|
||||
{
|
||||
return (projectType() == ApplicationTemplate) || (projectType() == LibraryTemplate);
|
||||
@@ -689,7 +697,7 @@ void Qt4ProFileNode::update()
|
||||
emit qt4Watcher->variablesChanged(this, m_varValues, newVarValues);
|
||||
}
|
||||
|
||||
updateGeneratedFiles();
|
||||
updateUiFiles();
|
||||
|
||||
foreach (NodesWatcher *watcher, watchers())
|
||||
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
|
||||
@@ -698,15 +706,6 @@ void Qt4ProFileNode::update()
|
||||
delete reader;
|
||||
}
|
||||
|
||||
void Qt4ProFileNode::fileChanged(const QString &filePath)
|
||||
{
|
||||
CppTools::CppModelManagerInterface *modelManager =
|
||||
ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
|
||||
|
||||
// TODO compress
|
||||
modelManager->updateSourceFiles(QStringList() << filePath);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// find all ui files in project
|
||||
class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor {
|
||||
@@ -726,53 +725,54 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Adds ui_xxx.h files to tree and monitors them / the UI_DIR directory for changes
|
||||
*/
|
||||
void Qt4ProFileNode::updateGeneratedFiles()
|
||||
// This function is triggered after a build, and updates the state ui files
|
||||
// That is it adds files that didn't exist yet to the project tree, and calls
|
||||
// updateSourceFiles() for files that changed
|
||||
// It does so by storing a modification time for each ui file we know about.
|
||||
|
||||
// TODO this function should also be called if the build configuration changes
|
||||
// since the build directory could change, and thus the generated files that are present
|
||||
// TODO check that it works
|
||||
void Qt4ProFileNode::updateUiFiles()
|
||||
{
|
||||
// Only those two project types can have ui files for us
|
||||
if (m_projectType != ApplicationTemplate
|
||||
&& m_projectType != LibraryTemplate)
|
||||
return;
|
||||
|
||||
// Find all ui files
|
||||
FindUiFileNodesVisitor uiFilesVisitor;
|
||||
this->accept(&uiFilesVisitor);
|
||||
const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
|
||||
|
||||
// monitor uic dir (only if there are .ui files)
|
||||
// Find the UiDir, there can only ever be one
|
||||
QString uiDir; // We should default to the build directory
|
||||
QStringList tmp = m_varValues[UiDirVar];
|
||||
if (tmp.size() != 0)
|
||||
uiDir = tmp.first();
|
||||
|
||||
QSet<QString> oldUiDirs = m_dirWatcher->directories().toSet();
|
||||
QSet<QString> newUiDirs =
|
||||
(!uiFiles.isEmpty()) ? m_varValues[UiDirVar].toSet() : QSet<QString>();
|
||||
foreach (const QString &uiDir, oldUiDirs - newUiDirs)
|
||||
m_dirWatcher->removeDirectory(uiDir);
|
||||
foreach (const QString &uiDir, newUiDirs - oldUiDirs)
|
||||
m_dirWatcher->addDirectory(uiDir);
|
||||
|
||||
// update generated files
|
||||
|
||||
// Already existing FileNodes
|
||||
// Collect all existing generated files
|
||||
QList<FileNode*> existingFileNodes;
|
||||
foreach (FileNode *file, fileNodes()) {
|
||||
if (file->isGenerated())
|
||||
existingFileNodes << file;
|
||||
}
|
||||
|
||||
|
||||
// Convert uiFile to uiHeaderFilePath, find all headers that correspond
|
||||
// and try to find them in uicDirs
|
||||
// and try to find them in uiDir
|
||||
QStringList newFilePaths;
|
||||
foreach (const QString &uicDir, m_varValues[UiDirVar]) {
|
||||
foreach (FileNode *uiFile, uiFiles) {
|
||||
const QString uiHeaderFilePath
|
||||
= QString("%1/ui_%2.h").arg(uicDir, QFileInfo(uiFile->path()).baseName());
|
||||
if (QFileInfo(uiHeaderFilePath).exists())
|
||||
newFilePaths << uiHeaderFilePath;
|
||||
}
|
||||
foreach (FileNode *uiFile, uiFiles) {
|
||||
const QString uiHeaderFilePath
|
||||
= QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).baseName());
|
||||
if (QFileInfo(uiHeaderFilePath).exists())
|
||||
newFilePaths << uiHeaderFilePath;
|
||||
}
|
||||
|
||||
// Create a diff between those lists
|
||||
QList<FileNode*> toRemove;
|
||||
QList<FileNode*> toAdd;
|
||||
// The list of files for which we call updateSourceFile
|
||||
QStringList toUpdate;
|
||||
|
||||
qSort(newFilePaths);
|
||||
qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
|
||||
@@ -788,6 +788,13 @@ void Qt4ProFileNode::updateGeneratedFiles()
|
||||
toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
|
||||
++newPathIter;
|
||||
} else { // *existingNodeIter->path() == *newPathIter
|
||||
QString fileName = (*existingNodeIter)->path();
|
||||
QMap<QString, QDateTime>::const_iterator it = m_uitimestamps.find(fileName);
|
||||
QDateTime lastModified = QFileInfo(fileName).lastModified();
|
||||
if (it == m_uitimestamps.constEnd() || it.value() < lastModified) {
|
||||
toUpdate << fileName;
|
||||
m_uitimestamps[fileName] = lastModified;
|
||||
}
|
||||
++existingNodeIter;
|
||||
++newPathIter;
|
||||
}
|
||||
@@ -801,16 +808,33 @@ void Qt4ProFileNode::updateGeneratedFiles()
|
||||
++newPathIter;
|
||||
}
|
||||
|
||||
// Update project tree
|
||||
if (!toRemove.isEmpty()) {
|
||||
foreach (FileNode *file, toRemove)
|
||||
m_dirWatcher->removeFile(file->path());
|
||||
m_uitimestamps.remove(file->path());
|
||||
removeFileNodes(toRemove, this);
|
||||
}
|
||||
|
||||
CppTools::CppModelManagerInterface *modelManager =
|
||||
ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
|
||||
|
||||
if (!toAdd.isEmpty()) {
|
||||
foreach (FileNode *file, toAdd)
|
||||
m_dirWatcher->addFile(file->path());
|
||||
foreach (FileNode *file, toAdd) {
|
||||
m_uitimestamps.insert(file->path(), QFileInfo(file->path()).lastModified());
|
||||
toUpdate << file->path();
|
||||
|
||||
// Also adding files depending on that.
|
||||
QString fileName = QFileInfo(file->path()).fileName();
|
||||
foreach (CPlusPlus::Document::Ptr doc, modelManager->snapshot()) {
|
||||
if (doc->includedFiles().contains(fileName)) {
|
||||
if (!toUpdate.contains(doc->fileName()))
|
||||
toUpdate << doc->fileName();
|
||||
}
|
||||
}
|
||||
}
|
||||
addFileNodes(toAdd, this);
|
||||
}
|
||||
modelManager->updateSourceFiles(toUpdate);
|
||||
}
|
||||
|
||||
ProFileReader *Qt4PriFileNode::createProFileReader() const
|
||||
@@ -957,13 +981,6 @@ void Qt4ProFileNode::invalidate()
|
||||
|
||||
clear();
|
||||
|
||||
// remove monitored files/directories
|
||||
foreach (const QString &file, m_dirWatcher->files())
|
||||
m_dirWatcher->removeFile(file);
|
||||
foreach (const QString &dir, m_dirWatcher->directories())
|
||||
m_dirWatcher->removeDirectory(dir);
|
||||
|
||||
|
||||
// change project type
|
||||
Qt4ProjectType oldType = m_projectType;
|
||||
m_projectType = InvalidProject;
|
||||
|
@@ -35,10 +35,13 @@
|
||||
#define QT4NODES_H
|
||||
|
||||
#include <projectexplorer/projectnodes.h>
|
||||
#include <projectexplorer/project.h>
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
// defined in proitems.h
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -184,8 +187,8 @@ public slots:
|
||||
void scheduleUpdate();
|
||||
void update();
|
||||
private slots:
|
||||
void fileChanged(const QString &filePath);
|
||||
void updateGeneratedFiles();
|
||||
void updateUiFiles();
|
||||
void buildStateChanged(ProjectExplorer::Project*);
|
||||
|
||||
private:
|
||||
Qt4ProFileNode *createSubProFileNode(const QString &path);
|
||||
@@ -205,7 +208,7 @@ private:
|
||||
bool m_isQBuildProject;
|
||||
QTimer m_updateTimer;
|
||||
|
||||
DirectoryWatcher *m_dirWatcher;
|
||||
QMap<QString, QDateTime> m_uitimestamps;
|
||||
friend class Qt4NodeHierarchy;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user