forked from qt-creator/qt-creator
Vcs: Hide topic cache in new IVersionControl pimpl
Potentially more stable api long-term. Also simplifies the user side code a bit. Change-Id: I6913e27e2a5dc14907e72f252081cdbed34842a3 Reviewed-by: Orgad Shaneh <orgads@gmail.com> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -14,44 +14,40 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
|
||||
/*!
|
||||
\class Core::IVersionControl::TopicCache
|
||||
\inheaderfile coreplugin/iversioncontrol.h
|
||||
\inmodule QtCreator
|
||||
|
||||
\brief The TopicCache class stores a cache which maps a directory to a topic.
|
||||
|
||||
A VCS topic is typically the current active branch name, but it can also have other
|
||||
values (for example the latest tag) when there is no active branch.
|
||||
|
||||
It is displayed:
|
||||
\list
|
||||
\li In the project tree, next to each root project - corresponding to the project.
|
||||
\li In the main window title - corresponding to the current active editor.
|
||||
\endlist
|
||||
|
||||
In order to enable topic display, an IVersionControl subclass needs to create
|
||||
an instance of the TopicCache subclass with appropriate overrides for its
|
||||
pure virtual functions, and pass this instance to IVersionControl's constructor.
|
||||
|
||||
The cache tracks a file in the repository, which is expected to change when the
|
||||
topic changes. When the file is modified, the cache is refreshed.
|
||||
For example: for Git this file is typically <repository>/.git/HEAD
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn Utils::FilePath Core::IVersionControl::TopicCache::trackFile(const Utils::FilePath &repository)
|
||||
Returns the path to the file that invalidates the cache for \a repository when
|
||||
the file is modified.
|
||||
|
||||
\fn QString Core::IVersionControl::TopicCache::refreshTopic(const Utils::FilePath &repository)
|
||||
Returns the current topic for \a repository.
|
||||
*/
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class TopicData
|
||||
{
|
||||
public:
|
||||
QDateTime timeStamp;
|
||||
QString topic;
|
||||
};
|
||||
|
||||
class IVersionControlPrivate
|
||||
{
|
||||
public:
|
||||
IVersionControl::FileTracker m_fileTracker;
|
||||
IVersionControl::TopicRefresher m_topicRefresher;
|
||||
QHash<FilePath, TopicData> m_topicCache;
|
||||
};
|
||||
|
||||
} // Internal
|
||||
|
||||
IVersionControl::IVersionControl()
|
||||
: d(new Internal::IVersionControlPrivate)
|
||||
{
|
||||
Core::VcsManager::addVersionControl(this);
|
||||
}
|
||||
|
||||
IVersionControl::~IVersionControl()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
QString IVersionControl::vcsOpenText() const
|
||||
{
|
||||
return Tr::tr("Open with VCS (%1)").arg(displayName());
|
||||
@@ -111,24 +107,83 @@ IVersionControl::RepoUrl IVersionControl::getRepoUrl(const QString &location) co
|
||||
return RepoUrl(location);
|
||||
}
|
||||
|
||||
void IVersionControl::setTopicCache(TopicCache *topicCache)
|
||||
FilePath IVersionControl::trackFile(const FilePath &repository)
|
||||
{
|
||||
m_topicCache = topicCache;
|
||||
QTC_ASSERT(d->m_fileTracker, return {});
|
||||
return d->m_fileTracker(repository);
|
||||
}
|
||||
|
||||
QString IVersionControl::refreshTopic(const FilePath &repository)
|
||||
{
|
||||
QTC_ASSERT(d->m_topicRefresher, return {});
|
||||
return d->m_topicRefresher(repository);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the topic for repository under \a topLevel.
|
||||
|
||||
A VCS topic is typically the current active branch name, but it can also have other
|
||||
values (for example the latest tag) when there is no active branch.
|
||||
|
||||
It is displayed:
|
||||
\list
|
||||
\li In the project tree, next to each root project - corresponding to the project.
|
||||
\li In the main window title - corresponding to the current active editor.
|
||||
\endlist
|
||||
|
||||
In order to enable topic display, an IVersionControl subclass needs to create
|
||||
an instance of the TopicCache subclass with appropriate overrides for its
|
||||
pure virtual functions, and pass this instance to IVersionControl's constructor.
|
||||
|
||||
The cache tracks a file in the repository, which is expected to change when the
|
||||
topic changes. When the file is modified, the cache is refreshed.
|
||||
For example: for Git this file is typically <repository>/.git/HEAD
|
||||
|
||||
The base implementation features a cache. If the cache for \a topLevel is valid,
|
||||
it will be used. Otherwise it will be refreshed using the items provided by
|
||||
\c setTopicFileTracker() and \c setTopicRefresher().
|
||||
|
||||
\sa setTopicFileTracker(), setTopicRefresher().
|
||||
*/
|
||||
|
||||
QString IVersionControl::vcsTopic(const FilePath &topLevel)
|
||||
{
|
||||
return m_topicCache ? m_topicCache->topic(topLevel) : QString();
|
||||
QTC_ASSERT(!topLevel.isEmpty(), return QString());
|
||||
Internal::TopicData &data = d->m_topicCache[topLevel];
|
||||
const FilePath file = trackFile(topLevel);
|
||||
|
||||
if (file.isEmpty())
|
||||
return QString();
|
||||
const QDateTime lastModified = file.lastModified();
|
||||
if (lastModified == data.timeStamp)
|
||||
return data.topic;
|
||||
data.timeStamp = lastModified;
|
||||
return data.topic = refreshTopic(topLevel);
|
||||
}
|
||||
|
||||
IVersionControl::IVersionControl()
|
||||
/*!
|
||||
Provides the \a fileTracker function object for use in \c vscTopic() cache handling.
|
||||
|
||||
The parameter object takes a repository as input and returns the file
|
||||
that should trigger topic refresh (e.g. .git/HEAD for Git).
|
||||
|
||||
Modification of this file will invalidate the internal topic cache for the repository.
|
||||
*/
|
||||
|
||||
void IVersionControl::setTopicFileTracker(const FileTracker &fileTracker)
|
||||
{
|
||||
Core::VcsManager::addVersionControl(this);
|
||||
d->m_fileTracker = fileTracker;
|
||||
}
|
||||
|
||||
IVersionControl::~IVersionControl()
|
||||
/*!
|
||||
Provides the \a topicRefresher function object for use in \c vscTopic() cache handling.
|
||||
|
||||
The parameter object takes a repository as input and returns its current topic.
|
||||
*/
|
||||
|
||||
void IVersionControl::setTopicRefresher(const TopicRefresher &topicRefresher)
|
||||
{
|
||||
delete m_topicCache;
|
||||
d->m_topicRefresher = topicRefresher;
|
||||
}
|
||||
|
||||
FilePaths IVersionControl::unmanagedFiles(const FilePaths &filePaths) const
|
||||
@@ -143,29 +198,6 @@ IVersionControl::OpenSupportMode IVersionControl::openSupportMode(const FilePath
|
||||
Q_UNUSED(filePath)
|
||||
return NoOpen;
|
||||
}
|
||||
|
||||
IVersionControl::TopicCache::~TopicCache() = default;
|
||||
|
||||
/*!
|
||||
Returns the topic for repository under \a topLevel.
|
||||
|
||||
If the cache for \a topLevel is valid, it will be used. Otherwise it will be refreshed.
|
||||
*/
|
||||
QString IVersionControl::TopicCache::topic(const FilePath &topLevel)
|
||||
{
|
||||
QTC_ASSERT(!topLevel.isEmpty(), return QString());
|
||||
TopicData &data = m_cache[topLevel];
|
||||
const FilePath file = trackFile(topLevel);
|
||||
|
||||
if (file.isEmpty())
|
||||
return QString();
|
||||
const QDateTime lastModified = file.lastModified();
|
||||
if (lastModified == data.timeStamp)
|
||||
return data.topic;
|
||||
data.timeStamp = lastModified;
|
||||
return data.topic = refreshTopic(topLevel);
|
||||
}
|
||||
|
||||
void IVersionControl::fillLinkContextMenu(QMenu *, const FilePath &, const QString &)
|
||||
{
|
||||
}
|
||||
|
@@ -8,16 +8,15 @@
|
||||
#include <utils/id.h>
|
||||
#include <utils/filepath.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFlags>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QMenu);
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace Internal { class IVersionControlPrivate; }
|
||||
|
||||
class CORE_EXPORT IVersionControl : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -43,28 +42,6 @@ public:
|
||||
OpenMandatory /*!< Files must always be opened by the VCS */
|
||||
};
|
||||
|
||||
class CORE_EXPORT TopicCache
|
||||
{
|
||||
public:
|
||||
virtual ~TopicCache();
|
||||
QString topic(const Utils::FilePath &topLevel);
|
||||
|
||||
protected:
|
||||
virtual Utils::FilePath trackFile(const Utils::FilePath &repository) = 0;
|
||||
virtual QString refreshTopic(const Utils::FilePath &repository) = 0;
|
||||
|
||||
private:
|
||||
class TopicData
|
||||
{
|
||||
public:
|
||||
QDateTime timeStamp;
|
||||
QString topic;
|
||||
};
|
||||
|
||||
QHash<Utils::FilePath, TopicData> m_cache;
|
||||
|
||||
};
|
||||
|
||||
IVersionControl();
|
||||
~IVersionControl() override;
|
||||
|
||||
@@ -218,7 +195,14 @@ public:
|
||||
};
|
||||
virtual RepoUrl getRepoUrl(const QString &location) const;
|
||||
|
||||
void setTopicCache(TopicCache *topicCache);
|
||||
// Topic cache
|
||||
using FileTracker = std::function<Utils::FilePath(const Utils::FilePath &)>;
|
||||
Utils::FilePath trackFile(const Utils::FilePath &repository);
|
||||
void setTopicFileTracker(const FileTracker &fileTracker);
|
||||
|
||||
using TopicRefresher = std::function<QString(const Utils::FilePath &)>;
|
||||
QString refreshTopic(const Utils::FilePath &repository);
|
||||
void setTopicRefresher(const TopicRefresher &topicRefresher);
|
||||
|
||||
signals:
|
||||
void repositoryChanged(const Utils::FilePath &repository);
|
||||
@@ -226,7 +210,7 @@ signals:
|
||||
void configurationChanged();
|
||||
|
||||
private:
|
||||
TopicCache *m_topicCache = nullptr;
|
||||
Internal::IVersionControlPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
@@ -61,23 +61,6 @@ using namespace std::placeholders;
|
||||
|
||||
namespace Fossil::Internal {
|
||||
|
||||
class FossilTopicCache final : public IVersionControl::TopicCache
|
||||
{
|
||||
public:
|
||||
FossilTopicCache() = default;
|
||||
|
||||
protected:
|
||||
FilePath trackFile(const FilePath &repository) final
|
||||
{
|
||||
return repository.pathAppended(Constants::FOSSILREPO);
|
||||
}
|
||||
|
||||
QString refreshTopic(const FilePath &repository) final
|
||||
{
|
||||
return fossilClient().synchronousTopic(repository);
|
||||
}
|
||||
};
|
||||
|
||||
class FossilPluginPrivate final : public VersionControlBase
|
||||
{
|
||||
public:
|
||||
@@ -222,7 +205,13 @@ FossilPluginPrivate::FossilPluginPrivate()
|
||||
{
|
||||
Context context(Constants::FOSSIL_CONTEXT);
|
||||
|
||||
setTopicCache(new FossilTopicCache);
|
||||
setTopicFileTracker([](const FilePath &repository) {
|
||||
return repository.pathAppended(Constants::FOSSILREPO);
|
||||
});
|
||||
setTopicRefresher([](const FilePath &repository) {
|
||||
return fossilClient().synchronousTopic(repository);
|
||||
});
|
||||
|
||||
connect(&fossilClient(), &VcsBaseClient::changed, this, &FossilPluginPrivate::changed);
|
||||
|
||||
m_commandLocator = new CommandLocator("Fossil", "fossil", "fossil", this);
|
||||
|
@@ -382,25 +382,6 @@ public:
|
||||
|
||||
static GitPluginPrivate *dd = nullptr;
|
||||
|
||||
class GitTopicCache : public IVersionControl::TopicCache
|
||||
{
|
||||
public:
|
||||
GitTopicCache() {}
|
||||
|
||||
protected:
|
||||
FilePath trackFile(const FilePath &repository) override
|
||||
{
|
||||
const FilePath gitDir = gitClient().findGitDirForRepository(repository);
|
||||
return gitDir.isEmpty() ? FilePath() : gitDir / "HEAD";
|
||||
}
|
||||
|
||||
QString refreshTopic(const FilePath &repository) override
|
||||
{
|
||||
emit dd->repositoryChanged(repository);
|
||||
return gitClient().synchronousTopic(repository);
|
||||
}
|
||||
};
|
||||
|
||||
GitPluginPrivate::~GitPluginPrivate()
|
||||
{
|
||||
cleanCommitMessageFile();
|
||||
@@ -560,7 +541,14 @@ GitPluginPrivate::GitPluginPrivate()
|
||||
{
|
||||
dd = this;
|
||||
|
||||
setTopicCache(new GitTopicCache);
|
||||
setTopicFileTracker([](const FilePath &repository) {
|
||||
const FilePath gitDir = gitClient().findGitDirForRepository(repository);
|
||||
return gitDir.isEmpty() ? FilePath() : gitDir / "HEAD";
|
||||
});
|
||||
setTopicRefresher([this](const FilePath &repository) {
|
||||
emit repositoryChanged(repository);
|
||||
return gitClient().synchronousTopic(repository);
|
||||
});
|
||||
|
||||
m_fileActions.reserve(10);
|
||||
m_projectActions.reserve(10);
|
||||
|
@@ -51,23 +51,6 @@ using namespace std::placeholders;
|
||||
|
||||
namespace Mercurial::Internal {
|
||||
|
||||
class MercurialTopicCache : public Core::IVersionControl::TopicCache
|
||||
{
|
||||
public:
|
||||
MercurialTopicCache() = default;
|
||||
|
||||
protected:
|
||||
FilePath trackFile(const FilePath &repository) override
|
||||
{
|
||||
return repository.pathAppended(".hg/branch");
|
||||
}
|
||||
|
||||
QString refreshTopic(const FilePath &repository) override
|
||||
{
|
||||
return mercurialClient().branchQuerySync(repository.toString());
|
||||
}
|
||||
};
|
||||
|
||||
class MercurialPluginPrivate final : public VcsBase::VersionControlBase
|
||||
{
|
||||
public:
|
||||
@@ -203,7 +186,12 @@ MercurialPluginPrivate::MercurialPluginPrivate()
|
||||
[] { return new CommitEditor; }
|
||||
});
|
||||
|
||||
setTopicCache(new MercurialTopicCache);
|
||||
setTopicFileTracker([](const FilePath &repository) {
|
||||
return repository.pathAppended(".hg/branch");
|
||||
});
|
||||
setTopicRefresher([](const FilePath &repository) {
|
||||
return mercurialClient().branchQuerySync(repository.toString());
|
||||
});
|
||||
|
||||
Core::Context context(Constants::MERCURIAL_CONTEXT);
|
||||
|
||||
|
@@ -126,24 +126,6 @@ static inline QStringList svnDirectories()
|
||||
return rc;
|
||||
}
|
||||
|
||||
class SubversionPluginPrivate;
|
||||
|
||||
class SubversionTopicCache : public Core::IVersionControl::TopicCache
|
||||
{
|
||||
public:
|
||||
SubversionTopicCache(SubversionPluginPrivate *plugin) :
|
||||
m_plugin(plugin)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
FilePath trackFile(const FilePath &repository) override;
|
||||
|
||||
QString refreshTopic(const FilePath &repository) override;
|
||||
|
||||
private:
|
||||
SubversionPluginPrivate *m_plugin;
|
||||
};
|
||||
|
||||
class SubversionPluginPrivate final : public VcsBase::VersionControlBase
|
||||
{
|
||||
public:
|
||||
@@ -312,7 +294,12 @@ SubversionPluginPrivate::SubversionPluginPrivate()
|
||||
{
|
||||
dd = this;
|
||||
|
||||
setTopicCache(new SubversionTopicCache(this));
|
||||
setTopicFileTracker([this](const FilePath &repository) {
|
||||
return FilePath::fromString(monitorFile(repository));
|
||||
});
|
||||
setTopicRefresher([this](const FilePath &repository) {
|
||||
return synchronousTopic(repository);
|
||||
});
|
||||
|
||||
using namespace Constants;
|
||||
using namespace Core::Constants;
|
||||
@@ -1153,16 +1140,6 @@ VcsCommand *SubversionPluginPrivate::createInitialCheckoutCommand(const QString
|
||||
return command;
|
||||
}
|
||||
|
||||
FilePath SubversionTopicCache::trackFile(const FilePath &repository)
|
||||
{
|
||||
return FilePath::fromString(m_plugin->monitorFile(repository));
|
||||
}
|
||||
|
||||
QString SubversionTopicCache::refreshTopic(const FilePath &repository)
|
||||
{
|
||||
return m_plugin->synchronousTopic(repository);
|
||||
}
|
||||
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
|
||||
|
Reference in New Issue
Block a user