Cleanup (minor optimization; typos; formatting)

GitOrigin-RevId: 8e141475419e5987111a7e039cb318230c994697
This commit is contained in:
Roman Shevchenko
2024-07-30 18:11:47 +02:00
committed by intellij-monorepo-bot
parent 3160e0cf4e
commit 60f1bdbd8b
13 changed files with 411 additions and 615 deletions

View File

@@ -69,7 +69,7 @@ open class ApplicationStoreImpl(private val app: Application) : ComponentStoreWi
coroutineScope {
launch {
super.doSave(saveResult = saveResult, forceSavingAllSettings = forceSavingAllSettings)
super.doSave(saveResult, forceSavingAllSettings)
}
val projectManager = serviceAsync<ProjectManager>() as ProjectManagerEx
@@ -110,7 +110,7 @@ class ApplicationStateStorageManager(pathMacroManager: PathMacroManager? = null,
Files.deleteIfExists(storage.file)
}
else {
writer.writeTo(file = storage.file, requestor = null, LineSeparator.LF, isUseXmlProlog)
writer.writeTo(storage.file, requestor = null, LineSeparator.LF, isUseXmlProlog)
}
}.getOrLogException(LOG)
}

View File

@@ -1,7 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceJavaStaticMethodWithKotlinAnalog", "ReplaceGetOrSet")
@file:OptIn(SettingsInternalApi::class)
package com.intellij.configurationStore
import com.intellij.codeWithMe.ClientId
@@ -11,7 +9,6 @@ import com.intellij.ide.impl.runUnderModalProgressIfIsEdt
import com.intellij.ide.plugins.PluginManager
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.notification.NotificationsManager
import com.intellij.openapi.application.AppUIExecutor
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.readAction
@@ -132,47 +129,37 @@ abstract class ComponentStoreImpl : IComponentStore {
return
}
val componentInfo = createComponentInfo(
component = component,
stateSpec = null,
serviceDescriptor = serviceDescriptor,
pluginId = pluginId,
)
initComponent(info = componentInfo, changedStorages = null, reloadData = ThreeState.NO)
val componentInfo = createComponentInfo(component, stateSpec = null, serviceDescriptor, pluginId)
initComponent(componentInfo, changedStorages = null, reloadData = ThreeState.NO)
}
else {
componentName = stateSpec.name
val componentInfo = createComponentInfo(
component = component,
stateSpec = stateSpec,
serviceDescriptor = serviceDescriptor,
pluginId = pluginId,
)
val componentInfo = createComponentInfo(component, stateSpec, serviceDescriptor, pluginId)
// still must be added to a component list to support explicit save later
if (!stateSpec.allowLoadInTests && !(loadPolicy == StateLoadPolicy.LOAD ||
(loadPolicy == StateLoadPolicy.LOAD_ONLY_DEFAULT && stateSpec.defaultStateAsResource))) {
if (!stateSpec.allowLoadInTests &&
!(loadPolicy == StateLoadPolicy.LOAD || (loadPolicy == StateLoadPolicy.LOAD_ONLY_DEFAULT && stateSpec.defaultStateAsResource))) {
component.noStateLoaded()
component.initializeComponent()
registerComponent(name = componentName, info = componentInfo)
registerComponent(componentName, componentInfo)
return
}
if (initComponent(info = componentInfo, changedStorages = null, reloadData = ThreeState.NO) && serviceDescriptor != null) {
if (initComponent(componentInfo, changedStorages = null, reloadData = ThreeState.NO) && serviceDescriptor != null) {
// if not service, so, component manager will check it later for all components
val project = project
if (project != null && project.isInitialized) {
val app = ApplicationManager.getApplication()
if (!app.isHeadlessEnvironment && !app.isUnitTestMode) {
notifyUnknownMacros(store = this, project = project, componentName = componentName)
notifyUnknownMacros(store = this, project, componentName = componentName)
}
}
}
registerComponent(name = componentName, info = componentInfo)
registerComponent(componentName, componentInfo)
}
component.initializeComponent()
}
else if (loadPolicy == StateLoadPolicy.LOAD && component is com.intellij.openapi.util.JDOMExternalizable) {
componentName = initJdom(component = component, pluginId = pluginId)
componentName = initJdom(component, pluginId)
}
}
catch (e: CancellationException) {
@@ -182,50 +169,33 @@ abstract class ComponentStoreImpl : IComponentStore {
if (e is ControlFlowException) {
throw e
}
LOG.error(PluginException("Cannot init component state " +
"(componentName=$componentName, componentClass=${component.javaClass.simpleName})", e, pluginId))
LOG.error(PluginException("Cannot init component state (componentName=$componentName, componentClass=${component.javaClass.simpleName})", e, pluginId))
}
}
private fun initJdom(@Suppress("DEPRECATION") component: com.intellij.openapi.util.JDOMExternalizable, pluginId: PluginId): String {
if (component.javaClass.name !in ignoredDeprecatedJDomExternalizableComponents) {
LOG.error(PluginException("""
|Component ${component.javaClass.name} implements deprecated JDOMExternalizable interface to serialize its state.
|IntelliJ Platform will stop supporting such components in the future, so it must be migrated to use PersistentStateComponent.
|See https://plugins.jetbrains.com/docs/intellij/persisting-state-of-components.html for details.
""".trimMargin(), pluginId))
|Component ${component.javaClass.name} implements deprecated JDOMExternalizable interface to serialize its state.
|IntelliJ Platform will stop supporting such components in the future, so it must be migrated to use PersistentStateComponent.
|See https://plugins.jetbrains.com/docs/intellij/persisting-state-of-components.html for details.
""".trimMargin(), pluginId))
}
val componentName = getComponentName(component)
val componentInfo = createComponentInfo(
component = component,
stateSpec = null,
serviceDescriptor = null,
pluginId = pluginId,
)
val element = storageManager.getOldStorage(
component = component,
componentName = componentName,
operation = StateStorageOperation.READ,
) ?.getState(
component = component,
componentName = componentName,
pluginId = pluginId,
stateClass = Element::class.java,
mergeInto = null,
reload = false,
)
val componentInfo = createComponentInfo(component, stateSpec = null, serviceDescriptor = null, pluginId = pluginId)
val element = storageManager.getOldStorage(component, componentName, StateStorageOperation.READ)
?.getState(component, componentName, pluginId, stateClass = Element::class.java, mergeInto = null, reload = false)
if (element != null) {
component.readExternal(element)
}
registerComponent(name = componentName, info = componentInfo)
registerComponent(componentName, componentInfo)
return componentName
}
private fun getComponentName(component: Any): String {
@Suppress("DEPRECATION")
return if (component is NamedComponent) component.componentName else component.javaClass.name
}
@Suppress("DEPRECATION")
private fun getComponentName(component: Any): String =
if (component is NamedComponent) component.componentName else component.javaClass.name
override fun unloadComponent(component: Any) {
@Suppress("DEPRECATION")
@@ -239,26 +209,19 @@ abstract class ComponentStoreImpl : IComponentStore {
final override fun initPersistencePlainComponent(component: Any, key: String, pluginId: PluginId) {
val stateSpec = StateAnnotation(key, FileStorageAnnotation(StoragePathMacros.WORKSPACE_FILE, false))
registerComponent(
name = stateSpec.name,
info = createComponentInfo(
component = PersistenceStateAdapter(component),
stateSpec = stateSpec,
serviceDescriptor = null,
pluginId = pluginId,
),
)
val componentInfo = createComponentInfo(PersistenceStateAdapter(component), stateSpec, serviceDescriptor = null, pluginId)
registerComponent(stateSpec.name, componentInfo)
}
override suspend fun save(forceSavingAllSettings: Boolean) {
val result = SaveResult()
doSave(saveResult = result, forceSavingAllSettings = forceSavingAllSettings)
result.rethrow()
val saveResult = SaveResult()
doSave(saveResult, forceSavingAllSettings)
saveResult.rethrow()
}
internal open suspend fun doSave(saveResult: SaveResult, forceSavingAllSettings: Boolean) {
val saveSessionManager = createSaveSessionProducerManager()
commitComponents(isForce = forceSavingAllSettings, sessionManager = saveSessionManager, saveResult = saveResult)
commitComponents(forceSavingAllSettings, saveSessionManager, saveResult)
saveSessionManager.save(saveResult)
}
@@ -289,9 +252,11 @@ abstract class ComponentStoreImpl : IComponentStore {
}
else {
if (isSaveModLogEnabled) {
SAVE_MOD_LOG.debug("Skip $name: was already saved in last " +
"${TimeUnit.SECONDS.toMinutes(NOT_ROAMABLE_COMPONENT_SAVE_THRESHOLD_DEFAULT.toLong())} minutes " +
"(lastSaved ${info.lastSaved}, now: $nowInSeconds)")
SAVE_MOD_LOG.debug(
"Skip $name: was already saved in last " +
"${TimeUnit.SECONDS.toMinutes(NOT_ROAMABLE_COMPONENT_SAVE_THRESHOLD_DEFAULT.toLong())} minutes " +
"(lastSaved ${info.lastSaved}, now: $nowInSeconds)"
)
}
continue
}
@@ -313,13 +278,7 @@ abstract class ComponentStoreImpl : IComponentStore {
modificationCountChanged = true
}
}
commitComponent(
sessionManager = sessionManager,
info = info,
componentName = name,
modificationCountChanged = modificationCountChanged,
)
commitComponent(sessionManager, info, name, modificationCountChanged)
info.updateModificationCount(currentModificationCount)
}
catch (e: Throwable) {
@@ -349,26 +308,17 @@ abstract class ComponentStoreImpl : IComponentStore {
val stateSpec = getStateSpec(component)
LOG.debug { "saveComponent is called for ${stateSpec.name}" }
val saveManager = createSaveSessionProducerManager()
val storages = getStorageSpecs(component = component, stateSpec = stateSpec, operation = StateStorageOperation.WRITE)
val storages = getStorageSpecs(component, stateSpec, StateStorageOperation.WRITE)
val storage = storages.firstOrNull { !it.deprecated } ?: throw AssertionError("All storages are deprecated")
val absolutePath = storageManager.expandMacro(storage.path).toString()
val componentInfo = components.get(stateSpec.name)
Disposer.newDisposable().use {
VfsRootAccess.allowRootAccess(it, absolutePath)
@Suppress("DEPRECATION")
runUnderModalProgressIfIsEdt {
commitComponent(
sessionManager = saveManager,
info = componentInfo ?: ComponentInfoImpl(
pluginId = PluginManager.getPluginByClass(component::class.java)?.pluginId ?: PluginManagerCore.CORE_ID,
component = component,
stateSpec = stateSpec,
),
componentName = null,
modificationCountChanged = false,
)
val pluginId = PluginManager.getPluginByClass(component::class.java)?.pluginId ?: PluginManagerCore.CORE_ID
val componentInfo = componentInfo ?: ComponentInfoImpl(pluginId, component, stateSpec)
commitComponent(saveManager, componentInfo, componentName = null, modificationCountChanged = false)
val saveResult = SaveResult()
saveManager.save(saveResult)
saveResult.rethrow()
@@ -376,9 +326,8 @@ abstract class ComponentStoreImpl : IComponentStore {
}
}
internal open fun createSaveSessionProducerManager(): SaveSessionProducerManager {
return SaveSessionProducerManager(isUseVfsForWrite = false, collectVfsEvents = false)
}
internal open fun createSaveSessionProducerManager(): SaveSessionProducerManager =
SaveSessionProducerManager(isUseVfsForWrite = false, collectVfsEvents = false)
private suspend fun commitComponent(
sessionManager: SaveSessionProducerManager,
@@ -391,12 +340,7 @@ abstract class ComponentStoreImpl : IComponentStore {
if (component is com.intellij.openapi.util.JDOMExternalizable) {
val effectiveComponentName = componentName ?: getComponentName(component)
storageManager.getOldStorage(component, effectiveComponentName, StateStorageOperation.WRITE)?.let {
sessionManager.getProducer(it)?.setState(
component = component,
componentName = effectiveComponentName,
pluginId = info.pluginId,
state = component,
)
sessionManager.getProducer(it)?.setState(component, effectiveComponentName, info.pluginId, component)
}
return
}
@@ -410,11 +354,7 @@ abstract class ComponentStoreImpl : IComponentStore {
val stateStorageChooser = component as? StateStorageChooserEx
@Suppress("UNCHECKED_CAST")
val storageSpecs = getStorageSpecs(
component = component as PersistentStateComponent<Any>,
stateSpec = stateSpec,
operation = StateStorageOperation.WRITE,
)
val storageSpecs = getStorageSpecs(component as PersistentStateComponent<Any>, stateSpec, StateStorageOperation.WRITE)
for (storageSpec in storageSpecs) {
var resolution = stateStorageChooser?.getResolution(storageSpec, StateStorageOperation.WRITE) ?: Resolution.DO
if (resolution == Resolution.SKIP) {
@@ -433,7 +373,7 @@ abstract class ComponentStoreImpl : IComponentStore {
val sessionProducer = sessionManager.getProducer(storage) ?: continue
if (resolution == Resolution.CLEAR ||
(storageSpec.deprecated && storageSpecs.none { !it.deprecated && it.value == storageSpec.value })) {
sessionProducer.setState(component = component, componentName = effectiveComponentName, pluginId = info.pluginId, state = null)
sessionProducer.setState(component, effectiveComponentName, info.pluginId, state = null)
}
else {
if (!stateRequested) {
@@ -445,25 +385,20 @@ abstract class ComponentStoreImpl : IComponentStore {
featureUsageSettingManager.logConfigurationChanged(effectiveComponentName, state)
}
setStateToSaveSessionProducer(
state = state,
info = info,
effectiveComponentName = effectiveComponentName,
sessionProducer = sessionProducer,
)
setStateToSaveSessionProducer(state, info, effectiveComponentName, sessionProducer)
}
}
}
// method is not called if storage is deprecated or clear was requested (state in these cases is null),
// but called if state is null if returned so from a component
// the method is not called if storage is deprecated or clear was requested (the state in these cases is `null`),
// but is called if the state is `null` if returned so from a component
protected open fun setStateToSaveSessionProducer(
state: Any?,
info: ComponentInfo,
effectiveComponentName: String,
sessionProducer: SaveSessionProducer,
) {
sessionProducer.setState(component = info.component, componentName = effectiveComponentName, pluginId = info.pluginId, state = state)
sessionProducer.setState(info.component, effectiveComponentName, info.pluginId, state)
}
private fun registerComponent(name: String, info: ComponentInfo): ComponentInfo? {
@@ -474,7 +409,10 @@ abstract class ComponentStoreImpl : IComponentStore {
val existing = components.putIfAbsent(name, info)
if (existing != null && existing.component !== info.component) {
LOG.error("Conflicting component name '$name': ${existing.component.javaClass} and ${info.component.javaClass} (componentManager=${storageManager.componentManager})")
LOG.error(
"Conflicting component name '$name': ${existing.component.javaClass} and " +
"${info.component.javaClass} (componentManager=${storageManager.componentManager})"
)
return existing
}
else {
@@ -486,11 +424,12 @@ abstract class ComponentStoreImpl : IComponentStore {
@Suppress("UNCHECKED_CAST")
val component = info.component as PersistentStateComponent<Any>
if (info.stateSpec == null) {
val configurationSchemaKey = info.configurationSchemaKey ?: throw UnsupportedOperationException("configurationSchemaKey must be specified for ${component.javaClass.name}")
return initComponentWithoutStateSpec(component = component, configurationSchemaKey = configurationSchemaKey, pluginId = info.pluginId)
val configurationSchemaKey = info.configurationSchemaKey ?:
throw UnsupportedOperationException("configurationSchemaKey must be specified for ${component.javaClass.name}")
return initComponentWithoutStateSpec(component, configurationSchemaKey, info.pluginId)
}
else {
doInitComponent(info = info, component = component, changedStorages = changedStorages, reloadData = reloadData)
doInitComponent(info, component, changedStorages, reloadData)
return true
}
}
@@ -501,19 +440,8 @@ abstract class ComponentStoreImpl : IComponentStore {
pluginId: PluginId,
): Boolean {
val stateClass = ComponentSerializationUtil.getStateClass<Any>(component.javaClass)
val storage = getReadOnlyStorage(
componentClass = component.javaClass,
stateClass = stateClass,
configurationSchemaKey = configurationSchemaKey,
)
val state = storage?.getState(
component = component,
componentName = "",
pluginId = pluginId,
stateClass = stateClass,
mergeInto = null,
reload = false,
)
val storage = getReadOnlyStorage(component.javaClass, stateClass, configurationSchemaKey)
val state = storage?.getState(component, componentName = "", pluginId, stateClass, mergeInto = null, reload = false)
if (state == null) {
component.noStateLoaded()
}
@@ -523,9 +451,7 @@ abstract class ComponentStoreImpl : IComponentStore {
return true
}
protected open fun getReadOnlyStorage(componentClass: Class<Any>, stateClass: Class<Any>, configurationSchemaKey: String): StateStorage? {
return null
}
protected open fun getReadOnlyStorage(componentClass: Class<Any>, stateClass: Class<Any>, configurationSchemaKey: String): StateStorage? = null
private fun doInitComponent(
info: ComponentInfo,
@@ -542,14 +468,14 @@ abstract class ComponentStoreImpl : IComponentStore {
val stateSpec = info.stateSpec!!
val name = stateSpec.name
// KT-39968: PathMacrosImpl could increase modCount on loadState, and the change has to be persisted
// all other components follow a general rule: initial modCount is calculated after loadState phase
// KT-39968: `PathMacrosImpl` could increase `modCount` on `loadState`, and the change has to be persisted;
// all other components follow a general rule: initial `modCount` is calculated after the ` loadState ` phase
val postLoadStateUpdateModificationCount = name != "PathMacrosImpl"
val defaultState = if (stateSpec.defaultStateAsResource) getDefaultState(component = component, componentName = name, stateClass = stateClass) else null
val defaultState = if (stateSpec.defaultStateAsResource) getDefaultState(component, name, stateClass) else null
if (loadPolicy == StateLoadPolicy.LOAD || info.stateSpec?.allowLoadInTests == true) {
val storageChooser = component as? StateStorageChooserEx
for (storageSpec in getStorageSpecs(component = component, stateSpec = stateSpec, operation = StateStorageOperation.READ)) {
for (storageSpec in getStorageSpecs(component, stateSpec, StateStorageOperation.READ)) {
if (storageChooser?.getResolution(storageSpec, StateStorageOperation.READ) == Resolution.SKIP) {
continue
}
@@ -558,26 +484,19 @@ abstract class ComponentStoreImpl : IComponentStore {
// if storage marked as changed, it means that analyzeExternalChangesAndUpdateIfNeeded was called for it and storage is already reloaded
val isReloadDataForStorage = if (reloadData == ThreeState.UNSURE) {
changedStorages == null || isStorageChanged(changedStorages = changedStorages, storage = storage)
changedStorages == null || isStorageChanged(changedStorages, storage)
}
else {
reloadData.toBoolean()
}
val stateGetter = doCreateStateGetter(
reloadData = isReloadDataForStorage,
storage = storage,
info = info,
componentName = name,
stateClass = stateClass,
useLoadedStateAsExisting = stateSpec.useLoadedStateAsExisting,
)
val stateGetter = doCreateStateGetter(isReloadDataForStorage, storage, info, name, stateClass, stateSpec.useLoadedStateAsExisting)
var state = stateGetter.getState(defaultState)
if (state == null) {
if (changedStorages != null && isStorageChanged(changedStorages, storage)) {
// state will be null if file deleted
// we must create empty (initial) state to reinit component
state = deserializeState(stateElement = Element("state"), stateClass = stateClass)!!
// the state will be `null` if a file is deleted;
// we must create an empty (initial) state to reinit the component
state = deserializeState(Element("state"), stateClass)!!
}
else {
if (isReportStatisticAllowed(stateSpec, storageSpec)) {
@@ -593,7 +512,7 @@ abstract class ComponentStoreImpl : IComponentStore {
component.loadState(state)
val stateAfterLoad = stateGetter.archiveState()
if (isReportStatisticAllowed(stateSpec, storageSpec)) {
featureUsageSettingManager.logConfigurationState(componentName = name, state = stateAfterLoad ?: state)
featureUsageSettingManager.logConfigurationState(name, stateAfterLoad ?: state)
}
if (postLoadStateUpdateModificationCount) {
@@ -619,9 +538,8 @@ abstract class ComponentStoreImpl : IComponentStore {
}
}
protected open fun isReportStatisticAllowed(stateSpec: State, storageSpec: Storage): Boolean {
return !storageSpec.deprecated && stateSpec.reportStatistic && storageSpec.value != StoragePathMacros.CACHE_FILE
}
protected open fun isReportStatisticAllowed(stateSpec: State, storageSpec: Storage): Boolean =
!storageSpec.deprecated && stateSpec.reportStatistic && storageSpec.value != StoragePathMacros.CACHE_FILE
protected open fun doCreateStateGetter(
reloadData: Boolean,
@@ -636,34 +554,19 @@ abstract class ComponentStoreImpl : IComponentStore {
// getting state after loading with an active controller can lead to unusual issues - disable write protection
if (useLoadedStateAsExisting && storage is XmlElementStorage && (storage.controller == null || project != null) && isUseLoadedStateAsExisting(storage)) {
return storage.createGetSession(
component = component,
componentName = componentName,
pluginId = info.pluginId,
stateClass = stateClass,
reload = reloadData,
)
return storage.createGetSession(component, componentName, info.pluginId, stateClass, reloadData)
}
return object : StateGetter<Any> {
override fun getState(mergeInto: Any?): Any? {
return storage.getState(
component = component,
componentName = componentName,
pluginId = info.pluginId,
stateClass = stateClass,
mergeInto = mergeInto,
reload = reloadData,
)
}
override fun getState(mergeInto: Any?): Any? =
storage.getState(component, componentName, info.pluginId, stateClass, mergeInto, reloadData)
override fun archiveState(): Any? = null
}
}
protected open fun isUseLoadedStateAsExisting(storage: StateStorage): Boolean {
return (storage as? XmlElementStorage)?.roamingType != RoamingType.DISABLED && isUseLoadedStateAsExistingVmProperty
}
protected open fun isUseLoadedStateAsExisting(storage: StateStorage): Boolean =
(storage as? XmlElementStorage)?.roamingType != RoamingType.DISABLED && isUseLoadedStateAsExistingVmProperty
protected open fun getPathMacroManagerForDefaults(): PathMacroManager? = null
@@ -694,9 +597,9 @@ abstract class ComponentStoreImpl : IComponentStore {
if (stateSpec.defaultStateAsResource) {
return emptyList()
}
throw AssertionError("No storage specified")
}
return sortStoragesByDeprecated(storages)
}
@@ -735,37 +638,40 @@ abstract class ComponentStoreImpl : IComponentStore {
}
override fun reloadStates(componentNames: Set<String>) {
reinitComponents(componentNames = componentNames, changedStorages = emptySet(), notReloadableComponents = emptySet())
reinitComponents(componentNames, changedStorages = emptySet(), notReloadableComponents = emptySet())
}
internal fun batchReloadStates(componentNames: Set<String>, messageBus: MessageBus) {
val publisher = messageBus.syncPublisher(BatchUpdateListener.TOPIC)
publisher.onBatchUpdateStarted()
try {
reinitComponents(componentNames = componentNames, changedStorages = emptySet(), notReloadableComponents = emptySet())
reinitComponents(componentNames, changedStorages = emptySet(), notReloadableComponents = emptySet())
}
finally {
publisher.onBatchUpdateFinished()
}
}
private fun reloadPerClientState(componentClass: Class<out PersistentStateComponent<*>>,
info: ComponentInfo,
changedStorages: Set<StateStorage>) {
private fun reloadPerClientState(
componentClass: Class<out PersistentStateComponent<*>>,
info: ComponentInfo,
changedStorages: Set<StateStorage>
) {
if (ClientId.isCurrentlyUnderLocalId) {
throw AssertionError("This method must be called under remote client id")
}
val perClientComponent = (storageManager.componentManager ?: application).getService(componentClass)
if (perClientComponent == null || perClientComponent === info.component) {
LOG.error("Failed to reload per-client component '${info.stateSpec?.name ?: componentClass.simpleName}: " +
"looks like it is not registered as a per-client service " +
"(componentManager=${storageManager.componentManager})")
LOG.error(
"Failed to reload per-client component '${info.stateSpec?.name ?: componentClass.simpleName}: " +
"looks like it is not registered as a per-client service (componentManager=${storageManager.componentManager})"
)
return
}
val newInfo = ComponentInfoImpl(info.pluginId, perClientComponent, info.stateSpec)
initComponent(info = newInfo, changedStorages = changedStorages.ifEmpty { null }, reloadData = ThreeState.YES)
initComponent(newInfo, changedStorages.ifEmpty { null }, reloadData = ThreeState.YES)
}
final override fun reloadState(componentClass: Class<out PersistentStateComponent<*>>) {
@@ -777,7 +683,7 @@ abstract class ComponentStoreImpl : IComponentStore {
return
}
initComponent(info = info, changedStorages = emptySet(), reloadData = ThreeState.YES)
initComponent(info, changedStorages = emptySet(), reloadData = ThreeState.YES)
}
}
@@ -794,7 +700,7 @@ abstract class ComponentStoreImpl : IComponentStore {
}
val isChangedStoragesEmpty = changedStorages.isEmpty()
initComponent(info = info, changedStorages = if (isChangedStoragesEmpty) null else changedStorages, reloadData = ThreeState.UNSURE)
initComponent(info, changedStorages = if (isChangedStoragesEmpty) null else changedStorages, reloadData = ThreeState.UNSURE)
return true
}
@@ -810,19 +716,19 @@ abstract class ComponentStoreImpl : IComponentStore {
val componentNames = HashSet<String>()
for (storage in changedStorages) {
LOG.runAndLogException {
// we must update (reload in-memory storage data) even if non-reloadable component is detected later
// we must update (reload in-memory storage data) even if a non-reloadable component is detected later
// not saved -> user does a modification -> new (on disk) state will be overwritten and not applied
storage.analyzeExternalChangesAndUpdateIfNeeded(componentNames)
}
}
@Suppress("UsePropertyAccessSyntax")
if (componentNames.isEmpty()) {
return emptySet()
}
LOG.debug { "Reload components: $componentNames" }
val notReloadableComponents = getNotReloadableComponents(componentNames)
reinitComponents(componentNames = componentNames, changedStorages = changedStorages, notReloadableComponents = notReloadableComponents)
reinitComponents(componentNames, changedStorages, notReloadableComponents)
return notReloadableComponents.ifEmpty { null }
}
@@ -861,6 +767,7 @@ interface ExternalStorageWithInternalPart {
* Provides a way to temporarily ignore a known component extending deprecated JDOMExternalizable interface to avoid having unnecessary
* errors in the log. Each entry must be accompanied by a link to the corresponding YouTrack issue.
*/
@Suppress("ReplaceJavaStaticMethodWithKotlinAnalog")
private val ignoredDeprecatedJDomExternalizableComponents = java.util.Set.of(
"jetbrains.buildServer.codeInspection.InspectionPassRegistrar", //TW-82189
)
@@ -889,7 +796,8 @@ private fun notifyUnknownMacros(store: IComponentStore, project: Project, compon
}
val macros = LinkedHashSet(immutableMacros)
AppUIExecutor.onUiThread().expireWith(project).submit {
@Suppress("DEPRECATION")
com.intellij.openapi.application.AppUIExecutor.onUiThread().expireWith(project).submit {
var notified: MutableList<String>? = null
val manager = NotificationsManager.getNotificationsManager()
for (notification in manager.getNotificationsOfType(
@@ -903,23 +811,21 @@ private fun notifyUnknownMacros(store: IComponentStore, project: Project, compon
macros.removeAll(notified)
}
@Suppress("UsePropertyAccessSyntax")
if (macros.isEmpty()) {
return@submit
}
LOG.debug("Reporting unknown path macros $macros in component $componentName")
doNotify(macros = macros, project = project, substitutorToStore = java.util.Map.of(substitutor, store))
doNotify(macros, project, substitutorToStore = java.util.Map.of(substitutor, store))
}
}
internal suspend fun getStateForComponent(component: PersistentStateComponent<*>, stateSpec: State): Any? {
return when {
component is SerializablePersistentStateComponent<*> -> component.state
stateSpec.getStateRequiresEdt -> withContext(Dispatchers.EDT) { component.state }
else -> readAction { component.state }
}
internal suspend fun getStateForComponent(component: PersistentStateComponent<*>, stateSpec: State): Any? = when {
component is SerializablePersistentStateComponent<*> -> component.state
stateSpec.getStateRequiresEdt -> withContext(Dispatchers.EDT) { component.state }
else -> readAction { component.state }
}
private fun isStorageChanged(changedStorages: Set<StateStorage>, storage: StateStorage): Boolean {
return changedStorages.contains(storage) || (storage is ExternalStorageWithInternalPart && changedStorages.contains(storage.internalStorage))
}
private fun isStorageChanged(changedStorages: Set<StateStorage>, storage: StateStorage): Boolean =
changedStorages.contains(storage) || (storage is ExternalStorageWithInternalPart && changedStorages.contains(storage.internalStorage))

View File

@@ -54,13 +54,9 @@ abstract class ComponentStoreWithExtraComponents : ComponentStoreImpl() {
}
override suspend fun doSave(saveResult: SaveResult, forceSavingAllSettings: Boolean) {
val saveSessionManager = createSaveSessionProducerManager()
saveSettingsAndCommitComponents(
saveResult = saveResult,
forceSavingAllSettings = forceSavingAllSettings,
sessionManager = saveSessionManager,
)
saveSessionManager.save(saveResult)
val sessionManager = createSaveSessionProducerManager()
saveSettingsAndCommitComponents(saveResult, forceSavingAllSettings, sessionManager)
sessionManager.save(saveResult)
}
internal suspend fun saveSettingsAndCommitComponents(
@@ -74,9 +70,7 @@ abstract class ComponentStoreWithExtraComponents : ComponentStoreImpl() {
try {
settingsSavingComponent.save()
}
catch (e: CancellationException) {
throw e
}
catch (e: CancellationException) { throw e }
catch (e: Throwable) {
saveResult.addError(e)
}
@@ -86,40 +80,32 @@ abstract class ComponentStoreWithExtraComponents : ComponentStoreImpl() {
// SchemeManager (asyncSettingsSavingComponent) must be saved before saving components
// (component state uses scheme manager in an ipr project, so, we must save it before) so, call it sequentially
commitComponents(isForce = forceSavingAllSettings, sessionManager = sessionManager, saveResult = saveResult)
commitComponents(forceSavingAllSettings, sessionManager, saveResult)
}
final override suspend fun commitComponents(isForce: Boolean, sessionManager: SaveSessionProducerManager, saveResult: SaveResult) {
// ensure that this task will not interrupt regular saving
runCatching {
commitObsoleteComponents(session = sessionManager, isProjectLevel = false)
commitObsoleteComponents(sessionManager, isProjectLevel = false)
}.getOrLogException(LOG)
super.commitComponents(isForce = isForce, sessionManager = sessionManager, saveResult = saveResult)
super.commitComponents(isForce, sessionManager, saveResult)
}
internal open fun commitObsoleteComponents(session: SaveSessionProducerManager, isProjectLevel: Boolean) {
val storageManager = storageManager as? StateStorageManagerImpl ?: return
for (item in ObsoleteStorageBean.EP_NAME.filterableLazySequence()) {
val bean = item.instance ?: continue
if (bean.isProjectLevel != isProjectLevel) {
continue
}
val storage = storageManager.getOrCreateStorage(collapsedPath = bean.file ?: continue, roamingType = RoamingType.DISABLED)
if (bean.isProjectLevel != isProjectLevel) continue
val collapsedPath = bean.file ?: continue
val storage = storageManager.getOrCreateStorage(collapsedPath, roamingType = RoamingType.DISABLED)
for (componentName in bean.components) {
session.getProducer(storage)?.setState(
component = null,
componentName = componentName,
pluginId = item.pluginDescriptor.pluginId,
state = null,
)
session.getProducer(storage)?.setState(component = null, componentName, item.pluginDescriptor.pluginId, state = null)
}
}
}
final override fun release() {
asyncSettingsSavingComponents.drop()
super.release()
}
}

View File

@@ -288,7 +288,7 @@ open class DirectoryBasedStorage(
val dir = if (useVfs) {
var dir = storage.getVirtualFile()
if (dir == null || !dir.exists()) {
dir = createDir(storage.dir, this)
dir = createDir(storage.dir, requestor = this)
storage.cachedVirtualFile = dir
}
dir

View File

@@ -1,6 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplacePutWithAssignment", "ReplaceGetOrSet")
package com.intellij.configurationStore
import com.intellij.ide.highlighter.ProjectFileType
@@ -112,7 +110,7 @@ open class ProjectStoreImpl(final override val project: Project) : ComponentStor
macros.add(Macro(StoragePathMacros.WORKSPACE_FILE, workspacePath))
if (isUnitTestMode) {
// we don't load default state in tests as app store does, because:
// we don't load the default state in tests as the app store does, because:
// 1) we should not do it
// 2) it was so before, so, we preserve the old behavior (otherwise RunManager will load template run configurations)
// load state only if there are existing files
@@ -211,14 +209,13 @@ open class ProjectStoreImpl(final override val project: Project) : ComponentStor
return "$prefix${Integer.toHexString(path.invariantSeparatorsPathString.hashCode())}"
}
final override fun getPresentableUrl(): String {
final override fun getPresentableUrl(): String =
if (isDirectoryBased) {
return (dirOrFile ?: throw IllegalStateException("setPath was not yet called")).invariantSeparatorsPathString
(dirOrFile ?: throw IllegalStateException("setPath was not yet called")).invariantSeparatorsPathString
}
else {
return projectFilePath.invariantSeparatorsPathString
projectFilePath.invariantSeparatorsPathString
}
}
final override fun getProjectWorkspaceId(): String? = ProjectIdManager.getInstance(project).id
@@ -388,8 +385,7 @@ open class ProjectStoreImpl(final override val project: Project) : ComponentStor
saveResult: SaveResult,
forceSavingAllSettings: Boolean,
projectSessionManager: ProjectSaveSessionProducerManager
) {
}
) { }
override fun createSaveSessionProducerManager(): ProjectSaveSessionProducerManager = ProjectSaveSessionProducerManager(project, storageManager.isUseVfsForWrite)
@@ -411,33 +407,24 @@ private class ProjectStateStorageManager(private val project: Project) : StateSt
override fun normalizeFileSpec(fileSpec: String): String = removeMacroIfStartsWith(path = super.normalizeFileSpec(fileSpec), macro = PROJECT_CONFIG_DIR)
override fun expandMacro(collapsedPath: String): Path {
if (collapsedPath[0] == '$') {
return super.expandMacro(collapsedPath)
}
else {
// PROJECT_CONFIG_DIR is the first macro
return macros.get(0).value.resolve(collapsedPath)
}
}
override fun expandMacro(collapsedPath: String): Path =
if (collapsedPath[0] == '$') super.expandMacro(collapsedPath)
else macros[0].value.resolve(collapsedPath) // PROJECT_CONFIG_DIR is the first macro
override fun beforeElementSaved(elements: MutableList<Element>, rootAttributes: MutableMap<String, String>) {
rootAttributes.put(VERSION_OPTION, "4")
}
override fun getOldStorageSpec(component: Any, componentName: String, operation: StateStorageOperation): String {
return if (ComponentManagerImpl.badWorkspaceComponents.contains(componentName)) StoragePathMacros.WORKSPACE_FILE else PROJECT_FILE
}
override fun getOldStorageSpec(component: Any, componentName: String, operation: StateStorageOperation): String =
if (ComponentManagerImpl.badWorkspaceComponents.contains(componentName)) StoragePathMacros.WORKSPACE_FILE else PROJECT_FILE
override val isExternalSystemStorageEnabled: Boolean
get() = project.isExternalStorageEnabled
}
@CalledInAny
internal suspend fun ensureFilesWritable(project: Project, files: Collection<VirtualFile>): ReadonlyStatusHandler.OperationStatus {
return withContext(Dispatchers.EDT) {
ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(files)
}
internal suspend fun ensureFilesWritable(project: Project, files: Collection<VirtualFile>): ReadonlyStatusHandler.OperationStatus = withContext(Dispatchers.EDT) {
ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(files)
}
internal val useBackgroundSave: Boolean

View File

@@ -43,22 +43,7 @@ internal open class SaveSessionProducerManager(private val isUseVfsForWrite: Boo
if (isUseVfsForWrite) {
writeAction {
for (saveSession in saveSessions) {
try {
saveSession.saveBlocking()
}
catch (e: ReadOnlyModificationException) {
LOG.warn(e)
saveResult.addReadOnlyFile(SaveSessionAndFile(e.session ?: saveSession, e.file))
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: CancellationException) {
throw e
}
catch (e: Exception) {
saveResult.addError(e)
}
saveSessionBlocking(saveSession, saveResult)
}
}
}
@@ -66,22 +51,7 @@ internal open class SaveSessionProducerManager(private val isUseVfsForWrite: Boo
val events = if (collectVfsEvents) ArrayList<VFileEvent>() else null
val syncList = if (events == null) null else Collections.synchronizedList(events)
for (saveSession in saveSessions) {
try {
saveSession.save(syncList)
}
catch (e: ReadOnlyModificationException) {
LOG.warn(e)
saveResult.addReadOnlyFile(SaveSessionAndFile(e.session ?: saveSession, e.file))
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: CancellationException) {
throw e
}
catch (e: Exception) {
saveResult.addError(e)
}
saveSession(saveSession, syncList, saveResult)
}
if (!events.isNullOrEmpty()) {
blockingContext {
@@ -90,4 +60,34 @@ internal open class SaveSessionProducerManager(private val isUseVfsForWrite: Boo
}
}
}
private fun saveSessionBlocking(saveSession: SaveSession, saveResult: SaveResult) {
try {
saveSession.saveBlocking()
}
catch (e: ReadOnlyModificationException) {
LOG.warn(e)
saveResult.addReadOnlyFile(SaveSessionAndFile(e.session ?: saveSession, e.file))
}
catch (e: ProcessCanceledException) { throw e }
catch (e: CancellationException) { throw e }
catch (e: Exception) {
saveResult.addError(e)
}
}
private suspend fun saveSession(saveSession: SaveSession, events: MutableList<VFileEvent>?, saveResult: SaveResult) {
try {
saveSession.save(events)
}
catch (e: ReadOnlyModificationException) {
LOG.warn(e)
saveResult.addReadOnlyFile(SaveSessionAndFile(e.session ?: saveSession, e.file))
}
catch (e: ProcessCanceledException) { throw e }
catch (e: CancellationException) { throw e }
catch (e: Exception) {
saveResult.addError(e)
}
}
}

View File

@@ -1,6 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet")
package com.intellij.configurationStore.schemeManager
import com.intellij.configurationStore.LazySchemeProcessor
@@ -8,6 +6,7 @@ import com.intellij.configurationStore.SchemeContentChangedHandler
import com.intellij.openapi.options.Scheme
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.annotations.VisibleForTesting
import java.util.function.Function
internal sealed interface SchemeChangeEvent<T : Scheme, M : T> {
@@ -18,24 +17,16 @@ internal sealed interface SchemeAddOrUpdateEvent {
val file: VirtualFile
}
private fun <T : Scheme, M : T> findExternalizableSchemeByFileName(fileName: String, schemeManager: SchemeManagerImpl<T, M>): T? {
return schemeManager.schemes.firstOrNull {
fileName == getSchemeFileName(schemeManager, it)
}
}
internal fun <T : Scheme, M : T> getSchemeFileName(schemeManager: SchemeManagerImpl<T, M>, scheme: T): String {
return "${schemeManager.getFileName(scheme)}${schemeManager.schemeExtension}"
}
internal fun <T : Scheme, M : T> getSchemeFileName(schemeManager: SchemeManagerImpl<T, M>, scheme: T): String =
"${schemeManager.getFileName(scheme)}${schemeManager.schemeExtension}"
internal fun <T : Scheme, M : T> readSchemeFromFile(file: VirtualFile, schemeLoader: SchemeLoader<T, M>, schemeManager: SchemeManagerImpl<T, M>): T? {
val fileName = file.name
if (file.isDirectory || !schemeManager.canRead(fileName)) {
return null
}
return catchAndLog({ file.path }) {
schemeLoader.loadScheme(fileName = fileName, input = null, preloadedBytes = file.contentsToByteArray())
schemeLoader.loadScheme(fileName, input = null, file.contentsToByteArray())
}
}
@@ -68,7 +59,7 @@ internal class SchemeChangeApplicator<T : Scheme, M : T>(private val schemeManag
val fileName = file.name
@Suppress("UNCHECKED_CAST")
val changedScheme = findExternalizableSchemeByFileName(fileName, schemeManager) as M?
val changedScheme = schemeManager.schemes.firstOrNull<T> { getSchemeFileName(schemeManager, it) == fileName } as M?
if (callSchemeContentChangedIfSupported(changedScheme, fileName, file, schemeManager)) {
continue
}
@@ -108,7 +99,7 @@ internal class SchemeChangeApplicator<T : Scheme, M : T>(private val schemeManag
}
}
// exposed for test only
@VisibleForTesting
internal fun <T : Scheme, M : T> sortSchemeChangeEvents(inputEvents: Collection<SchemeChangeEvent<T, M>>): Collection<SchemeChangeEvent<T, M>> {
if (inputEvents.size < 2) {
return inputEvents
@@ -116,13 +107,12 @@ internal fun <T : Scheme, M : T> sortSchemeChangeEvents(inputEvents: Collection<
var isThereSomeRemoveEvent = false
val existingAddOrUpdate = HashSet<String>()
val removedFileNames = HashSet<String>()
val result = ArrayList(inputEvents)
// first, remove any event before RemoveAllSchemes and remove RemoveScheme event if there is any subsequent add/update
// first, remove any event before `RemoveAllSchemes` and remove `RemoveScheme` events if there is any subsequent add/update
for (i in (result.size - 1) downTo 0) {
val event = result.get(i)
val event = result[i]
if (event is RemoveAllSchemes) {
for (j in (i - 1) downTo 0) {
result.removeAt(j)
@@ -152,8 +142,8 @@ internal fun <T : Scheme, M : T> sortSchemeChangeEvents(inputEvents: Collection<
fun weight(event: SchemeChangeEvent<T, M>): Int = if (event is SchemeAddOrUpdateEvent) 1 else 0
if (isThereSomeRemoveEvent) {
// second, move all RemoveScheme to first place, to ensure that SchemeLoader will be not created during processing of RemoveScheme event
// (because RemoveScheme removes schemes from scheme manager directly)
// second, move all `RemoveScheme` events to the top - to ensure that `SchemeLoader` won't be created during processing of `RemoveScheme` events
// (because `RemoveScheme` removes schemes from the scheme manager directly)
result.sortWith(Comparator { o1, o2 ->
weight(o1) - weight(o2)
})
@@ -162,10 +152,12 @@ internal fun <T : Scheme, M : T> sortSchemeChangeEvents(inputEvents: Collection<
return result
}
private fun <T : Scheme, M : T> callSchemeContentChangedIfSupported(changedScheme: M?,
fileName: String,
file: VirtualFile,
schemeManager: SchemeManagerImpl<T, M>): Boolean {
private fun <T : Scheme, M : T> callSchemeContentChangedIfSupported(
changedScheme: M?,
fileName: String,
file: VirtualFile,
schemeManager: SchemeManagerImpl<T, M>
): Boolean {
if (changedScheme == null || schemeManager.processor !is SchemeContentChangedHandler<*> || schemeManager.processor !is LazySchemeProcessor) {
return false
}
@@ -179,13 +171,10 @@ private fun <T : Scheme, M : T> callSchemeContentChangedIfSupported(changedSchem
val schemeName = name
?: schemeManager.processor.getSchemeKey(attributeProvider, FileUtilRt.getNameWithoutExtension(fileName))
?: throw nameIsMissed(bytes)
val dataHolder = SchemeDataHolderImpl(schemeManager.processor, bytes, externalInfo)
val processor = schemeManager.processor
@Suppress("UNCHECKED_CAST")
(processor as SchemeContentChangedHandler<M>).schemeContentChanged(changedScheme, schemeName, dataHolder)
(schemeManager.processor as SchemeContentChangedHandler<M>).schemeContentChanged(changedScheme, schemeName, dataHolder)
}
true
} ?: false
}
} == true
}

View File

@@ -47,8 +47,8 @@ internal class SchemeFileTracker<T : Scheme, M : T>(
else if (schemeManager.canRead(event.childName) && isMyDirectory(event.parent)) {
val virtualFile = event.file
LOG.debug { "CREATED ${event.path} (virtualFile: ${if (virtualFile == null) "not " else ""}found)" }
virtualFile?.let {
list.add(AddScheme(it))
if (virtualFile != null) {
list.add(AddScheme(virtualFile))
}
}
}

View File

@@ -22,11 +22,10 @@ import com.intellij.util.addSuppressed
import com.intellij.util.containers.ContainerUtil
import kotlinx.coroutines.CancellationException
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.TestOnly
import java.nio.file.Path
@NonNls const val ROOT_CONFIG: String = "\$ROOT_CONFIG$"
const val ROOT_CONFIG: String = "\$ROOT_CONFIG\$"
internal typealias FileChangeSubscriber = (schemeManager: SchemeManagerImpl<*, *>) -> Unit
@@ -59,11 +58,11 @@ sealed class SchemeManagerFactoryBase : SchemeManagerFactory(), SettingsSavingCo
processor,
streamProvider ?: componentManager?.stateStore?.storageManager?.streamProvider,
ioDirectory = directoryPath ?: pathToFile(path),
roamingType = roamingType,
presentableName = presentableName,
schemeNameToFileName = schemeNameToFileName,
fileChangeSubscriber = fileChangeSubscriber,
settingsCategory = settingsCategory,
roamingType,
presentableName,
schemeNameToFileName,
fileChangeSubscriber,
settingsCategory,
)
if (isAutoSave) {
@Suppress("UNCHECKED_CAST")
@@ -91,12 +90,8 @@ sealed class SchemeManagerFactoryBase : SchemeManagerFactory(), SettingsSavingCo
try {
processor(manager)
}
catch (e: CancellationException) {
throw e
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: CancellationException) { throw e }
catch (e: ProcessCanceledException) { throw e }
catch (e: Throwable) {
LOG.error("Cannot reload settings for ${manager.javaClass.name}", e)
}
@@ -111,12 +106,8 @@ sealed class SchemeManagerFactoryBase : SchemeManagerFactory(), SettingsSavingCo
try {
registeredManager.saveImpl(events)
}
catch (e: CancellationException) {
throw e
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: CancellationException) { throw e }
catch (e: ProcessCanceledException) { throw e }
catch (e: Throwable) {
error = addSuppressed(error, e)
}
@@ -153,20 +144,17 @@ sealed class SchemeManagerFactoryBase : SchemeManagerFactory(), SettingsSavingCo
return path
}
override fun pathToFile(path: String): Path {
return ApplicationManager.getApplication().stateStore.storageManager.expandMacro(ROOT_CONFIG).resolve(path)
}
override fun pathToFile(path: String): Path =
ApplicationManager.getApplication().stateStore.storageManager.expandMacro(ROOT_CONFIG).resolve(path)
}
@Suppress("unused")
private class ProjectSchemeManagerFactory(private val project: Project) : SchemeManagerFactoryBase() {
override val componentManager = project
override fun createFileChangeSubscriber(): FileChangeSubscriber {
return { schemeManager ->
if (!ApplicationManager.getApplication().isUnitTestMode || project.getUserData(LISTEN_SCHEME_VFS_CHANGES_IN_TEST_MODE) == true) {
project.messageBus.simpleConnect().subscribe(VirtualFileManager.VFS_CHANGES, SchemeFileTracker(schemeManager, project))
}
override fun createFileChangeSubscriber(): FileChangeSubscriber = { schemeManager ->
if (!ApplicationManager.getApplication().isUnitTestMode || project.getUserData(LISTEN_SCHEME_VFS_CHANGES_IN_TEST_MODE) == true) {
project.messageBus.simpleConnect().subscribe(VirtualFileManager.VFS_CHANGES, SchemeFileTracker(schemeManager, project))
}
}
@@ -178,11 +166,10 @@ sealed class SchemeManagerFactoryBase : SchemeManagerFactory(), SettingsSavingCo
val projectStore = project.stateStore as? IProjectStore
val projectFileDir = projectStore?.directoryStorePath
if (projectFileDir == null) {
return if (projectStore != null) projectStore.projectBasePath.resolve(".$path") else Path.of(project.basePath!!, ".$path")
}
else {
return projectFileDir.resolve(path)
return when {
projectFileDir != null -> projectFileDir.resolve(path)
projectStore != null -> projectStore.projectBasePath.resolve(".$path")
else -> Path.of(project.basePath!!, ".${path}")
}
}
}

View File

@@ -103,8 +103,8 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
get() = schemes.isEmpty()
private fun refreshVirtualDirectory() {
// store refreshes root directory, so, we don't need to use refreshAndFindFile
val directory = LocalFileSystem.getInstance().findFileByPath(ioDirectory.invariantSeparatorsPathString) ?: return
// a parent component store refreshes the root directory, so we don't have to use `refreshAndFind*`
val directory = LocalFileSystem.getInstance().findFileByNioFile(ioDirectory) ?: return
cachedVirtualDirectory = directory
directory.children
(directory as? NewVirtualFile)?.markDirty()
@@ -120,9 +120,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
val schemeKey = provider.schemeKey
val fileNameWithoutExtension = schemeNameToFileName(schemeKey)
val externalInfo = ExternalInfo(fileNameWithoutExtension = fileNameWithoutExtension,
fileExtension = fileNameWithoutExtension + ComponentStorageUtil.DEFAULT_EXT)
val externalInfo = ExternalInfo(fileNameWithoutExtension, fileExtension = fileNameWithoutExtension + ComponentStorageUtil.DEFAULT_EXT)
externalInfo.schemeKey = schemeKey
val scheme = provider.createScheme()
@@ -145,26 +143,18 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
override fun loadBundledScheme(resourceName: String, requestor: Any?, pluginDescriptor: PluginDescriptor?): T? {
try {
val bytes = loadBytes(pluginDescriptor = pluginDescriptor, requestor = requestor, resourceName = resourceName) ?: return null
lazyPreloadScheme(bytes = bytes, isOldSchemeNaming = isOldSchemeNaming) { name, parser ->
val bytes = loadBytes(pluginDescriptor, requestor, resourceName) ?: return null
lazyPreloadScheme(bytes, isOldSchemeNaming) { name, parser ->
val attributeProvider: (String) -> String? = { parser.getAttributeValue(null, it) }
val fileName = PathUtilRt.getFileName(resourceName)
val extension = getFileExtension(fileName = fileName, isAllowAny = true)
val externalInfo = ExternalInfo(fileNameWithoutExtension = fileName.substring(0, fileName.length - extension.length),
fileExtension = extension)
val extension = getFileExtension(fileName, isAllowAny = true)
val externalInfo = ExternalInfo(fileNameWithoutExtension = fileName.substring(0, fileName.length - extension.length), extension)
val schemeKey = name
?: (processor as LazySchemeProcessor).getSchemeKey(attributeProvider, externalInfo.fileNameWithoutExtension)
?: throw nameIsMissed(bytes)
externalInfo.schemeKey = schemeKey
val scheme = (processor as LazySchemeProcessor).createScheme(dataHolder = SchemeDataHolderImpl(processor = processor,
bytes = bytes,
externalInfo = externalInfo),
name = schemeKey,
attributeProvider = attributeProvider,
isBundled = true)
val dataHolder = SchemeDataHolderImpl(processor, bytes, externalInfo)
val scheme = (processor as LazySchemeProcessor).createScheme(dataHolder, schemeKey, attributeProvider, isBundled = true)
val oldInfo = schemeListManager.data.putSchemeInfo(scheme, externalInfo)
LOG.assertTrue(oldInfo == null)
val oldScheme = schemeListManager.readOnlyExternalizableSchemes.put(schemeKey, scheme)
@@ -183,9 +173,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
return null
}
private fun loadBytes(pluginDescriptor: PluginDescriptor?,
requestor: Any?,
resourceName: String): ByteArray? {
private fun loadBytes(pluginDescriptor: PluginDescriptor?, requestor: Any?, resourceName: String): ByteArray? {
val bytes: ByteArray?
if (pluginDescriptor == null) {
when (requestor) {
@@ -200,8 +188,8 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
}
}
else -> {
bytes = ResourceUtil.getResourceAsBytes(resourceName.removePrefix("/"),
(if (requestor is ClassLoader) requestor else requestor!!.javaClass.classLoader))
val classLoader = if (requestor is ClassLoader) requestor else requestor!!.javaClass.classLoader
bytes = ResourceUtil.getResourceAsBytes(resourceName.removePrefix("/"), classLoader)
if (bytes == null) {
LOG.error("Cannot read scheme from $resourceName")
return null
@@ -224,9 +212,9 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
val filesToDelete = HashSet(filesToDelete)
// the caller must call SchemeLoader.apply to bring back scheduled for deleting files
this.filesToDelete.removeAll(filesToDelete)
// `SchemeLoader` can use a retained list to bring back previously scheduled for deleting file;
// but what if someone calls `save()` during a load and file will be deleted, although it should be loaded by a new load session
// (because modified on disk)?
// `SchemeLoader` can use a retained list to bring back a previously scheduled for deleting file;
// but what if someone calls `save()` during a load and the file will be deleted,
// although it should be loaded by a new load session (because modified on disk)?
return SchemeLoader(schemeManager = this, oldList = schemeListManager.data, filesToDelete, isDuringLoad)
}
@@ -243,8 +231,8 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
}
try {
// isDuringLoad is true even if loadSchemes called not first time, but on reload,
// because scheme processor should use cumulative event `reloaded` to update runtime state/caches
// `isDuringLoad` is `true` even if `loadSchemes` called not first time, but on reload,
// because a scheme processor should use a cumulative `reloaded` event to update runtime state/caches
val schemeLoader = createSchemeLoader(isDuringLoad = true)
val isLoadOnlyFromProvider = provider != null && provider.processChildren(fileSpec, roamingType, { canRead(it) }) { name, input, readOnly ->
catchAndLog({ "${provider.javaClass.name}: $name" }) {
@@ -293,7 +281,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
override fun reload(retainFilter: ((scheme: T) -> Boolean)?) {
processor.beforeReloaded(this)
// we must not remove non-persistent (e.g., predefined) schemes, because we cannot load it (obviously)
// do not schedule scheme file removing because we just need to update our runtime state, not state on disk
// do not schedule the scheme file removing because we just need to update our runtime state, not state on disk
removeExternalizableSchemesFromRuntimeState()
processor.reloaded(this, loadSchemes())
}
@@ -326,6 +314,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
@Suppress("UNCHECKED_CAST")
processor.onSchemeDeleted(scheme as MUTABLE_SCHEME)
}
retainExternalInfo(isScheduleToDelete = false, schemeToInfo = list.schemeToInfo, newSchemes = list.list)
}
@@ -389,7 +378,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
this.filesToDelete.removeAll(filesToDelete)
deleteFiles(errorCollector, filesToDelete, events)
// remove empty directory only if some file was deleted - avoid check on each save
// remove an empty directory only if some file was deleted - avoid check on each save
if (!hasSchemes && (provider == null || !provider.isApplicable(fileSpec, roamingType))) {
removeDirectoryIfEmpty(errorCollector, events)
}
@@ -454,11 +443,10 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
externalInfo.isDigestEquals(newDigest)) {
return
}
else if (isEqualToBundledScheme(externalInfo = externalInfo, newDigest = newDigest, scheme = scheme, filesToDelete = filesToDelete)) {
return
else if (isEqualToBundledScheme(externalInfo, newDigest, scheme, filesToDelete)) {
// we must check it only here to avoid deleting an old scheme just because it is empty
// (an old idea save -> a new idea deletes on open)
// (an old version saves -> a new version deletes)
return
}
else if (processor is LazySchemeProcessor && processor.isSchemeDefault(scheme, newDigest)) {
externalInfo?.scheduleDelete(filesToDelete, "equals to default")
@@ -529,15 +517,17 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
externalInfo.schemeKey = processor.getSchemeKey(scheme)
}
private fun isEqualToBundledScheme(externalInfo: ExternalInfo?,
newDigest: Long,
scheme: MUTABLE_SCHEME,
filesToDelete: MutableSet<String>): Boolean {
private fun isEqualToBundledScheme(
externalInfo: ExternalInfo?,
newDigest: Long,
scheme: MUTABLE_SCHEME,
filesToDelete: MutableSet<String>
): Boolean {
fun serializeIfPossible(scheme: T): Element? {
@Suppress("UNCHECKED_CAST")
val bundledAsMutable = scheme as? MUTABLE_SCHEME ?: return null
return runCatching {
@Suppress("UNCHECKED_CAST")
val bundledAsMutable = scheme as? MUTABLE_SCHEME ?: return@runCatching null
return@runCatching processor.writeScheme(bundledAsMutable) as Element
processor.writeScheme(bundledAsMutable) as Element
}.getOrLogException(LOG)
}
@@ -665,8 +655,8 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
override fun toString(): String = fileSpec
/**
* Call this method before invoking [com.intellij.openapi.components.impl.stores.IComponentStore.save] to ensure that schema will be saved
* even if there were no changes.
* Call this method before invoking [com.intellij.openapi.components.impl.stores.IComponentStore.save]
* to ensure that a schema will be saved even when there are no changes.
*/
@TestOnly
fun forceSaving() {
@@ -702,14 +692,7 @@ class SchemeManagerImpl<T : Scheme, MUTABLE_SCHEME : T>(
if (error is CancellationException || error is ProcessCanceledException) {
throw error
}
val compoundError = this.error
if (compoundError == null) {
this.error = error
}
else {
compoundError.addSuppressed(error)
}
this.error = addSuppressed(this.error, error)
}
fun getError(): Throwable? = error

View File

@@ -1,6 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("PropertyName")
package com.intellij.openapi.editor.colors.impl
import com.intellij.configurationStore.BundledSchemeEP
@@ -61,7 +59,6 @@ import com.intellij.util.ui.StartupUiUtil
import com.intellij.util.xml.dom.createXmlStreamReader
import com.intellij.util.xmlb.annotations.OptionTag
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.TestOnly
import org.jetbrains.annotations.VisibleForTesting
import java.nio.file.Path
@@ -72,14 +69,19 @@ import javax.xml.stream.XMLStreamReader
private val LOG: Logger
get() = logger<EditorColorsManagerImpl>()
private const val TEMP_SCHEME_KEY: @NonNls String = "TEMP_SCHEME_KEY"
private const val TEMP_SCHEME_FILE_KEY: @NonNls String = "TEMP_SCHEME_FILE_KEY"
private const val TEMP_SCHEME_KEY: String = "TEMP_SCHEME_KEY"
private const val TEMP_SCHEME_FILE_KEY: String = "TEMP_SCHEME_FILE_KEY"
@State(name = EditorColorsManagerImpl.COMPONENT_NAME, storages = [Storage(EditorColorsManagerImpl.STORAGE_NAME)],
additionalExportDirectory = EditorColorsManagerImpl.FILE_SPEC, category = SettingsCategory.UI)
@State(
name = EditorColorsManagerImpl.COMPONENT_NAME,
storages = [Storage(EditorColorsManagerImpl.STORAGE_NAME)],
additionalExportDirectory = EditorColorsManagerImpl.FILE_SPEC,
category = SettingsCategory.UI
)
@ApiStatus.Internal
class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: SchemeManagerFactory)
: EditorColorsManager(), PersistentStateComponent<EditorColorsManagerImpl.State?> {
: EditorColorsManager(), PersistentStateComponent<EditorColorsManagerImpl.State?>
{
private val treeDispatcher = ComponentTreeEventDispatcher.create(EditorColorsListener::class.java)
private val schemeModificationCounter = AtomicLong()
@@ -92,12 +94,17 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
constructor() : this(SchemeManagerFactory.getInstance())
init {
val additionalTextAttributes = collectAdditionalTextAttributesEPs()
schemeManager = schemeManagerFactory.create(directoryName = FILE_SPEC,
processor = EditorColorSchemeProcessor(additionalTextAttributes),
presentableName = null,
directoryPath = null,
settingsCategory = SettingsCategory.UI)
val additionalTextAttributes = HashMap<String, MutableList<AdditionalTextAttributesEP>>()
ADDITIONAL_TEXT_ATTRIBUTES_EP_NAME.forEachExtensionSafe {
additionalTextAttributes.computeIfAbsent(it.scheme) { ArrayList() }.add(it)
}
schemeManager = schemeManagerFactory.create(
directoryName = FILE_SPEC,
EditorColorSchemeProcessor(additionalTextAttributes),
presentableName = null,
directoryPath = null,
SettingsCategory.UI
)
for (defaultScheme in DefaultColorSchemesManager.getInstance().allSchemes) {
schemeManager.addScheme(defaultScheme)
}
@@ -106,7 +113,6 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
}
schemeManager.loadSchemes()
loadRemainAdditionalTextAttributes(additionalTextAttributes)
initEditableDefaultSchemesCopies()
initEditableBundledSchemesCopies()
resolveLinksToBundledSchemes()
@@ -114,17 +120,15 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
companion object {
@VisibleForTesting
val ADDITIONAL_TEXT_ATTRIBUTES_EP_NAME: ExtensionPointName<AdditionalTextAttributesEP> = ExtensionPointName(
"com.intellij.additionalTextAttributes")
val ADDITIONAL_TEXT_ATTRIBUTES_EP_NAME: ExtensionPointName<AdditionalTextAttributesEP> =
ExtensionPointName("com.intellij.additionalTextAttributes")
const val COMPONENT_NAME: String = "EditorColorsManagerImpl"
const val STORAGE_NAME: String = "colors.scheme.xml"
const val FILE_SPEC: String = "colors"
fun isTempScheme(scheme: EditorColorsScheme?): Boolean {
return (scheme ?: return false).getMetaProperties().getProperty(TEMP_SCHEME_KEY).toBoolean()
}
fun isTempScheme(scheme: EditorColorsScheme?): Boolean =
scheme?.getMetaProperties()?.getProperty(TEMP_SCHEME_KEY).toBoolean()
fun getTempSchemeOriginalFilePath(scheme: EditorColorsScheme): Path? {
if (isTempScheme(scheme)) {
@@ -193,11 +197,9 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
createEditableCopy(initialScheme = scheme, editableCopyName = Scheme.EDITABLE_COPY_PREFIX + scheme.name, to)
}
}
for (scheme in to) {
schemeManager.addScheme(scheme)
}
}
private fun resolveLinksToBundledSchemes() {
@@ -242,7 +244,6 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
fun schemeChangedOrSwitched(newScheme: EditorColorsScheme?) {
dropPsiCaches()
callGlobalSchemeChange(newScheme)
}
@@ -287,7 +288,6 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
}
continue
}
loadAdditionalTextAttributesForScheme(scheme = scheme, attributeEps = value)
}
additionalTextAttributes.clear()
@@ -295,6 +295,7 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
class State {
@JvmField
@Suppress("PropertyName")
var USE_ONLY_MONOSPACED_FONTS: Boolean = true
@JvmField
@@ -316,23 +317,20 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
}
}
override fun getAllSchemes(): Array<EditorColorsScheme> {
return schemeManager.allSchemes.asSequence()
override fun getAllSchemes(): Array<EditorColorsScheme> =
schemeManager.allSchemes.asSequence()
.filter { AbstractColorsScheme.isVisible(it) }
.sortedWith(EditorColorSchemesComparator.INSTANCE)
.toList()
.toTypedArray()
}
fun setGlobalScheme(scheme: EditorColorsScheme?, processChangeSynchronously: Boolean) {
val notify = LoadingState.COMPONENTS_LOADED.isOccurred
schemeManager.setCurrent(scheme = scheme ?: getDefaultScheme(),
notify = notify,
processChangeSynchronously = processChangeSynchronously)
schemeManager.setCurrent(scheme ?: getDefaultScheme(), notify, processChangeSynchronously)
}
override fun setGlobalScheme(scheme: EditorColorsScheme?) {
setGlobalScheme(scheme = scheme, processChangeSynchronously = false)
setGlobalScheme(scheme, processChangeSynchronously = false)
}
@RequiresEdt
@@ -405,7 +403,7 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
return editableCopy
}
override fun getScheme(schemeName: @NonNls String): EditorColorsScheme? {
override fun getScheme(schemeName: String): EditorColorsScheme? {
if (schemeName.endsWith(".xml")) {
val path = schemeName.removeSuffix(".xml").removePrefix("/")
return schemeManager.allSchemes.firstOrNull {
@@ -420,8 +418,11 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
if (isTempScheme(scheme)) {
return scheme
}
val editableCopyName = getEditableCopyName(scheme)
val editableCopyName = when {
scheme is DefaultColorsScheme && scheme.hasEditableCopy() -> scheme.editableCopyName
scheme is BundledEditorColorScheme -> Scheme.EDITABLE_COPY_PREFIX + scheme.name
else -> null
}
if (editableCopyName != null) {
getScheme(editableCopyName)?.let {
return it
@@ -438,9 +439,7 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
return state
}
override fun isUseOnlyMonospacedFonts(): Boolean {
return state.USE_ONLY_MONOSPACED_FONTS
}
override fun isUseOnlyMonospacedFonts(): Boolean = state.USE_ONLY_MONOSPACED_FONTS
override fun setUseOnlyMonospacedFonts(value: Boolean) {
state.USE_ONLY_MONOSPACED_FONTS = value
@@ -455,7 +454,6 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
if (colorSchemeName != null) {
LOG.warn("$colorSchemeName color scheme is missing")
}
noStateLoaded()
return
}
@@ -474,6 +472,7 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
schemeManager.setCurrent(scheme = colorScheme, notify = isInitialConfigurationLoaded)
isInitialConfigurationLoaded = true
@Suppress("UNNECESSARY_SAFE_CALL")
colorScheme?.let {
notifyAboutSolarizedColorSchemeDeprecationIfSet(scheme = it)
}
@@ -547,7 +546,7 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
scheme.readExternal(dataHolder.read())
// We don't need to update digest for a bundled scheme because:
// 1) it can be computed on demand later (because a bundled scheme is not mutable)
// 2) in the future user copy of a bundled scheme will use a bundled scheme as parent (not as full copy)
// 2) in the future, user copies of bundled schemes will use a bundled scheme as parent (not as full copy)
if (isBundled ||
(ApplicationManager.getApplication().isUnitTestMode() && scheme.metaProperties.getProperty("forceOptimize").toBoolean())) {
if (scheme.parentScheme is AbstractColorsScheme) {
@@ -562,9 +561,8 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
return scheme
}
override fun getState(scheme: EditorColorsScheme): SchemeState {
return if (scheme.isReadOnly) SchemeState.NON_PERSISTENT else SchemeState.POSSIBLY_CHANGED
}
override fun getState(scheme: EditorColorsScheme): SchemeState =
if (scheme.isReadOnly) SchemeState.NON_PERSISTENT else SchemeState.POSSIBLY_CHANGED
override fun onCurrentSchemeSwitched(
oldScheme: EditorColorsScheme?,
@@ -594,16 +592,15 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
schemeChangedOrSwitched(newScheme)
}
override val schemeExtension: @NonNls String
override val schemeExtension: String
get() = getColorSchemeFileExtension()
override fun isSchemeEqualToBundled(scheme: EditorColorsSchemeImpl): Boolean {
if (!scheme.getName().startsWith(Scheme.EDITABLE_COPY_PREFIX)) {
if (!scheme.name.startsWith(Scheme.EDITABLE_COPY_PREFIX)) {
return false
}
val bundledScheme =
(schemeManager.findSchemeByName(scheme.getName().substring(Scheme.EDITABLE_COPY_PREFIX.length)) as AbstractColorsScheme?)
(schemeManager.findSchemeByName(scheme.name.substring(Scheme.EDITABLE_COPY_PREFIX.length)) as AbstractColorsScheme?)
?: return false
return scheme.settingsEqual(bundledScheme)
}
@@ -618,17 +615,111 @@ class EditorColorsManagerImpl @NonInjectable constructor(schemeManagerFactory: S
ApplicationManager.getApplication().getMessageBus().syncPublisher(EditorColorsManagerListener.TOPIC).schemesReloaded()
}
}
}
private val isHeadlessMode: Boolean
get() = ApplicationManager.getApplication().isHeadlessEnvironment()
private val isHeadlessMode: Boolean
get() = ApplicationManager.getApplication().isHeadlessEnvironment()
private fun collectAdditionalTextAttributesEPs(): MutableMap<String, MutableList<AdditionalTextAttributesEP>> {
val result = HashMap<String, MutableList<AdditionalTextAttributesEP>>()
EditorColorsManagerImpl.ADDITIONAL_TEXT_ATTRIBUTES_EP_NAME.forEachExtensionSafe {
result.computeIfAbsent(it.scheme) { ArrayList() }.add(it)
private fun notifyAboutSolarizedColorSchemeDeprecationIfSet(scheme: EditorColorsScheme) {
val solarizedColorSchemeNames = setOf(
"Solarized (dark)", "Solarized (light)", "Solarized Dark", "Solarized Light", "Solarized Dark (Darcula)"
)
val name = scheme.getName().removePrefix(Scheme.EDITABLE_COPY_PREFIX)
if (!solarizedColorSchemeNames.contains(name)) {
return
}
if (name == "Solarized Dark" || name == "Solarized Light") {
@Suppress("SpellCheckingInspection")
val solarizedPluginsContainingSchemesWithTheSameName = arrayOf(
PluginId.getId("solarized"),
PluginId.getId("com.tylerthrailkill.intellij.solarized")
)
for (t in solarizedPluginsContainingSchemesWithTheSameName) {
if (PluginManager.getInstance().findEnabledPlugin(t) != null) {
return
}
}
}
val connection = ApplicationManager.getApplication().getMessageBus().connect()
connection.subscribe<ProjectManagerListener>(ProjectManager.TOPIC, object : ProjectManagerListener {
@Suppress("removal", "OVERRIDE_DEPRECATION")
override fun projectOpened(project: Project) {
connection.disconnect()
ApplicationManager.getApplication().invokeLater(
{
val pluginId = PluginId.getId("com.4lex4.intellij.solarized")
val isDark = ColorUtil.isDark(scheme.getDefaultBackground())
val neededThemeName = if (isDark) "Solarized Dark" else "Solarized Light"
val neededTheme = UiThemeProviderListManager.getInstance().findThemeByName(neededThemeName)
val notification = Notification("ColorSchemeDeprecation", IdeBundle.message(
"notification.title.solarized.color.scheme.deprecation"), NotificationType.ERROR)
if (neededTheme != null) {
notification.setContent(IdeBundle.message(
"notification.content.solarized.color.scheme.deprecation.enable", name,
neededThemeName))
notification.addAction(object : NotificationAction(IdeBundle.message(
"notification.title.enable.action.solarized.color.scheme.deprecation",
neededThemeName)) {
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
val lafManager = LafManager.getInstance()
lafManager.setCurrentLookAndFeel(neededTheme, false)
lafManager.updateUI()
notification.expire()
}
})
}
else {
notification.setContent(IdeBundle.message(
"notification.content.solarized.color.scheme.deprecation.install", name,
"Solarized Themes"))
notification.addAction(object : NotificationAction(IdeBundle.message(
"notification.title.install.action.solarized.color.scheme.deprecation")) {
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
@Suppress("NAME_SHADOWING")
val connection = ApplicationManager.getApplication().getMessageBus().connect()
// Needed to enable matching theme after plugin installation.
// Since the plugin provides two themes, we need to wait for both of them to be added
// (and applied) to reapply the needed one if it wasn't added last.
connection.subscribe(LafManagerListener.TOPIC, object : LafManagerListener {
private var matchingTheme: UIThemeLookAndFeelInfo? = null
private var otherWasSet: Boolean = false
override fun lookAndFeelChanged(source: LafManager) {
val themeInfo = source.getCurrentUIThemeLookAndFeel()
if (themeInfo.name.contains("Solarized")) {
if ((isDark && themeInfo.isDark) || (!isDark && !themeInfo.isDark)) {
matchingTheme = themeInfo
}
else {
otherWasSet = true
}
}
if (matchingTheme != null && otherWasSet) {
connection.disconnect()
if (source.getCurrentUIThemeLookAndFeel() != matchingTheme) {
source.setCurrentLookAndFeel(matchingTheme!!, false)
source.updateUI()
}
}
}
})
installAndEnable(project = project, pluginIds = setOf(pluginId), onSuccess = notification::expire)
}
})
}
notification.notify(project)
},
ModalityState.nonModal(),
)
}
})
}
return result
}
private fun loadAdditionalTextAttributesForScheme(scheme: AbstractColorsScheme, attributeEps: Collection<AdditionalTextAttributesEP>) {
@@ -652,122 +743,6 @@ private fun loadAdditionalTextAttributesForScheme(scheme: AbstractColorsScheme,
}
}
private fun getEditableCopyName(scheme: EditorColorsScheme?): String? {
return when {
scheme is DefaultColorsScheme && scheme.hasEditableCopy() -> scheme.editableCopyName
scheme is BundledEditorColorScheme -> Scheme.EDITABLE_COPY_PREFIX + scheme.getName()
else -> null
}
}
private fun notifyAboutSolarizedColorSchemeDeprecationIfSet(scheme: EditorColorsScheme) {
val solarizedColorSchemeNames = setOf("Solarized (dark)",
"Solarized (light)",
"Solarized Dark",
"Solarized Light",
"Solarized Dark (Darcula)")
val name = scheme.getName().removePrefix(Scheme.EDITABLE_COPY_PREFIX)
if (!solarizedColorSchemeNames.contains(name)) {
return
}
if (name == "Solarized Dark" || name == "Solarized Light") {
@Suppress("SpellCheckingInspection")
val solarizedPluginsContainingSchemesWithTheSameName = arrayOf(
PluginId.getId("solarized"),
PluginId.getId("com.tylerthrailkill.intellij.solarized")
)
for (t in solarizedPluginsContainingSchemesWithTheSameName) {
if (PluginManager.getInstance().findEnabledPlugin(t) != null) {
return
}
}
}
val connection = ApplicationManager.getApplication().getMessageBus().connect()
connection.subscribe<ProjectManagerListener>(ProjectManager.TOPIC, object : ProjectManagerListener {
@Suppress("removal", "OVERRIDE_DEPRECATION")
override fun projectOpened(project: Project) {
connection.disconnect()
ApplicationManager.getApplication().invokeLater(
{
val pluginId = PluginId.getId("com.4lex4.intellij.solarized")
val isDark = ColorUtil.isDark(scheme.getDefaultBackground())
val neededThemeName = if (isDark) "Solarized Dark" else "Solarized Light"
val neededTheme = UiThemeProviderListManager.getInstance().findThemeByName(neededThemeName)
val notification = Notification("ColorSchemeDeprecation",
IdeBundle.message(
"notification.title.solarized.color.scheme.deprecation"),
"",
NotificationType.ERROR)
if (neededTheme != null) {
notification.setContent(IdeBundle.message(
"notification.content.solarized.color.scheme.deprecation.enable", name,
neededThemeName))
notification.addAction(object : NotificationAction(IdeBundle.message(
"notification.title.enable.action.solarized.color.scheme.deprecation",
neededThemeName)) {
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
val lafManager = LafManager.getInstance()
lafManager.setCurrentLookAndFeel(neededTheme, false)
lafManager.updateUI()
notification.expire()
}
})
}
else {
notification.setContent(IdeBundle.message(
"notification.content.solarized.color.scheme.deprecation.install", name,
"Solarized Themes"))
notification.addAction(object : NotificationAction(IdeBundle.message(
"notification.title.install.action.solarized.color.scheme.deprecation")) {
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
@Suppress("NAME_SHADOWING")
val connection = ApplicationManager.getApplication().getMessageBus().connect()
// Needed to enable matching theme after plugin installation.
// Since the plugin provides two themes, we need to wait for both of them to be added
// (and applied) to reapply the needed one if it wasn't added last.
connection.subscribe(LafManagerListener.TOPIC, object : LafManagerListener {
private var matchingTheme: UIThemeLookAndFeelInfo? = null
private var otherWasSet: Boolean = false
override fun lookAndFeelChanged(source: LafManager) {
val themeInfo = source.getCurrentUIThemeLookAndFeel()
if (themeInfo.name.contains("Solarized")) {
if ((isDark && themeInfo.isDark) || (!isDark && !themeInfo.isDark)) {
matchingTheme = themeInfo
}
else {
otherWasSet = true
}
}
if (matchingTheme != null && otherWasSet) {
connection.disconnect()
if (source.getCurrentUIThemeLookAndFeel() != matchingTheme) {
source.setCurrentLookAndFeel(matchingTheme!!, false)
source.updateUI()
}
}
}
})
installAndEnable(project = project, pluginIds = setOf(pluginId), onSuccess = notification::expire)
}
})
}
notification.notify(project)
},
ModalityState.nonModal(),
)
}
})
}
@VisibleForTesting
fun readEditorSchemeNameFromXml(parser: XMLStreamReader): String? {
var eventType = parser.eventType
@@ -787,8 +762,7 @@ private val BUNDLED_EP_NAME = ExtensionPointName<BundledSchemeEP>("com.intellij.
fun createLoadBundledSchemeRequests(
additionalTextAttributes: MutableMap<String, MutableList<AdditionalTextAttributesEP>>,
checkId: Boolean = false,
)
: Sequence<SchemeManager.LoadBundleSchemeRequest<EditorColorsScheme>> {
) : Sequence<SchemeManager.LoadBundleSchemeRequest<EditorColorsScheme>> {
return sequence {
for (item in BUNDLED_EP_NAME.filterableLazySequence()) {
val pluginDescriptor = item.pluginDescriptor
@@ -796,8 +770,8 @@ fun createLoadBundledSchemeRequests(
val resourcePath = (bean.path ?: continue).removePrefix("/").let { if (it.endsWith(".xml")) it else "$it.xml" }
yield(object : SchemeManager.LoadBundleSchemeRequest<EditorColorsScheme> {
override val pluginId: PluginId
get() = pluginDescriptor.pluginId
override val pluginId: PluginId = pluginDescriptor.pluginId
override val schemeKey: String
get() {
val idFromExtension = item.id
@@ -821,13 +795,11 @@ fun createLoadBundledSchemeRequests(
}
}
override fun loadBytes(): ByteArray {
return ResourceUtil.getResourceAsBytes(resourcePath, pluginDescriptor.classLoader)!!
}
override fun loadBytes(): ByteArray =
ResourceUtil.getResourceAsBytes(resourcePath, pluginDescriptor.classLoader)!!
override fun createScheme(): EditorColorsScheme {
return createBundledEditorColorScheme(resourcePath, additionalTextAttributes, loadBytes(), pluginId)
}
override fun createScheme(): EditorColorsScheme =
createBundledEditorColorScheme(resourcePath, additionalTextAttributes, loadBytes(), pluginId)
})
}
@@ -839,7 +811,7 @@ fun createLoadBundledSchemeRequests(
val uiTheme = item.theme.get() ?: continue
val editorSchemeId = uiTheme.theme.originalEditorSchemeId ?: continue
// we must check originalEditorSchemeId to load its corresponding editor scheme on `reloadKeepingActiveScheme` call
// we must check `originalEditorSchemeId` to load its corresponding editor scheme on the `reloadKeepingActiveScheme` call
if (!editorSchemeId.endsWith(".xml")) {
continue
}
@@ -861,20 +833,15 @@ fun createLoadBundledSchemeRequests(
reader.close()
}
// update id to make sure that other consumers of id uses a correct one
// update ID to make sure that other consumers use a correct one
uiTheme.theme.editorSchemeId = colorSchemeId
yield(object : SchemeManager.LoadBundleSchemeRequest<EditorColorsScheme> {
override val pluginId: PluginId
get() = pluginDescriptor.pluginId
override val schemeKey: String
get() = colorSchemeId
override val pluginId: PluginId = pluginDescriptor.pluginId
override val schemeKey: String = colorSchemeId
override fun loadBytes(): ByteArray = data
override fun createScheme(): EditorColorsScheme {
return createBundledEditorColorScheme(resourcePath, additionalTextAttributes, loadBytes(), pluginId)
}
override fun createScheme(): EditorColorsScheme =
createBundledEditorColorScheme(resourcePath, additionalTextAttributes, loadBytes(), pluginId)
})
}
}
@@ -891,11 +858,11 @@ private fun createBundledEditorColorScheme(
scheme.readExternal(JDOMUtil.load(data))
// We don't need to update digest for a bundled scheme because:
// 1) it can be computed on demand later (because a bundled scheme is not mutable)
// 2) in the future user copy of a bundled scheme will use a bundled scheme as parent (not as full copy)
// 2) in the future, user copies of bundled schemes will use a bundled scheme as parent (not as full copy)
if (scheme.parentScheme is AbstractColorsScheme) {
val attributesEPs = additionalTextAttributes.remove(scheme.parentScheme.getName())
if (!attributesEPs.isNullOrEmpty()) {
loadAdditionalTextAttributesForScheme(scheme = scheme.parentScheme as AbstractColorsScheme, attributeEps = attributesEPs)
loadAdditionalTextAttributesForScheme(scheme.parentScheme as AbstractColorsScheme, attributesEPs)
}
}
@@ -904,12 +871,9 @@ private fun createBundledEditorColorScheme(
return scheme
}
internal class BundledEditorColorScheme(@JvmField val resourcePath: String) : EditorColorsSchemeImpl(/* parentScheme = */ null) {
private class BundledEditorColorScheme(@JvmField val resourcePath: String) : EditorColorsSchemeImpl(/* parentScheme = */ null) {
override fun isVisible() = false
override fun isReadOnly() = true
override fun getSchemeState() = SchemeState.UNCHANGED
override fun isFromIntellij(): Boolean = (metaProperties.get("pluginId") as? String)?.startsWith("com.intellij") == true
}
override fun isFromIntellij(): Boolean = (metaProperties["pluginId"] as? String)?.startsWith("com.intellij") == true
}

View File

@@ -5,8 +5,8 @@ import com.intellij.openapi.progress.blockingContext
import org.jetbrains.annotations.ApiStatus.Internal
/**
* Service, which implements this interfaces, will be asked to [save] custom settings (in their own custom way)
* when application (for Application level services) or project (for Project level services) is invoked.
* Services which implement this interface will be asked to [save] custom settings (in their own custom way)
* when application (for application level services) or project (for project level services) settings save is called.
*/
@Internal
interface SettingsSavingComponent {
@@ -21,4 +21,4 @@ interface SettingsSavingComponentJavaAdapter : SettingsSavingComponent {
}
fun doSave()
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.options
import com.intellij.openapi.components.SettingsCategory
@@ -17,7 +17,7 @@ abstract class SchemeManager<T> {
abstract val activeScheme: T?
/**
* If the schemes are lazily loaded, you can utilize this method to delay scheme selection.
* If schemes are lazily loaded, you can use this method to delay scheme selection.
* The scheme will then be located by its name upon the first use.
*/
abstract var currentSchemeName: String?
@@ -35,7 +35,7 @@ abstract class SchemeManager<T> {
abstract fun reload(retainFilter: ((scheme: T) -> Boolean)?)
fun addScheme(scheme: T) {
addScheme(scheme, true)
addScheme(scheme, replaceExisting = true)
}
abstract fun addScheme(scheme: T, replaceExisting: Boolean)
@@ -45,17 +45,15 @@ abstract class SchemeManager<T> {
abstract fun setCurrentSchemeName(schemeName: String?, notify: Boolean)
@JvmOverloads
open fun setCurrent(scheme: T?, notify: Boolean = true, processChangeSynchronously: Boolean = false) {
}
open fun setCurrent(scheme: T?, notify: Boolean = true, processChangeSynchronously: Boolean = false) { }
abstract fun removeScheme(scheme: T): Boolean
abstract fun removeScheme(name: String): T?
/**
* Must be called before [.loadSchemes].
*
* Scheme manager processor must be LazySchemeProcessor
* Must be called before [loadSchemes].
* Scheme manager processor must be [com.intellij.configurationStore.LazySchemeProcessor].
*/
@ApiStatus.Internal
abstract fun loadBundledScheme(resourceName: String, requestor: Any?, pluginDescriptor: PluginDescriptor?): T?
@@ -63,11 +61,8 @@ abstract class SchemeManager<T> {
@ApiStatus.Internal
interface LoadBundleSchemeRequest<T> {
val pluginId: PluginId
val schemeKey: String
fun loadBytes(): ByteArray
fun createScheme(): T
}
@@ -75,8 +70,7 @@ abstract class SchemeManager<T> {
abstract fun loadBundledSchemes(providers: Sequence<LoadBundleSchemeRequest<T>>)
@JvmOverloads
open fun setSchemes(newSchemes: List<T>, newCurrentScheme: T? = null, removeCondition: Predicate<T>? = null) {
}
open fun setSchemes(newSchemes: List<T>, newCurrentScheme: T? = null, removeCondition: Predicate<T>? = null) { }
/**
* Bundled / read-only (or overriding) scheme cannot be renamed or deleted.