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 <QRegularExpression>
|
||||||
#include <QStringList>
|
#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;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Core {
|
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
|
QString IVersionControl::vcsOpenText() const
|
||||||
{
|
{
|
||||||
return Tr::tr("Open with VCS (%1)").arg(displayName());
|
return Tr::tr("Open with VCS (%1)").arg(displayName());
|
||||||
@@ -111,24 +107,83 @@ IVersionControl::RepoUrl IVersionControl::getRepoUrl(const QString &location) co
|
|||||||
return RepoUrl(location);
|
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)
|
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
|
FilePaths IVersionControl::unmanagedFiles(const FilePaths &filePaths) const
|
||||||
@@ -143,29 +198,6 @@ IVersionControl::OpenSupportMode IVersionControl::openSupportMode(const FilePath
|
|||||||
Q_UNUSED(filePath)
|
Q_UNUSED(filePath)
|
||||||
return NoOpen;
|
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 &)
|
void IVersionControl::fillLinkContextMenu(QMenu *, const FilePath &, const QString &)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@@ -8,16 +8,15 @@
|
|||||||
#include <utils/id.h>
|
#include <utils/id.h>
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
#include <QHash>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
QT_FORWARD_DECLARE_CLASS(QMenu);
|
QT_FORWARD_DECLARE_CLASS(QMenu);
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
|
namespace Internal { class IVersionControlPrivate; }
|
||||||
|
|
||||||
class CORE_EXPORT IVersionControl : public QObject
|
class CORE_EXPORT IVersionControl : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -43,28 +42,6 @@ public:
|
|||||||
OpenMandatory /*!< Files must always be opened by the VCS */
|
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();
|
||||||
~IVersionControl() override;
|
~IVersionControl() override;
|
||||||
|
|
||||||
@@ -218,7 +195,14 @@ public:
|
|||||||
};
|
};
|
||||||
virtual RepoUrl getRepoUrl(const QString &location) const;
|
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:
|
signals:
|
||||||
void repositoryChanged(const Utils::FilePath &repository);
|
void repositoryChanged(const Utils::FilePath &repository);
|
||||||
@@ -226,7 +210,7 @@ signals:
|
|||||||
void configurationChanged();
|
void configurationChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TopicCache *m_topicCache = nullptr;
|
Internal::IVersionControlPrivate *d;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
@@ -61,23 +61,6 @@ using namespace std::placeholders;
|
|||||||
|
|
||||||
namespace Fossil::Internal {
|
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
|
class FossilPluginPrivate final : public VersionControlBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -222,7 +205,13 @@ FossilPluginPrivate::FossilPluginPrivate()
|
|||||||
{
|
{
|
||||||
Context context(Constants::FOSSIL_CONTEXT);
|
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);
|
connect(&fossilClient(), &VcsBaseClient::changed, this, &FossilPluginPrivate::changed);
|
||||||
|
|
||||||
m_commandLocator = new CommandLocator("Fossil", "fossil", "fossil", this);
|
m_commandLocator = new CommandLocator("Fossil", "fossil", "fossil", this);
|
||||||
|
@@ -382,25 +382,6 @@ public:
|
|||||||
|
|
||||||
static GitPluginPrivate *dd = nullptr;
|
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()
|
GitPluginPrivate::~GitPluginPrivate()
|
||||||
{
|
{
|
||||||
cleanCommitMessageFile();
|
cleanCommitMessageFile();
|
||||||
@@ -560,7 +541,14 @@ GitPluginPrivate::GitPluginPrivate()
|
|||||||
{
|
{
|
||||||
dd = this;
|
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_fileActions.reserve(10);
|
||||||
m_projectActions.reserve(10);
|
m_projectActions.reserve(10);
|
||||||
|
@@ -51,23 +51,6 @@ using namespace std::placeholders;
|
|||||||
|
|
||||||
namespace Mercurial::Internal {
|
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
|
class MercurialPluginPrivate final : public VcsBase::VersionControlBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -203,7 +186,12 @@ MercurialPluginPrivate::MercurialPluginPrivate()
|
|||||||
[] { return new CommitEditor; }
|
[] { 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);
|
Core::Context context(Constants::MERCURIAL_CONTEXT);
|
||||||
|
|
||||||
|
@@ -126,24 +126,6 @@ static inline QStringList svnDirectories()
|
|||||||
return rc;
|
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
|
class SubversionPluginPrivate final : public VcsBase::VersionControlBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -312,7 +294,12 @@ SubversionPluginPrivate::SubversionPluginPrivate()
|
|||||||
{
|
{
|
||||||
dd = this;
|
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 Constants;
|
||||||
using namespace Core::Constants;
|
using namespace Core::Constants;
|
||||||
@@ -1153,16 +1140,6 @@ VcsCommand *SubversionPluginPrivate::createInitialCheckoutCommand(const QString
|
|||||||
return command;
|
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
|
#ifdef WITH_TESTS
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user