Index: kdevplatform/interfaces/iprojectcontroller.h =================================================================== --- kdevplatform/interfaces/iprojectcontroller.h +++ kdevplatform/interfaces/iprojectcontroller.h @@ -117,6 +117,11 @@ */ static bool parseAllProjectSources(); + /** + * @returns whether project directories should be monitored for changes or not + */ + virtual bool watchAllProjectDirectories(); + public Q_SLOTS: /** * Tries finding a project-file for the given source-url and opens it. Index: kdevplatform/interfaces/iprojectcontroller.cpp =================================================================== --- kdevplatform/interfaces/iprojectcontroller.cpp +++ kdevplatform/interfaces/iprojectcontroller.cpp @@ -42,5 +42,12 @@ return group.readEntry( "Parse All Project Sources", true ); } +bool IProjectController::watchAllProjectDirectories() +{ + KConfigGroup group = ICore::self()->activeSession()->config()->group( "Project Manager" ); + return group.readEntry( "Monitor All Project Directories", true ); +} + + } Index: kdevplatform/project/CMakeLists.txt =================================================================== --- kdevplatform/project/CMakeLists.txt +++ kdevplatform/project/CMakeLists.txt @@ -15,6 +15,7 @@ abstractfilemanagerplugin.cpp filemanagerlistjob.cpp projectfiltermanager.cpp + projectwatcher.cpp interfaces/iprojectbuilder.cpp interfaces/iprojectfilemanager.cpp interfaces/ibuildsystemmanager.cpp Index: kdevplatform/project/abstractfilemanagerplugin.h =================================================================== --- kdevplatform/project/abstractfilemanagerplugin.h +++ kdevplatform/project/abstractfilemanagerplugin.h @@ -35,6 +35,8 @@ class AbstractFileManagerPluginPrivate; +class ProjectWatcher; + /** * This class can be used as a common base for file managers. * Index: kdevplatform/project/abstractfilemanagerplugin.cpp =================================================================== --- kdevplatform/project/abstractfilemanagerplugin.cpp +++ kdevplatform/project/abstractfilemanagerplugin.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,7 @@ #include #include "projectfiltermanager.h" +#include "projectwatcher.h" #include "debug.h" #define ifDebug(x) @@ -86,7 +88,7 @@ const KIO::UDSEntryList& entries); void deleted(const QString &path); - void created(const QString &path); + void dirty(const QString &path); void projectClosing(IProject* project); void jobFinished(KJob* job); @@ -100,7 +102,7 @@ void removeFolder(ProjectFolderItem* folder); - QHash m_watchers; + QHash m_watchers; QHash > m_projectJobs; QVector m_stoppedFolders; ProjectFilterManager m_filters; @@ -117,13 +119,32 @@ } m_projectJobs.remove(project); } - delete m_watchers.take(project); + QElapsedTimer timer; + if (m_watchers.contains(project)) { + timer.start(); + } + ProjectWatcher* watcher = m_watchers.value(project, nullptr); + int nWatched = 0; + if (watcher) { + nWatched = watcher->size(); + delete watcher; + m_watchers.remove(project); + } + if (timer.isValid()) { + qCInfo(FILEMANAGER) << "Deleting" << nWatched << "entry dir watcher took" << timer.elapsed() / 1000.0 << "seconds for project" << project->name(); + } m_filters.remove(project); } KIO::Job* AbstractFileManagerPluginPrivate::eventuallyReadFolder(ProjectFolderItem* item) { - FileManagerListJob* listJob = new FileManagerListJob( item ); + ProjectWatcher* watcher; + if (ICore::self()->projectController()->watchAllProjectDirectories() && m_watchers.contains(item->project())) { + watcher = m_watchers[item->project()]; + } else { + watcher = nullptr; + } + FileManagerListJob* listJob = new FileManagerListJob( item, watcher ); m_projectJobs[ item->project() ] << listJob; qCDebug(FILEMANAGER) << "adding job" << listJob << item << item->path() << "for project" << item->project(); @@ -247,17 +268,17 @@ } } -void AbstractFileManagerPluginPrivate::created(const QString& path_) +void AbstractFileManagerPluginPrivate::dirty(const QString& path_) { - qCDebug(FILEMANAGER) << "created:" << path_; + qCDebug(FILEMANAGER) << "dirty:" << path_; QFileInfo info(path_); ///FIXME: share memory with parent const Path path(path_); const IndexedString indexedPath(path.pathOrUrl()); const IndexedString indexedParent(path.parent().pathOrUrl()); - QHashIterator it(m_watchers); + QHashIterator it(m_watchers); while (it.hasNext()) { const auto p = it.next().key(); if ( !p->projectItem()->model() ) { @@ -285,21 +306,6 @@ // also gets triggered for kate's backup files continue; } - foreach ( ProjectFolderItem* parentItem, p->foldersForPath(indexedParent) ) { - if ( info.isDir() ) { - ProjectFolderItem* folder = q->createFolderItem( p, path, parentItem ); - if (folder) { - emit q->folderAdded( folder ); - auto job = eventuallyReadFolder( folder ); - job->start(); - } - } else { - ProjectFileItem* file = q->createFileItem( p, path, parentItem ); - if (file) { - emit q->fileAdded( file ); - } - } - } } } @@ -320,7 +326,7 @@ const Path path(QUrl::fromLocalFile(path_)); const IndexedString indexed(path.pathOrUrl()); - QHashIterator it(m_watchers); + QHashIterator it(m_watchers); while (it.hasNext()) { const auto p = it.next().key(); if (path == p->path()) { @@ -388,20 +394,31 @@ if ( !folder->path().isLocalFile() ) { return; } - Q_ASSERT(m_watchers.contains(folder->project())); const QString path = folder->path().toLocalFile(); - m_watchers[folder->project()]->stopDirScan(path); + ProjectWatcher* watcher = m_watchers.value(folder->project(), nullptr); + if (watcher && watcher->contains(path)) { + m_watchers[folder->project()]->stopDirScan(path); + } m_stoppedFolders.append(path); } void AbstractFileManagerPluginPrivate::continueWatcher(ProjectFolderItem* folder) { if ( !folder->path().isLocalFile() ) { return; } - Q_ASSERT(m_watchers.contains(folder->project())); const QString path = folder->path().toLocalFile(); - m_watchers[folder->project()]->restartDirScan(path); + if (ICore::self()->projectController()->watchAllProjectDirectories()) { + // restart the watcher on if @p folder corresponds to a directory + // and the project is being monitored. Add the directory if not + // being watched already. + if (m_watchers.contains(folder->project()) && QFileInfo(path).isDir()) { + if (!m_watchers[folder->project()]->restartDirScan(path)) { + // path wasn't being watched yet + m_watchers[folder->project()]->addDir(path); + } + } + } const int idx = m_stoppedFolders.indexOf(path); if (idx != -1) { m_stoppedFolders.remove(idx); @@ -431,6 +448,10 @@ job->removeSubDir(folder); } } + ProjectWatcher* watcher = m_watchers.value(folder->project(), nullptr); + if (watcher) { + watcher->removeDir(folder->path().toLocalFile()); + } folder->parent()->removeRow( folder->row() ); } @@ -472,14 +493,13 @@ ///TODO: check if this works for remote files when something gets changed through another KDE app if ( project->path().isLocalFile() ) { - d->m_watchers[project] = new KDirWatch( project ); + d->m_watchers[project] = new ProjectWatcher(project); - connect(d->m_watchers[project], &KDirWatch::created, - this, [&] (const QString& path_) { d->created(path_); }); + // set up the signal handling; feeding the dirwatcher is handled elsewhere. + connect(d->m_watchers[project], &KDirWatch::dirty, + this, [&] (const QString& path_) { d->dirty(path_); }); connect(d->m_watchers[project], &KDirWatch::deleted, this, [&] (const QString& path_) { d->deleted(path_); }); - - d->m_watchers[project]->addDir(project->path().toLocalFile(), KDirWatch::WatchSubDirs | KDirWatch:: WatchFiles ); } d->m_filters.add(project); Index: kdevplatform/project/filemanagerlistjob.h =================================================================== --- kdevplatform/project/filemanagerlistjob.h +++ kdevplatform/project/filemanagerlistjob.h @@ -22,6 +22,7 @@ #include #include +#include // uncomment to time imort jobs // #define TIME_IMPORT_JOB @@ -33,13 +34,14 @@ namespace KDevelop { class ProjectFolderItem; + class ProjectWatcher; class FileManagerListJob : public KIO::Job { Q_OBJECT public: - explicit FileManagerListJob(ProjectFolderItem* item); + explicit FileManagerListJob(ProjectFolderItem* item, ProjectWatcher* watcher = nullptr); ProjectFolderItem* item() const; void addSubDir(ProjectFolderItem* item); @@ -73,6 +75,9 @@ QElapsedTimer m_subTimer; qint64 m_subWaited = 0; #endif + void maybeWatch(const QString& path); + // local copy of the project's dirwatcher + ProjectWatcher* m_watcher; }; } Index: kdevplatform/project/filemanagerlistjob.cpp =================================================================== --- kdevplatform/project/filemanagerlistjob.cpp +++ kdevplatform/project/filemanagerlistjob.cpp @@ -18,6 +18,7 @@ */ #include "filemanagerlistjob.h" +#include "projectwatcher.h" #include #include @@ -28,14 +29,17 @@ #include #include +#include + using namespace KDevelop; -FileManagerListJob::FileManagerListJob(ProjectFolderItem* item) - : KIO::Job(), m_item(item), m_aborted(false) +FileManagerListJob::FileManagerListJob(ProjectFolderItem* item, ProjectWatcher* watcher) + : KIO::Job(), m_item(item), m_aborted(false), m_watcher(watcher) { qRegisterMetaType("KIO::UDSEntryList"); qRegisterMetaType(); qRegisterMetaType(); + setObjectName(QStringLiteral("FileManager list job for project") + item->project()->name()); /* the following line is not an error in judgment, apparently starting a * listJob while the previous one hasn't self-destructed takes a lot of time, @@ -73,6 +77,13 @@ entryList.append(entriesIn); } +void FileManagerListJob::maybeWatch(const QString& path) +{ + if (m_watcher) { + m_watcher->addDir(path); + } +} + void FileManagerListJob::startNextJob() { if ( m_listQueue.isEmpty() || m_aborted ) { @@ -95,12 +106,14 @@ if (m_aborted) { return; } + maybeWatch(path.toLocalFile()); KIO::UDSEntryList results; - std::transform(entries.begin(), entries.end(), std::back_inserter(results), [] (const QFileInfo& info) -> KIO::UDSEntry { + std::transform(entries.begin(), entries.end(), std::back_inserter(results), [this] (const QFileInfo& info) -> KIO::UDSEntry { KIO::UDSEntry entry; entry.insert(KIO::UDSEntry::UDS_NAME, info.fileName()); if (info.isDir()) { entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR); + maybeWatch(info.absoluteFilePath()); } if (info.isSymLink()) { entry.insert(KIO::UDSEntry::UDS_LINK_DEST, info.symLinkTarget()); Index: kdevplatform/shell/settings/projectconfig.kcfg =================================================================== --- kdevplatform/shell/settings/projectconfig.kcfg +++ kdevplatform/shell/settings/projectconfig.kcfg @@ -23,5 +23,10 @@ If this option is set all open documents will be automatically saved before any build is started. + + true + + If this option is set, KDevelop will monitor all project directories for changes. This keeps the overview in the Projects toolview in sync with the directory on disk. It can also really slow down the import of very big projects and consume considerable resources. + Index: kdevplatform/shell/settings/projectpreferences.ui =================================================================== --- kdevplatform/shell/settings/projectpreferences.ui +++ kdevplatform/shell/settings/projectpreferences.ui @@ -62,6 +62,16 @@ + + + Monitor all directories of a project for changes. + + + Monitor all project directories for changes + + + + Qt::Vertical