Changeset View
Changeset View
Standalone View
Standalone View
plugins/cgit/projectvcstracker.cpp
- This file was added.
1 | /* This file is part of KDevelop | ||||
---|---|---|---|---|---|
2 | Copyright 2013 Aleix Pol <aleixpol@kde.org> | ||||
3 | Copyright 2017 Friedrich W. H. Kossebau <kossebau@kde.org> | ||||
4 | | ||||
5 | This library is free software; you can redistribute it and/or | ||||
6 | modify it under the terms of the GNU Library General Public | ||||
7 | License as published by the Free Software Foundation; either | ||||
8 | version 2 of the License, or (at your option) any later version. | ||||
9 | | ||||
10 | This library is distributed in the hope that it will be useful, | ||||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
13 | Library General Public License for more details. | ||||
14 | | ||||
15 | You should have received a copy of the GNU Library General Public License | ||||
16 | along with this library; see the file COPYING.LIB. If not, write to | ||||
17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
18 | Boston, MA 02110-1301, USA. | ||||
19 | */ | ||||
20 | | ||||
21 | #include "projectvcstracker.h" | ||||
22 | | ||||
23 | #include <interfaces/icore.h> | ||||
24 | #include <interfaces/iprojectcontroller.h> | ||||
25 | #include <interfaces/iproject.h> | ||||
26 | #include <interfaces/iruncontroller.h> | ||||
27 | #include <interfaces/iplugin.h> | ||||
28 | #include <vcs/interfaces/ibranchingversioncontrol.h> | ||||
29 | #include <vcs/vcsjob.h> | ||||
30 | #include <util/path.h> | ||||
31 | | ||||
32 | #include <QPointer> | ||||
33 | | ||||
34 | using namespace KDevelop; | ||||
35 | | ||||
36 | using SafeProjectPointer = QPointer<KDevelop::IProject>; | ||||
37 | Q_DECLARE_METATYPE(SafeProjectPointer) | ||||
38 | | ||||
39 | ProjectVcsTracker::ProjectVcsTracker(QObject* parent) | ||||
40 | : QObject(parent) | ||||
41 | { | ||||
42 | auto projectController = ICore::self()->projectController(); | ||||
43 | | ||||
44 | connect(projectController, &IProjectController::projectOpened, | ||||
45 | this, &ProjectVcsTracker::startProjectTracking); | ||||
46 | connect(projectController, &IProjectController::projectClosing, | ||||
47 | this, &ProjectVcsTracker::stopProjectTracking); | ||||
48 | | ||||
49 | for (const auto project : projectController->projects()) { | ||||
50 | startProjectTracking(project); | ||||
51 | } | ||||
52 | } | ||||
53 | | ||||
54 | QString ProjectVcsTracker::currentBranchName(IProject* project) const | ||||
55 | { | ||||
56 | return m_branchNamePerProject.value(project); | ||||
57 | } | ||||
58 | | ||||
59 | void ProjectVcsTracker::startProjectTracking(IProject* project) | ||||
60 | { | ||||
61 | IPlugin* plugin = project->versionControlPlugin(); | ||||
62 | if (!plugin) { | ||||
63 | return; | ||||
64 | } | ||||
65 | | ||||
66 | // TODO: Show revision in case we're in "detached" state | ||||
67 | IBranchingVersionControl* branchingExtension = plugin->extension<IBranchingVersionControl>(); | ||||
68 | if (branchingExtension) { | ||||
69 | const QUrl url = project->path().toUrl(); | ||||
70 | branchingExtension->registerRepositoryForCurrentBranchChanges(url); | ||||
71 | //can't use new signal/slot syntax here, IBranchingVersionControl is not a QObject | ||||
72 | connect(plugin, SIGNAL(repositoryBranchChanged(QUrl)), SLOT(handleRepositoryBranchChange(QUrl))); | ||||
73 | handleRepositoryBranchChange(url); | ||||
74 | } | ||||
75 | } | ||||
76 | | ||||
77 | void ProjectVcsTracker::handleRepositoryBranchChange(const QUrl& url) | ||||
78 | { | ||||
79 | const auto allProjects = ICore::self()->projectController()->projects(); | ||||
80 | for (IProject* project : allProjects) { | ||||
81 | const QUrl projectUrl = project->path().toUrl(); | ||||
82 | const bool isExactMatch = url.matches(projectUrl, QUrl::StripTrailingSlash); | ||||
83 | const bool isParentOf = url.isParentOf(projectUrl); | ||||
84 | if (isParentOf || isExactMatch) { | ||||
85 | // example projects in KDevelop: | ||||
86 | // - /path/to/mygitrepo/: isParentOf=0 isExactMatch=1, | ||||
87 | // - /path/to/mygitrepo/myproject: isParentOf=1 isExactMatch=0 | ||||
88 | // - /path/to/norepo: isParentOf=0 isExactMatch=0 | ||||
89 | // isParentOf=1 isExactMatch=1 is not a valid combination | ||||
90 | | ||||
91 | IPlugin* v = project->versionControlPlugin(); | ||||
92 | Q_ASSERT(!isExactMatch || v); // project url == 'change' url => project should be associated with a VCS plugin | ||||
93 | if (!v) { | ||||
94 | continue; | ||||
95 | } | ||||
96 | | ||||
97 | IBranchingVersionControl* branching = v->extension<IBranchingVersionControl>(); | ||||
98 | Q_ASSERT(branching); | ||||
99 | VcsJob* job = branching->currentBranch(url); | ||||
100 | connect(job, &VcsJob::resultsReady, this, &ProjectVcsTracker::onBranchNameReady); | ||||
101 | // pass the current project instance as QPointer, to track if this very instance | ||||
102 | // is still alive when the job is done. this protects against the rare chance | ||||
103 | // that another project instance reuses the same address and wrongly would be | ||||
104 | // matched by just comparing pointers | ||||
105 | job->setProperty("project", QVariant::fromValue<SafeProjectPointer>(project)); | ||||
106 | ICore::self()->runController()->registerJob(job); | ||||
107 | if (!m_branchNamePerProject.contains(project) && !m_pendingProjects.contains(project)) { | ||||
108 | m_pendingProjects.insert(project); | ||||
109 | } | ||||
110 | } | ||||
111 | } | ||||
112 | } | ||||
113 | | ||||
114 | void ProjectVcsTracker::onBranchNameReady(VcsJob* job) | ||||
115 | { | ||||
116 | if (job->status() == VcsJob::JobSucceeded) { | ||||
117 | SafeProjectPointer p = job->property("project").value<SafeProjectPointer>(); | ||||
118 | IProject* project = p.data(); | ||||
119 | if (project) { | ||||
120 | const auto branchName = job->fetchResults().toString(); | ||||
121 | | ||||
122 | if (m_pendingProjects.contains(project)) { | ||||
123 | m_pendingProjects.remove(project); | ||||
124 | m_branchNamePerProject.insert(project, branchName); | ||||
125 | } else { | ||||
126 | auto it = m_branchNamePerProject.find(project); | ||||
127 | if (it != m_branchNamePerProject.end()) { | ||||
128 | it.value() = branchName; | ||||
129 | } | ||||
130 | } | ||||
131 | } | ||||
132 | } | ||||
133 | } | ||||
134 | | ||||
135 | void ProjectVcsTracker::stopProjectTracking(IProject* project) | ||||
136 | { | ||||
137 | m_pendingProjects.remove(project); | ||||
138 | m_branchNamePerProject.remove(project); | ||||
139 | } |