Clang: Move path watcher to clang support

Change-Id: I3b54304bcbba750ef826e6244b8f6c7000c9cf9b
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2017-10-25 17:51:39 +02:00
parent 7d550cd7c4
commit b0137c3a01
10 changed files with 15 additions and 22 deletions

View File

@@ -0,0 +1,448 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "clangpathwatcherinterface.h"
#include "clangpathwatchernotifier.h"
#include "changedfilepathcompressor.h"
#include "filepathcachinginterface.h"
#include "stringcache.h"
#include <QTimer>
namespace ClangBackEnd {
class WatcherEntry
{
public:
int id;
FilePathId pathId;
friend bool operator==(const WatcherEntry &first, const WatcherEntry &second)
{
return first.id == second.id && first.pathId == second.pathId;
}
friend bool operator<(const WatcherEntry &first, const WatcherEntry &second)
{
return std::tie(first.pathId, first.id) < std::tie(second.pathId, second.id);
}
friend bool operator<(const WatcherEntry &entry, FilePathId pathId)
{
return entry.pathId < pathId;
}
friend bool operator<(FilePathId pathId, const WatcherEntry &entry)
{
return pathId < entry.pathId;
}
};
using WatcherEntries = std::vector<WatcherEntry>;
using IdCache = StringCache<Utils::SmallString,
int,
NonLockingMutex,
decltype(&Utils::compare),
Utils::compare>;
template <typename FileSystemWatcher,
typename Timer>
class ClangPathWatcher : public ClangPathWatcherInterface
{
public:
ClangPathWatcher(FilePathCachingInterface &pathCache,
ClangPathWatcherNotifier *notifier=nullptr)
: m_pathCache(pathCache),
m_notifier(notifier)
{
QObject::connect(&m_fileSystemWatcher,
&FileSystemWatcher::fileChanged,
[&] (const QString &filePath) { compressChangedFilePath(filePath); });
m_changedFilePathCompressor.setCallback([&] (Utils::PathStringVector &&filePaths) {
addChangedPathForFilePath(std::move(filePaths));
});
}
~ClangPathWatcher()
{
m_changedFilePathCompressor.setCallback([&] (Utils::PathStringVector &&) {});
}
void updateIdPaths(const std::vector<IdPaths> &idPaths) override
{
auto entriesAndIds = convertIdPathsToWatcherEntriesAndIds(idPaths);
addEntries(entriesAndIds.first);
removeUnusedEntries(entriesAndIds.first, entriesAndIds.second);
}
void removeIds(const Utils::SmallStringVector &ids) override
{
auto removedEntries = removeIdsFromWatchedEntries(convertToIdNumbers(ids));
auto filteredPaths = filterNotWatchedPaths(removedEntries);
if (!filteredPaths.empty())
m_fileSystemWatcher.removePaths(convertWatcherEntriesToQStringList(filteredPaths));
}
void setNotifier(ClangPathWatcherNotifier *notifier) override
{
m_notifier = notifier;
}
unittest_public:
static std::vector<uint> idsFromIdPaths(const std::vector<IdPaths> &idPaths)
{
std::vector<uint> ids;
ids.reserve(idPaths.size());
auto extractId = [] (const IdPaths &idPath) {
return idPath.id;
};
std::transform(idPaths.begin(),
idPaths.end(),
std::back_inserter(ids),
extractId);
std::sort(ids.begin(), ids.end());
return ids;
}
std::vector<int> convertToIdNumbers(const Utils::SmallStringVector &ids)
{
std::vector<int> idNumbers = m_idCache.stringIds(ids);
std::sort(idNumbers.begin(), idNumbers.end());
return idNumbers;
}
std::size_t sizeOfIdPaths(const std::vector<IdPaths> &idPaths)
{
auto sumSize = [] (std::size_t size, const IdPaths &idPath) {
return size + idPath.filePathIds.size();
};
return std::accumulate(idPaths.begin(), idPaths.end(), std::size_t(0), sumSize);
}
std::pair<WatcherEntries, std::vector<int>>
convertIdPathsToWatcherEntriesAndIds(const std::vector<IdPaths> &idPaths)
{
WatcherEntries entries;
entries.reserve(sizeOfIdPaths(idPaths));
std::vector<int> ids;
ids.reserve(ids.size());
auto outputIterator = std::back_inserter(entries);
for (const IdPaths &idPath : idPaths)
{
int id = m_idCache.stringId(idPath.id);
ids.push_back(id);
outputIterator = std::transform(idPath.filePathIds.begin(),
idPath.filePathIds.end(),
outputIterator,
[&] (FilePathId pathId) { return WatcherEntry{id, pathId}; });
}
std::sort(entries.begin(), entries.end());
std::sort(ids.begin(), ids.end());
return {entries, ids};
}
void addEntries(const WatcherEntries &entries)
{
auto newEntries = notWatchedEntries(entries);
auto filteredPaths = filterNotWatchedPaths(newEntries);
mergeToWatchedEntries(newEntries);
if (!filteredPaths.empty())
m_fileSystemWatcher.addPaths(convertWatcherEntriesToQStringList(filteredPaths));
}
void removeUnusedEntries(const WatcherEntries &entries,
const std::vector<int> &ids)
{
auto oldEntries = notAnymoreWatchedEntriesWithIds(entries, ids);
removeFromWatchedEntries(oldEntries);
auto filteredPaths = filterNotWatchedPaths(oldEntries);
if (!filteredPaths.empty())
m_fileSystemWatcher.removePaths(convertWatcherEntriesToQStringList(filteredPaths));
}
FileSystemWatcher &fileSystemWatcher()
{
return m_fileSystemWatcher;
}
QStringList convertWatcherEntriesToQStringList(
const WatcherEntries &watcherEntries)
{
QStringList paths;
paths.reserve(int(watcherEntries.size()));
std::transform(watcherEntries.begin(),
watcherEntries.end(),
std::back_inserter(paths),
[&] (const WatcherEntry &entry) {
return QString(m_pathCache.filePath(entry.pathId).path());
});
return paths;
}
template <typename Compare>
WatcherEntries notWatchedEntries(const WatcherEntries &entries,
Compare compare) const
{
WatcherEntries notWatchedEntries;
notWatchedEntries.reserve(entries.size());
std::set_difference(entries.begin(),
entries.end(),
m_watchedEntries.cbegin(),
m_watchedEntries.cend(),
std::back_inserter(notWatchedEntries),
compare);
return notWatchedEntries;
}
WatcherEntries notWatchedEntries(const WatcherEntries &entries) const
{
return notWatchedEntries(entries, std::less<WatcherEntry>());
}
WatcherEntries notWatchedPaths(const WatcherEntries &entries) const
{
auto compare = [] (const WatcherEntry &first, const WatcherEntry &second) {
return first.pathId < second.pathId;
};
return notWatchedEntries(entries, compare);
}
template <typename Compare>
WatcherEntries notAnymoreWatchedEntries(
const WatcherEntries &newEntries,
Compare compare) const
{
WatcherEntries notAnymoreWatchedEntries;
notAnymoreWatchedEntries.reserve(m_watchedEntries.size());
std::set_difference(m_watchedEntries.cbegin(),
m_watchedEntries.cend(),
newEntries.begin(),
newEntries.end(),
std::back_inserter(notAnymoreWatchedEntries),
compare);
return notAnymoreWatchedEntries;
}
WatcherEntries notAnymoreWatchedEntriesWithIds(
const WatcherEntries &newEntries,
const std::vector<int> &ids) const
{
auto oldEntries = notAnymoreWatchedEntries(newEntries, std::less<WatcherEntry>());
auto newEnd = std::remove_if(oldEntries.begin(),
oldEntries.end(),
[&] (WatcherEntry entry) {
return !std::binary_search(ids.begin(), ids.end(), entry.id);
});
oldEntries.erase(newEnd, oldEntries.end());
return oldEntries;
}
void mergeToWatchedEntries(const WatcherEntries &newEntries)
{
WatcherEntries newWatchedEntries;
newWatchedEntries.reserve(m_watchedEntries.size() + newEntries.size());
std::merge(m_watchedEntries.cbegin(),
m_watchedEntries.cend(),
newEntries.begin(),
newEntries.end(),
std::back_inserter(newWatchedEntries));
m_watchedEntries = std::move(newWatchedEntries);
}
static
WatcherEntries uniquePaths(const WatcherEntries &pathEntries)
{
WatcherEntries uniqueEntries;
uniqueEntries.reserve(pathEntries.size());
auto compare = [] (const WatcherEntry &first, const WatcherEntry &second) {
return first.pathId == second.pathId;
};
std::unique_copy(pathEntries.begin(),
pathEntries.end(),
std::back_inserter(uniqueEntries),
compare);
return uniqueEntries;
}
WatcherEntries filterNotWatchedPaths(const WatcherEntries &entries)
{
return notWatchedPaths(uniquePaths(entries));
}
const WatcherEntries &watchedEntries() const
{
return m_watchedEntries;
}
WatcherEntries removeIdsFromWatchedEntries(const std::vector<int> &ids)
{
auto keep = [&] (const WatcherEntry &entry) {
return !std::binary_search(ids.begin(), ids.end(), entry.id);
};
auto found = std::stable_partition(m_watchedEntries.begin(),
m_watchedEntries.end(),
keep);
WatcherEntries removedEntries(found, m_watchedEntries.end());
m_watchedEntries.erase(found, m_watchedEntries.end());
return removedEntries;
}
void removeFromWatchedEntries(const WatcherEntries &oldEntries)
{
WatcherEntries newWatchedEntries;
newWatchedEntries.reserve(m_watchedEntries.size() - oldEntries.size());
std::set_difference(m_watchedEntries.cbegin(),
m_watchedEntries.cend(),
oldEntries.begin(),
oldEntries.end(),
std::back_inserter(newWatchedEntries));
m_watchedEntries = newWatchedEntries;
}
void compressChangedFilePath(const QString &filePath)
{
m_changedFilePathCompressor.addFilePath(filePath);
}
WatcherEntries watchedEntriesForPaths(Utils::PathStringVector &&filePaths)
{
FilePathIds pathIds = m_pathCache.filePathIds(filePaths);
WatcherEntries foundEntries;
foundEntries.reserve(pathIds.size());
for (FilePathId pathId : pathIds) {
auto range = std::equal_range(m_watchedEntries.begin(), m_watchedEntries.end(), pathId);
foundEntries.insert(foundEntries.end(), range.first, range.second);
}
return foundEntries;
}
Utils::SmallStringVector idsForWatcherEntries(const WatcherEntries &foundEntries)
{
Utils::SmallStringVector ids;
ids.reserve(foundEntries.size());
std::transform(foundEntries.begin(),
foundEntries.end(),
std::back_inserter(ids),
[&] (const WatcherEntry &entry) {
return Utils::SmallString(m_idCache.string(entry.id));
});
return ids;
}
Utils::SmallStringVector uniqueIds(Utils::SmallStringVector &&ids)
{
std::sort(ids.begin(), ids.end());
auto newEnd = std::unique(ids.begin(), ids.end());
ids.erase(newEnd, ids.end());
return std::move(ids);
}
void addChangedPathForFilePath(Utils::PathStringVector &&filePaths)
{
if (m_notifier) {
WatcherEntries foundEntries = watchedEntriesForPaths(std::move(filePaths));
Utils::SmallStringVector changedIds = idsForWatcherEntries(foundEntries);
m_notifier->pathsWithIdsChanged(uniqueIds(std::move(changedIds)));
}
}
FilePathCachingInterface &pathCache()
{
return m_pathCache;
}
IdCache &idCache()
{
return m_idCache;
}
private:
IdCache m_idCache;
WatcherEntries m_watchedEntries;
ChangedFilePathCompressor<Timer> m_changedFilePathCompressor;
FileSystemWatcher m_fileSystemWatcher;
FilePathCachingInterface &m_pathCache;
ClangPathWatcherNotifier *m_notifier;
};
} // namespace ClangBackEnd