VCS: Detect VCS directories even for disabled VCS plugins

Most of the version control support plugins detect that a directory/file
is under version control by looking for certain files in the directory
structure. This search doesn't really need anything specific from the
plugin, except for a list of file names.

Allow version control plugins to specify the list of files to look for in
their plugin meta data as "VcsDetectionFiles". When it is checked if a
directory is under version control, and none is found from the enabled
plugins, use the meta data to find out if any installed but disabled
plugin feels responsible for that directory.

Show a notification if a plugin is found that handles such a detected
version control system, with the option to enable the plugin and restart
QtC if necessary.

Since this adds discoverability of the version control support even when
plugins are disabled, disable the VCS plugins that can use this
mechanism (except for Git).

Change-Id: Ib507572c0065dd889a2f9b780c794f9cd985e265
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
Eike Ziller
2024-11-29 10:21:20 +01:00
parent 65a3e484a9
commit 2a990b84fa
8 changed files with 125 additions and 0 deletions

View File

@@ -4,6 +4,7 @@
"Name" : "Bazaar", "Name" : "Bazaar",
"Version" : "${IDE_VERSION}", "Version" : "${IDE_VERSION}",
"CompatVersion" : "${IDE_VERSION_COMPAT}", "CompatVersion" : "${IDE_VERSION_COMPAT}",
"DisabledByDefault" : true,
"VendorId" : "huguesdelorme", "VendorId" : "huguesdelorme",
"Vendor" : "Hugues Delorme", "Vendor" : "Hugues Delorme",
"Copyright" : "(C) 2016 Hugues Delorme, ${IDE_COPYRIGHT}", "Copyright" : "(C) 2016 Hugues Delorme, ${IDE_COPYRIGHT}",
@@ -23,5 +24,8 @@
], ],
"Url" : "https://www.qt.io", "Url" : "https://www.qt.io",
"DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-vcs-bazaar.html", "DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-vcs-bazaar.html",
"VcsDetectionFiles" : [
".bzr/branch-format"
],
${IDE_PLUGIN_DEPENDENCIES} ${IDE_PLUGIN_DEPENDENCIES}
} }

View File

@@ -1126,6 +1126,35 @@ void ICore::restart()
exit(); exit();
} }
/*!
Asks the user if they want to enable the \a plugins and their dependencies.
If the user agrees, the plugins are enabled.
If all plugins are soft loadable without restart, they get loaded directly.
Otherwise the "Restart Required" dialog is shown.
Returns whether the user agreed to enabling the plugins.
*/
bool ICore::enablePlugins(const QSet<ExtensionSystem::PluginSpec *> &plugins)
{
std::optional<QSet<PluginSpec *>> additionalPlugins
= PluginManager::askForEnablingPlugins(dialogParent(), plugins, /*enable=*/true);
if (!additionalPlugins) // canceled
return false;
const QSet<PluginSpec *> affectedPlugins = plugins + *additionalPlugins;
bool softloadable = true;
for (PluginSpec *spec : affectedPlugins) {
spec->setEnabledBySettings(true);
softloadable = softloadable && spec->isSoftLoadable();
}
ExtensionSystem::PluginManager::writeSettings();
if (softloadable) {
PluginManager::loadPluginsAtRuntime(affectedPlugins);
} else {
ICore::askForRestart(Tr::tr("Plugin changes will take effect after restart."));
}
return true;
}
/*! /*!
\internal \internal
*/ */

View File

