IDEA-157763 Settings repository for IDE: init wizard + plugin support

This commit is contained in:
Alexander Lobas
2016-09-19 14:29:36 +03:00
parent fd969b56ac
commit 7366aac212
14 changed files with 268 additions and 19 deletions
@@ -65,7 +65,7 @@ import java.util.zip.ZipFile;
public class PluginManagerCore {
private static final Logger LOG = Logger.getInstance(PluginManagerCore.class);
private static final String DISABLED_PLUGINS_FILENAME = "disabled_plugins.txt";
public static final String DISABLED_PLUGINS_FILENAME = "disabled_plugins.txt";
public static final String CORE_PLUGIN_ID = "com.intellij";
private static final String META_INF = "META-INF";
public static final String PLUGIN_XML = "plugin.xml";
@@ -101,6 +101,8 @@ public class PluginManagerCore {
}
}
private static List<Runnable> myDisablePluginListeners;
/**
* do not call this method during bootstrap, should be called in a copy of PluginManager, loaded by IdeaClassLoader
*/
@@ -140,6 +142,7 @@ public class PluginManagerCore {
reader.close();
if (!requiredPlugins.isEmpty()) {
savePluginsList(disabledPlugins, false, new File(PathManager.getConfigPath(), DISABLED_PLUGINS_FILENAME));
fireEditDisablePlugins();
}
}
}
@@ -204,6 +207,27 @@ public class PluginManagerCore {
return app != null && app.isUnitTestMode();
}
public static void addDisablePluginListener(@NotNull Runnable listener) {
if (myDisablePluginListeners == null) {
myDisablePluginListeners = new ArrayList<Runnable>();
}
myDisablePluginListeners.add(listener);
}
public static void removeDisablePluginListener(@NotNull Runnable listener) {
if (myDisablePluginListeners != null) {
myDisablePluginListeners.remove(listener);
}
}
private static void fireEditDisablePlugins() {
if (myDisablePluginListeners != null) {
for (Runnable listener : myDisablePluginListeners) {
listener.run();
}
}
}
public static void savePluginsList(@NotNull Collection<String> ids, boolean append, @NotNull File plugins) throws IOException {
if (!plugins.isFile()) {
FileUtil.ensureCanCreateFile(plugins);
@@ -256,6 +280,7 @@ public class PluginManagerCore {
File plugins = new File(PathManager.getConfigPath(), DISABLED_PLUGINS_FILENAME);
savePluginsList(ids, append, plugins);
ourDisabledPlugins = null;
fireEditDisablePlugins();
}
public static Logger getLogger() {
@@ -0,0 +1,60 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.cloudConfig;
import com.intellij.ide.customize.AbstractCustomizeWizardStep;
import com.intellij.openapi.application.ConfigImportSettings;
import com.intellij.openapi.application.ImportOldConfigsPanel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.util.List;
/**
* @author Alexander Lobas
*/
public abstract class CloudConfigProvider {
private static CloudConfigProvider myProvider;
@Nullable
public static CloudConfigProvider getProvider() {
return myProvider;
}
public static void setProvider(@Nullable CloudConfigProvider provider) {
myProvider = provider;
}
public abstract void initConfigsPanel(@NotNull ImportOldConfigsPanel dialog,
@NotNull JPanel parentPanel,
@NotNull ConfigImportSettings settings);
public abstract void importFinished(@NotNull File newConfigDir);
public abstract void beforeStartupWizard();
@Nullable
public abstract String getLafClassName();
@NotNull
public abstract List<String> getInstalledPlugins();
public abstract int initSteps(@NotNull List<AbstractCustomizeWizardStep> steps);
public abstract void startupWizardFinished();
}
@@ -23,6 +23,7 @@ import com.intellij.openapi.progress.util.AbstractProgressIndicatorExBase;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.updateSettings.impl.PluginDownloader;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.JBColor;
@@ -84,7 +85,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt
int i = s.indexOf(':');
String topic = s.substring(0, i);
int j = s.indexOf(':', i + 1);
final String description = s.substring(i + 1, j);
String description = s.substring(i + 1, j);
final String pluginId = s.substring(j + 1);
IdeaPluginDescriptor foundDescriptor = null;
for (IdeaPluginDescriptor descriptor : pluginsFromRepository) {
@@ -96,18 +97,29 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt
if (foundDescriptor == null) continue;
final IdeaPluginDescriptor descriptor = foundDescriptor;
final boolean isVIM = PluginGroups.IDEA_VIM_PLUGIN_ID.equals(descriptor.getPluginId().getIdString());
boolean isCloud = "#Cloud".equals(topic);
if (isCloud) {
title = descriptor.getName();
description = StringUtil.defaultIfEmpty(descriptor.getDescription(), "No description available");
topic = StringUtil.defaultIfEmpty(descriptor.getCategory(), "Unknown");
}
JLabel titleLabel = new JLabel("<html><body><h2 style=\"text-align:left;\">" + title + "</h2></body></html>");
JLabel topicLabel = new JLabel("<html><body><h4 style=\"text-align:left;color:#808080;font-weight:bold;\">" + topic + "</h4></body></html>");
JLabel descriptionLabel = createHTMLLabel(description);
JLabel warningLabel = null;
if (isVIM) {
warningLabel = createHTMLLabel("Recommended only if you are<br> familiar with Vim.");
warningLabel.setIcon(AllIcons.General.BalloonWarning);
if (isVIM || isCloud) {
if (isCloud) {
warningLabel = createHTMLLabel("JBA account");
warningLabel.setIcon(AllIcons.General.BalloonInformation);
}
else {
warningLabel = createHTMLLabel("Recommended only if you are<br> familiar with Vim.");
warningLabel.setIcon(AllIcons.General.BalloonWarning);
}
if (!SystemInfo.isWindows) UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, warningLabel);
}
@@ -15,6 +15,7 @@
*/
package com.intellij.ide.customize;
import com.intellij.ide.cloudConfig.CloudConfigProvider;
import com.intellij.ide.startup.StartupActionScriptManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.ui.DialogWrapper;
@@ -58,6 +59,12 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis
getPeer().setAppIcons();
stepsProvider.initSteps(this, mySteps);
CloudConfigProvider configProvider = CloudConfigProvider.getProvider();
if (configProvider != null) {
myIndex = configProvider.initSteps(mySteps);
}
if (mySteps.isEmpty()) {
throw new IllegalArgumentException(stepsProvider + " provided no steps");
}
@@ -17,6 +17,7 @@ package com.intellij.ide.customize;
import com.intellij.CommonBundle;
import com.intellij.ide.WelcomeWizardUtil;
import com.intellij.ide.cloudConfig.CloudConfigProvider;
import com.intellij.ide.ui.LafManager;
import com.intellij.ide.ui.laf.IntelliJLaf;
import com.intellij.ide.ui.laf.LafManagerImpl;
@@ -26,7 +27,9 @@ import com.intellij.openapi.options.OptionsBundle;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.IconUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
@@ -89,7 +92,7 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep {
myColumnMode = myThemes.size() > 2;
JPanel buttonsPanel = new JPanel(new GridLayout(myColumnMode ? myThemes.size() : 1, myColumnMode ? 1 : myThemes.size(), 5, 5));
ButtonGroup group = new ButtonGroup();
final ThemeInfo myDefaultTheme = myThemes.iterator().next();
final ThemeInfo myDefaultTheme = getDefaultTheme();
for (final ThemeInfo theme : myThemes) {
final JRadioButton radioButton = new JRadioButton(theme.name, myDefaultTheme == theme);
@@ -146,6 +149,21 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep {
}
}
@NotNull
private ThemeInfo getDefaultTheme() {
CloudConfigProvider provider = CloudConfigProvider.getProvider();
if (provider != null) {
String lafClassName = provider.getLafClassName();
if (lafClassName != null) {
ThemeInfo result = ContainerUtil.find(myThemes, theme -> lafClassName.equals(theme.laf));
if (result != null) {
return result;
}
}
}
return myThemes.iterator().next();
}
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
@@ -16,6 +16,7 @@
package com.intellij.ide.customize;
import com.intellij.ide.WelcomeWizardUtil;
import com.intellij.ide.cloudConfig.CloudConfigProvider;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.ide.plugins.RepositoryHelper;
@@ -32,6 +33,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
public class PluginGroups {
@@ -84,6 +86,7 @@ public class PluginGroups {
PluginManagerCore.loadDisabledPlugins(new File(PathManager.getConfigPath()).getPath(), myDisabledPluginIds);
initGroups(myTree, myFeaturedPlugins);
initCloudPlugins();
}
public void setLoadingCallback(Runnable loadingCallback) {
@@ -93,6 +96,29 @@ public class PluginGroups {
}
}
private void initCloudPlugins() {
CloudConfigProvider provider = CloudConfigProvider.getProvider();
if (provider == null) {
return;
}
List<String> plugins = provider.getInstalledPlugins();
if (plugins.isEmpty()) {
return;
}
for (Iterator<Entry<String, String>> I = myFeaturedPlugins.entrySet().iterator(); I.hasNext(); ) {
String value = I.next().getValue();
if (ContainerUtil.find(plugins, plugin -> value.endsWith(":" + plugin)) != null) {
I.remove();
}
}
for (String plugin : plugins) {
myFeaturedPlugins.put(plugin, "#Cloud:#Cloud:" + plugin);
}
}
protected void
initGroups(Map<String, Pair<Icon, List<String>>> tree, Map<String, String> featuredPlugins) {
tree.put(CORE, Pair.create((Icon)null, Arrays.asList(
@@ -263,7 +289,7 @@ public class PluginGroups {
private void initIfNeed() {
if (myInitialized) return;
myInitialized = true;
for (Map.Entry<String, Pair<Icon, List<String>>> entry : myTree.entrySet()) {
for (Entry<String, Pair<Icon, List<String>>> entry : myTree.entrySet()) {
final String group = entry.getKey();
if (CORE.equals(group)) continue;
@@ -45,7 +45,6 @@ import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.ui.StatusText;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -151,7 +150,7 @@ public class InstalledPluginsManagerMain extends PluginManagerMain {
}
}
PluginInstaller.install(file, file.getName(), false);
PluginInstaller.install(file, file.getName(), false, pluginDescriptor);
ourState.onPluginInstall(pluginDescriptor);
checkInstalledPluginDependencies(model, pluginDescriptor, parent);
callback.consume(pair(file, (IdeaPluginDescriptor)pluginDescriptor));
@@ -30,10 +30,10 @@ import com.intellij.openapi.updateSettings.impl.UpdateSettings;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.GuiUtils;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.ZipUtil;
import com.intellij.util.ui.UIUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
@@ -232,6 +232,8 @@ public class PluginInstaller {
if (pluginDescriptor != null) {
StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(pluginDescriptor.getPath());
StartupActionScriptManager.addActionCommand(deleteOld);
fireState(pluginDescriptor, false);
}
else {
PluginManagerMain.LOG.error("Plugin not found: " + pluginId);
@@ -240,7 +242,10 @@ public class PluginInstaller {
}
}
public static void install(final File fromFile, final String pluginName, boolean deleteFromFile) throws IOException {
public static void install(@NotNull File fromFile,
@NotNull String pluginName,
boolean deleteFromFile,
@NotNull IdeaPluginDescriptor descriptor) throws IOException {
//noinspection HardCodedStringLiteral
if (fromFile.getName().endsWith(".jar")) {
// add command to copy file to the IDEA/plugins path
@@ -267,5 +272,37 @@ public class PluginInstaller {
StartupActionScriptManager.ActionCommand deleteTemp = new StartupActionScriptManager.DeleteCommand(fromFile);
StartupActionScriptManager.addActionCommand(deleteTemp);
}
fireState(descriptor, true);
}
private static List<PluginStateListener> myStateListeners;
public static void addStateListener(@NotNull PluginStateListener listener) {
if (myStateListeners == null) {
myStateListeners = new ArrayList<>();
}
myStateListeners.add(listener);
}
public static void removeStateListener(@NotNull PluginStateListener listener) {
if (myStateListeners != null) {
myStateListeners.remove(listener);
}
}
private static void fireState(@NotNull IdeaPluginDescriptor descriptor, boolean install) {
if (myStateListeners != null) {
UIUtil.invokeLaterIfNeeded(() -> {
for (PluginStateListener listener : myStateListeners) {
if (install) {
listener.install(descriptor);
}
else {
listener.uninstall(descriptor);
}
}
});
}
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.plugins;
import org.jetbrains.annotations.NotNull;
/**
* @author Alexander Lobas
*/
public interface PluginStateListener {
void install(@NotNull IdeaPluginDescriptor descriptor);
void uninstall(@NotNull IdeaPluginDescriptor descriptor);
}
@@ -15,6 +15,7 @@
*/
package com.intellij.idea;
import com.intellij.ide.cloudConfig.CloudConfigProvider;
import com.intellij.ide.customize.CustomizeIDEWizardDialog;
import com.intellij.ide.customize.CustomizeIDEWizardStepsProvider;
import com.intellij.ide.plugins.PluginManagerCore;
@@ -83,6 +84,8 @@ public class StartupUtil {
interface AppStarter {
void start(boolean newConfigFolder);
default void beforeImportConfigs() {}
}
static void prepareAndStart(String[] args, AppStarter appStarter) {
@@ -125,6 +128,7 @@ public class StartupUtil {
}
if (newConfigFolder) {
appStarter.beforeImportConfigs();
ConfigImportHelper.importConfigsTo(PathManager.getConfigPath());
}
@@ -422,8 +426,18 @@ public class StartupUtil {
return;
}
CloudConfigProvider configProvider = CloudConfigProvider.getProvider();
if (configProvider != null) {
configProvider.beforeStartupWizard();
}
new CustomizeIDEWizardDialog(provider).show();
PluginManagerCore.invalidatePlugins();
if (configProvider != null) {
configProvider.startupWizardFinished();
}
return;
}
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.application;
import com.intellij.ide.cloudConfig.CloudConfigProvider;
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.ide.plugins.PluginManagerCore;
@@ -79,6 +80,12 @@ public class ConfigImportHelper {
doImport(newConfigDir, oldConfigDir, settings, installationHome);
settings.importFinished(newConfigPath);
System.setProperty(CONFIG_IMPORTED_IN_CURRENT_SESSION_KEY, Boolean.TRUE.toString());
CloudConfigProvider provider = CloudConfigProvider.getProvider();
if (provider != null) {
provider.importFinished(newConfigDir);
}
break;
}
}
@@ -230,7 +237,7 @@ public class ConfigImportHelper {
}
@Nullable
private static File getOldConfigDir(@Nullable File oldInstallHome, ConfigImportSettings settings) {
public static File getOldConfigDir(@Nullable File oldInstallHome, ConfigImportSettings settings) {
if (oldInstallHome == null) {
return null;
}
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.application.ImportOldConfigsPanel">
<grid id="47e28" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="47e28" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="10" left="10" bottom="10" right="10"/>
<constraints>
<xy x="28" y="33" width="566" height="272"/>
<xy x="28" y="33" width="575" height="286"/>
</constraints>
<properties/>
<border type="none"/>
@@ -11,7 +11,7 @@
<grid id="1c1db" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
@@ -38,7 +38,7 @@
</grid>
<vspacer id="ccd5d">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="865b3" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
@@ -123,6 +123,16 @@
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<grid id="ece75" binding="myCustomPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<visible value="false"/>
</properties>
<border type="none"/>
<children/>
</grid>
</children>
</grid>
</form>
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.application;
import com.intellij.ide.cloudConfig.CloudConfigProvider;
import com.intellij.openapi.MnemonicHelper;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.SystemInfo;
@@ -45,6 +46,7 @@ public class ImportOldConfigsPanel extends JDialog {
private JLabel mySuggestLabel;
private JLabel myHomeLabel;
private JRadioButton myRbImportAuto;
private JPanel myCustomPanel;
private final File myGuessedOldConfig;
private final ConfigImportSettings mySettings;
@@ -120,6 +122,11 @@ public class ImportOldConfigsPanel extends JDialog {
}
});
CloudConfigProvider configProvider = CloudConfigProvider.getProvider();
if (configProvider != null) {
configProvider.initConfigsPanel(this, myCustomPanel, mySettings);
}
getContentPane().setLayout(new BorderLayout());
getContentPane().add(myRootPanel);
getRootPane().setDefaultButton(myOkButton);
@@ -218,7 +218,7 @@ public class PluginDownloader {
StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(myOldFile);
StartupActionScriptManager.addActionCommand(deleteOld);
}
PluginInstaller.install(myFile, getPluginName(), true);
PluginInstaller.install(myFile, getPluginName(), true, myDescriptor);
InstalledPluginsState state = InstalledPluginsState.getInstanceIfLoaded();
if (state != null) {
state.onPluginInstall(myDescriptor);