mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
OPENIDE config import: skip plugins bundled in the IDE, report download progress
This commit is contained in:
@@ -2670,6 +2670,7 @@ dialog.title.configuration.changed=Configuration Changed
|
||||
dialog.message.component.could.not.be.reloaded=The component cannot be reloaded. Reload project?
|
||||
dialog.title.migrating.plugins=Migrating Plugins
|
||||
progress.text.migrating.plugins=Migrating plugins\u2026
|
||||
progress.text.downloading.plugin.of=Downloading ''{0}'' ({1} of {2})
|
||||
dialog.message.unknown.error=Unknown error
|
||||
http.velocity={0}<hr> <font face="verdana" size="-1">\
|
||||
<a href=''https://velocity.apache.org/engine/devel/user-guide.html#Velocity_Template_Language_VTL:_An_Introduction''>\nApache Velocity</a> \
|
||||
|
||||
+101
-7
@@ -7,6 +7,7 @@ import com.intellij.ide.BootstrapBundle;
|
||||
import com.intellij.ide.ConfigImportOptions;
|
||||
import com.intellij.ide.ConfigImportSettings;
|
||||
import com.intellij.ide.GeneralSettings;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.ImportOldConfigsUsagesCollector;
|
||||
import com.intellij.ide.SpecialConfigFiles;
|
||||
import com.intellij.ide.highlighter.ArchiveFileType;
|
||||
@@ -23,6 +24,8 @@ import com.intellij.ide.plugins.PluginManagerCore;
|
||||
import com.intellij.ide.plugins.PluginNode;
|
||||
import com.intellij.ide.plugins.PluginVersionIsSuperseded;
|
||||
import com.intellij.ide.plugins.PluginsDiscoveryResult;
|
||||
import com.intellij.ide.plugins.PluginsSourceContext;
|
||||
import com.intellij.ide.plugins.marketplace.MarketplacePluginDownloadService;
|
||||
import com.intellij.ide.plugins.marketplace.MarketplaceRequests;
|
||||
import com.intellij.ide.plugins.newui.PluginUiModel;
|
||||
import com.intellij.ide.startup.StartupActionScriptManager;
|
||||
@@ -74,6 +77,7 @@ import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.WindowConstants;
|
||||
import java.awt.Dialog;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
@@ -120,6 +124,8 @@ public final class ConfigImportHelper {
|
||||
public static final Pattern SELECTOR_PATTERN = Pattern.compile("\\.?(\\D+)(\\d+(?:\\.\\d+)*)");
|
||||
|
||||
private static final String UPDATE_ONLY_INCOMPATIBLE_PLUGINS_PROPERTY = "idea.config.import.update.incompatible.plugins.only"; // if true, only incompatible will be updated
|
||||
// OpenIDE: if true, plugins bundled with this IDE are migrated/updated from the previous installation as before
|
||||
private static final String MIGRATE_BUNDLED_PLUGINS_PROPERTY = "idea.config.import.migrate.bundled.plugins";
|
||||
|
||||
private static final String CONFIG = "config";
|
||||
private static final String BIN = "bin";
|
||||
@@ -1060,7 +1066,7 @@ public final class ConfigImportHelper {
|
||||
options.compatibleBuildNumber, Collections.emptySet(), Collections.emptySet(), brokenPluginVersions
|
||||
);
|
||||
var nonLoadablePlugins = new HashMap<PluginId, PluginMainDescriptor>();
|
||||
var loadablePlugins = PluginInitContextSelectPluginsToLoadKt.selectPluginsToLoad(
|
||||
var allLoadablePlugins = PluginInitContextSelectPluginsToLoadKt.selectPluginsToLoad(
|
||||
initContext,
|
||||
oldIdePlugins,
|
||||
(plugin, reason) -> {
|
||||
@@ -1074,13 +1080,21 @@ public final class ConfigImportHelper {
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
).getPlugins();
|
||||
|
||||
// OpenIDE: a plugin that this IDE ships itself must be neither updated nor copied over during the import.
|
||||
// The bundled build is the one matching the new platform, while the copy inherited from the previous version would
|
||||
// shadow it — and downloading it again is pure waste when the very same build is already in the distribution.
|
||||
var bundledPluginVersions = collectBundledPluginVersions(oldIdePlugins);
|
||||
var loadablePlugins = dropPluginsBundledInThisIde(allLoadablePlugins, bundledPluginVersions, options.log);
|
||||
var nonLoadableToProcess = dropPluginsBundledInThisIde(nonLoadablePlugins.values(), bundledPluginVersions, options.log);
|
||||
|
||||
// TODO 'plugin is broken' is already applied by 'selectPluginsToLoad'
|
||||
if (Boolean.getBoolean(UPDATE_ONLY_INCOMPATIBLE_PLUGINS_PROPERTY)) {
|
||||
partitionNonBundled(loadablePlugins, pluginsToDownload, pluginsToMigrate, descriptor -> {
|
||||
var brokenVersions = brokenPluginVersions != null ? brokenPluginVersions.get(descriptor.getPluginId()) : null;
|
||||
return brokenVersions != null && brokenVersions.contains(descriptor.getVersion());
|
||||
});
|
||||
partitionNonBundled(nonLoadablePlugins.values(), pluginsToDownload, pluginsToMigrate, _ -> true);
|
||||
partitionNonBundled(nonLoadableToProcess, pluginsToDownload, pluginsToMigrate, _ -> true);
|
||||
}
|
||||
else {
|
||||
// The first partition in the branch above puts only broken plugins to pluginsToDownload.
|
||||
@@ -1088,7 +1102,7 @@ public final class ConfigImportHelper {
|
||||
// So the only difference is that here we try to download more plugins.
|
||||
var nonBundledPlugins = new ArrayList<IdeaPluginDescriptor>();
|
||||
partitionNonBundled(loadablePlugins, nonBundledPlugins, pluginsToMigrate, _ -> true);
|
||||
partitionNonBundled(nonLoadablePlugins.values(), nonBundledPlugins, pluginsToMigrate, _ -> true);
|
||||
partitionNonBundled(nonLoadableToProcess, nonBundledPlugins, pluginsToMigrate, _ -> true);
|
||||
var updates = fetchPluginUpdatesFromMarketplace(options, ContainerUtil.map2Set(nonBundledPlugins, d -> d.getPluginId()));
|
||||
partitionNonBundled(loadablePlugins, pluginsToDownload, pluginsToMigrate, d -> {
|
||||
if (updates != null && updates.containsKey(d.getPluginId()) && !updates.get(d.getPluginId()).getVersion().equals(d.getVersion())) {
|
||||
@@ -1097,7 +1111,7 @@ public final class ConfigImportHelper {
|
||||
var brokenVersions = brokenPluginVersions != null ? brokenPluginVersions.get(d.getPluginId()) : null;
|
||||
return brokenVersions != null && brokenVersions.contains(d.getVersion());
|
||||
});
|
||||
partitionNonBundled(nonLoadablePlugins.values(), pluginsToDownload, pluginsToMigrate, _ -> true);
|
||||
partitionNonBundled(nonLoadableToProcess, pluginsToDownload, pluginsToMigrate, _ -> true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -1153,6 +1167,57 @@ public final class ConfigImportHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/// OpenIDE: plugin ids shipped with the IDE being started, mapped to the bundled version.
|
||||
///
|
||||
/// The descriptors of the previous installation are discovered together with the bundled plugins of *this* IDE (see the
|
||||
/// `bundledPluginPath` note in [#collectPluginsToMigrate]), and only the [PluginsSourceContext.Custom] list comes from the
|
||||
/// previous plugin directory — everything else describes the new distribution. Matching on `Custom` rather than on
|
||||
/// `Bundled` matters: a real distribution carries a `plugin-classpath.txt` index, and the plugins read through it are
|
||||
/// reported as [PluginsSourceContext.ClassPathProvided]; `Bundled` only appears on the fallback path used when there is no
|
||||
/// such index (notably in tests).
|
||||
private static Map<PluginId, String> collectBundledPluginVersions(PluginsDiscoveryResult discoveryResult) {
|
||||
var result = new HashMap<PluginId, String>();
|
||||
for (var pluginList : discoveryResult.getPluginLists()) {
|
||||
if (pluginList.getSource() instanceof PluginsSourceContext.Custom) {
|
||||
continue;
|
||||
}
|
||||
for (var plugin : pluginList.getPlugins()) {
|
||||
result.putIfAbsent(plugin.getPluginId(), plugin.getVersion());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// OpenIDE: drops the plugins inherited from the previous installation that the current IDE bundles itself.
|
||||
///
|
||||
/// Such a plugin needs neither a download (the distribution already carries the build matching this platform) nor a copy
|
||||
/// (the inherited copy would shadow the bundled one, typically with a build made for the previous platform branch).
|
||||
/// Set `-Didea.config.import.migrate.bundled.plugins=true` to restore the previous behaviour.
|
||||
private static <T extends IdeaPluginDescriptor> List<T> dropPluginsBundledInThisIde(
|
||||
Collection<? extends T> descriptors,
|
||||
Map<PluginId, String> bundledPluginVersions,
|
||||
Logger log
|
||||
) {
|
||||
if (Boolean.getBoolean(MIGRATE_BUNDLED_PLUGINS_PROPERTY)) {
|
||||
return new ArrayList<>(descriptors);
|
||||
}
|
||||
|
||||
var result = new ArrayList<T>(descriptors.size());
|
||||
for (var descriptor : descriptors) {
|
||||
var bundledVersion = bundledPluginVersions.get(descriptor.getPluginId());
|
||||
if (bundledVersion != null && !descriptor.isBundled()) {
|
||||
log.info(
|
||||
"Skipping plugin '" + descriptor.getPluginId() + "' version: " + descriptor.getVersion() +
|
||||
", because this IDE bundles it (bundled version: " + bundledVersion + ')'
|
||||
);
|
||||
}
|
||||
else {
|
||||
result.add(descriptor);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Predicate<IdeaPluginDescriptor> collectPendingPluginUpdates(List<ActionCommand> actionCommands, FileSystem fs, Logger log) {
|
||||
var result = new LinkedHashSet<PluginId>();
|
||||
for (var command : actionCommands) {
|
||||
@@ -1229,9 +1294,15 @@ public final class ConfigImportHelper {
|
||||
|
||||
var dialog = new ConfigImportProgressDialog();
|
||||
dialog.setModalityType(Dialog.ModalityType.TOOLKIT_MODAL);
|
||||
dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
AppUIUtil.updateAppWindowIcon(dialog);
|
||||
SplashManagerKt.hideSplash();
|
||||
runSynchronouslyInBackground(() -> {
|
||||
|
||||
// OpenIDE: the worker must not be joined before the dialog is shown. `runSynchronouslyInBackground` joins the thread,
|
||||
// so the EDT used to stay blocked for the whole download and `setVisible(true)` was reached only once everything had
|
||||
// been downloaded — the dialog never appeared, the splash was already hidden, and the IDE looked frozen with a blank
|
||||
// screen for as long as the download took. Showing the modal dialog first is what pumps the EDT; the worker closes it.
|
||||
var worker = new Thread(() -> {
|
||||
try {
|
||||
downloadUpdatesForPlugins(newPluginsDir, options, plugins, brokenPluginVersions, dialog.getIndicator());
|
||||
}
|
||||
@@ -1239,8 +1310,19 @@ public final class ConfigImportHelper {
|
||||
options.log.info("Failed to download updates for plugins", e);
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> dialog.setVisible(false));
|
||||
});
|
||||
}, "Plugin downloader");
|
||||
worker.start();
|
||||
|
||||
// Returns once the worker has hidden the dialog. If the worker finished before we got here, its `setVisible(false)` is
|
||||
// already queued and the modal event loop dispatches it right away.
|
||||
dialog.setVisible(true);
|
||||
|
||||
try {
|
||||
worker.join();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1255,14 +1337,26 @@ public final class ConfigImportHelper {
|
||||
ThreadingAssertions.assertBackgroundThread();
|
||||
|
||||
var log = options.log;
|
||||
// OpenIDE: the default service is replaced by one that reports the transferred size and speed through the indicator,
|
||||
// so that the import dialog can show what a multi-minute download is actually doing.
|
||||
var downloadService = options.downloadService != null ? options.downloadService : new MarketplacePluginDownloadService(true);
|
||||
var total = plugins.size();
|
||||
var index = 0;
|
||||
for (var iterator = plugins.iterator(); iterator.hasNext(); ) {
|
||||
var descriptor = iterator.next();
|
||||
var pluginId = descriptor.getPluginId();
|
||||
|
||||
// OpenIDE: name the plugin being downloaded and its place in the queue
|
||||
index++;
|
||||
var pluginName = descriptor.getName() != null ? descriptor.getName() : pluginId.getIdString();
|
||||
indicator.setText(IdeBundle.message("progress.text.downloading.plugin.of", pluginName, index, total));
|
||||
indicator.setText2("");
|
||||
indicator.setFraction(0.0);
|
||||
|
||||
try {
|
||||
var downloader = PluginDownloader.createDownloader(descriptor)
|
||||
.withErrorsConsumer(_ -> {})
|
||||
.withDownloadService(options.downloadService);
|
||||
.withDownloadService(downloadService);
|
||||
|
||||
if (downloader.prepareToInstall(indicator)) {
|
||||
PluginInstaller.unpackPlugin(downloader.getFilePath(), newPluginsDir);
|
||||
|
||||
+82
-11
@@ -3,61 +3,130 @@ package com.intellij.openapi.application;
|
||||
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.util.ui.EDT;
|
||||
import com.intellij.util.ui.GridBag;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.SwingConstants;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Frame;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
|
||||
/// The dialog shown while the initial config import downloads plugin updates.
|
||||
///
|
||||
/// OpenIDE: the step used to be silent — a bare progress bar with no indication of what was being downloaded, while a single
|
||||
/// large plugin can keep the IDE from starting for minutes. It now reports the plugin being downloaded, its position in the
|
||||
/// queue, and the transferred/total size, so that waiting is at least explainable and the Cancel button is discoverable.
|
||||
@ApiStatus.Internal
|
||||
public final class ConfigImportProgressDialog extends JDialog {
|
||||
private static final int WIDTH = 500;
|
||||
|
||||
private final ProgressIndicatorAdapter myIndicator = new ProgressIndicatorAdapter();
|
||||
private final JLabel myProgressTextLabel = new JLabel(" ");
|
||||
private final JLabel myPluginLabel = new JLabel(" ");
|
||||
private final JLabel myDetailsLabel = new JLabel(" ");
|
||||
private final JProgressBar myProgressBar = new JProgressBar(0, 100);
|
||||
private boolean myCanceled;
|
||||
private volatile boolean myCanceled;
|
||||
|
||||
public ConfigImportProgressDialog() {
|
||||
super((Frame)null, IdeBundle.message("dialog.title.migrating.plugins"), true);
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
GridBag gridBag = new GridBag();
|
||||
panel.setLayout(new GridBagLayout());
|
||||
|
||||
panel.add(new JLabel(IdeBundle.message("progress.text.migrating.plugins")), gridBag.nextLine().anchor(GridBagConstraints.WEST));
|
||||
panel.add(myProgressTextLabel, gridBag.nextLine().insetBottom(20));
|
||||
myProgressBar.setPreferredSize(new Dimension(JBUI.scale(500), myProgressBar.getPreferredSize().height));
|
||||
panel.add(myProgressBar, gridBag.nextLine().fillCell().insetBottom(20));
|
||||
|
||||
myPluginLabel.setFont(JBUI.Fonts.label().asBold());
|
||||
panel.add(fixedWidth(myPluginLabel), gridBag.nextLine().anchor(GridBagConstraints.WEST).insetTop(10));
|
||||
|
||||
myProgressBar.setPreferredSize(new Dimension(JBUI.scale(WIDTH), myProgressBar.getPreferredSize().height));
|
||||
panel.add(myProgressBar, gridBag.nextLine().fillCell().insetTop(6));
|
||||
|
||||
myDetailsLabel.setForeground(JBUI.CurrentTheme.Label.disabledForeground());
|
||||
// The label spans the width of the progress bar, so centering the text puts it right under the middle of the bar.
|
||||
myDetailsLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
panel.add(fixedWidth(myDetailsLabel), gridBag.nextLine().anchor(GridBagConstraints.CENTER).insetBottom(20));
|
||||
|
||||
JButton cancelButton = new JButton(IdeBundle.message("button.cancel.without.mnemonic"));
|
||||
panel.add(cancelButton, gridBag.nextLine());
|
||||
panel.setBorder(JBUI.Borders.empty(10, 20));
|
||||
cancelButton.addActionListener((e) -> {
|
||||
cancelButton.addActionListener(_ -> {
|
||||
myCanceled = true;
|
||||
cancelButton.setEnabled(false);
|
||||
});
|
||||
panel.add(cancelButton, gridBag.nextLine());
|
||||
|
||||
panel.setBorder(JBUI.Borders.empty(10, 20));
|
||||
setContentPane(panel);
|
||||
pack();
|
||||
|
||||
setLocationRelativeTo(null);
|
||||
}
|
||||
|
||||
/// Keeps a long plugin name or a long details line from resizing the dialog: the label clips with an ellipsis instead.
|
||||
private static JLabel fixedWidth(JLabel label) {
|
||||
label.setPreferredSize(new Dimension(JBUI.scale(WIDTH), label.getPreferredSize().height));
|
||||
return label;
|
||||
}
|
||||
|
||||
ProgressIndicatorAdapter getIndicator() {
|
||||
return myIndicator;
|
||||
}
|
||||
|
||||
private void setText(@Nullable @NlsSafe String text) {
|
||||
onEdt(() -> myPluginLabel.setText(blankIfEmpty(text)));
|
||||
}
|
||||
|
||||
private void setDetails(@Nullable @NlsSafe String text) {
|
||||
onEdt(() -> myDetailsLabel.setText(blankIfEmpty(text)));
|
||||
}
|
||||
|
||||
private static @NlsSafe String blankIfEmpty(@Nullable @NlsSafe String text) {
|
||||
return text == null || text.isEmpty() ? " " : text;
|
||||
}
|
||||
|
||||
/// The indicator is driven from the download thread, while Swing state may only be touched on the EDT.
|
||||
private static void onEdt(Runnable runnable) {
|
||||
if (EDT.isCurrentThreadEdt()) {
|
||||
runnable.run();
|
||||
}
|
||||
else {
|
||||
EventQueue.invokeLater(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
final class ProgressIndicatorAdapter extends AbstractProgressIndicatorBase {
|
||||
@Override
|
||||
public void setFraction(double fraction) {
|
||||
myProgressBar.setValue((int)(fraction * 100));
|
||||
onEdt(() -> myProgressBar.setValue((int)(fraction * 100)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndeterminate(boolean indeterminate) {
|
||||
super.setIndeterminate(indeterminate);
|
||||
onEdt(() -> myProgressBar.setIndeterminate(indeterminate));
|
||||
}
|
||||
|
||||
/// The plugin currently being downloaded and its place in the queue.
|
||||
@Override
|
||||
public void setText(String text) {
|
||||
super.setText(text);
|
||||
ConfigImportProgressDialog.this.setText(text);
|
||||
}
|
||||
|
||||
/// Transferred/total size and speed, as reported by `NetUtils.copyStreamContent`.
|
||||
@Override
|
||||
public void setText2(String text) {
|
||||
myProgressTextLabel.setText(text);
|
||||
super.setText2(text);
|
||||
ConfigImportProgressDialog.this.setDetails(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,7 +143,9 @@ public final class ConfigImportProgressDialog extends JDialog {
|
||||
@SuppressWarnings("HardCodedStringLiteral")
|
||||
public static void main(String[] args) {
|
||||
ConfigImportProgressDialog dialog = new ConfigImportProgressDialog();
|
||||
dialog.getIndicator().setText2("Downloading plugin 'Scala'");
|
||||
dialog.getIndicator().setText("Downloading 'Scala' (2 of 5)");
|
||||
dialog.getIndicator().setText2("<html><code>42% · 276 MB</code></html>");
|
||||
dialog.getIndicator().setFraction(0.42);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -27,7 +27,9 @@ import kotlin.io.path.moveTo
|
||||
import kotlin.io.path.outputStream
|
||||
|
||||
@ApiStatus.Internal
|
||||
open class MarketplacePluginDownloadService {
|
||||
// OpenIDE: `showDownloadDetails` makes the download report bytes and speed via `ProgressIndicator.setText2`
|
||||
// (see `NetUtils.copyStreamContent`). Used by the initial config import, which downloads before any UI exists.
|
||||
open class MarketplacePluginDownloadService @JvmOverloads constructor(private val showDownloadDetails: Boolean = false) {
|
||||
companion object {
|
||||
private val LOG = logger<MarketplacePluginDownloadService>()
|
||||
|
||||
@@ -61,7 +63,7 @@ open class MarketplacePluginDownloadService {
|
||||
.gzip(false)
|
||||
.productNameAsUserAgent()
|
||||
.connect(HttpRequests.RequestProcessor { request ->
|
||||
request.saveToFile(file, indicator)
|
||||
request.saveToFile(file, indicator, showDownloadDetails)
|
||||
val pluginFileUrl = getPluginFileUrl(request.connection)
|
||||
if (pluginFileUrl.endsWith(".zip")) {
|
||||
renameFileToZipRoot(file)
|
||||
|
||||
+42
@@ -278,6 +278,48 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
assertThat(newPluginsDir).doesNotExist()
|
||||
}
|
||||
|
||||
// OpenIDE: a plugin bundled with the new IDE is neither downloaded nor copied even when the previous installation carried
|
||||
// a higher version of it — the inherited copy would shadow the bundled build made for the current platform branch.
|
||||
@Test fun `skip plugins bundled in this ide even if the inherited copy is newer`() {
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = oldConfigDir.resolve("plugins").createDirectories()
|
||||
val oldBundledPluginsDir = newTempDir("oldBundled")
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.0" }.buildMainJar(oldBundledPluginsDir.resolve("my-plugin-bundled.jar"))
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.1" }.buildMainJar(oldPluginsDir.resolve("my-plugin.jar"))
|
||||
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
options.bundledPluginPath = oldBundledPluginsDir
|
||||
options.downloadService = object : MarketplacePluginDownloadService() {
|
||||
override fun downloadPlugin(pluginUrl: String, indicator: ProgressIndicator?): Path =
|
||||
throw AssertionError("a bundled plugin must not be downloaded during the import")
|
||||
}
|
||||
doImport(oldConfigDir, newConfigDir, options)
|
||||
assertThat(newPluginsDir).doesNotExist()
|
||||
}
|
||||
|
||||
@Test fun `migrate plugins bundled in this ide when the escape hatch is set`() {
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = oldConfigDir.resolve("plugins").createDirectories()
|
||||
val oldBundledPluginsDir = newTempDir("oldBundled")
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.0" }.buildMainJar(oldBundledPluginsDir.resolve("my-plugin-bundled.jar"))
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.1" }.buildMainJar(oldPluginsDir.resolve("my-plugin.jar"))
|
||||
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
options.bundledPluginPath = oldBundledPluginsDir
|
||||
System.setProperty("idea.config.import.migrate.bundled.plugins", "true")
|
||||
try {
|
||||
doImport(oldConfigDir, newConfigDir, options)
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("idea.config.import.migrate.bundled.plugins")
|
||||
}
|
||||
assertThat(newPluginsDir).isDirectoryContaining { it.name == "my-plugin.jar" }
|
||||
}
|
||||
|
||||
@Test fun `skip broken plugins`() {
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = oldConfigDir.resolve("plugins").createDirectories()
|
||||
|
||||
@@ -206,18 +206,27 @@ public final class NetUtils {
|
||||
private static void updateIndicator(ProgressIndicator indicator, long downloadSpeed, long bytesDownloaded, long contentLength, boolean progressDescription) {
|
||||
double fraction = (double)bytesDownloaded / contentLength;
|
||||
if (progressDescription) {
|
||||
// OpenIDE: this line is centered under the progress bar and updated many times a second, so every field is rendered at
|
||||
// a constant width — a fixed fraction precision plus left padding to the widest form the field can take. Otherwise the
|
||||
// whole line shifts sideways whenever a digit is gained or lost (`1,7 MB` -> `194,56 MB`, `9%` -> `100%`).
|
||||
int rankForContentLength = StringUtil.rankForFileSize(contentLength);
|
||||
String formattedDownloadSpeed = StringUtil.formatFileSize(downloadSpeed) + "⧸s";
|
||||
String formattedContentLength = StringUtil.formatFileSize(contentLength, " ", rankForContentLength);
|
||||
String formattedTotalProgress = StringUtil.formatFileSize(bytesDownloaded, " ", rankForContentLength);
|
||||
String formattedDownloadSpeed = StringUtil.formatFileSize(downloadSpeed, " ", -1, true) + "⧸s";
|
||||
String formattedContentLength = StringUtil.formatFileSize(contentLength, " ", rankForContentLength, true);
|
||||
String formattedTotalProgress =
|
||||
padToWidth(StringUtil.formatFileSize(bytesDownloaded, " ", rankForContentLength, true), formattedContentLength.length());
|
||||
@NlsSafe String indicatorText = String.format(
|
||||
"<html><code>%.0f%% · %s⧸%s · %s</code></html>", fraction * 100,
|
||||
"<html><code>%s%% · %s⧸%s · %s</code></html>", padToWidth(String.format("%.0f", fraction * 100), 3),
|
||||
formattedTotalProgress, formattedContentLength, formattedDownloadSpeed);
|
||||
indicator.setText2(indicatorText);
|
||||
}
|
||||
indicator.setFraction(fraction);
|
||||
}
|
||||
|
||||
/// Left-pads with non-breaking spaces, which HTML keeps as written — plain spaces would be collapsed away.
|
||||
private static String padToWidth(String text, int width) {
|
||||
return text.length() >= width ? text : " ".repeat(width - text.length()) + text;
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #copyStreamContent(ProgressIndicator, InputStream, OutputStream, long)} instead */
|
||||
@Deprecated(forRemoval = true)
|
||||
public static int copyStreamContent(
|
||||
|
||||
Reference in New Issue
Block a user