forked from qt-creator/qt-creator
Clang: Add reset to ModifiedTimeChecker
We can reset some file once to flag a file dirty if the included file has changed. Change-Id: I8763bb80f65882fba4e70057f569234e77097927 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -157,6 +157,7 @@ HEADERS += \
|
|||||||
$$PWD/refactoringserverinterface.h \
|
$$PWD/refactoringserverinterface.h \
|
||||||
$$PWD/refactoringserverproxy.h \
|
$$PWD/refactoringserverproxy.h \
|
||||||
$$PWD/referencesmessage.h \
|
$$PWD/referencesmessage.h \
|
||||||
|
$$PWD/set_algorithm.h \
|
||||||
$$PWD/unsavedfilesupdatedmessage.h \
|
$$PWD/unsavedfilesupdatedmessage.h \
|
||||||
$$PWD/removeprojectpartsmessage.h \
|
$$PWD/removeprojectpartsmessage.h \
|
||||||
$$PWD/requestannotationsmessage.h \
|
$$PWD/requestannotationsmessage.h \
|
||||||
|
@@ -26,6 +26,8 @@
|
|||||||
#include "filestatuscache.h"
|
#include "filestatuscache.h"
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
|
|
||||||
|
#include <set_algorithm.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
@@ -51,49 +53,15 @@ void FileStatusCache::update(FilePathId filePathId)
|
|||||||
found->lastModified = m_fileSystem.lastModified(filePathId);
|
found->lastModified = m_fileSystem.lastModified(filePathId);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
template<class InputIt1, class InputIt2, class Callable>
|
|
||||||
void set_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))
|
|
||||||
callable(*first1++);
|
|
||||||
++first2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class InputIt1, class InputIt2, class Callable>
|
|
||||||
void set_difference_call(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable callable)
|
|
||||||
{
|
|
||||||
while (first1 != last1) {
|
|
||||||
if (first2 == last2) {
|
|
||||||
std::for_each(first1, last1, callable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (*first1 < *first2) {
|
|
||||||
callable(*first1++);
|
|
||||||
} else {
|
|
||||||
if (!(*first2 < *first1))
|
|
||||||
++first1;
|
|
||||||
++first2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void FileStatusCache::update(FilePathIds filePathIds)
|
void FileStatusCache::update(FilePathIds filePathIds)
|
||||||
{
|
{
|
||||||
set_intersection_call(m_cacheEntries.begin(),
|
std::set_intersection(m_cacheEntries.begin(),
|
||||||
m_cacheEntries.end(),
|
m_cacheEntries.end(),
|
||||||
filePathIds.begin(),
|
filePathIds.begin(),
|
||||||
filePathIds.end(),
|
filePathIds.end(),
|
||||||
[&](auto &entry) {
|
make_iterator([&](auto &entry) {
|
||||||
entry.lastModified = m_fileSystem.lastModified(entry.filePathId);
|
entry.lastModified = m_fileSystem.lastModified(entry.filePathId);
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePathIds FileStatusCache::modified(FilePathIds filePathIds) const
|
FilePathIds FileStatusCache::modified(FilePathIds filePathIds) const
|
||||||
@@ -101,30 +69,30 @@ FilePathIds FileStatusCache::modified(FilePathIds filePathIds) const
|
|||||||
FilePathIds modifiedFilePathIds;
|
FilePathIds modifiedFilePathIds;
|
||||||
modifiedFilePathIds.reserve(filePathIds.size());
|
modifiedFilePathIds.reserve(filePathIds.size());
|
||||||
|
|
||||||
set_intersection_call(m_cacheEntries.begin(),
|
std::set_intersection(m_cacheEntries.begin(),
|
||||||
m_cacheEntries.end(),
|
m_cacheEntries.end(),
|
||||||
filePathIds.begin(),
|
filePathIds.begin(),
|
||||||
filePathIds.end(),
|
filePathIds.end(),
|
||||||
[&](auto &entry) {
|
make_iterator([&](auto &entry) {
|
||||||
auto newLastModified = m_fileSystem.lastModified(entry.filePathId);
|
auto newLastModified = m_fileSystem.lastModified(entry.filePathId);
|
||||||
if (newLastModified > entry.lastModified) {
|
if (newLastModified > entry.lastModified) {
|
||||||
modifiedFilePathIds.push_back(entry.filePathId);
|
modifiedFilePathIds.push_back(entry.filePathId);
|
||||||
entry.lastModified = newLastModified;
|
entry.lastModified = newLastModified;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
Internal::FileStatusCacheEntries newEntries;
|
Internal::FileStatusCacheEntries newEntries;
|
||||||
newEntries.reserve(filePathIds.size());
|
newEntries.reserve(filePathIds.size());
|
||||||
|
|
||||||
set_difference_call(filePathIds.begin(),
|
std::set_difference(filePathIds.begin(),
|
||||||
filePathIds.end(),
|
filePathIds.end(),
|
||||||
m_cacheEntries.begin(),
|
m_cacheEntries.begin(),
|
||||||
m_cacheEntries.end(),
|
m_cacheEntries.end(),
|
||||||
[&](FilePathId newFilePathId) {
|
make_iterator([&](FilePathId newFilePathId) {
|
||||||
newEntries.emplace_back(newFilePathId,
|
newEntries.emplace_back(newFilePathId,
|
||||||
m_fileSystem.lastModified(newFilePathId));
|
m_fileSystem.lastModified(newFilePathId));
|
||||||
modifiedFilePathIds.push_back(newFilePathId);
|
modifiedFilePathIds.push_back(newFilePathId);
|
||||||
});
|
}));
|
||||||
|
|
||||||
if (newEntries.size()) {
|
if (newEntries.size()) {
|
||||||
Internal::FileStatusCacheEntries mergedEntries;
|
Internal::FileStatusCacheEntries mergedEntries;
|
||||||
|
@@ -25,24 +25,23 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "clangpathwatcher.h"
|
#include "filesysteminterface.h"
|
||||||
#include "filepathcachinginterface.h"
|
|
||||||
#include "modifiedtimecheckerinterface.h"
|
#include "modifiedtimecheckerinterface.h"
|
||||||
|
#include "set_algorithm.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
namespace ClangBackEnd {
|
namespace ClangBackEnd {
|
||||||
|
|
||||||
template<typename SourceEntries = ::ClangBackEnd::SourceEntries>
|
template<typename SourceEntries = ::ClangBackEnd::SourceEntries>
|
||||||
class ModifiedTimeChecker final : public ModifiedTimeCheckerInterface<SourceEntries>
|
class ModifiedTimeChecker final : public ModifiedTimeCheckerInterface<SourceEntries>
|
||||||
{
|
{
|
||||||
using SourceEntry = typename SourceEntries::value_type;
|
using SourceEntry = typename SourceEntries::value_type;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using GetModifiedTime = std::function<ClangBackEnd::TimeStamp(ClangBackEnd::FilePathView filePath)>;
|
ModifiedTimeChecker(FileSystemInterface &fileSystem)
|
||||||
ModifiedTimeChecker(GetModifiedTime &getModifiedTime, FilePathCachingInterface &filePathCache)
|
: m_fileSystem(fileSystem)
|
||||||
: m_getModifiedTime(getModifiedTime)
|
|
||||||
, m_filePathCache(filePathCache)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool isUpToDate(const SourceEntries &sourceEntries) const
|
bool isUpToDate(const SourceEntries &sourceEntries) const
|
||||||
@@ -52,165 +51,101 @@ public:
|
|||||||
|
|
||||||
updateCurrentSourceTimeStamps(sourceEntries);
|
updateCurrentSourceTimeStamps(sourceEntries);
|
||||||
|
|
||||||
return compareEntries(sourceEntries);
|
return compareEntries(sourceEntries) && notReseted(sourceEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pathsChanged(const FilePathIds &filePathIds) override
|
void pathsChanged(const FilePathIds &filePathIds) override
|
||||||
{
|
{
|
||||||
using SourceTimeStampReferences = std::vector<std::reference_wrapper<SourceTimeStamp>>;
|
|
||||||
|
|
||||||
SourceTimeStampReferences timeStampsToUpdate;
|
|
||||||
timeStampsToUpdate.reserve(filePathIds.size());
|
|
||||||
|
|
||||||
std::set_intersection(m_currentSourceTimeStamps.begin(),
|
std::set_intersection(m_currentSourceTimeStamps.begin(),
|
||||||
m_currentSourceTimeStamps.end(),
|
m_currentSourceTimeStamps.end(),
|
||||||
filePathIds.begin(),
|
filePathIds.begin(),
|
||||||
filePathIds.end(),
|
filePathIds.end(),
|
||||||
std::back_inserter(timeStampsToUpdate));
|
make_iterator([&](SourceTimeStamp &sourceTimeStamp) {
|
||||||
|
sourceTimeStamp.timeStamp = m_fileSystem.lastModified(
|
||||||
|
sourceTimeStamp.sourceId);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
for (SourceTimeStamp &sourceTimeStamp : timeStampsToUpdate) {
|
void reset(const FilePathIds &filePathIds)
|
||||||
sourceTimeStamp.timeStamp = m_getModifiedTime(
|
{
|
||||||
m_filePathCache.filePath(sourceTimeStamp.sourceId));
|
FilePathIds newResetFilePathIds;
|
||||||
}
|
newResetFilePathIds.reserve(newResetFilePathIds.size() + m_resetFilePathIds.size());
|
||||||
|
|
||||||
|
std::set_union(m_resetFilePathIds.begin(),
|
||||||
|
m_resetFilePathIds.end(),
|
||||||
|
filePathIds.begin(),
|
||||||
|
filePathIds.end(),
|
||||||
|
std::back_inserter(newResetFilePathIds));
|
||||||
|
|
||||||
|
m_resetFilePathIds = std::move(newResetFilePathIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool compareEntries(const SourceEntries &sourceEntries) const
|
bool compareEntries(const SourceEntries &sourceEntries) const
|
||||||
{
|
{
|
||||||
class CompareSourceId
|
return set_intersection_compare(
|
||||||
{
|
m_currentSourceTimeStamps.begin(),
|
||||||
public:
|
m_currentSourceTimeStamps.end(),
|
||||||
bool operator()(SourceTimeStamp first, SourceTimeStamp second)
|
sourceEntries.begin(),
|
||||||
{
|
sourceEntries.end(),
|
||||||
return first.sourceId < second.sourceId;
|
[](auto first, auto second) { return second.timeStamp > first.timeStamp; },
|
||||||
}
|
[](auto first, auto second) { return first.sourceId < second.sourceId; });
|
||||||
|
|
||||||
bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second)
|
|
||||||
{
|
|
||||||
return first.sourceId < second.sourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second)
|
|
||||||
{
|
|
||||||
return first.sourceId < second.sourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second)
|
|
||||||
{
|
|
||||||
return first.sourceId < second.sourceId;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SourceTimeStamps currentSourceTimeStamp;
|
|
||||||
currentSourceTimeStamp.reserve(sourceEntries.size());
|
|
||||||
std::set_intersection(m_currentSourceTimeStamps.begin(),
|
|
||||||
m_currentSourceTimeStamps.end(),
|
|
||||||
sourceEntries.begin(),
|
|
||||||
sourceEntries.end(),
|
|
||||||
std::back_inserter(currentSourceTimeStamp),
|
|
||||||
CompareSourceId{});
|
|
||||||
|
|
||||||
class CompareTime
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool operator()(SourceTimeStamp first, SourceTimeStamp second)
|
|
||||||
{
|
|
||||||
return first.timeStamp <= second.timeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second)
|
|
||||||
{
|
|
||||||
return first.timeStamp <= second.timeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second)
|
|
||||||
{
|
|
||||||
return first.timeStamp <= second.timeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second)
|
|
||||||
{
|
|
||||||
return first.timeStamp <= second.timeStamp;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return std::lexicographical_compare(currentSourceTimeStamp.begin(),
|
|
||||||
currentSourceTimeStamp.end(),
|
|
||||||
sourceEntries.begin(),
|
|
||||||
sourceEntries.end(),
|
|
||||||
CompareTime{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateCurrentSourceTimeStamps(const SourceEntries &sourceEntries) const
|
void updateCurrentSourceTimeStamps(const SourceEntries &sourceEntries) const
|
||||||
{
|
{
|
||||||
SourceTimeStamps sourceTimeStamps = newSourceTimeStamps(sourceEntries);
|
SourceTimeStamps sourceTimeStamps = newSourceTimeStamps(sourceEntries);
|
||||||
|
|
||||||
for (SourceTimeStamp &newSourceTimeStamp : sourceTimeStamps) {
|
|
||||||
newSourceTimeStamp.timeStamp = m_getModifiedTime(
|
|
||||||
m_filePathCache.filePath(newSourceTimeStamp.sourceId));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto split = sourceTimeStamps.insert(sourceTimeStamps.end(),
|
auto split = sourceTimeStamps.insert(sourceTimeStamps.end(),
|
||||||
m_currentSourceTimeStamps.begin(),
|
m_currentSourceTimeStamps.begin(),
|
||||||
m_currentSourceTimeStamps.end());
|
m_currentSourceTimeStamps.end());
|
||||||
std::inplace_merge(sourceTimeStamps.begin(), split, sourceTimeStamps.end());
|
std::inplace_merge(sourceTimeStamps.begin(), split, sourceTimeStamps.end());
|
||||||
|
|
||||||
m_currentSourceTimeStamps = sourceTimeStamps;
|
m_currentSourceTimeStamps = std::move(sourceTimeStamps);
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceTimeStamps newSourceTimeStamps(const SourceEntries &sourceEntries) const
|
SourceTimeStamps newSourceTimeStamps(const SourceEntries &sourceEntries) const
|
||||||
{
|
{
|
||||||
SourceEntries newSourceEntries;
|
SourceTimeStamps newTimeStamps;
|
||||||
newSourceEntries.reserve(sourceEntries.size());
|
newTimeStamps.reserve(sourceEntries.size());
|
||||||
|
|
||||||
class CompareSourceId
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool operator()(SourceTimeStamp first, SourceTimeStamp second)
|
|
||||||
{
|
|
||||||
return first.sourceId < second.sourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second)
|
|
||||||
{
|
|
||||||
return first.sourceId < second.sourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second)
|
|
||||||
{
|
|
||||||
return first.sourceId < second.sourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second)
|
|
||||||
{
|
|
||||||
return first.sourceId < second.sourceId;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::set_difference(sourceEntries.begin(),
|
std::set_difference(sourceEntries.begin(),
|
||||||
sourceEntries.end(),
|
sourceEntries.end(),
|
||||||
m_currentSourceTimeStamps.begin(),
|
m_currentSourceTimeStamps.begin(),
|
||||||
m_currentSourceTimeStamps.end(),
|
m_currentSourceTimeStamps.end(),
|
||||||
std::back_inserter(newSourceEntries),
|
make_iterator([&](const SourceEntry &sourceEntry) {
|
||||||
CompareSourceId{});
|
newTimeStamps.emplace_back(sourceEntry.sourceId,
|
||||||
|
m_fileSystem.lastModified(
|
||||||
SourceTimeStamps newTimeStamps;
|
sourceEntry.sourceId));
|
||||||
newTimeStamps.reserve(newSourceEntries.size());
|
}),
|
||||||
|
[](auto first, auto second) {
|
||||||
std::transform(newSourceEntries.begin(),
|
return first.sourceId < second.sourceId && first.timeStamp > 0;
|
||||||
newSourceEntries.end(),
|
});
|
||||||
std::back_inserter(newTimeStamps),
|
|
||||||
[](SourceEntry entry) {
|
|
||||||
return SourceTimeStamp{entry.sourceId, {}};
|
|
||||||
});
|
|
||||||
|
|
||||||
return newTimeStamps;
|
return newTimeStamps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool notReseted(const SourceEntries &sourceEntries) const
|
||||||
|
{
|
||||||
|
auto oldSize = m_resetFilePathIds.size();
|
||||||
|
FilePathIds newResetFilePathIds;
|
||||||
|
newResetFilePathIds.reserve(newResetFilePathIds.size());
|
||||||
|
|
||||||
|
std::set_difference(m_resetFilePathIds.begin(),
|
||||||
|
m_resetFilePathIds.end(),
|
||||||
|
sourceEntries.begin(),
|
||||||
|
sourceEntries.end(),
|
||||||
|
std::back_inserter(newResetFilePathIds));
|
||||||
|
|
||||||
|
m_resetFilePathIds = std::move(newResetFilePathIds);
|
||||||
|
|
||||||
|
return oldSize == m_resetFilePathIds.size();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable SourceTimeStamps m_currentSourceTimeStamps;
|
mutable SourceTimeStamps m_currentSourceTimeStamps;
|
||||||
GetModifiedTime &m_getModifiedTime;
|
mutable FilePathIds m_resetFilePathIds;
|
||||||
FilePathCachingInterface &m_filePathCache;
|
FileSystemInterface &m_fileSystem;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ClangBackEnd
|
} // namespace ClangBackEnd
|
||||||
|
101
src/libs/clangsupport/set_algorithm.h
Normal file
101
src/libs/clangsupport/set_algorithm.h
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2019 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 <algorithm>
|
||||||
|
|
||||||
|
namespace ClangBackEnd {
|
||||||
|
|
||||||
|
template<class Callable>
|
||||||
|
class function_output_iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::output_iterator_tag iterator_category;
|
||||||
|
typedef void value_type;
|
||||||
|
typedef void difference_type;
|
||||||
|
typedef void pointer;
|
||||||
|
typedef void reference;
|
||||||
|
|
||||||
|
explicit function_output_iterator() {}
|
||||||
|
|
||||||
|
explicit function_output_iterator(const Callable &callable)
|
||||||
|
: m_callable(&callable)
|
||||||
|
{}
|
||||||
|
|
||||||
|
function_output_iterator &operator=(const function_output_iterator &iterator)
|
||||||
|
{
|
||||||
|
m_callable = iterator.m_callable;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct helper
|
||||||
|
{
|
||||||
|
helper(const Callable *callable)
|
||||||
|
: m_callable(callable)
|
||||||
|
{}
|
||||||
|
template<class T>
|
||||||
|
helper &operator=(T &&value)
|
||||||
|
{
|
||||||
|
(*m_callable)(std::forward<T>(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
const Callable *m_callable;
|
||||||
|
};
|
||||||
|
|
||||||
|
helper operator*() { return helper(m_callable); }
|
||||||
|
function_output_iterator &operator++() { return *this; }
|
||||||
|
function_output_iterator &operator++(int) { return *this; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Callable *m_callable;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Callable>
|
||||||
|
function_output_iterator<Callable> make_iterator(const Callable &callable)
|
||||||
|
{
|
||||||
|
return function_output_iterator<Callable>(callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class InputIt1, class InputIt2, class Callable, class Compare>
|
||||||
|
bool set_intersection_compare(
|
||||||
|
InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable call, Compare comp)
|
||||||
|
{
|
||||||
|
while (first1 != last1 && first2 != last2) {
|
||||||
|
if (comp(*first1, *first2)) {
|
||||||
|
++first1;
|
||||||
|
} else {
|
||||||
|
if (!comp(*first2, *first1)) {
|
||||||
|
if (call(*first2, *first1++))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++first2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace ClangBackEnd
|
@@ -131,6 +131,10 @@ public:
|
|||||||
return first.sourceId < second.sourceId;
|
return first.sourceId < second.sourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend bool operator<(SourceEntry first, FilePathId second) { return first.sourceId < second; }
|
||||||
|
|
||||||
|
friend bool operator<(FilePathId first, SourceEntry second) { return first < second.sourceId; }
|
||||||
|
|
||||||
friend bool operator==(SourceEntry first, SourceEntry second)
|
friend bool operator==(SourceEntry first, SourceEntry second)
|
||||||
{
|
{
|
||||||
return first.sourceId == second.sourceId && first.sourceType == second.sourceType
|
return first.sourceId == second.sourceId && first.sourceType == second.sourceType
|
||||||
|
@@ -214,12 +214,7 @@ struct Data // because we have a cycle dependency
|
|||||||
ClangBackEnd::BuildDependencyCollector buildDependencyCollector{filePathCache,
|
ClangBackEnd::BuildDependencyCollector buildDependencyCollector{filePathCache,
|
||||||
generatedFiles,
|
generatedFiles,
|
||||||
environment};
|
environment};
|
||||||
std::function<TimeStamp(FilePathView filePath)> getModifiedTime{
|
ClangBackEnd::ModifiedTimeChecker<ClangBackEnd::SourceEntries> modifiedTimeChecker{fileSystem};
|
||||||
[&](ClangBackEnd::FilePathView path) -> TimeStamp {
|
|
||||||
return QFileInfo(QString(path)).lastModified().toSecsSinceEpoch();
|
|
||||||
}};
|
|
||||||
ClangBackEnd::ModifiedTimeChecker<ClangBackEnd::SourceEntries> modifiedTimeChecker{getModifiedTime,
|
|
||||||
filePathCache};
|
|
||||||
ClangBackEnd::BuildDependenciesProvider buildDependencyProvider{buildDependencyStorage,
|
ClangBackEnd::BuildDependenciesProvider buildDependencyProvider{buildDependencyStorage,
|
||||||
modifiedTimeChecker,
|
modifiedTimeChecker,
|
||||||
buildDependencyCollector,
|
buildDependencyCollector,
|
||||||
|
@@ -148,12 +148,7 @@ private:
|
|||||||
FileStatusCache m_fileStatusCache{m_fileSytem};
|
FileStatusCache m_fileStatusCache{m_fileSytem};
|
||||||
SymbolsCollectorManager m_collectorManger;
|
SymbolsCollectorManager m_collectorManger;
|
||||||
ProgressCounter m_progressCounter;
|
ProgressCounter m_progressCounter;
|
||||||
std::function<TimeStamp(FilePathView filePath)> getModifiedTime{
|
ModifiedTimeChecker<ClangBackEnd::SourceTimeStamps> m_modifiedTimeChecker{m_fileSytem};
|
||||||
[&](ClangBackEnd::FilePathView path) -> TimeStamp {
|
|
||||||
return QFileInfo(QString(path)).lastModified().toSecsSinceEpoch();
|
|
||||||
}};
|
|
||||||
ModifiedTimeChecker<ClangBackEnd::SourceTimeStamps> m_modifiedTimeChecker{getModifiedTime,
|
|
||||||
m_filePathCache};
|
|
||||||
SymbolIndexer m_indexer;
|
SymbolIndexer m_indexer;
|
||||||
SymbolIndexerTaskQueue m_indexerQueue;
|
SymbolIndexerTaskQueue m_indexerQueue;
|
||||||
SymbolIndexerTaskScheduler m_indexerScheduler;
|
SymbolIndexerTaskScheduler m_indexerScheduler;
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "googletest.h"
|
#include "googletest.h"
|
||||||
|
#include "mockfilesystem.h"
|
||||||
|
|
||||||
#include <filepathcaching.h>
|
#include <filepathcaching.h>
|
||||||
#include <modifiedtimechecker.h>
|
#include <modifiedtimechecker.h>
|
||||||
@@ -39,29 +40,24 @@ using ClangBackEnd::FilePathView;
|
|||||||
class ModifiedTimeChecker : public testing::Test
|
class ModifiedTimeChecker : public testing::Test
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
ClangBackEnd::FilePathId id(const Utils::SmallStringView &path) const
|
|
||||||
{
|
|
||||||
return filePathCache.filePathId(ClangBackEnd::FilePathView{path});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ModifiedTimeChecker()
|
ModifiedTimeChecker()
|
||||||
{
|
{
|
||||||
ON_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path1")))).WillByDefault(Return(50));
|
ON_CALL(mockFileSystem, lastModified(Eq(1))).WillByDefault(Return(50));
|
||||||
ON_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path2")))).WillByDefault(Return(30));
|
ON_CALL(mockFileSystem, lastModified(Eq(2))).WillByDefault(Return(30));
|
||||||
|
ON_CALL(mockFileSystem, lastModified(Eq(3))).WillByDefault(Return(50));
|
||||||
|
ON_CALL(mockFileSystem, lastModified(Eq(4))).WillByDefault(Return(30));
|
||||||
}
|
}
|
||||||
NiceMock<MockFunction<ClangBackEnd::TimeStamp(ClangBackEnd::FilePathView filePath)>> getModifiedTimeCallback;
|
|
||||||
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
|
NiceMock<MockFileSystem> mockFileSystem;
|
||||||
ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database};
|
ClangBackEnd::ModifiedTimeChecker<> checker{mockFileSystem};
|
||||||
ClangBackEnd::FilePathCaching filePathCache{database};
|
SourceEntries upToDateEntries = {{1, SourceType::UserInclude, 100},
|
||||||
decltype(getModifiedTimeCallback.AsStdFunction()) callback = getModifiedTimeCallback
|
{2, SourceType::SystemInclude, 30},
|
||||||
.AsStdFunction();
|
{3, SourceType::UserInclude, 100},
|
||||||
ClangBackEnd::ModifiedTimeChecker<> checker{callback, filePathCache};
|
{4, SourceType::SystemInclude, 30}};
|
||||||
SourceEntries upToDateEntries = {{id("/path1"), SourceType::UserInclude, 100},
|
SourceEntries notUpToDateEntries = {{1, SourceType::UserInclude, 50},
|
||||||
{id("/path2"), SourceType::SystemInclude, 30}};
|
{2, SourceType::SystemInclude, 20},
|
||||||
SourceEntries notUpToDateEntries = {{id("/path1"), SourceType::UserInclude, 50},
|
{3, SourceType::UserInclude, 100},
|
||||||
{id("/path2"), SourceType::SystemInclude, 20}};
|
{4, SourceType::SystemInclude, 30}};
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ModifiedTimeChecker, IsUpToDate)
|
TEST_F(ModifiedTimeChecker, IsUpToDate)
|
||||||
@@ -71,6 +67,15 @@ TEST_F(ModifiedTimeChecker, IsUpToDate)
|
|||||||
ASSERT_TRUE(isUpToDate);
|
ASSERT_TRUE(isUpToDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ModifiedTimeChecker, IsUpToDateSecondRun)
|
||||||
|
{
|
||||||
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
|
||||||
|
auto isUpToDate = checker.isUpToDate(upToDateEntries);
|
||||||
|
|
||||||
|
ASSERT_TRUE(isUpToDate);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ModifiedTimeChecker, IsNotUpToDateIfSourceEntriesAreEmpty)
|
TEST_F(ModifiedTimeChecker, IsNotUpToDateIfSourceEntriesAreEmpty)
|
||||||
{
|
{
|
||||||
auto isUpToDate = checker.isUpToDate({});
|
auto isUpToDate = checker.isUpToDate({});
|
||||||
@@ -80,7 +85,16 @@ TEST_F(ModifiedTimeChecker, IsNotUpToDateIfSourceEntriesAreEmpty)
|
|||||||
|
|
||||||
TEST_F(ModifiedTimeChecker, IsNotUpToDate)
|
TEST_F(ModifiedTimeChecker, IsNotUpToDate)
|
||||||
{
|
{
|
||||||
auto isUpToDate = checker.isUpToDate({});
|
auto isUpToDate = checker.isUpToDate(notUpToDateEntries);
|
||||||
|
|
||||||
|
ASSERT_FALSE(isUpToDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ModifiedTimeChecker, IsNotUpToDateSecondRun)
|
||||||
|
{
|
||||||
|
checker.isUpToDate(notUpToDateEntries);
|
||||||
|
|
||||||
|
auto isUpToDate = checker.isUpToDate(notUpToDateEntries);
|
||||||
|
|
||||||
ASSERT_FALSE(isUpToDate);
|
ASSERT_FALSE(isUpToDate);
|
||||||
}
|
}
|
||||||
@@ -89,21 +103,80 @@ TEST_F(ModifiedTimeChecker, PathChangesUpdatesTimeStamps)
|
|||||||
{
|
{
|
||||||
checker.isUpToDate(upToDateEntries);
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
|
||||||
EXPECT_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path1"))));
|
EXPECT_CALL(mockFileSystem, lastModified(Eq(2)));
|
||||||
EXPECT_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path2"))));
|
EXPECT_CALL(mockFileSystem, lastModified(Eq(3)));
|
||||||
|
|
||||||
checker.pathsChanged({id(FilePathView("/path1")), id(FilePathView("/path2")), id(FilePathView("/path3"))});
|
checker.pathsChanged({2, 3});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ModifiedTimeChecker, IsNotUpToDateAnyMoreAfterUpdating)
|
TEST_F(ModifiedTimeChecker, IsNotUpToDateAnyMoreAfterUpdating)
|
||||||
{
|
{
|
||||||
checker.isUpToDate(upToDateEntries);
|
checker.isUpToDate(upToDateEntries);
|
||||||
ON_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path1")))).WillByDefault(Return(120));
|
ON_CALL(mockFileSystem, lastModified(Eq(1))).WillByDefault(Return(120));
|
||||||
ON_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path2")))).WillByDefault(Return(30));
|
ON_CALL(mockFileSystem, lastModified(Eq(2))).WillByDefault(Return(30));
|
||||||
|
|
||||||
checker.pathsChanged({id(FilePathView("/path1")), id(FilePathView("/path2")), id(FilePathView("/path3"))});
|
checker.pathsChanged({1, 2, 3});
|
||||||
|
|
||||||
ASSERT_FALSE(checker.isUpToDate(upToDateEntries));
|
ASSERT_FALSE(checker.isUpToDate(upToDateEntries));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ModifiedTimeChecker, Reset)
|
||||||
|
{
|
||||||
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
|
||||||
|
checker.reset({2, 3});
|
||||||
|
|
||||||
|
ASSERT_FALSE(checker.isUpToDate(upToDateEntries));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ModifiedTimeChecker, UpdateNonResetedId)
|
||||||
|
{
|
||||||
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
|
||||||
|
checker.reset({2, 3});
|
||||||
|
|
||||||
|
ASSERT_TRUE(checker.isUpToDate({upToDateEntries[0]}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ModifiedTimeChecker, ResetTwoTimes)
|
||||||
|
{
|
||||||
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
checker.reset({2, 3});
|
||||||
|
|
||||||
|
checker.reset({2, 3});
|
||||||
|
|
||||||
|
ASSERT_FALSE(checker.isUpToDate(upToDateEntries));
|
||||||
|
ASSERT_TRUE(checker.isUpToDate(upToDateEntries));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ModifiedTimeChecker, ResetSecondUpdate)
|
||||||
|
{
|
||||||
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
checker.reset({2, 3});
|
||||||
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
|
||||||
|
auto isUpToDate = checker.isUpToDate(upToDateEntries);
|
||||||
|
|
||||||
|
ASSERT_TRUE(isUpToDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ModifiedTimeChecker, ResetPartialUpdate)
|
||||||
|
{
|
||||||
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
checker.reset({2, 3});
|
||||||
|
checker.isUpToDate({upToDateEntries[1]});
|
||||||
|
|
||||||
|
ASSERT_FALSE(checker.isUpToDate({upToDateEntries[2]}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ModifiedTimeChecker, ResetMoreIds)
|
||||||
|
{
|
||||||
|
checker.isUpToDate(upToDateEntries);
|
||||||
|
checker.reset({2, 3});
|
||||||
|
|
||||||
|
checker.reset({1, 5});
|
||||||
|
|
||||||
|
ASSERT_FALSE(checker.isUpToDate({upToDateEntries[2]}));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Reference in New Issue
Block a user