forked from qt-creator/qt-creator
Clang: Watch directories instead of files
Because there a limited resources to watch files we watch now directories. So we need much less resources. Change-Id: Iac558832e9521a7a1a67c5ea99b42ad1b0b5129c Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/****************************************************************************
|
||||
; /****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
@@ -27,71 +27,94 @@
|
||||
|
||||
#include "clangpathwatcherinterface.h"
|
||||
#include "clangpathwatchernotifier.h"
|
||||
#include "changedfilepathcompressor.h"
|
||||
#include "directorypathcompressor.h"
|
||||
#include "filepathcachinginterface.h"
|
||||
#include "filesystem.h"
|
||||
#include "stringcache.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
namespace ClangBackEnd {
|
||||
|
||||
template<class InputIt1, class InputIt2, class Callable>
|
||||
void set_greedy_intersection_call(
|
||||
InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable callable)
|
||||
{
|
||||
while (first1 != last1 && first2 != last2) {
|
||||
if (*first1 < *first2) {
|
||||
++first1;
|
||||
} else {
|
||||
if (*first2 < *first1)
|
||||
++first2;
|
||||
else
|
||||
callable(*first1++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WatcherEntry
|
||||
{
|
||||
public:
|
||||
ProjectPartId id;
|
||||
FilePathId pathId;
|
||||
DirectoryPathId directoryPathId;
|
||||
FilePathId filePathId;
|
||||
long long lastModified = -1;
|
||||
|
||||
friend bool operator==(WatcherEntry first, WatcherEntry second)
|
||||
{
|
||||
return first.id == second.id && first.pathId == second.pathId;
|
||||
return first.id == second.id && first.directoryPathId == second.directoryPathId
|
||||
&& first.filePathId == second.filePathId;
|
||||
}
|
||||
|
||||
friend bool operator<(WatcherEntry first, WatcherEntry second)
|
||||
{
|
||||
return std::tie(first.pathId, first.id) < std::tie(second.pathId, second.id);
|
||||
return std::tie(first.directoryPathId, first.filePathId, first.id)
|
||||
< std::tie(second.directoryPathId, second.filePathId, second.id);
|
||||
}
|
||||
|
||||
friend bool operator<(WatcherEntry entry, FilePathId pathId)
|
||||
friend bool operator<(DirectoryPathId directoryPathId, WatcherEntry entry)
|
||||
{
|
||||
return entry.pathId < pathId;
|
||||
return directoryPathId < entry.directoryPathId;
|
||||
}
|
||||
|
||||
friend bool operator<(FilePathId pathId, WatcherEntry entry)
|
||||
friend bool operator<(WatcherEntry entry, DirectoryPathId directoryPathId)
|
||||
{
|
||||
return pathId < entry.pathId;
|
||||
return entry.directoryPathId < directoryPathId;
|
||||
}
|
||||
|
||||
operator FilePathId() const
|
||||
{
|
||||
return pathId;
|
||||
}
|
||||
operator FilePathId() const { return filePathId; }
|
||||
|
||||
operator DirectoryPathId() const { return directoryPathId; }
|
||||
};
|
||||
|
||||
using WatcherEntries = std::vector<WatcherEntry>;
|
||||
|
||||
template <typename FileSystemWatcher,
|
||||
typename Timer>
|
||||
template<typename FileSystemWatcher, typename Timer>
|
||||
class CLANGSUPPORT_GCCEXPORT ClangPathWatcher : public ClangPathWatcherInterface
|
||||
{
|
||||
public:
|
||||
ClangPathWatcher(FilePathCachingInterface &pathCache,
|
||||
ClangPathWatcherNotifier *notifier=nullptr)
|
||||
: m_changedFilePathCompressor(pathCache),
|
||||
m_pathCache(pathCache),
|
||||
m_notifier(notifier)
|
||||
FileSystemInterface &fileSystem,
|
||||
ClangPathWatcherNotifier *notifier = nullptr)
|
||||
: m_pathCache(pathCache)
|
||||
, m_fileStatusCache(fileSystem)
|
||||
, m_fileSystem(fileSystem)
|
||||
, m_notifier(notifier)
|
||||
{
|
||||
QObject::connect(&m_fileSystemWatcher,
|
||||
&FileSystemWatcher::fileChanged,
|
||||
[&] (const QString &filePath) { compressChangedFilePath(filePath); });
|
||||
&FileSystemWatcher::directoryChanged,
|
||||
[&](const QString &path) { compressChangedDirectoryPath(path); });
|
||||
|
||||
m_changedFilePathCompressor.setCallback([&] (ClangBackEnd::FilePathIds &&filePathIds) {
|
||||
addChangedPathForFilePath(std::move(filePathIds));
|
||||
m_directoryPathCompressor.setCallback([&](ClangBackEnd::DirectoryPathIds &&directoryPathIds) {
|
||||
addChangedPathForFilePath(std::move(directoryPathIds));
|
||||
});
|
||||
}
|
||||
|
||||
~ClangPathWatcher()
|
||||
{
|
||||
m_changedFilePathCompressor.setCallback([&] (FilePathIds &&) {});
|
||||
m_directoryPathCompressor.setCallback([&](DirectoryPathIds &&) {});
|
||||
}
|
||||
|
||||
void updateIdPaths(const std::vector<IdPaths> &idPaths) override
|
||||
@@ -109,7 +132,7 @@ public:
|
||||
auto filteredPaths = filterNotWatchedPaths(removedEntries);
|
||||
|
||||
if (!filteredPaths.empty())
|
||||
m_fileSystemWatcher.removePaths(convertWatcherEntriesToQStringList(filteredPaths));
|
||||
m_fileSystemWatcher.removePaths(convertWatcherEntriesToDirectoryPathList(filteredPaths));
|
||||
}
|
||||
|
||||
void setNotifier(ClangPathWatcherNotifier *notifier) override
|
||||
@@ -164,7 +187,13 @@ public:
|
||||
outputIterator = std::transform(idPath.filePathIds.begin(),
|
||||
idPath.filePathIds.end(),
|
||||
outputIterator,
|
||||
[&] (FilePathId pathId) { return WatcherEntry{id, pathId}; });
|
||||
[&](FilePathId filePathId) {
|
||||
return WatcherEntry{
|
||||
id,
|
||||
m_pathCache.directoryPathId(filePathId),
|
||||
filePathId,
|
||||
m_fileStatusCache.lastModifiedTime(filePathId)};
|
||||
});
|
||||
}
|
||||
|
||||
std::sort(entries.begin(), entries.end());
|
||||
@@ -182,7 +211,7 @@ public:
|
||||
mergeToWatchedEntries(newEntries);
|
||||
|
||||
if (!filteredPaths.empty())
|
||||
m_fileSystemWatcher.addPaths(convertWatcherEntriesToQStringList(filteredPaths));
|
||||
m_fileSystemWatcher.addPaths(convertWatcherEntriesToDirectoryPathList(filteredPaths));
|
||||
}
|
||||
|
||||
void removeUnusedEntries(const WatcherEntries &entries, const ProjectPartIds &ids)
|
||||
@@ -194,33 +223,31 @@ public:
|
||||
auto filteredPaths = filterNotWatchedPaths(oldEntries);
|
||||
|
||||
if (!filteredPaths.empty())
|
||||
m_fileSystemWatcher.removePaths(convertWatcherEntriesToQStringList(filteredPaths));
|
||||
m_fileSystemWatcher.removePaths(convertWatcherEntriesToDirectoryPathList(filteredPaths));
|
||||
}
|
||||
|
||||
FileSystemWatcher &fileSystemWatcher()
|
||||
{
|
||||
return m_fileSystemWatcher;
|
||||
}
|
||||
FileSystemWatcher &fileSystemWatcher() { return m_fileSystemWatcher; }
|
||||
|
||||
QStringList convertWatcherEntriesToQStringList(
|
||||
const WatcherEntries &watcherEntries)
|
||||
QStringList convertWatcherEntriesToDirectoryPathList(const DirectoryPathIds &directoryPathIds) const
|
||||
{
|
||||
QStringList paths;
|
||||
paths.reserve(int(watcherEntries.size()));
|
||||
|
||||
std::transform(watcherEntries.begin(),
|
||||
watcherEntries.end(),
|
||||
std::back_inserter(paths),
|
||||
[&] (WatcherEntry entry) {
|
||||
return QString(m_pathCache.filePath(entry.pathId).path());
|
||||
return Utils::transform<QStringList>(directoryPathIds, [&](DirectoryPathId id) {
|
||||
return QString(m_pathCache.directoryPath(id));
|
||||
});
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
template <typename Compare>
|
||||
WatcherEntries notWatchedEntries(const WatcherEntries &entries,
|
||||
Compare compare) const
|
||||
QStringList convertWatcherEntriesToDirectoryPathList(const WatcherEntries &watcherEntries) const
|
||||
{
|
||||
DirectoryPathIds directoryPathIds = Utils::transform<DirectoryPathIds>(
|
||||
watcherEntries, [&](WatcherEntry entry) { return entry.directoryPathId; });
|
||||
|
||||
std::sort(directoryPathIds.begin(), directoryPathIds.end());
|
||||
directoryPathIds.erase(std::unique(directoryPathIds.begin(), directoryPathIds.end()),
|
||||
directoryPathIds.end());
|
||||
|
||||
return convertWatcherEntriesToDirectoryPathList(directoryPathIds);
|
||||
}
|
||||
|
||||
WatcherEntries notWatchedEntries(const WatcherEntries &entries) const
|
||||
{
|
||||
WatcherEntries notWatchedEntries;
|
||||
notWatchedEntries.reserve(entries.size());
|
||||
@@ -229,24 +256,23 @@ public:
|
||||
entries.end(),
|
||||
m_watchedEntries.cbegin(),
|
||||
m_watchedEntries.cend(),
|
||||
std::back_inserter(notWatchedEntries),
|
||||
compare);
|
||||
std::back_inserter(notWatchedEntries));
|
||||
|
||||
return notWatchedEntries;
|
||||
}
|
||||
|
||||
WatcherEntries notWatchedEntries(const WatcherEntries &entries) const
|
||||
DirectoryPathIds notWatchedPaths(const DirectoryPathIds &ids) const
|
||||
{
|
||||
return notWatchedEntries(entries, std::less<WatcherEntry>());
|
||||
}
|
||||
DirectoryPathIds notWatchedDirectoryIds;
|
||||
notWatchedDirectoryIds.reserve(ids.size());
|
||||
|
||||
WatcherEntries notWatchedPaths(const WatcherEntries &entries) const
|
||||
{
|
||||
auto compare = [] (WatcherEntry first, WatcherEntry second) {
|
||||
return first.pathId < second.pathId;
|
||||
};
|
||||
std::set_difference(ids.begin(),
|
||||
ids.end(),
|
||||
m_watchedEntries.cbegin(),
|
||||
m_watchedEntries.cend(),
|
||||
std::back_inserter(notWatchedDirectoryIds));
|
||||
|
||||
return notWatchedEntries(entries, compare);
|
||||
return notWatchedDirectoryIds;
|
||||
}
|
||||
|
||||
template <typename Compare>
|
||||
@@ -297,25 +323,24 @@ public:
|
||||
m_watchedEntries = std::move(newWatchedEntries);
|
||||
}
|
||||
|
||||
static
|
||||
WatcherEntries uniquePaths(const WatcherEntries &pathEntries)
|
||||
static DirectoryPathIds uniquePaths(const WatcherEntries &pathEntries)
|
||||
{
|
||||
WatcherEntries uniqueEntries;
|
||||
uniqueEntries.reserve(pathEntries.size());
|
||||
DirectoryPathIds uniqueDirectoryIds;
|
||||
uniqueDirectoryIds.reserve(pathEntries.size());
|
||||
|
||||
auto compare = [] (WatcherEntry first, WatcherEntry second) {
|
||||
return first.pathId == second.pathId;
|
||||
auto compare = [](WatcherEntry first, WatcherEntry second) {
|
||||
return first.directoryPathId == second.directoryPathId;
|
||||
};
|
||||
|
||||
std::unique_copy(pathEntries.begin(),
|
||||
pathEntries.end(),
|
||||
std::back_inserter(uniqueEntries),
|
||||
std::back_inserter(uniqueDirectoryIds),
|
||||
compare);
|
||||
|
||||
return uniqueEntries;
|
||||
return uniqueDirectoryIds;
|
||||
}
|
||||
|
||||
WatcherEntries filterNotWatchedPaths(const WatcherEntries &entries)
|
||||
DirectoryPathIds filterNotWatchedPaths(const WatcherEntries &entries) const
|
||||
{
|
||||
return notWatchedPaths(uniquePaths(entries));
|
||||
}
|
||||
@@ -351,40 +376,48 @@ public:
|
||||
oldEntries.end(),
|
||||
std::back_inserter(newWatchedEntries));
|
||||
|
||||
|
||||
m_watchedEntries = newWatchedEntries;
|
||||
m_watchedEntries = std::move(newWatchedEntries);
|
||||
}
|
||||
|
||||
void compressChangedFilePath(const QString &filePath)
|
||||
void compressChangedDirectoryPath(const QString &path)
|
||||
{
|
||||
m_changedFilePathCompressor.addFilePath(filePath);
|
||||
m_directoryPathCompressor.addDirectoryPathId(
|
||||
m_pathCache.directoryPathId(Utils::PathString{path}));
|
||||
}
|
||||
|
||||
WatcherEntries watchedEntriesForPaths(ClangBackEnd::FilePathIds &&filePathIds)
|
||||
WatcherEntries watchedEntriesForPaths(ClangBackEnd::DirectoryPathIds &&directoryPathIds)
|
||||
{
|
||||
WatcherEntries foundEntries;
|
||||
foundEntries.reserve(filePathIds.size());
|
||||
foundEntries.reserve(m_watchedEntries.size());
|
||||
|
||||
for (FilePathId pathId : filePathIds) {
|
||||
auto range = std::equal_range(m_watchedEntries.begin(), m_watchedEntries.end(), pathId);
|
||||
foundEntries.insert(foundEntries.end(), range.first, range.second);
|
||||
}
|
||||
set_greedy_intersection_call(m_watchedEntries.begin(),
|
||||
m_watchedEntries.end(),
|
||||
directoryPathIds.begin(),
|
||||
directoryPathIds.end(),
|
||||
[&](WatcherEntry &entry) {
|
||||
m_fileStatusCache.update(entry.filePathId);
|
||||
auto currentLastModified = m_fileStatusCache.lastModifiedTime(
|
||||
entry.filePathId);
|
||||
if (entry.lastModified < currentLastModified) {
|
||||
foundEntries.push_back(entry);
|
||||
entry.lastModified = currentLastModified;
|
||||
}
|
||||
});
|
||||
|
||||
return foundEntries;
|
||||
}
|
||||
|
||||
FilePathIds watchedPaths(const FilePathIds &filePathIds) const
|
||||
FilePathIds watchedPaths(const WatcherEntries &entries) const
|
||||
{
|
||||
FilePathIds watchedFilePathIds;
|
||||
watchedFilePathIds.reserve(filePathIds.size());
|
||||
auto filePathIds = Utils::transform<FilePathIds>(entries, [](WatcherEntry entry) {
|
||||
return entry.filePathId;
|
||||
});
|
||||
|
||||
std::set_intersection(m_watchedEntries.begin(),
|
||||
m_watchedEntries.end(),
|
||||
filePathIds.begin(),
|
||||
filePathIds.end(),
|
||||
std::back_inserter(watchedFilePathIds));
|
||||
std::sort(filePathIds.begin(), filePathIds.end());
|
||||
|
||||
return watchedFilePathIds;
|
||||
filePathIds.erase(std::unique(filePathIds.begin(), filePathIds.end()), filePathIds.end());
|
||||
|
||||
return filePathIds;
|
||||
}
|
||||
|
||||
ProjectPartIds idsForWatcherEntries(const WatcherEntries &foundEntries)
|
||||
@@ -403,21 +436,20 @@ public:
|
||||
ProjectPartIds uniqueIds(ProjectPartIds &&ids)
|
||||
{
|
||||
std::sort(ids.begin(), ids.end());
|
||||
auto newEnd = std::unique(ids.begin(), ids.end());
|
||||
ids.erase(newEnd, ids.end());
|
||||
ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
|
||||
|
||||
return std::move(ids);
|
||||
}
|
||||
|
||||
void addChangedPathForFilePath(FilePathIds &&filePathIds)
|
||||
void addChangedPathForFilePath(DirectoryPathIds &&directoryPathIds)
|
||||
{
|
||||
if (m_notifier) {
|
||||
WatcherEntries foundEntries = watchedEntriesForPaths(std::move(filePathIds));
|
||||
WatcherEntries foundEntries = watchedEntriesForPaths(std::move(directoryPathIds));
|
||||
|
||||
ProjectPartIds changedIds = idsForWatcherEntries(foundEntries);
|
||||
|
||||
m_notifier->pathsWithIdsChanged(uniqueIds(std::move(changedIds)));
|
||||
m_notifier->pathsChanged(watchedPaths(filePathIds));
|
||||
m_notifier->pathsChanged(watchedPaths(foundEntries));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,10 +460,12 @@ public:
|
||||
|
||||
private:
|
||||
WatcherEntries m_watchedEntries;
|
||||
ChangedFilePathCompressor<Timer> m_changedFilePathCompressor;
|
||||
FileSystemWatcher m_fileSystemWatcher;
|
||||
FileStatusCache m_fileStatusCache;
|
||||
FileSystemInterface &m_fileSystem;
|
||||
FilePathCachingInterface &m_pathCache;
|
||||
ClangPathWatcherNotifier *m_notifier;
|
||||
DirectoryPathCompressor<Timer> m_directoryPathCompressor;
|
||||
};
|
||||
|
||||
} // namespace ClangBackEnd
|
||||
|
||||
Reference in New Issue
Block a user