IDEA-342804 New Import Settings: components stored in project.default.xml are not imported from other IntelliJ-based IDEs

GitOrigin-RevId: c12ecfb6204d440a090f25d641d95d2f105d884a
This commit is contained in:
Sergey Pak
2024-02-08 19:52:37 +00:00
committed by intellij-monorepo-bot
parent 8f6e52499f
commit cfb343476f
3 changed files with 102 additions and 30 deletions
@@ -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<out StreamProvider>) {}
override fun removeStreamProvider(aClass: Class<out StreamProvider>) {
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("<application>").append(lineSeparator)
writer.append(""" <component name="ProjectManager">""")
writer.append(lineSeparatorWithIndent)
(dataWriter as StringDataWriter).writeTo(writer, lineSeparatorWithIndent, filter)
writer.append(lineSeparator)
writer.append(" </component>").append(lineSeparator)
writer.append("</application>")
}
})
override fun writeTo(writer: Writer, lineSeparator: String, filter: DataWriterFilter?) {
val lineSeparatorWithIndent = "${lineSeparator} "
writer.append("<application>").append(lineSeparator)
writer.append(""" <component name="ProjectManager">""")
writer.append(lineSeparatorWithIndent)
(dataWriter as StringDataWriter).writeTo(writer, lineSeparatorWithIndent, filter)
writer.append(lineSeparator)
writer.append(" </component>").append(lineSeparator)
writer.append("</application>")
}
})
}
}
}
@@ -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())
@@ -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<SettingsCategory>, pluginIds: Set<String>) {
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<String>, Set<String>> {
private fun loadProjectDefaultComponentNames() : Set<String> {
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<String>()
for (componentElement in defaultProjectElement.getChildren("component")) {
val componentName = componentElement.getAttributeValue("name")
retval.add(componentName)
}
return retval
}
private fun findComponentsAndFiles(): Pair<Set<String>, Set<String>> {
val optionsPath = configDirPath / PathManager.OPTIONS_DIRECTORY
val allFiles = mutableSetOf<String>()
val components = mutableSetOf<String>()
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<StateStorageManager>, 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<String>,
pluginIds: Set<String>?
): Set<String> {
@@ -193,7 +231,6 @@ class JbSettingsImporter(private val configDirPath: Path,
val notLoadedComponents = arrayListOf<String>()
notLoadedComponents.addAll(componentsToLoad)
val foundComponents = hashMapOf<String, Class<*>>()
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()))