diff --git a/src/plugins/vcsbase/vcsbase.pro b/src/plugins/vcsbase/vcsbase.pro index 91b4e110136..5f4eae91c02 100644 --- a/src/plugins/vcsbase/vcsbase.pro +++ b/src/plugins/vcsbase/vcsbase.pro @@ -2,6 +2,7 @@ DEFINES += VCSBASE_LIBRARY include(../../qtcreatorplugin.pri) HEADERS += vcsbase_global.h \ vcsbaseconstants.h \ + vcsprojectcache.h \ wizard/vcsconfigurationpage.h \ wizard/vcsjsextension.h \ vcsplugin.h \ @@ -34,6 +35,7 @@ HEADERS += vcsbase_global.h \ SOURCES += vcsplugin.cpp \ vcsbaseplugin.cpp \ + vcsprojectcache.cpp \ wizard/vcsconfigurationpage.cpp \ wizard/vcsjsextension.cpp \ corelistener.cpp \ diff --git a/src/plugins/vcsbase/vcsbase.qbs b/src/plugins/vcsbase/vcsbase.qbs index 36072b9e25b..111ec889628 100644 --- a/src/plugins/vcsbase/vcsbase.qbs +++ b/src/plugins/vcsbase/vcsbase.qbs @@ -76,6 +76,8 @@ QtcPlugin { "vcsoutputwindow.h", "vcsplugin.cpp", "vcsplugin.h", + "vcsprojectcache.cpp", + "vcsprojectcache.h", "images/diff.png", "images/removesubmitfield.png", "images/submit.png", diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index c6cc51479d5..cc4b7eea417 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -38,6 +38,7 @@ #include "submitfilemodel.h" #include "vcsoutputwindow.h" #include "vcsplugin.h" +#include "vcsprojectcache.h" #include #include @@ -52,14 +53,13 @@ #include #include -#include #include -#include #include -#include #include #include +#include +#include #include #include #include @@ -144,8 +144,9 @@ static inline QString submitMessageCheckScript() return VcsPlugin::instance()->settings().submitMessageCheckScript; } -struct VcsBaseSubmitEditorPrivate +class VcsBaseSubmitEditorPrivate { +public: VcsBaseSubmitEditorPrivate(const VcsBaseSubmitEditorParameters *parameters, SubmitEditorWidget *editorWidget, VcsBaseSubmitEditor *q); @@ -747,39 +748,27 @@ QIcon VcsBaseSubmitEditor::submitIcon() return QIcon(QLatin1String(":/vcsbase/images/submit.png")); } -// Compile a list if files in the current projects. TODO: Recurse down qrc files? -QStringList VcsBaseSubmitEditor::currentProjectFiles(bool nativeSeparators, QString *name) -{ - if (name) - name->clear(); - - if (const ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectTree::currentProject()) { - QStringList files = currentProject->files(ProjectExplorer::Project::ExcludeGeneratedFiles); - if (name) - *name = currentProject->displayName(); - if (nativeSeparators && !files.empty()) { - const QStringList::iterator end = files.end(); - for (QStringList::iterator it = files.begin(); it != end; ++it) - *it = QDir::toNativeSeparators(*it); - } - return files; - } - return QStringList(); -} - // Reduce a list of untracked files reported by a VCS down to the files // that are actually part of the current project(s). -void VcsBaseSubmitEditor::filterUntrackedFilesOfProject(const QString &repositoryDirectory, QStringList *untrackedFiles) +void VcsBaseSubmitEditor::filterUntrackedFilesOfProject(const QString &repositoryDirectory, + QStringList *untrackedFiles) { if (untrackedFiles->empty()) return; - const QStringList nativeProjectFiles = VcsBaseSubmitEditor::currentProjectFiles(true); - if (nativeProjectFiles.empty()) + + ProjectExplorer::Project *vcsProject = VcsProjectCache::projectFor(repositoryDirectory); + if (!vcsProject) + return; + + const QSet projectFiles + = QSet::fromList(vcsProject->files(ProjectExplorer::Project::ExcludeGeneratedFiles)); + + if (projectFiles.empty()) return; const QDir repoDir(repositoryDirectory); for (QStringList::iterator it = untrackedFiles->begin(); it != untrackedFiles->end(); ) { - const QString path = QDir::toNativeSeparators(repoDir.absoluteFilePath(*it)); - if (nativeProjectFiles.contains(path)) + const QString path = repoDir.absoluteFilePath(*it); + if (projectFiles.contains(path)) ++it; else it = untrackedFiles->erase(it); diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h index dfd92bf0f5f..0f7b50bf066 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.h +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h @@ -42,14 +42,16 @@ class QIcon; class QAction; QT_END_NAMESPACE +namespace ProjectExplorer { class Project; } + namespace VcsBase { namespace Internal { class CommonVcsSettings; class SubmitEditorFile; -} -struct VcsBaseSubmitEditorPrivate; +} // namespace Internal class SubmitEditorWidget; class SubmitFileModel; +class VcsBaseSubmitEditorPrivate; class VCSBASE_EXPORT VcsBaseSubmitEditorParameters { @@ -128,10 +130,6 @@ public: static QIcon diffIcon(); static QIcon submitIcon(); - // Utility returning all project files in case submit lists need to - // be restricted to them - static QStringList currentProjectFiles(bool nativeSeparators, QString *name = 0); - // Reduce a list of untracked files reported by a VCS down to the files // that are actually part of the current project(s). static void filterUntrackedFilesOfProject(const QString &repositoryDirectory, QStringList *untrackedFiles); @@ -168,6 +166,7 @@ private: QString promptForNickName(); VcsBaseSubmitEditorPrivate *d; + friend class Internal::SubmitEditorFile; // for the file contents }; diff --git a/src/plugins/vcsbase/vcsplugin.cpp b/src/plugins/vcsbase/vcsplugin.cpp index a70794ad413..433835d8223 100644 --- a/src/plugins/vcsbase/vcsplugin.cpp +++ b/src/plugins/vcsbase/vcsplugin.cpp @@ -35,6 +35,7 @@ #include "commonsettingspage.h" #include "nicknamedialog.h" #include "vcsoutputwindow.h" +#include "vcsprojectcache.h" #include "corelistener.h" #include "wizard/vcsconfigurationpage.h" #include "wizard/vcsjsextension.h" @@ -70,6 +71,7 @@ VcsPlugin::VcsPlugin() : VcsPlugin::~VcsPlugin() { + VcsProjectCache::destroy(); m_instance = 0; } @@ -127,6 +129,7 @@ bool VcsPlugin::initialize(const QStringList &arguments, QString *errorMessage) void VcsPlugin::extensionsInitialized() { + VcsProjectCache::create(); } VcsPlugin *VcsPlugin::instance() diff --git a/src/plugins/vcsbase/vcsprojectcache.cpp b/src/plugins/vcsbase/vcsprojectcache.cpp new file mode 100644 index 00000000000..5fd620b6f6e --- /dev/null +++ b/src/plugins/vcsbase/vcsprojectcache.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "vcsprojectcache.h" + +#include "vcsbasesubmiteditor.h" + +#include +#include + +#include +#include + +#include + +namespace { + +class PathMatcher +{ +public: + PathMatcher() : m_count(std::numeric_limits::max()), m_project(0) { } + ProjectExplorer::Project *project() { return m_project; } + + void match(ProjectExplorer::Project *project, + const Utils::FileName &base, const Utils::FileName &child) { + int count = std::numeric_limits::max(); + if (child.isChildOf(base)) { + const QString relative = child.toString().mid(base.count() + 1); + count = relative.count(QLatin1Char('/')); + } + if (count < m_count) { + m_count = count; + m_project = project; + } + } + +private: + int m_count; + ProjectExplorer::Project *m_project; +}; + +} // namespace + +namespace VcsBase { +namespace Internal { + +VcsProjectCache *VcsProjectCache::m_instance = 0; + +VcsProjectCache::VcsProjectCache() +{ + QTC_ASSERT(!m_instance, return); + m_instance = this; + + connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::projectAdded, + this, []() { VcsProjectCache::invalidate(); }); + connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::projectRemoved, + this, []() { VcsProjectCache::invalidate(); }); +} + +VcsProjectCache::~VcsProjectCache() +{ + m_instance = 0; +} + +ProjectExplorer::Project *VcsProjectCache::projectFor(const QString &repo) +{ + ProjectExplorer::Project *project; + + const int pos = Utils::indexOf(m_instance->m_cache, + [repo](const CacheNode &n) { return n.repository == repo; }); + if (pos >= 0) { + if (pos > 0) { + m_instance->m_cache.prepend(m_instance->m_cache.at(pos)); + m_instance->m_cache.removeAt(pos + 1); + } + return m_instance->m_cache.at(0).project; + } + + project = projectForToplevel(Utils::FileName::fromString(repo)); + m_instance->m_cache.prepend(CacheNode(repo, project)); + while (m_instance->m_cache.count() > 10) + m_instance->m_cache.removeLast(); + + return project; +} + +void VcsProjectCache::invalidate() +{ + m_instance->m_cache.clear(); +} + +void VcsProjectCache::create() +{ + new VcsProjectCache; +} + +void VcsProjectCache::destroy() +{ + delete m_instance; +} + +ProjectExplorer::Project *VcsProjectCache::projectForToplevel(const Utils::FileName &vcsTopLevel) +{ + PathMatcher parentMatcher; + PathMatcher childMatcher; + foreach (ProjectExplorer::Project *project, ProjectExplorer::SessionManager::projects()) { + const Utils::FileName projectDir = project->projectDirectory(); + if (projectDir == vcsTopLevel) + return project; + parentMatcher.match(project, vcsTopLevel, projectDir); + childMatcher.match(project, projectDir, vcsTopLevel); + } + + if (parentMatcher.project()) + return parentMatcher.project(); + + return childMatcher.project(); +} + +} // Internal +} // namespace VcsBase diff --git a/src/plugins/vcsbase/vcsprojectcache.h b/src/plugins/vcsbase/vcsprojectcache.h new file mode 100644 index 00000000000..81ae7134ba5 --- /dev/null +++ b/src/plugins/vcsbase/vcsprojectcache.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VCSBASE_PROJECTCACHE_H +#define VCSBASE_PROJECTCACHE_H + +#include + +#include +#include + +namespace ProjectExplorer { class Project; } + +namespace VcsBase { +namespace Internal { + +class VcsPlugin; + +class VcsProjectCache : public QObject { +public: + static ProjectExplorer::Project *projectFor(const QString &repo); + +private: + VcsProjectCache(); + ~VcsProjectCache(); + + static void invalidate(); + static ProjectExplorer::Project *projectForToplevel(const Utils::FileName &vcsTopLevel); + + static void create(); + static void destroy(); + + class CacheNode { + public: + CacheNode(const QString &r, ProjectExplorer::Project *p) : repository(r), project(p) + { } + + QString repository; + ProjectExplorer::Project *project; + }; + QList m_cache; + + static VcsProjectCache *m_instance; + + friend class VcsPlugin; +}; + +} // namespace Internal +} // namespace VcsBase + +#endif // VCSBASE_PROJECTCACHE_H