From cfb343476f770cead4a3aec16d0daed26c1b250f Mon Sep 17 00:00:00 2001 From: Sergey Pak Date: Thu, 8 Feb 2024 18:45:01 +0100 Subject: [PATCH] IDEA-342804 New Import Settings: components stored in project.default.xml are not imported from other IntelliJ-based IDEs GitOrigin-RevId: c12ecfb6204d440a090f25d641d95d2f105d884a --- .../src/DefaultProjectStoreImpl.kt | 57 +++++++++++----- .../src/XmlElementStorage.kt | 7 +- .../importSettings/jb/JbSettingsImporter.kt | 68 ++++++++++++++++--- 3 files changed, 102 insertions(+), 30 deletions(-) diff --git a/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt b/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt index 20d2c815d595..c7ca2c9c350b 100644 --- a/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt +++ b/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt @@ -6,19 +6,27 @@ import com.intellij.openapi.components.* import com.intellij.openapi.project.Project import com.intellij.serviceContainer.ComponentManagerImpl import org.jdom.Element +import java.io.InputStream import java.io.Writer import java.nio.file.Path -private const val FILE_SPEC = "$APP_CONFIG/project.default.xml" +const val PROJECT_DEFAULT_FILE_NAME = "project.default.xml" +const val PROJECT_DEFAULT_FILE_SPEC = "$APP_CONFIG/${PROJECT_DEFAULT_FILE_NAME}" internal class DefaultProjectStoreImpl(override val project: Project) : ComponentStoreWithExtraComponents() { // see note about default state in project store + private val compoundStreamProvider = CompoundStreamProvider() + override val loadPolicy: StateLoadPolicy get() = if (ApplicationManager.getApplication().isUnitTestMode) StateLoadPolicy.NOT_LOAD else StateLoadPolicy.LOAD private val storage by lazy { - val file = ApplicationManager.getApplication().stateStore.storageManager.expandMacro(FILE_SPEC) - DefaultProjectStorage(file = file, fileSpec = FILE_SPEC, pathMacroManager = PathMacroManager.getInstance(project)) + val file = ApplicationManager.getApplication().stateStore.storageManager.expandMacro(PROJECT_DEFAULT_FILE_SPEC) + DefaultProjectStorage(file = file, + fileSpec = PROJECT_DEFAULT_FILE_SPEC, + pathMacroManager = PathMacroManager.getInstance(project), + streamProvider = compoundStreamProvider + ) } override val serviceContainer: ComponentManagerImpl @@ -28,9 +36,13 @@ internal class DefaultProjectStoreImpl(override val project: Project) : Componen override val componentManager: ComponentManager? get() = null - override fun addStreamProvider(provider: StreamProvider, first: Boolean) {} + override fun addStreamProvider(provider: StreamProvider, first: Boolean) { + compoundStreamProvider.addStreamProvider(provider, first) + } - override fun removeStreamProvider(aClass: Class) {} + override fun removeStreamProvider(aClass: Class) { + compoundStreamProvider.removeStreamProvider(aClass) + } override fun getStateStorage(storageSpec: Storage): DefaultProjectStorage = storage @@ -57,17 +69,26 @@ internal class DefaultProjectStoreImpl(override val project: Project) : Componen override fun toString(): String = "default project" - private class DefaultProjectStorage(file: Path, fileSpec: String, pathMacroManager: PathMacroManager) : FileBasedStorage( + private class DefaultProjectStorage(file: Path, fileSpec: String, pathMacroManager: PathMacroManager, streamProvider: StreamProvider) : FileBasedStorage( file = file, fileSpec = fileSpec, rootElementName = "defaultProject", pathMacroManager = pathMacroManager.createTrackingSubstitutor(), roamingType = RoamingType.DISABLED, + provider = streamProvider, controller = null, ) { public override fun loadLocalData(): Element? { + return postProcessLoadedData { super.loadLocalData() } + } + + override fun loadFromStreamProvider(inputStream: InputStream): Element? { + return postProcessLoadedData { super.loadFromStreamProvider(inputStream) } + } + + private fun postProcessLoadedData(elementProvider: () -> Element?): Element? { return try { - super.loadLocalData()?.getChild("component")?.getChild("defaultProject") + elementProvider()?.getChild("component")?.getChild("defaultProject") } catch (_: NullPointerException) { LOG.warn("Cannot read default project") @@ -83,17 +104,17 @@ internal class DefaultProjectStoreImpl(override val project: Project) : Componen else object : StringDataWriter() { override fun hasData(filter: DataWriterFilter): Boolean = dataWriter.hasData(filter) - override fun writeTo(writer: Writer, lineSeparator: String, filter: DataWriterFilter?) { - val lineSeparatorWithIndent = "${lineSeparator} " - writer.append("").append(lineSeparator) - writer.append(""" """) - writer.append(lineSeparatorWithIndent) - (dataWriter as StringDataWriter).writeTo(writer, lineSeparatorWithIndent, filter) - writer.append(lineSeparator) - writer.append(" ").append(lineSeparator) - writer.append("") - } - }) + override fun writeTo(writer: Writer, lineSeparator: String, filter: DataWriterFilter?) { + val lineSeparatorWithIndent = "${lineSeparator} " + writer.append("").append(lineSeparator) + writer.append(""" """) + writer.append(lineSeparatorWithIndent) + (dataWriter as StringDataWriter).writeTo(writer, lineSeparatorWithIndent, filter) + writer.append(lineSeparator) + writer.append(" ").append(lineSeparator) + writer.append("") + } + }) } } } diff --git a/platform/configuration-store-impl/src/XmlElementStorage.kt b/platform/configuration-store-impl/src/XmlElementStorage.kt index 430be2e99043..584b492c8d78 100644 --- a/platform/configuration-store-impl/src/XmlElementStorage.kt +++ b/platform/configuration-store-impl/src/XmlElementStorage.kt @@ -18,6 +18,7 @@ import org.jdom.Attribute import org.jdom.Element import org.jetbrains.annotations.ApiStatus.Internal import java.io.FileNotFoundException +import java.io.InputStream import java.io.Writer import kotlin.math.min @@ -53,7 +54,7 @@ abstract class XmlElementStorage protected constructor( if (provider != null) { isLoadLocalData = !provider.read(fileSpec, effectiveRoamingType) { inputStream -> inputStream?.let { - element = JDOMUtil.load(inputStream) + element = loadFromStreamProvider(inputStream) val writer = object : StringDataWriter() { override fun hasData(filter: DataWriterFilter) = filter.hasData(element!!) override fun writeTo(writer: Writer, lineSeparator: String, filter: DataWriterFilter?) { @@ -91,6 +92,10 @@ abstract class XmlElementStorage protected constructor( return StateMap.fromMap(ComponentStorageUtil.load(element, pathMacroSubstitutor)) } + open fun loadFromStreamProvider(inputStream: InputStream): Element? { + return JDOMUtil.load(inputStream) + } + final override fun createSaveSessionProducer(): SaveSessionProducer? = if (checkIsSavingDisabled()) null else createSaveSession(getStorageData()) diff --git a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/jb/JbSettingsImporter.kt b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/jb/JbSettingsImporter.kt index 151838c93071..aca7fee75f27 100644 --- a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/jb/JbSettingsImporter.kt +++ b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/jb/JbSettingsImporter.kt @@ -18,6 +18,7 @@ import com.intellij.openapi.keymap.impl.KeymapManagerImpl import com.intellij.openapi.options.SchemeManagerFactory import com.intellij.openapi.progress.EmptyProgressIndicator import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.util.JDOMUtil import com.intellij.openapi.util.registry.* import com.intellij.psi.codeStyle.CodeStyleSchemes @@ -50,8 +51,9 @@ class JbSettingsImporter(private val configDirPath: Path, suspend fun importOptionsAfterRestart(categories: Set, pluginIds: Set) { val storageManager = componentStore.storageManager as StateStorageManagerImpl val (components, files) = findComponentsAndFiles() - withExternalStreamProvider(storageManager) { - val availableComponents = loadNotLoadedComponents(EmptyProgressIndicator(), components, pluginIds) + withExternalStreamProvider(arrayOf(storageManager)) { + val componentManagerImpl = ApplicationManager.getApplication() as ComponentManagerImpl + val availableComponents = loadNotLoadedComponents(EmptyProgressIndicator(), componentManagerImpl, components, pluginIds) componentStore.reloadComponents(files, emptyList(), availableComponents) if (categories.contains(SettingsCategory.KEYMAP)) { // ensure component is loaded @@ -68,11 +70,29 @@ class JbSettingsImporter(private val configDirPath: Path, } } - private fun findComponentsAndFiles() : Pair, Set> { + private fun loadProjectDefaultComponentNames() : Set { + val projectDefaultXmlPath = configDirPath / PathManager.OPTIONS_DIRECTORY / PROJECT_DEFAULT_FILE_NAME + if (!projectDefaultXmlPath.isRegularFile()) + return emptySet() + + val parentElement = JDOMUtil.load(projectDefaultXmlPath) + val defaultProjectElement = parentElement.getChild("component")?.getChild("defaultProject") ?: return emptySet() + + val retval = mutableSetOf() + for (componentElement in defaultProjectElement.getChildren("component")) { + val componentName = componentElement.getAttributeValue("name") + retval.add(componentName) + } + return retval + } + + private fun findComponentsAndFiles(): Pair, Set> { val optionsPath = configDirPath / PathManager.OPTIONS_DIRECTORY val allFiles = mutableSetOf() val components = mutableSetOf() for (optionsEntry in optionsPath.listDirectoryEntries()) { + if (optionsEntry.name == PROJECT_DEFAULT_FILE_NAME) + continue if (optionsEntry.name.lowercase().endsWith(".xml")) { allFiles.add(optionsEntry.name) val element = JDOMUtil.load(optionsEntry) @@ -121,9 +141,17 @@ class JbSettingsImporter(private val configDirPath: Path, LOG.info("NOT loaded components(${notLoadedComponents.size}):\n${notLoadedComponents.joinToString()}") LOG.info("NOT loaded storages(${unknownStorage.size}):\n${unknownStorage.joinToString()}") progressIndicator.checkCanceled() - val loadNotLoadedComponents = loadNotLoadedComponents(progressIndicator, notLoadedComponents, null) + val componentManagerImpl = ApplicationManager.getApplication() as ComponentManagerImpl + val loadNotLoadedComponents = loadNotLoadedComponents(progressIndicator, componentManagerImpl, notLoadedComponents, null) notLoadedComponents.removeAll(loadNotLoadedComponents) + val defaultProject = ProjectManager.getInstance().defaultProject + val projectDefaultComponentNames = loadProjectDefaultComponentNames() + if (projectDefaultComponentNames.isNotEmpty()) { + loadNotLoadedComponents(progressIndicator, defaultProject.actualComponentManager as ComponentManagerImpl, + projectDefaultComponentNames, null) + } + for (component in notLoadedComponents) { LOG.info("Component $component was not found and loaded. Its settings will not be migrated") } @@ -158,9 +186,14 @@ class JbSettingsImporter(private val configDirPath: Path, schemeFiles.forEach { (configDirPath / it).copy(PathManager.getConfigDir() / it) } - withExternalStreamProvider(storageManager) { + + val defaultProjectStore = (defaultProject as ComponentManager).stateStore as ComponentStoreImpl + val defaultProjectStorage = defaultProjectStore.storageManager.getStateStorage(FileStorageAnnotation("", false)) + + withExternalStreamProvider(arrayOf(storageManager, defaultProjectStore.storageManager)) { progressIndicator.checkCanceled() componentStore.reloadComponents(componentFiles + schemeFiles, emptyList(), componentAndFilesMap.keys) + defaultProjectStore.reinitComponents(projectDefaultComponentNames, setOf(defaultProjectStorage), emptySet()) } RegistryManager.getInstanceAsync().resetValueChangeListener() @@ -174,18 +207,23 @@ class JbSettingsImporter(private val configDirPath: Path, } } - private suspend fun withExternalStreamProvider(storageManager: StateStorageManagerImpl, action: suspend () -> Unit) { + private suspend fun withExternalStreamProvider(storageManagers: Array, action: suspend () -> Unit) { val provider = ImportStreamProvider(configDirPath) - storageManager.addStreamProvider(provider) + for (storageManager in storageManagers) { + storageManager.addStreamProvider(provider) + } action() - storageManager.removeStreamProvider(provider::class.java) + for (storageManager in storageManagers) { + storageManager.removeStreamProvider(provider::class.java) + } saveSettings(ApplicationManager.getApplication(), true) } private fun loadNotLoadedComponents( progressIndicator: ProgressIndicator, + componentManagerImpl: ComponentManagerImpl, componentsToLoad: Collection, pluginIds: Set? ): Set { @@ -193,7 +231,6 @@ class JbSettingsImporter(private val configDirPath: Path, val notLoadedComponents = arrayListOf() notLoadedComponents.addAll(componentsToLoad) val foundComponents = hashMapOf>() - val componentManagerImpl = ApplicationManager.getApplication() as ComponentManagerImpl componentManagerImpl.processAllHolders { key, clazz, pluginDescriptor -> progressIndicator.checkCanceled() if (pluginDescriptor != null && pluginIds != null && !pluginIds.contains(pluginDescriptor.pluginId.idString)) @@ -245,7 +282,8 @@ class JbSettingsImporter(private val configDirPath: Path, else { retval.add("$prefix/${entry.name}") } - } else { + } + else { val folderFiles = filesFromFolder(entry, "$prefix/${entry.name}") retval.addAll(folderFiles) } @@ -355,7 +393,8 @@ class JbSettingsImporter(private val configDirPath: Path, val currentVMFile = PathManager.getConfigDir().resolve(VMOptions.getFileName()) if (currentVMFile.exists()) { ConfigImportHelper.mergeVmOptions(externalVmOptionsFile, currentVMFile, LOG) - } else { + } + else { Files.copy(externalVmOptionsFile, currentVMFile) } ConfigImportHelper.updateVMOptions(PathManager.getConfigDir(), LOG) @@ -395,6 +434,13 @@ class JbSettingsImporter(private val configDirPath: Path, } 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())) + return true + } (configDirPath / PathManager.OPTIONS_DIRECTORY / fileSpec).let { if (it.exists()) { consumer(FileInputStream(it.toFile()))