@@ -6,6 +6,7 @@
#include "core_global.h" #include "core_global.h"
#include "icontext.h" #include "icontext.h"
#include <extensionsystem/pluginspec.h>
#include <utils/appmainwindow.h> #include <utils/appmainwindow.h>
#include <utils/filepath.h> #include <utils/filepath.h>
#include <utils/qtcsettings.h> #include <utils/qtcsettings.h>
@@ -116,6 +117,8 @@ public:
static void restart(); static void restart();
static bool enablePlugins(const QSet<ExtensionSystem::PluginSpec *> &plugins);
enum SaveSettingsReason { enum SaveSettingsReason {
SettingsDialogDone, SettingsDialogDone,
ModeChanged, ModeChanged,

View File

@@ -12,12 +12,15 @@
#include "iversioncontrol.h" #include "iversioncontrol.h"
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/infobar.h> #include <utils/infobar.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QJsonArray>
#include <QLabel>
#include <QList> #include <QList>
#include <QMap> #include <QMap>
#include <QMessageBox> #include <QMessageBox>
@@ -177,6 +180,74 @@ static FilePath fixedDir(const FilePath &directory)
return directory; return directory;
} }
static void askForDisabledVcsPlugins(const FilePath &inputDirectory)
{
using namespace ExtensionSystem;
FilePath toplevel;
PluginSpec *spec = Utils::findOrDefault(
PluginManager::plugins(), [&toplevel, inputDirectory](PluginSpec *plugin) {
if (plugin->isEffectivelyEnabled())
return false;
const QJsonObject metaData = plugin->metaData();
const QJsonArray filesArray = metaData.value("VcsDetectionFiles").toArray();
if (filesArray.isEmpty())
return false;
QStringList files;
for (const QJsonValue &v : filesArray) {
const QString str = v.toString();
if (!str.isEmpty())
files.append(str);
}
if (files.isEmpty())
return false;
qCDebug(findRepoLog) << "Checking if plugin" << plugin->displayName() << "can handle"
<< inputDirectory.toUserOutput();
qCDebug(findRepoLog) << "by checking for" << files;
const FilePath dir = VcsManager::findRepositoryForFiles(inputDirectory, files);
if (dir.isEmpty())
return false;
qCDebug(findRepoLog) << "The plugin" << plugin->displayName() << "can handle"
<< inputDirectory.toUserOutput();
toplevel = dir;
return true;
});
if (!spec)
return;
const Id vcsSuggestion = Id("VcsManager.Suggestion.").withSuffix(spec->id());
InfoBar *infoBar = ICore::infoBar();
if (!infoBar->canInfoBeAdded(vcsSuggestion))
return;
const QString pluginDisplayName = spec->displayName();
Utils::InfoBarEntry info(
vcsSuggestion,
Tr::tr("A directory under version control was detected that is supported by the %1 plugin.")
.arg(pluginDisplayName),
Utils::InfoBarEntry::GlobalSuppression::Enabled);
info.addCustomButton(Tr::tr("Enable %1").arg(pluginDisplayName), [vcsSuggestion, spec] {
// TODO In case the plugin is actually loaded during runtime (softloadable),
// we'd need to restructure findVersionControlForDirectory below to take the new plugin
// into account.
// At the moment softloadable VCS plugins are not supported though.
if (ICore::enablePlugins({spec}))
ICore::infoBar()->removeInfo(vcsSuggestion);
});
info.setDetailsWidgetCreator([toplevel, pluginDisplayName]() -> QWidget * {
auto label = new QLabel;
label->setWordWrap(true);
label->setOpenExternalLinks(true);
label->setText(Tr::tr("The directory \"%1\" seems to be under version control that can be "
"handled by the disabled %2 plugin.")
.arg(toplevel.toUserOutput(), pluginDisplayName));
label->setContentsMargins(0, 0, 0, 8);
return label;
});
ICore::infoBar()->addInfo(info);
};
IVersionControl* VcsManager::findVersionControlForDirectory(const FilePath &inputDirectory, IVersionControl* VcsManager::findVersionControlForDirectory(const FilePath &inputDirectory,
FilePath *topLevelDirectory) FilePath *topLevelDirectory)
@@ -221,6 +292,8 @@ IVersionControl* VcsManager::findVersionControlForDirectory(const FilePath &inpu
// report result; // report result;
if (topLevelDirectory) if (topLevelDirectory)
topLevelDirectory->clear(); topLevelDirectory->clear();
askForDisabledVcsPlugins(directory);
return nullptr; return nullptr;
} }

View File

@@ -24,6 +24,9 @@
], ],
"Url" : "https://www.qt.io", "Url" : "https://www.qt.io",
"DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-vcs-fossil.html", "DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-vcs-fossil.html",
"VcsDetectionFiles" : [
".fslckout"
],
${IDE_PLUGIN_DEPENDENCIES}, ${IDE_PLUGIN_DEPENDENCIES},
"JsonWizardPaths" : [":/fossil/wizard"] "JsonWizardPaths" : [":/fossil/wizard"]

View File

@@ -30,6 +30,10 @@
"Description" : "Show given commit hash" "Description" : "Show given commit hash"
} }
], ],
"VcsDetectionFiles" : [
".git",
".git/config"
],
${IDE_PLUGIN_DEPENDENCIES}, ${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [ "Mimetypes" : [

View File

@@ -4,6 +4,7 @@
"Name" : "Mercurial", "Name" : "Mercurial",
"Version" : "${IDE_VERSION}", "Version" : "${IDE_VERSION}",
"CompatVersion" : "${IDE_VERSION_COMPAT}", "CompatVersion" : "${IDE_VERSION_COMPAT}",
"DisabledByDefault" : true,
"VendorId" : "brianmcgillion", "VendorId" : "brianmcgillion",
"Vendor" : "Brian McGillion", "Vendor" : "Brian McGillion",
"Copyright" : "(C) 2016 Brian McGillion, ${IDE_COPYRIGHT}", "Copyright" : "(C) 2016 Brian McGillion, ${IDE_COPYRIGHT}",
@@ -23,5 +24,8 @@
], ],
"Url" : "https://www.qt.io", "Url" : "https://www.qt.io",
"DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-vcs-mercurial.html", "DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-vcs-mercurial.html",
"VcsDetectionFiles" : [
".hg/requires"
],
${IDE_PLUGIN_DEPENDENCIES} ${IDE_PLUGIN_DEPENDENCIES}
} }

View File

@@ -4,6 +4,7 @@
"Name" : "Subversion", "Name" : "Subversion",
"Version" : "${IDE_VERSION}", "Version" : "${IDE_VERSION}",
"CompatVersion" : "${IDE_VERSION_COMPAT}", "CompatVersion" : "${IDE_VERSION_COMPAT}",
"DisabledByDefault" : true,
"VendorId" : "theqtcompany", "VendorId" : "theqtcompany",
"Vendor" : "The Qt Company Ltd", "Vendor" : "The Qt Company Ltd",
"Copyright" : "${IDE_COPYRIGHT}", "Copyright" : "${IDE_COPYRIGHT}",
@@ -23,6 +24,10 @@
], ],
"Url" : "https://www.qt.io", "Url" : "https://www.qt.io",
"DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-vcs-subversion.html", "DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-vcs-subversion.html",
"VcsDetectionFiles" : [
".svn/wc.db",
"_svn/wc.db"
],
${IDE_PLUGIN_DEPENDENCIES}, ${IDE_PLUGIN_DEPENDENCIES},
"Mimetypes" : [ "Mimetypes" : [