mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[platform] updating ConfigImportHelper and the surroundings (IJPL-198038 preparation)
Pure NIO (most tests are now using the in-memory FS); no more deprecated code; leaner API; typos; formatting. GitOrigin-RevId: e97f7088100ecf191b653202753b320f9bdab12a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
af090daf62
commit
464bc08113
@@ -1,8 +1,7 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.application
|
||||
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.io.NioFiles
|
||||
import com.intellij.util.io.delete
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -26,7 +25,7 @@ class ConfigBackup(private val configDir: Path) {
|
||||
|
||||
val backupPath = getNextBackupPath(configDir)
|
||||
LOG.info("Move backup from $dirToMove to $backupPath")
|
||||
FileUtil.copyDir(dirToMove.toFile(), backupPath.toFile())
|
||||
NioFiles.copyRecursively(dirToMove, backupPath)
|
||||
NioFiles.deleteRecursively(dirToMove)
|
||||
}
|
||||
|
||||
@@ -36,7 +35,7 @@ class ConfigBackup(private val configDir: Path) {
|
||||
val oldBackup = backupDir.resolve("1970-01-01-00-00").createDirectory()
|
||||
for (file in backupDir.listDirectoryEntries()) {
|
||||
if (!file.isDirectory() || !file.name.looksLikeDate()) {
|
||||
FileUtil.copyDir(file.toFile(), oldBackup.resolve(file.name).toFile())
|
||||
NioFiles.copyRecursively(file, oldBackup.resolve(file.name))
|
||||
NioFiles.deleteRecursively(file)
|
||||
}
|
||||
}
|
||||
|
||||
+437
-548
File diff suppressed because it is too large
Load Diff
+1
-14
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.application;
|
||||
|
||||
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
||||
@@ -51,19 +51,6 @@ public interface ConfigImportSettings {
|
||||
@Nullable Map<PluginId, Set<String>> brokenPluginVersions,
|
||||
@NotNull List<IdeaPluginDescriptor> pluginsToMigrate,
|
||||
@NotNull List<IdeaPluginDescriptor> pluginsToDownload
|
||||
) {
|
||||
processPluginsToMigrate(newConfigDir, oldConfigDir, pluginsToMigrate, pluginsToDownload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override {@link #processPluginsToMigrate(Path, Path, Path, ConfigImportHelper.ConfigImportOptions, Map, List, List)} instead
|
||||
*/
|
||||
@ApiStatus.Obsolete
|
||||
default void processPluginsToMigrate(
|
||||
@NotNull Path newConfigDir,
|
||||
@NotNull Path oldConfigDir,
|
||||
@NotNull List<IdeaPluginDescriptor> pluginsToMigrate,
|
||||
@NotNull List<IdeaPluginDescriptor> pluginsToDownload
|
||||
) { }
|
||||
|
||||
/**
|
||||
|
||||
+48
-40
@@ -1,7 +1,8 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.application;
|
||||
|
||||
import com.intellij.ide.BootstrapBundle;
|
||||
import com.intellij.ide.actions.ImportSettingsFilenameFilter;
|
||||
import com.intellij.openapi.MnemonicHelper;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.fileChooser.impl.FileChooserFactoryImpl;
|
||||
@@ -10,26 +11,23 @@ import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.local.CoreLocalFileSystem;
|
||||
import com.intellij.openapi.vfs.local.CoreLocalVirtualFile;
|
||||
import com.intellij.ui.CollectionComboBoxModel;
|
||||
import com.intellij.ui.ComponentUtil;
|
||||
import com.intellij.util.system.OS;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
final class ImportOldConfigsPanel extends JDialog {
|
||||
private JPanel myRootPanel;
|
||||
@@ -41,12 +39,12 @@ final class ImportOldConfigsPanel extends JDialog {
|
||||
private ComboBox<Path> myComboBoxOldPaths;
|
||||
|
||||
private final List<Path> myGuessedOldConfigDirs;
|
||||
private final Function<? super Path, ? extends Pair<Path, Path>> myValidator;
|
||||
private final Function<Path, Pair<Path, Path>> myValidator;
|
||||
private final String myProductName;
|
||||
private Path myLastSelection = null;
|
||||
private Pair<Path, Path> myResult;
|
||||
|
||||
ImportOldConfigsPanel(List<Path> guessedOldConfigDirs, Function<? super Path, ? extends Pair<Path, Path>> validator) {
|
||||
ImportOldConfigsPanel(@NotNull List<Path> guessedOldConfigDirs, @NotNull Function<Path, Pair<Path, Path>> validator) {
|
||||
super((Dialog)null, true);
|
||||
|
||||
ComponentUtil.decorateWindowHeader(rootPane);
|
||||
@@ -61,7 +59,7 @@ final class ImportOldConfigsPanel extends JDialog {
|
||||
private void init() {
|
||||
MnemonicHelper.init(getContentPane());
|
||||
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
var group = new ButtonGroup();
|
||||
group.add(myRbImportAuto);
|
||||
group.add(myRbImport);
|
||||
group.add(myRbDoNotImport);
|
||||
@@ -73,51 +71,53 @@ final class ImportOldConfigsPanel extends JDialog {
|
||||
}
|
||||
else {
|
||||
myComboBoxOldPaths.setModel(new CollectionComboBoxModel<>(myGuessedOldConfigDirs));
|
||||
myComboBoxOldPaths.setSelectedItem(myGuessedOldConfigDirs.get(0));
|
||||
myComboBoxOldPaths.setSelectedItem(myGuessedOldConfigDirs.getFirst());
|
||||
myRbImportAuto.setSelected(true);
|
||||
}
|
||||
for (Enumeration<AbstractButton> e = group.getElements(); e.hasMoreElements(); ) {
|
||||
for (var e = group.getElements(); e.hasMoreElements(); ) {
|
||||
e.nextElement().addChangeListener(event -> update());
|
||||
}
|
||||
if (SystemInfo.isMac) {
|
||||
myLastSelection = Paths.get("/Applications");
|
||||
if (OS.CURRENT == OS.macOS) {
|
||||
myLastSelection = Path.of("/Applications");
|
||||
}
|
||||
else if (SystemInfo.isWindows) {
|
||||
String programFiles = System.getenv("ProgramFiles");
|
||||
else if (OS.CURRENT == OS.Windows) {
|
||||
var programFiles = System.getenv("ProgramFiles");
|
||||
if (programFiles != null) {
|
||||
Path candidate = Paths.get(programFiles, "JetBrains");
|
||||
myLastSelection = Files.isDirectory(candidate) ? candidate : Paths.get(programFiles);
|
||||
var candidate = Path.of(programFiles, "JetBrains");
|
||||
myLastSelection = Files.isDirectory(candidate) ? candidate : Path.of(programFiles);
|
||||
}
|
||||
}
|
||||
myPrevInstallation.setTextFieldPreferredWidth(50);
|
||||
myPrevInstallation.addActionListener(e -> {
|
||||
var chooserDescriptor = FileChooserDescriptorFactory.createSingleLocalFileDescriptor().withHideIgnored(false);
|
||||
var chooserDescriptor = FileChooserDescriptorFactory.singleFile().withHideIgnored(false);
|
||||
ConfigImportHelper.setSettingsFilter(chooserDescriptor);
|
||||
var fileRef = Ref.<File>create();
|
||||
var fileRef = Ref.<Path>create();
|
||||
var chooser = FileChooserFactoryImpl.createNativePathChooserIfEnabled(chooserDescriptor, null, myRootPanel);
|
||||
if (chooser != null) {
|
||||
VirtualFile vf = myLastSelection != null ? new CoreLocalVirtualFile(new CoreLocalFileSystem(), myLastSelection) : null;
|
||||
chooser.choose(vf, files -> fileRef.set(new File(files.get(0).getPresentableUrl())));
|
||||
var vf = myLastSelection != null ? new CoreLocalVirtualFile(new CoreLocalFileSystem(), myLastSelection) : null;
|
||||
chooser.choose(vf, files -> fileRef.set(Path.of(files.getFirst().getPresentableUrl())));
|
||||
}
|
||||
else {
|
||||
JFileChooser fc = new JFileChooser(myLastSelection != null ? myLastSelection.getParent().toFile() : null);
|
||||
fc.setSelectedFile(myLastSelection != null ? myLastSelection.toFile() : null);
|
||||
@SuppressWarnings("IO_FILE_USAGE") var directory = myLastSelection != null ? myLastSelection.getParent().toFile() : null;
|
||||
@SuppressWarnings("IO_FILE_USAGE") var selectedFile = myLastSelection != null ? myLastSelection.toFile() : null;
|
||||
var fc = new JFileChooser();
|
||||
fc.setCurrentDirectory(directory);
|
||||
fc.setSelectedFile(selectedFile);
|
||||
fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
|
||||
fc.setFileHidingEnabled(SystemInfo.isWindows || SystemInfo.isMac);
|
||||
fc.setFileHidingEnabled(OS.CURRENT == OS.Windows || OS.CURRENT == OS.macOS);
|
||||
fc.setFileFilter(new FileNameExtensionFilter(BootstrapBundle.message("import.settings.filter"), "zip", "jar"));
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
int returnVal = fc.showOpenDialog(this);
|
||||
var returnVal = fc.showOpenDialog(this);
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
File file = fc.getSelectedFile();
|
||||
var file = fc.getSelectedFile();
|
||||
if (file != null) {
|
||||
fileRef.set(file);
|
||||
fileRef.set(file.toPath());
|
||||
myPrevInstallation.setText(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fileRef.isNull()) {
|
||||
myLastSelection = fileRef.get().toPath();
|
||||
myPrevInstallation.setText(fileRef.get().getAbsolutePath());
|
||||
myLastSelection = fileRef.get();
|
||||
myPrevInstallation.setText(fileRef.get().toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -140,29 +140,28 @@ final class ImportOldConfigsPanel extends JDialog {
|
||||
|
||||
private void close() {
|
||||
if (myRbImport.isSelected()) {
|
||||
String text = myPrevInstallation.getText();
|
||||
if (StringUtil.isEmptyOrSpaces(text)) {
|
||||
var text = myPrevInstallation.getText().trim();
|
||||
if (text.isEmpty()) {
|
||||
showError(BootstrapBundle.message("import.chooser.error.empty", myProductName));
|
||||
return;
|
||||
}
|
||||
|
||||
Path selectedDir = Paths.get(FileUtil.toCanonicalPath(text.trim()));
|
||||
var selectedDir = Path.of(text).toAbsolutePath().normalize();
|
||||
|
||||
if (Files.isRegularFile(selectedDir)) {
|
||||
if (!ConfigImportHelper.isValidSettingsFile(selectedDir.toFile())) {
|
||||
if (!isValidSettingsFile(selectedDir)) {
|
||||
showError(BootstrapBundle.message("import.chooser.error.invalid", selectedDir));
|
||||
return;
|
||||
}
|
||||
myResult = new Pair<>(selectedDir, null);
|
||||
}
|
||||
else {
|
||||
if (FileUtil.pathsEqual(selectedDir.toString(), PathManager.getHomePath()) ||
|
||||
FileUtil.pathsEqual(selectedDir.toString(), PathManager.getConfigPath())) {
|
||||
if (selectedDir.equals(PathManager.getHomeDir()) || selectedDir.equals(PathManager.getConfigDir())) {
|
||||
showError(BootstrapBundle.message("import.chooser.error.current", myProductName));
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<Path, Path> result = myValidator.apply(selectedDir);
|
||||
var result = myValidator.apply(selectedDir);
|
||||
if (result == null) {
|
||||
showError(BootstrapBundle.message("import.chooser.error.unrecognized", selectedDir, myProductName));
|
||||
return;
|
||||
@@ -176,10 +175,19 @@ final class ImportOldConfigsPanel extends JDialog {
|
||||
}
|
||||
|
||||
private void showError(@NlsContexts.DialogMessage String message) {
|
||||
String title = BootstrapBundle.message("import.chooser.error.title");
|
||||
var title = BootstrapBundle.message("import.chooser.error.title");
|
||||
JOptionPane.showMessageDialog(this, message, title, JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
private static boolean isValidSettingsFile(Path file) {
|
||||
try (@SuppressWarnings("IO_FILE_USAGE") var zip = new ZipFile(file.toFile())) {
|
||||
return zip.getEntry(ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER) != null;
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable Pair<Path, Path> getSelectedFile() {
|
||||
ImportOldConfigsState.Companion.getInstance().saveImportOldConfigType(myRbImportAuto, myRbImport, myRbDoNotImport, myResult != null);
|
||||
|
||||
|
||||
+27
-40
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.application
|
||||
|
||||
import com.intellij.openapi.application.ConfigBackup.Companion.MAX_BACKUPS_NUMBER
|
||||
import com.intellij.util.io.write
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
@@ -18,37 +17,33 @@ class ConfigBackupTest : ConfigImportHelperBaseTest() {
|
||||
private lateinit var configDir: Path
|
||||
private lateinit var backupDir: Path
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
dirToBackup = createConfigDirToBackup()
|
||||
configDir = localTempDir.rootPath.resolve(CONFIG_PREFIX)
|
||||
backupDir = configDir.resolveSibling("$CONFIG_PREFIX-backup")
|
||||
@Before fun setup() {
|
||||
dirToBackup = newTempDir("temp-settings").apply { resolve("options/config.xml").write("config data") }
|
||||
configDir = dirToBackup.resolveSibling(CONFIG_PREFIX)
|
||||
backupDir = dirToBackup.resolveSibling("${CONFIG_PREFIX}-backup")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `next backup path`() {
|
||||
@Test fun `next backup path`() {
|
||||
val now = LocalDateTime.now()
|
||||
val date = getDateFormattedForBackupDir(now)
|
||||
val dir = memoryFs.fs.getPath("${PathManager.getConfigPath()}-backup").resolve(date)
|
||||
val dir = memoryFs.fs.getPath("${PathManager.getConfigDir()}-backup").resolve(date)
|
||||
|
||||
val path = ConfigBackup.getNextBackupPath(memoryFs.fs.getPath(PathManager.getConfigPath()), now)
|
||||
val path = ConfigBackup.getNextBackupPath(memoryFs.fs.getPath(PathManager.getConfigDir().toString()), now)
|
||||
assertEquals("Next backup path is incorrect", dir, path)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `make simple backup`() {
|
||||
@Test fun `make simple backup`() {
|
||||
moveDirToBackup()
|
||||
|
||||
assertTrue("Backup dir doesn't exist", backupDir.exists())
|
||||
val child = backupDir.getSingleChild()
|
||||
val backedupDir = child.getSingleChild()
|
||||
assertEquals("Wrong backed up dir", "options", backedupDir.name)
|
||||
val backedupFile = backedupDir.getSingleChild()
|
||||
assertFile(backedupFile, "config.xml", "config data")
|
||||
val backedUpDir = child.getSingleChild()
|
||||
assertEquals("Wrong backed up dir", "options", backedUpDir.name)
|
||||
val backedUpFile = backedUpDir.getSingleChild()
|
||||
assertFile(backedUpFile, "config.xml", "config data")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `migrate previous backup format`() {
|
||||
@Test fun `migrate previous backup format`() {
|
||||
val optionsDir = backupDir.resolve("options").createDirectories()
|
||||
optionsDir.resolve("other.xml").createFile().writeText("old content")
|
||||
val inspectionsDir = backupDir.resolve("inspections").createDirectories()
|
||||
@@ -71,16 +66,15 @@ class ConfigBackupTest : ConfigImportHelperBaseTest() {
|
||||
val migratedOptionsFile = migratedOptions.getSingleChild()
|
||||
assertFile(migratedOptionsFile, "other.xml", "old content")
|
||||
|
||||
val backedupDir = children[1].getSingleChild()
|
||||
assertEquals("Wrong backed up dir", "options", backedupDir.name)
|
||||
val backedupFile = backedupDir.getSingleChild()
|
||||
assertFile(backedupFile, "config.xml", "config data")
|
||||
val backedUpDir = children[1].getSingleChild()
|
||||
assertEquals("Wrong backed up dir", "options", backedUpDir.name)
|
||||
val backedUpFile = backedUpDir.getSingleChild()
|
||||
assertFile(backedUpFile, "config.xml", "config data")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cleanup backups if there are too many of them`() {
|
||||
@Test fun `cleanup backups if there are too many of them`() {
|
||||
val now = LocalDateTime.now()
|
||||
for (i in 1..MAX_BACKUPS_NUMBER) {
|
||||
for (i in 1..ConfigBackup.MAX_BACKUPS_NUMBER) {
|
||||
val date = getDateFormattedForBackupDir(now.minusDays(i.toLong()))
|
||||
createBackupDirForDate(date)
|
||||
}
|
||||
@@ -88,13 +82,12 @@ class ConfigBackupTest : ConfigImportHelperBaseTest() {
|
||||
moveDirToBackup()
|
||||
|
||||
val children = backupDir.listDirectoryEntries().sortedBy { it.name }
|
||||
assertEquals("Unexpected number of entries inside $backupDir: $children", MAX_BACKUPS_NUMBER, children.size)
|
||||
val oldestDate = getDateFormattedForBackupDir(now.minusDays(MAX_BACKUPS_NUMBER.toLong()))
|
||||
assertEquals("Unexpected number of entries inside $backupDir: $children", ConfigBackup.MAX_BACKUPS_NUMBER, children.size)
|
||||
val oldestDate = getDateFormattedForBackupDir(now.minusDays(ConfigBackup.MAX_BACKUPS_NUMBER.toLong()))
|
||||
assertFalse("The oldest dir should have been deleted", children.any { it.name == oldestDate })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create backup with index if there is already folder with current date`() {
|
||||
@Test fun `create backup with index if there is already folder with current date`() {
|
||||
// during the test this date can become not now, i.e. non-conflicting with the next backup, effectively making the test useless,
|
||||
// however, it is ok if the test will be useful
|
||||
val now = LocalDateTime.now()
|
||||
@@ -111,10 +104,10 @@ class ConfigBackupTest : ConfigImportHelperBaseTest() {
|
||||
val children = backupDir.listDirectoryEntries().sortedBy { it.name }
|
||||
assertEquals("Unexpected number of entries inside $backupDir: $children", 4, children.size)
|
||||
val createdDir = children.find { it.name !in dates }!!
|
||||
val backedupDir = createdDir.getSingleChild()
|
||||
assertEquals("Wrong backed up dir", "options", backedupDir.name)
|
||||
val backedupFile = backedupDir.getSingleChild()
|
||||
assertFile(backedupFile, "config.xml", "config data")
|
||||
val backedUpDir = createdDir.getSingleChild()
|
||||
assertEquals("Wrong backed up dir", "options", backedUpDir.name)
|
||||
val backedUpFile = backedUpDir.getSingleChild()
|
||||
assertFile(backedUpFile, "config.xml", "config data")
|
||||
}
|
||||
|
||||
private fun moveDirToBackup() {
|
||||
@@ -123,12 +116,6 @@ class ConfigBackupTest : ConfigImportHelperBaseTest() {
|
||||
|
||||
private fun createBackupDirForDate(date1: String): Path = backupDir.resolve(date1).createDirectories()
|
||||
|
||||
private fun createConfigDirToBackup(): Path {
|
||||
val configDir = localTempDir.newDirectoryPath("temp-settings")
|
||||
configDir.resolve("options/config.xml").write("config data")
|
||||
return configDir
|
||||
}
|
||||
|
||||
private fun getDateFormattedForBackupDir(dateTime: LocalDateTime): String =
|
||||
dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm"))
|
||||
|
||||
|
||||
+13
-12
@@ -1,29 +1,30 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.application
|
||||
|
||||
import com.intellij.openapi.application.ConfigImportHelper.ConfigImportOptions.BrokenPluginsFetcher
|
||||
import com.intellij.openapi.application.ConfigImportHelper.ConfigImportOptions.LastCompatiblePluginUpdatesFetcher
|
||||
import com.intellij.openapi.application.ConfigImportHelper.findConfigDirectories
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.testFramework.fixtures.BareTestFixtureTestCase
|
||||
import com.intellij.testFramework.rules.InMemoryFsRule
|
||||
import com.intellij.testFramework.rules.TempDirectory
|
||||
import com.intellij.util.SystemProperties
|
||||
import com.intellij.util.system.OS
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.ExternalResource
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.util.function.Function
|
||||
|
||||
abstract class ConfigImportHelperBaseTest : BareTestFixtureTestCase() {
|
||||
@JvmField @Rule val memoryFs = InMemoryFsRule(SystemInfo.isWindows)
|
||||
@JvmField @Rule val localTempDir = TempDirectory()
|
||||
@JvmField @Rule val memoryFs = InMemoryFsRule(windows = OS.CURRENT == OS.Windows)
|
||||
@JvmField @Rule val configImportMarketplaceStub = ConfigImportMarketplaceStub()
|
||||
|
||||
protected fun newTempDir(name: String): Path =
|
||||
Files.createDirectories(memoryFs.fs.getPath("_temp", name).toAbsolutePath())
|
||||
|
||||
protected fun createConfigDir(version: String, modern: Boolean = version >= "2020.1", product: String = "IntelliJIdea", storageTS: Long = 0): Path {
|
||||
val path = when {
|
||||
modern -> PathManager.getDefaultConfigPathFor("${product}${version}")
|
||||
SystemInfo.isMac -> "${SystemProperties.getUserHome()}/Library/Preferences/${product}${version}"
|
||||
OS.CURRENT == OS.macOS -> "${SystemProperties.getUserHome()}/Library/Preferences/${product}${version}"
|
||||
else -> "${SystemProperties.getUserHome()}/.${product}${version}/config"
|
||||
}
|
||||
val dir = Files.createDirectories(memoryFs.fs.getPath(path).normalize())
|
||||
@@ -34,19 +35,19 @@ abstract class ConfigImportHelperBaseTest : BareTestFixtureTestCase() {
|
||||
protected fun writeStorageFile(config: Path, lastModified: Long) {
|
||||
val file = config.resolve("${PathManager.OPTIONS_DIRECTORY}/${StoragePathMacros.NON_ROAMABLE_FILE}")
|
||||
Files.createDirectories(file.parent)
|
||||
Files.write(file, "<application/>".toByteArray())
|
||||
Files.writeString(file, "<application/>")
|
||||
Files.setLastModifiedTime(file, FileTime.fromMillis(lastModified))
|
||||
}
|
||||
|
||||
protected fun findConfigDirectories(newConfigPath: Path): List<Path> = ConfigImportHelper.findConfigDirectories(newConfigPath).paths
|
||||
protected fun findConfigDirectories(newConfigPath: Path): List<Path> = findConfigDirectories(newConfigPath, null, emptyList()).paths
|
||||
|
||||
// disables broken plugins fetcher from the Marketplace by default
|
||||
class ConfigImportMarketplaceStub : ExternalResource() {
|
||||
override fun before() {
|
||||
assert(ConfigImportHelper.testBrokenPluginsFetcherStub == null)
|
||||
ConfigImportHelper.testBrokenPluginsFetcherStub = BrokenPluginsFetcher { null } // force use of brokenPlugins from the distribution
|
||||
ConfigImportHelper.testBrokenPluginsFetcherStub = Function { null } // using broken plugins from the distribution
|
||||
assert(ConfigImportHelper.testLastCompatiblePluginUpdatesFetcher == null)
|
||||
ConfigImportHelper.testLastCompatiblePluginUpdatesFetcher = LastCompatiblePluginUpdatesFetcher { null }
|
||||
ConfigImportHelper.testLastCompatiblePluginUpdatesFetcher = Function { null }
|
||||
}
|
||||
|
||||
fun unset() {
|
||||
|
||||
+113
-122
@@ -12,8 +12,6 @@ import com.intellij.ide.startup.StartupActionScriptManager
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.idea.TestFor
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ConfigImportHelper.ConfigImportOptions.BrokenPluginsFetcher
|
||||
import com.intellij.openapi.application.ConfigImportHelper.ConfigImportOptions.LastCompatiblePluginUpdatesFetcher
|
||||
import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.components.impl.stores.stateStore
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
@@ -22,7 +20,6 @@ import com.intellij.openapi.observable.util.setSystemProperty
|
||||
import com.intellij.openapi.observable.util.whenDisposed
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.util.BuildNumber
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.testFramework.plugins.buildMainJar
|
||||
import com.intellij.platform.testFramework.plugins.buildZip
|
||||
@@ -34,31 +31,28 @@ import com.intellij.testFramework.replaceService
|
||||
import com.intellij.util.SystemProperties
|
||||
import com.intellij.util.io.createDirectories
|
||||
import com.intellij.util.queryParameters
|
||||
import com.intellij.util.system.OS
|
||||
import com.sun.net.httpserver.HttpServer
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Condition
|
||||
import org.junit.Assume.assumeTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.io.IOException
|
||||
import java.net.InetSocketAddress
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.function.Function
|
||||
import java.util.function.Predicate
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.listDirectoryEntries
|
||||
import kotlin.io.path.readLines
|
||||
import kotlin.io.path.writeLines
|
||||
import kotlin.io.path.*
|
||||
import kotlin.test.fail
|
||||
|
||||
private val LOG = logger<ConfigImportHelperTest>()
|
||||
|
||||
class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
val options = ConfigImportHelper.ConfigImportOptions(LOG).apply { isHeadless = true; }
|
||||
val options = ConfigImportHelper.ConfigImportOptions(LOG).apply { headless = true }
|
||||
|
||||
@Test fun `config directory is valid for import`() {
|
||||
PropertiesComponent.getInstance().setValue("property.ConfigImportHelperTest", true)
|
||||
@@ -141,7 +135,7 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
}
|
||||
|
||||
private fun doKeyMapTest(version: String, isMigrationExpected: Boolean) {
|
||||
assumeTrue("macOS-only", SystemInfo.isMac)
|
||||
assumeTrue("macOS-only", OS.CURRENT == OS.macOS)
|
||||
|
||||
val oldConfigDir = createConfigDir(version, product = "DataGrip")
|
||||
val newConfigDir = createConfigDir("2019.2", product = "DataGrip")
|
||||
@@ -200,69 +194,67 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
}
|
||||
|
||||
@Test fun `migrate plugins to empty directory`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
plugin("my-plugin") { dependsIntellijModulesLang() }.buildMainJar(oldPluginsDir.resolve("my-plugin.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldPluginsDir, newPluginsDir, options)
|
||||
assertThat(newPluginsDir).isDirectoryContaining { it.fileName.toString() == "my-plugin.jar" }
|
||||
assertThat(newPluginsDir).isDirectoryContaining { it.name == "my-plugin.jar" }
|
||||
}
|
||||
|
||||
@Test fun `download incompatible plugin`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
plugin("my-plugin") {
|
||||
dependsIntellijModulesLang()
|
||||
untilBuild = "193.1"
|
||||
}.buildMainJar(oldPluginsDir.resolve("my-plugin.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
Registry.get("marketplace.certificate.signature.check").setValue(false, testRootDisposable) // skip verifying plugin certificates
|
||||
options.compatibleBuildNumber = BuildNumber.fromString("201.1")
|
||||
options.downloadService = object : MarketplacePluginDownloadService() {
|
||||
override fun downloadPlugin(pluginUrl: String, indicator: ProgressIndicator?): Path {
|
||||
val path = localTempDir.newDirectory("pluginTemp").toPath().resolve("my-plugin-new.jar")
|
||||
val path = newTempDir("pluginTemp").resolve("my-plugin-new.jar")
|
||||
plugin("my-plugin") { dependsIntellijModulesLang() }.buildMainJar(path)
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldPluginsDir, newPluginsDir, options)
|
||||
assertThat(newPluginsDir).isDirectoryContaining { it.fileName.toString() == "my-plugin-new.jar" }
|
||||
assertThat(newPluginsDir).isDirectoryContaining { it.name == "my-plugin-new.jar" }
|
||||
}
|
||||
|
||||
@Test fun `keep incompatible plugin if can't download compatible`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
plugin("my-plugin") {
|
||||
dependsIntellijModulesLang()
|
||||
untilBuild = "193.1"
|
||||
}.buildMainJar(oldPluginsDir.resolve("my-plugin.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
options.compatibleBuildNumber = BuildNumber.fromString("201.1")
|
||||
options.downloadService = object : MarketplacePluginDownloadService() {
|
||||
|
||||
override fun downloadPlugin(pluginUrl: String, indicator: ProgressIndicator?) =
|
||||
throw IOException("404")
|
||||
override fun downloadPlugin(pluginUrl: String, indicator: ProgressIndicator?) = throw IOException("404")
|
||||
}
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldPluginsDir, newPluginsDir, options)
|
||||
assertThat(newPluginsDir).isDirectoryContaining { it.fileName.toString() == "my-plugin.jar" }
|
||||
assertThat(newPluginsDir).isDirectoryContaining { it.name == "my-plugin.jar" }
|
||||
}
|
||||
|
||||
@Test fun `skip bundled plugins`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
val oldBundledPluginsDir = localTempDir.newDirectory("oldBundled").toPath()
|
||||
val oldBundledPluginsDir = newTempDir("oldBundled")
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.1" }.buildMainJar(oldBundledPluginsDir.resolve("my-plugin-bundled.jar"))
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.0" }.buildMainJar(oldPluginsDir.resolve("my-plugin.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
options.bundledPluginPath = oldBundledPluginsDir
|
||||
@@ -271,22 +263,22 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
}
|
||||
|
||||
@Test fun `skip broken plugins`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.0" }.buildMainJar(oldPluginsDir.resolve("my-plugin.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
options.brokenPluginsFetcher = BrokenPluginsFetcher { mapOf(PluginId.getId("my-plugin") to setOf("1.0")) }
|
||||
ConfigImportHelper.testBrokenPluginsFetcherStub = Function { mapOf(PluginId.getId("my-plugin") to setOf("1.0")) }
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldPluginsDir, newPluginsDir, options)
|
||||
assertThat(newPluginsDir).doesNotExist()
|
||||
}
|
||||
|
||||
@Test fun `skip pending upgrades`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("old/config").toPath()
|
||||
val oldConfigDir = newTempDir("old/config")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
val oldPluginsTempDir = localTempDir.newDirectory("old/system/plugins").toPath()
|
||||
val oldPluginsTempDir = newTempDir("old/system/plugins")
|
||||
|
||||
val tempPath = oldPluginsTempDir.resolve("my-plugin.jar")
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.1" }.buildMainJar(tempPath)
|
||||
@@ -296,19 +288,19 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.0" }.buildMainJar(oldPluginsDir.resolve("my-plugin-1.0.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldPluginsDir, newPluginsDir, options)
|
||||
assertThat(newPluginsDir)
|
||||
.isDirectoryContaining { it.fileName.toString() == "my-plugin-1.1.jar" }
|
||||
.isDirectoryNotContaining { it.fileName.toString() == "my-plugin-1.0.jar" }
|
||||
.isDirectoryContaining { it.name == "my-plugin-1.1.jar" }
|
||||
.isDirectoryNotContaining { it.name == "my-plugin-1.0.jar" }
|
||||
}
|
||||
|
||||
@Test fun `do not download updates for plugins with pending updates`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("old/config").toPath()
|
||||
val oldConfigDir = newTempDir("old/config")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
val oldPluginsTempDir = localTempDir.newDirectory("old/system/plugins").toPath()
|
||||
val oldPluginsTempDir = newTempDir("old/system/plugins")
|
||||
|
||||
val tempPath = oldPluginsTempDir.resolve("my-plugin.jar")
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.1" }.buildMainJar(tempPath)
|
||||
@@ -322,7 +314,7 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
untilBuild = "193.1"
|
||||
}.buildMainJar(oldPluginsDir.resolve("my-plugin-1.0.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
options.compatibleBuildNumber = BuildNumber.fromString("201.1")
|
||||
@@ -333,14 +325,14 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
}
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldPluginsDir, newPluginsDir, options)
|
||||
assertThat(newPluginsDir)
|
||||
.isDirectoryContaining { it.fileName.toString() == "my-plugin-1.1.jar" }
|
||||
.isDirectoryNotContaining { it.fileName.toString() == "my-plugin-1.0.jar" }
|
||||
.isDirectoryContaining { it.name == "my-plugin-1.1.jar" }
|
||||
.isDirectoryNotContaining { it.name == "my-plugin-1.0.jar" }
|
||||
}
|
||||
|
||||
@Test fun `skip pending upgrades for plugin zips`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("old/config").toPath()
|
||||
val oldConfigDir = newTempDir("old/config")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
val oldPluginsTempDir = localTempDir.newDirectory("old/system/plugins").toPath()
|
||||
val oldPluginsTempDir = newTempDir("old/system/plugins")
|
||||
|
||||
val tempPath = oldPluginsTempDir.resolve("my-plugin.zip")
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.1" }.buildZip(tempPath)
|
||||
@@ -350,21 +342,21 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.0" }.buildMainJar(oldPluginsDir.resolve("my-plugin-1.0.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldPluginsDir, newPluginsDir, options)
|
||||
assertThat(newPluginsDir)
|
||||
.isDirectoryContaining { it.fileName.toString() == "my-plugin" && it.isDirectory() }
|
||||
.isDirectoryNotContaining { it.fileName.toString() == "my-plugin-1.0.jar" }
|
||||
.isDirectoryContaining { it.name == "my-plugin" && it.isDirectory() }
|
||||
.isDirectoryNotContaining { it.name == "my-plugin-1.0.jar" }
|
||||
}
|
||||
|
||||
@Test fun `do not migrate plugins to existing directory`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
val oldPluginZip = Files.createFile(oldPluginsDir.resolve("old-plugin.zip"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = Files.createDirectories(newConfigDir.resolve("plugins"))
|
||||
val newPluginZip = Files.createFile(newPluginsDir.resolve("new-plugin.zip"))
|
||||
|
||||
@@ -376,11 +368,11 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
}
|
||||
|
||||
@Test fun `filtering custom VM options`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
@Suppress("SpellCheckingInspection") val outlaws = listOf(
|
||||
"-XX:MaxJavaStackTraceDepth=-1", "-Xverify:none", "-noverify", "-agentlib:yjpagent=opts", "-agentpath:/path/to/lib-yjpagent.so=opts")
|
||||
Files.write(oldConfigDir.resolve(VMOptions.getFileName()), outlaws)
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldConfigDir.resolve("plugins"), newConfigDir.resolve("plugins"), options)
|
||||
|
||||
@@ -390,7 +382,7 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
@Test fun `de-duplicating custom VM options`() {
|
||||
val platformOptions = listOf("-Xms128m", "-Xmx750m", "-XX:ReservedCodeCacheSize=512m", "-XX:+UseG1GC")
|
||||
val userOptions = listOf("-Xms512m", "-Xmx2g", "-XX:ReservedCodeCacheSize=240m", "-XX:+UseZGC")
|
||||
@Suppress("SpellCheckingInspection") val commonOptions = listOf(
|
||||
val commonOptions = listOf(
|
||||
"-XX:SoftRefLRUPolicyMSPerMB=50", "-XX:CICompilerCount=2", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:-OmitStackTraceInFastThrow",
|
||||
"-ea", "-Dsun.io.useCanonCaches=false", "-Djdk.http.auth.tunneling.disabledSchemes=\"\"", "-Djdk.attach.allowAttachSelf=true",
|
||||
"-Djdk.module.illegalAccess.silent=true", "-Dkotlinx.coroutines.debug=off")
|
||||
@@ -418,37 +410,37 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
|
||||
val cfg191 = createConfigDir("2019.1")
|
||||
populate(cfg191, null, null, null)
|
||||
if (!SystemInfo.isMac) {
|
||||
if (OS.CURRENT != OS.macOS) {
|
||||
Files.writeString(cfg191.parent.resolve("some_file.txt"), "...")
|
||||
}
|
||||
|
||||
val cfg192 = createConfigDir("2019.2")
|
||||
populate(cfg192, null, null, null)
|
||||
val expected192 = when {
|
||||
SystemInfo.isMac -> listOf(cfg192)
|
||||
val expected192 = when (OS.CURRENT) {
|
||||
OS.macOS -> listOf(cfg192)
|
||||
else -> listOf(cfg192.parent)
|
||||
}
|
||||
|
||||
val cfg193 = createConfigDir("2019.3")
|
||||
val plugins193 = when {
|
||||
SystemInfo.isMac -> cfg193.parent.parent.resolve("Application Support").resolve(cfg193.fileName)
|
||||
val plugins193 = when (OS.CURRENT) {
|
||||
OS.macOS -> cfg193.parent.parent.resolve("Application Support").resolve(cfg193.fileName)
|
||||
else -> cfg193.resolve("plugins")
|
||||
}
|
||||
val sys193 = when {
|
||||
SystemInfo.isMac -> cfg193.parent.parent.resolve("Caches").resolve(cfg193.fileName)
|
||||
val sys193 = when (OS.CURRENT) {
|
||||
OS.macOS -> cfg193.parent.parent.resolve("Caches").resolve(cfg193.fileName)
|
||||
else -> cfg193.parent.resolve("system")
|
||||
}
|
||||
val logs193 = when {
|
||||
SystemInfo.isMac -> cfg193.parent.parent.resolve("Logs").resolve(cfg193.fileName)
|
||||
val logs193 = when (OS.CURRENT) {
|
||||
OS.macOS -> cfg193.parent.parent.resolve("Logs").resolve(cfg193.fileName)
|
||||
else -> sys193.resolve("logs")
|
||||
}
|
||||
populate(cfg193, plugins193, sys193, logs193)
|
||||
val expected193 = when {
|
||||
SystemInfo.isMac -> listOf(cfg193, sys193, plugins193, logs193)
|
||||
val expected193 = when (OS.CURRENT) {
|
||||
OS.macOS -> listOf(cfg193, sys193, plugins193, logs193)
|
||||
else -> listOf(cfg193.parent)
|
||||
}
|
||||
val cachesAndLogs193 = when {
|
||||
SystemInfo.isMac -> listOf(sys193, logs193)
|
||||
val cachesAndLogs193 = when (OS.CURRENT) {
|
||||
OS.macOS -> listOf(sys193, logs193)
|
||||
else -> listOf(sys193)
|
||||
}
|
||||
|
||||
@@ -456,26 +448,26 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
populate(cfg201, null, null, null)
|
||||
|
||||
val cfg202 = createConfigDir("2020.2")
|
||||
val sys202 = cfg202.fileSystem.getPath(PathManager.getDefaultSystemPathFor(cfg202.fileName.toString()))
|
||||
val sys202 = cfg202.fileSystem.getPath(PathManager.getDefaultSystemPathFor(cfg202.name))
|
||||
populate(cfg202, null, sys202, null)
|
||||
|
||||
val cfg203 = createConfigDir("2020.3")
|
||||
val sys203 = cfg203.fileSystem.getPath(PathManager.getDefaultSystemPathFor(cfg203.fileName.toString()))
|
||||
val plugins203 = cfg203.fileSystem.getPath(PathManager.getDefaultPluginPathFor(cfg203.fileName.toString()))
|
||||
val logs203 = cfg203.fileSystem.getPath(PathManager.getDefaultLogPathFor(cfg203.fileName.toString()))
|
||||
val sys203 = cfg203.fileSystem.getPath(PathManager.getDefaultSystemPathFor(cfg203.name))
|
||||
val plugins203 = cfg203.fileSystem.getPath(PathManager.getDefaultPluginPathFor(cfg203.name))
|
||||
val logs203 = cfg203.fileSystem.getPath(PathManager.getDefaultLogPathFor(cfg203.name))
|
||||
populate(cfg203, plugins203, sys203, logs203)
|
||||
val expected203 = when {
|
||||
SystemInfo.isWindows -> listOf(cfg203, sys203)
|
||||
SystemInfo.isMac -> listOf(cfg203, sys203, logs203)
|
||||
val expected203 = when (OS.CURRENT) {
|
||||
OS.Windows -> listOf(cfg203, sys203)
|
||||
OS.macOS -> listOf(cfg203, sys203, logs203)
|
||||
else -> listOf(cfg203, sys203, plugins203)
|
||||
}
|
||||
val cachesAndLogs203 = when {
|
||||
SystemInfo.isMac -> listOf(sys203, logs203)
|
||||
OS.CURRENT == OS.macOS -> listOf(sys203, logs203)
|
||||
else -> listOf(sys203)
|
||||
}
|
||||
|
||||
val current = createConfigDir("2021.2")
|
||||
val result = ConfigImportHelper.findConfigDirectories(current)
|
||||
val result = ConfigImportHelper.findConfigDirectories(current, null, emptyList())
|
||||
assertThat(result.paths).containsExactlyInAnyOrder(cfg191, cfg192, cfg193, cfg201, cfg202, cfg203)
|
||||
|
||||
val related = result.paths.map { result.findRelatedDirectories(it, false) }
|
||||
@@ -491,21 +483,21 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
val defaultProjectPath = "${SystemProperties.getUserHome()}/PhpstormProjects"
|
||||
Files.createDirectories(memoryFs.fs.getPath(defaultProjectPath))
|
||||
val current = createConfigDir("2021.2", product = "PhpStorm")
|
||||
val result = ConfigImportHelper.findConfigDirectories(current)
|
||||
val result = ConfigImportHelper.findConfigDirectories(current, null, emptyList())
|
||||
assertThat(result.paths).isEmpty()
|
||||
}
|
||||
|
||||
@Test fun `suffix-less directories are excluded`() {
|
||||
createConfigDir(product = "Rider", version = "", modern = true)
|
||||
val current = createConfigDir(product = "Rider", version = "2022.1")
|
||||
val result = ConfigImportHelper.findConfigDirectories(current)
|
||||
val result = ConfigImportHelper.findConfigDirectories(current, null, emptyList())
|
||||
assertThat(result.paths).isEmpty()
|
||||
}
|
||||
|
||||
@Test fun `suffix-less directories are excluded case-insensitively`() {
|
||||
createConfigDir(product = "RIDER", version = "", modern = true)
|
||||
val current = createConfigDir(product = "Rider", version = "2022.1")
|
||||
val result = ConfigImportHelper.findConfigDirectories(current)
|
||||
val result = ConfigImportHelper.findConfigDirectories(current, null, emptyList())
|
||||
assertThat(result.paths).isEmpty()
|
||||
}
|
||||
|
||||
@@ -513,7 +505,7 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
createConfigDir(product = "RiderFlow", version = "", modern = true)
|
||||
createConfigDir(product = "RiderRemoteDebugger", version = "", modern = true)
|
||||
val current = createConfigDir(product = "Rider", version = "2023.2")
|
||||
val result = ConfigImportHelper.findConfigDirectories(current)
|
||||
val result = ConfigImportHelper.findConfigDirectories(current, null, emptyList())
|
||||
assertThat(result.paths).isEmpty()
|
||||
}
|
||||
|
||||
@@ -521,18 +513,17 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
createConfigDir(product = ".clion-vcpkg", version = "", modern = false) // was created at the user dir by older versions
|
||||
createConfigDir(product = "CLionNova", version = "2023.2", modern = true) // "CLion" + RADLER_SUFFIX = "CLionNova"
|
||||
val current = createConfigDir(product = "CLion", version = "2023.2")
|
||||
val result = ConfigImportHelper.findConfigDirectories(current)
|
||||
val result = ConfigImportHelper.findConfigDirectories(current, null, emptyList())
|
||||
assertThat(result.paths).isEmpty()
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@Test fun `merging VM options`() {
|
||||
val oldConfigDir = createConfigDir(version = "2023.1")
|
||||
val oldVmOptionsFile = oldConfigDir.resolve(VMOptions.getFileName()).writeLines(listOf("-Xmx4g", "-Dsome.prop=old.val"))
|
||||
val newConfigDir = createConfigDir(version = "2023.2")
|
||||
val newVmOptionsFile = newConfigDir.resolve(VMOptions.getFileName()).writeLines(listOf("-Xmx2048m", "-Dsome.prop=new.val"))
|
||||
options.mergeVmOptions = true
|
||||
|
||||
options.isMergeVmOptions = true
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldConfigDir.resolve("plugins"), newConfigDir.resolve("plugins"), options)
|
||||
assertThat(newVmOptionsFile.readLines()).containsExactly("-Xmx4g", "-Dsome.prop=new.val")
|
||||
|
||||
@@ -543,11 +534,10 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
assertThat(newVmOptionsFile.readLines()).containsExactly("-Xmx2048m", "-Dunique.prop=some.val")
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@TestFor(issues = ["IDEA-341860"])
|
||||
@Test fun `don't ask for VM options restart, if they are actual`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
|
||||
val otherXml = oldConfigDir.resolve(PathManager.OPTIONS_DIRECTORY + '/' + StoragePathMacros.NON_ROAMABLE_FILE)
|
||||
Files.createDirectories(otherXml.parent)
|
||||
@@ -573,12 +563,12 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
}
|
||||
|
||||
@Test fun `uses broken plugins from marketplace by default`() {
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
plugin("my-plugin") { dependsIntellijModulesLang(); version = "1.0" }.buildMainJar(oldPluginsDir.resolve("my-plugin.jar"))
|
||||
plugin("my-plugin-2") { dependsIntellijModulesLang(); version = "1.0" }.buildMainJar(oldPluginsDir.resolve("my-plugin-2.jar"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
val brokenPluginsDownloaded = AtomicInteger()
|
||||
@@ -601,31 +591,27 @@ class ConfigImportHelperTest : ConfigImportHelperBaseTest() {
|
||||
}, testRootDisposable)
|
||||
|
||||
configImportMarketplaceStub.unset() // enable marketplace fetching
|
||||
val options = ConfigImportHelper.ConfigImportOptions(LOG).apply { isHeadless = true } // reinstantiate
|
||||
options.compatibleBuildNumber = BuildNumber.fromString("201.1")
|
||||
val options = ConfigImportHelper.ConfigImportOptions(LOG).apply {
|
||||
headless = true
|
||||
compatibleBuildNumber = BuildNumber.fromString("201.1")
|
||||
}
|
||||
ConfigImportHelper.doImport(oldConfigDir, newConfigDir, null, oldPluginsDir, newPluginsDir, options)
|
||||
|
||||
assertThat(brokenPluginsDownloaded).hasValue(1)
|
||||
assertThat(newPluginsDir).exists()
|
||||
.isDirectoryContaining { it.fileName.toString() == "my-plugin-2.jar" }
|
||||
.isDirectoryNotContaining { it.fileName.toString() == "my-plugin.jar" }
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class ConfigImportHelperPluginUpdateModeTest(val updateIncompatibleOnly: Boolean) : ConfigImportHelperBaseTest() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "updateIncompatibleOnly={0}")
|
||||
fun data() = listOf(false, true)
|
||||
.isDirectoryContaining { it.name == "my-plugin-2.jar" }
|
||||
.isDirectoryNotContaining { it.name == "my-plugin.jar" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update plugins mode`() {
|
||||
@Test fun `update only incompatible plugins`() = updatePlugins(updateIncompatibleOnly = true)
|
||||
|
||||
@Test fun `update all plugins`() = updatePlugins(updateIncompatibleOnly = false)
|
||||
|
||||
private fun updatePlugins(updateIncompatibleOnly: Boolean) {
|
||||
// com.intellij.openapi.application.ConfigImportHelper.UPDATE_INCOMPATIBLE_PLUGINS_PROPERTY
|
||||
setSystemProperty("idea.config.import.update.incompatible.plugins.only", updateIncompatibleOnly.toString(), testRootDisposable)
|
||||
|
||||
val oldConfigDir = localTempDir.newDirectory("oldConfig").toPath()
|
||||
val oldConfigDir = newTempDir("oldConfig")
|
||||
val oldPluginsDir = Files.createDirectories(oldConfigDir.resolve("plugins"))
|
||||
|
||||
fun spec(id: String, version: String) = plugin(id) { dependsIntellijModulesLang(); this@plugin.version = version }
|
||||
@@ -634,14 +620,14 @@ class ConfigImportHelperPluginUpdateModeTest(val updateIncompatibleOnly: Boolean
|
||||
spec("migrate", "1.0").buildMainJar(oldPluginsDir.resolve("migrate.jar"))
|
||||
spec("disabled", "1.0").buildMainJar(oldPluginsDir.resolve("disabled.jar"))
|
||||
|
||||
val repoDir = localTempDir.newDirectory("repo").toPath()
|
||||
val repoDir = newTempDir("repo")
|
||||
spec("broken", "1.1").buildMainJar(repoDir.resolve("broken.jar"))
|
||||
spec("update", "1.1").buildMainJar(repoDir.resolve("update.jar"))
|
||||
spec("disabled", "1.1").buildMainJar(repoDir.resolve("disabled.jar"))
|
||||
|
||||
saveDisabledPluginsAndInvalidate(oldConfigDir, listOf("disabled"))
|
||||
|
||||
val newConfigDir = localTempDir.newDirectory("newConfig").toPath()
|
||||
val newConfigDir = newTempDir("newConfig")
|
||||
val newPluginsDir = newConfigDir.resolve("plugins")
|
||||
|
||||
val server = createTestServer(testRootDisposable)
|
||||
@@ -660,29 +646,34 @@ class ConfigImportHelperPluginUpdateModeTest(val updateIncompatibleOnly: Boolean
|
||||
handler.sendResponseHeaders(404, -1) // incompatible
|
||||
return@createContext
|
||||
}
|
||||
val content = repoDir.resolve("$id.jar").toFile().readBytes()
|
||||
handler.responseHeaders.add("Content-Disposition", "attachment; filename=$id.jar")
|
||||
val content = repoDir.resolve("${id}.jar").readBytes()
|
||||
handler.responseHeaders.add("Content-Disposition", "attachment; filename=${id}.jar")
|
||||
handler.sendResponseHeaders(200, content.size.toLong())
|
||||
handler.responseBody.use {
|
||||
it.write(content)
|
||||
}
|
||||
}
|
||||
ApplicationManager.getApplication().replaceService(MarketplaceCustomizationService::class.java, object : MarketplaceCustomizationService {
|
||||
override fun getPluginManagerUrl(): String = server.url
|
||||
override fun getPluginDownloadUrl(): String = server.url.trimEnd('/') + "/download"
|
||||
override fun getPluginsListUrl(): String = throw AssertionError("unexpected")
|
||||
override fun getPluginHomepageUrl(pluginId: PluginId): String = throw AssertionError("unexpected")
|
||||
}, testRootDisposable)
|
||||
ApplicationManager.getApplication().replaceService(
|
||||
MarketplaceCustomizationService::class.java,
|
||||
object : MarketplaceCustomizationService {
|
||||
override fun getPluginManagerUrl(): String = server.url
|
||||
override fun getPluginDownloadUrl(): String = server.url.trimEnd('/') + "/download"
|
||||
override fun getPluginsListUrl(): String = throw AssertionError("unexpected")
|
||||
override fun getPluginHomepageUrl(pluginId: PluginId): String = throw AssertionError("unexpected")
|
||||
},
|
||||
testRootDisposable
|
||||
)
|
||||
|
||||
configImportMarketplaceStub.unset() // enable marketplace fetching
|
||||
val options = ConfigImportHelper.ConfigImportOptions(LOG).apply { isHeadless = true } // reinstantiate
|
||||
options.compatibleBuildNumber = BuildNumber.fromString("201.1")
|
||||
options.pluginUpdatesFetcher = LastCompatiblePluginUpdatesFetcher {
|
||||
val options = ConfigImportHelper.ConfigImportOptions(LOG).apply {
|
||||
headless = true
|
||||
compatibleBuildNumber = BuildNumber.fromString("201.1")
|
||||
}
|
||||
ConfigImportHelper.testLastCompatiblePluginUpdatesFetcher = Function {
|
||||
buildMap {
|
||||
for (id in listOf("update", "disabled", "migrate")) {
|
||||
val pid = PluginId.getId(id)
|
||||
val node = PluginNode(pid)
|
||||
node.version = if (id == "migrate") "1.0" else "1.1"
|
||||
val node = PluginNode(pid).apply { version = if (id == "migrate") "1.0" else "1.1" }
|
||||
put(pid, node)
|
||||
}
|
||||
}
|
||||
@@ -696,12 +687,12 @@ class ConfigImportHelperPluginUpdateModeTest(val updateIncompatibleOnly: Boolean
|
||||
assertThat(newPluginsDir.resolve("migrate.jar")).hasSameBinaryContentAs(oldPluginsDir.resolve("migrate.jar"))
|
||||
assertThat(newPluginsDir.resolve("disabled.jar")).hasSameBinaryContentAs((if (updateIncompatibleOnly) oldPluginsDir else repoDir).resolve("disabled.jar"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTestServer(disposable: Disposable): HttpServer {
|
||||
val server = HttpServer.create()!!
|
||||
server.bind(InetSocketAddress(0), 1)
|
||||
server.start()
|
||||
disposable.whenDisposed { server.stop(0) }
|
||||
return server
|
||||
private fun createTestServer(disposable: Disposable): HttpServer {
|
||||
val server = HttpServer.create()!!
|
||||
server.bind(InetSocketAddress(0), 1)
|
||||
server.start()
|
||||
disposable.whenDisposed { server.stop(0) }
|
||||
return server
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.startup.importSettings.jb
|
||||
|
||||
import com.intellij.ide.AppLifecycleListener
|
||||
@@ -49,7 +49,7 @@ private class JbAfterRestartSettingsApplier(private val coroutineScope: Coroutin
|
||||
pluginIds.add(it.trim())
|
||||
}
|
||||
}
|
||||
val importer = JbSettingsImporter(oldConfDir, oldConfDir, null)
|
||||
val importer = JbSettingsImporter(oldConfDir, oldConfDir)
|
||||
coroutineScope.launch {
|
||||
withContext(Dispatchers.EDT) {
|
||||
importer.importOptionsAfterRestart(options, pluginIds)
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.startup.importSettings.jb
|
||||
|
||||
import com.intellij.configurationStore.getPerOsSettingsStorageFolderName
|
||||
@@ -208,7 +208,7 @@ class JbImportServiceImpl(private val coroutineScope: CoroutineScope) : JbServic
|
||||
val modalityState = ModalityState.current()
|
||||
ImportSettingsEventsCollector.customDirectorySelected()
|
||||
coroutineScope.async(modalityState.asContextElement()) {
|
||||
val importer = JbSettingsImporter(folderPath, folderPath, null)
|
||||
val importer = JbSettingsImporter(folderPath, folderPath)
|
||||
importer.importRaw()
|
||||
logger.info("Performing raw import from '$folderPath'")
|
||||
withContext(Dispatchers.EDT) {
|
||||
@@ -416,7 +416,7 @@ class JbImportServiceImpl(private val coroutineScope: CoroutineScope) : JbServic
|
||||
&& unselectedPlugins.isNullOrEmpty()
|
||||
|
||||
val importData = TransferSettingsProgress(productInfo)
|
||||
val importer = JbSettingsImporter(productInfo.configDir, productInfo.pluginDir, null)
|
||||
val importer = JbSettingsImporter(productInfo.configDir, productInfo.pluginDir)
|
||||
val progressIndicator = importData.createProgressIndicatorAdapter()
|
||||
val importLifetime = LifetimeDefinition()
|
||||
var importStartedDeferred: Deferred<Unit>? = null
|
||||
@@ -700,4 +700,4 @@ private fun RawProgressReporter.toBridgeIndicator(): ProgressIndicator {
|
||||
}
|
||||
}
|
||||
|
||||
private val logger = logger<JbImportServiceImpl>()
|
||||
private val logger = logger<JbImportServiceImpl>()
|
||||
|
||||
+50
-58
@@ -1,6 +1,5 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.ide.startup.importSettings.jb
|
||||
|
||||
import com.intellij.configurationStore.*
|
||||
@@ -44,7 +43,6 @@ import com.intellij.util.application
|
||||
import com.intellij.util.io.copy
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.nio.file.FileVisitResult
|
||||
import java.nio.file.Files
|
||||
@@ -53,15 +51,13 @@ import java.nio.file.SimpleFileVisitor
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import kotlin.io.path.*
|
||||
|
||||
class JbSettingsImporter(private val configDirPath: Path,
|
||||
private val pluginsPath: Path,
|
||||
private val prevIdeHome: Path?
|
||||
) {
|
||||
private val LOG = logger<JbSettingsImporter>()
|
||||
|
||||
class JbSettingsImporter(private val configDirPath: Path, private val pluginsPath: Path) {
|
||||
private val componentStore = ApplicationManager.getApplication().stateStore as ComponentStoreImpl
|
||||
private val defaultNewUIValue = true
|
||||
private val additionalSchemeDirs = mapOf(FileTemplatesScheme.TEMPLATES_DIR to SettingsCategory.CODE)
|
||||
|
||||
// will be used as toposort for dependencies
|
||||
// will be used as topological ordering for dependencies
|
||||
// TODO: move to the component declaration instead
|
||||
private val componentNamesDependencies = mapOf(
|
||||
//IDEA-342818
|
||||
@@ -105,12 +101,12 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
val parentElement = JDOMUtil.load(projectDefaultXmlPath)
|
||||
val defaultProjectElement = parentElement.getChild("component")?.getChild("defaultProject") ?: return emptySet()
|
||||
|
||||
val retval = mutableSetOf<String>()
|
||||
val retVal = mutableSetOf<String>()
|
||||
for (componentElement in defaultProjectElement.getChildren("component")) {
|
||||
val componentName = componentElement.getAttributeValue("name")
|
||||
retval.add(componentName)
|
||||
retVal.add(componentName)
|
||||
}
|
||||
return retval
|
||||
return retVal
|
||||
}
|
||||
|
||||
private fun findComponentsAndFiles(): Pair<Set<String>, Set<String>> {
|
||||
@@ -217,7 +213,7 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
}
|
||||
|
||||
// we use LinkedHashSet, because we need ordering here
|
||||
val appComponentNames: LinkedHashSet<String> = toposortComponentNames(componentAndFilesMap.keys)
|
||||
val appComponentNames: LinkedHashSet<String> = topoSortComponentNames(componentAndFilesMap.keys)
|
||||
|
||||
withExternalStreamProvider(arrayOf(storageManager, defaultProjectStore.storageManager)) {
|
||||
progressIndicator.checkCanceled()
|
||||
@@ -241,18 +237,18 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
return Registry.getInstance().isRestartNeeded
|
||||
}
|
||||
|
||||
// very basic and primitive toposort. Doesn't traverse, doesn't support transitive deps, etc.
|
||||
private fun toposortComponentNames(components: Collection<String>): LinkedHashSet<String> {
|
||||
val retval = LinkedHashSet<String>()
|
||||
// very basic and primitive topological sort. Doesn't traverse, doesn't support transitive deps, etc.
|
||||
private fun topoSortComponentNames(components: Collection<String>): LinkedHashSet<String> {
|
||||
val retVal = LinkedHashSet<String>()
|
||||
for (c in components) {
|
||||
for (d in componentNamesDependencies[c]?:emptyList()) {
|
||||
if (!retval.contains(d)){
|
||||
retval.add(d)
|
||||
if (!retVal.contains(d)){
|
||||
retVal.add(d)
|
||||
}
|
||||
}
|
||||
retval.add(c)
|
||||
retVal.add(c)
|
||||
}
|
||||
return retval
|
||||
return retVal
|
||||
}
|
||||
|
||||
private suspend fun withExternalStreamProvider(storageManagers: Array<StateStorageManager>, action: () -> Unit) {
|
||||
@@ -306,29 +302,29 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
}
|
||||
|
||||
private fun filesFromFolder(dir: Path, prefix: String = dir.name): Collection<String> {
|
||||
val retval = ArrayList<String>()
|
||||
val retVal = ArrayList<String>()
|
||||
for (entry in dir.listDirectoryEntries()) {
|
||||
if (entry.isRegularFile()) {
|
||||
if (prefix.isEmpty()) {
|
||||
retval.add(entry.name)
|
||||
retVal.add(entry.name)
|
||||
}
|
||||
else {
|
||||
retval.add("$prefix/${entry.name}")
|
||||
retVal.add("$prefix/${entry.name}")
|
||||
}
|
||||
}
|
||||
else {
|
||||
val folderFiles = filesFromFolder(entry, "$prefix/${entry.name}")
|
||||
retval.addAll(folderFiles)
|
||||
retVal.addAll(folderFiles)
|
||||
}
|
||||
}
|
||||
return retval
|
||||
return retVal
|
||||
}
|
||||
|
||||
|
||||
// key: PSC, value - file
|
||||
private fun filterComponents(allFiles: Set<String>, categories: Set<SettingsCategory>): Map<String, String> {
|
||||
val componentManager = ApplicationManager.getApplication() as ComponentManagerEx
|
||||
val retval = hashMapOf<String, String>()
|
||||
val retVal = hashMapOf<String, String>()
|
||||
val osFolderName = getPerOsSettingsStorageFolderName()
|
||||
componentManager.processAllImplementationClasses { aClass, _ ->
|
||||
val state = getStateOrNull(aClass) ?: return@processAllImplementationClasses
|
||||
@@ -341,13 +337,13 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
return@processAllImplementationClasses
|
||||
|
||||
if (activeStorage.roamingType.isOsSpecific && allFiles.contains("$osFolderName/${activeStorage.value}")) {
|
||||
retval[state.name] = "$osFolderName/${activeStorage.value}"
|
||||
retVal[state.name] = "$osFolderName/${activeStorage.value}"
|
||||
}
|
||||
else if (allFiles.contains(activeStorage.value)) {
|
||||
retval[state.name] = activeStorage.value
|
||||
retVal[state.name] = activeStorage.value
|
||||
}
|
||||
}
|
||||
return retval
|
||||
return retVal
|
||||
}
|
||||
|
||||
private fun getStateOrNull(aClass: Class<*>): State? {
|
||||
@@ -362,9 +358,9 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
}
|
||||
|
||||
private fun filterSchemes(allFiles: Set<String>, categories: Set<SettingsCategory>): Set<String> {
|
||||
val retval = hashSetOf<String>()
|
||||
val retVal = hashSetOf<String>()
|
||||
val schemeCategories = hashSetOf<String>()
|
||||
// fileSpec is e.g. keymaps/mykeymap.xml
|
||||
// fileSpec is e.g. `keymaps/my-keymap.xml`
|
||||
(SchemeManagerFactory.getInstance() as SchemeManagerFactoryBase).process {
|
||||
if (categories.contains(it.getSettingsCategory())) {
|
||||
schemeCategories.add(it.fileSpec)
|
||||
@@ -381,10 +377,10 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
continue
|
||||
|
||||
if (schemeCategories.contains(split[0])) {
|
||||
retval.add(file)
|
||||
retVal.add(file)
|
||||
}
|
||||
}
|
||||
return retval
|
||||
return retVal
|
||||
}
|
||||
|
||||
fun installPlugins(
|
||||
@@ -456,15 +452,17 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
}
|
||||
}
|
||||
|
||||
private fun configImportOptions(progressIndicator: ProgressIndicator,
|
||||
pluginIds: Collection<PluginId>): ConfigImportHelper.ConfigImportOptions {
|
||||
private fun configImportOptions(progressIndicator: ProgressIndicator, pluginIds: Collection<PluginId>): ConfigImportHelper.ConfigImportOptions {
|
||||
val importOptions = ConfigImportHelper.ConfigImportOptions(LOG)
|
||||
importOptions.isHeadless = true
|
||||
importOptions.headless = true
|
||||
importOptions.headlessProgressIndicator = progressIndicator
|
||||
importOptions.importSettings = object : ConfigImportSettings {
|
||||
override fun processPluginsToMigrate(
|
||||
newConfigDir: Path,
|
||||
oldConfigDir: Path,
|
||||
oldPluginsDir: Path,
|
||||
options: ConfigImportHelper.ConfigImportOptions,
|
||||
brokenPluginVersions: Map<PluginId?, Set<String?>?>?,
|
||||
bundledPlugins: MutableList<IdeaPluginDescriptor>, // FIXME wrong arg name
|
||||
nonBundledPlugins: MutableList<IdeaPluginDescriptor>, // FIXME wrong arg name
|
||||
) {
|
||||
@@ -503,43 +501,40 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
internal class ImportStreamProvider(private val configDirPath: Path) : StreamProvider {
|
||||
override val isExclusive = false
|
||||
|
||||
override val saveStorageDataOnReload: Boolean
|
||||
get() = false
|
||||
override val saveStorageDataOnReload: Boolean get() = false
|
||||
|
||||
override fun isApplicable(fileSpec: String, roamingType: RoamingType): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun isApplicable(fileSpec: String, roamingType: RoamingType): Boolean = false
|
||||
|
||||
override fun write(fileSpec: String, content: ByteArray, roamingType: RoamingType) {
|
||||
}
|
||||
override fun write(fileSpec: String, content: ByteArray, roamingType: RoamingType) { }
|
||||
|
||||
override fun read(fileSpec: String, roamingType: RoamingType, consumer: (InputStream?) -> Unit): Boolean {
|
||||
if (fileSpec == PROJECT_DEFAULT_FILE_SPEC) {
|
||||
val path = configDirPath / PathManager.OPTIONS_DIRECTORY / PROJECT_DEFAULT_FILE_NAME
|
||||
if (!path.isRegularFile())
|
||||
return false
|
||||
consumer(FileInputStream(path.toFile()))
|
||||
val path = configDirPath.resolve(PathManager.OPTIONS_DIRECTORY).resolve(PROJECT_DEFAULT_FILE_NAME)
|
||||
if (!path.isRegularFile()) return false
|
||||
consumer(Files.newInputStream(path))
|
||||
return true
|
||||
}
|
||||
(configDirPath / PathManager.OPTIONS_DIRECTORY / fileSpec).let {
|
||||
(configDirPath.resolve(PathManager.OPTIONS_DIRECTORY).resolve(fileSpec)).let {
|
||||
if (it.exists()) {
|
||||
consumer(FileInputStream(it.toFile()))
|
||||
consumer(Files.newInputStream(it))
|
||||
return true
|
||||
}
|
||||
}
|
||||
(configDirPath / fileSpec).let {
|
||||
(configDirPath.resolve(fileSpec)).let {
|
||||
if (it.exists()) {
|
||||
consumer(FileInputStream(it.toFile()))
|
||||
consumer(Files.newInputStream(it))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun processChildren(path: String,
|
||||
roamingType: RoamingType,
|
||||
filter: (name: String) -> Boolean,
|
||||
processor: (name: String, input: InputStream, readOnly: Boolean) -> Boolean): Boolean {
|
||||
override fun processChildren(
|
||||
path: String,
|
||||
roamingType: RoamingType,
|
||||
filter: (name: String) -> Boolean,
|
||||
processor: (name: String, input: InputStream, readOnly: Boolean) -> Boolean,
|
||||
): Boolean {
|
||||
LOG.debug("Process Children $path")
|
||||
val folder = configDirPath.resolve(path)
|
||||
if (!folder.exists()) return true
|
||||
@@ -566,8 +561,5 @@ class JbSettingsImporter(private val configDirPath: Path,
|
||||
LOG.debug("Deleting $fileSpec")
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private val LOG = logger<JbSettingsImporter>()
|
||||
|
||||
Reference in New Issue
Block a user