mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[rdct] IJPL-199766: Implement file-based settings sync
Some settings are only used in the monolith and on the backend. In this case, PersistentStateComponents aren't loaded on the frontend so the RdSettingsController can't be used to sync these settings. They can only be synced between the frontend and backend as raw files. GitOrigin-RevId: 226200051312e64123941aeacee626883b6c8b03
This commit is contained in:
committed by
intellij-monorepo-bot
parent
91a48987b8
commit
69bace9d1b
@@ -0,0 +1,30 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
/**
|
||||
* Contains multiple listeners and delegates method calls to them
|
||||
*/
|
||||
internal class CompoundOperationListener : OperationListener {
|
||||
|
||||
private val listeners = CopyOnWriteArrayList<OperationListener>()
|
||||
|
||||
override fun onWrite(fileSpec: String, content: ByteArray) {
|
||||
listeners.forEach { it.onWrite(fileSpec, content) }
|
||||
}
|
||||
|
||||
override fun onDelete(fileSpec: String) {
|
||||
listeners.forEach { it.onDelete(fileSpec) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to delegate method calls to
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
fun addOperationListener(listener: OperationListener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,7 +46,8 @@ abstract class FileBasedStorage internal constructor(
|
||||
pathMacroManager: PathMacroSubstitutor? = null,
|
||||
roamingType: RoamingType,
|
||||
provider: StreamProvider? = null,
|
||||
) : XmlElementStorage(fileSpec = fileSpec, rootElementName = rootElementName, pathMacroSubstitutor = pathMacroManager, storageRoamingType = roamingType, provider = provider) {
|
||||
listener: OperationListener? = null
|
||||
) : XmlElementStorage(fileSpec = fileSpec, rootElementName = rootElementName, pathMacroSubstitutor = pathMacroManager, storageRoamingType = roamingType, provider = provider, listener = listener) {
|
||||
@Volatile private var cachedVirtualFile: VirtualFile? = null
|
||||
|
||||
private var lineSeparator: LineSeparator? = null
|
||||
|
||||
@@ -187,6 +187,7 @@ private class ModuleStateStorageManager(macroSubstitutor: TrackingPathMacroSubst
|
||||
roamingType = roamingType,
|
||||
pathMacroManager = macroSubstitutor,
|
||||
provider = provider,
|
||||
listener = null,
|
||||
controller = null,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,12 @@ open class StateStorageManagerImpl(
|
||||
compoundStreamProvider.removeStreamProvider(aClass)
|
||||
}
|
||||
|
||||
private var compoundOperationListener = CompoundOperationListener()
|
||||
|
||||
override fun addOperationListener(listener: OperationListener) {
|
||||
compoundOperationListener.addOperationListener(listener)
|
||||
}
|
||||
|
||||
// access under storageLock
|
||||
private var isUseVfsListener = when (componentManager) {
|
||||
null -> ThreeState.NO
|
||||
@@ -268,6 +274,7 @@ open class StateStorageManagerImpl(
|
||||
roamingType = roamingType,
|
||||
pathMacroManager = macroSubstitutor.takeIf { usePathMacroManager },
|
||||
provider = compoundStreamProvider,
|
||||
listener = compoundOperationListener,
|
||||
controller = controller,
|
||||
)
|
||||
}
|
||||
@@ -280,6 +287,7 @@ open class StateStorageManagerImpl(
|
||||
roamingType: RoamingType,
|
||||
pathMacroManager: PathMacroSubstitutor?,
|
||||
provider: StreamProvider?,
|
||||
listener: OperationListener?,
|
||||
override val controller: SettingsController?,
|
||||
) : FileBasedStorage(
|
||||
file = file,
|
||||
@@ -288,6 +296,7 @@ open class StateStorageManagerImpl(
|
||||
pathMacroManager = pathMacroManager,
|
||||
roamingType = roamingType,
|
||||
provider = provider,
|
||||
listener = listener,
|
||||
), StorageVirtualFileTracker.TrackedStorage {
|
||||
override val isUseXmlProlog: Boolean
|
||||
get() = rootElementName != null && storageManager.isUseXmlProlog && !isSpecialStorage(fileSpec)
|
||||
|
||||
@@ -37,7 +37,8 @@ abstract class XmlElementStorage protected constructor(
|
||||
@JvmField protected val rootElementName: String?,
|
||||
private val pathMacroSubstitutor: PathMacroSubstitutor? = null,
|
||||
storageRoamingType: RoamingType,
|
||||
private val provider: StreamProvider? = null
|
||||
private val provider: StreamProvider? = null,
|
||||
private val listener: OperationListener? = null
|
||||
) : StateStorageBase<StateMap>() {
|
||||
final override val saveStorageDataOnReload: Boolean
|
||||
get() = provider == null || provider.saveStorageDataOnReload
|
||||
@@ -257,34 +258,30 @@ abstract class XmlElementStorage protected constructor(
|
||||
override suspend fun save(events: MutableList<VFileEvent>?) {
|
||||
var isSavedLocally = false
|
||||
val provider = storage.provider
|
||||
val listener = storage.listener
|
||||
|
||||
if (elements == null) {
|
||||
if (provider == null || !provider.delete(storage.fileSpec, storage.roamingType)) {
|
||||
if (provider != null && provider.delete(storage.fileSpec, storage.roamingType)) {
|
||||
listener?.onDelete(storage.fileSpec)
|
||||
}
|
||||
else {
|
||||
isSavedLocally = true
|
||||
if (writer == null) {
|
||||
remove(events)
|
||||
}
|
||||
else {
|
||||
saveLocally(dataWriter = writer, events = events)
|
||||
}
|
||||
removeOrSaveLocally(events)
|
||||
}
|
||||
}
|
||||
else if (provider != null && provider.isApplicable(storage.fileSpec, storage.roamingType)) {
|
||||
// we should use standard line-separator (\n) - stream provider can share file content on any OS
|
||||
val content = writer!!.toBufferExposingByteArray(LineSeparator.LF).toByteArray()
|
||||
provider.write(
|
||||
fileSpec = storage.fileSpec,
|
||||
content = writer!!.toBufferExposingByteArray(LineSeparator.LF).toByteArray(),
|
||||
content = content,
|
||||
roamingType = storage.roamingType,
|
||||
)
|
||||
listener?.onWrite(storage.fileSpec, content)
|
||||
}
|
||||
else {
|
||||
isSavedLocally = true
|
||||
if (writer == null) {
|
||||
remove(events)
|
||||
}
|
||||
else {
|
||||
saveLocally(dataWriter = writer, events = events)
|
||||
}
|
||||
removeOrSaveLocally(events)
|
||||
}
|
||||
|
||||
if (!isSavedLocally) {
|
||||
@@ -293,6 +290,17 @@ abstract class XmlElementStorage protected constructor(
|
||||
|
||||
storage.setStates(originalStates, stateMap)
|
||||
}
|
||||
|
||||
private fun removeOrSaveLocally(events: MutableList<VFileEvent>?) {
|
||||
if (writer == null) {
|
||||
remove(events)
|
||||
storage.listener?.onDelete(storage.fileSpec)
|
||||
}
|
||||
else {
|
||||
saveLocally(dataWriter = writer, events = events)
|
||||
storage.listener?.onWrite(storage.fileSpec, writer.toBufferExposingByteArray(LineSeparator.LF).toByteArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setSerializedState(componentName: String, element: Element?) {
|
||||
|
||||
@@ -309,6 +309,11 @@ bf:com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate$$serializer
|
||||
- f:deserialize(kotlinx.serialization.encoding.Decoder):com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate
|
||||
- f:getDescriptor():kotlinx.serialization.descriptors.SerialDescriptor
|
||||
- f:serialize(kotlinx.serialization.encoding.Encoder,com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate):V
|
||||
com.intellij.ide.settings.fileBasedSync.FileBasedSettingsSyncListProvider
|
||||
- sf:Companion:com.intellij.ide.settings.fileBasedSync.FileBasedSettingsSyncListProvider$Companion
|
||||
- a:getSettingsFileList():java.util.List
|
||||
f:com.intellij.ide.settings.fileBasedSync.FileBasedSettingsSyncListProvider$Companion
|
||||
- f:getEP_NAME():com.intellij.openapi.extensions.ExtensionPointName
|
||||
f:com.intellij.ide.startup.StartupActionScriptManager
|
||||
- s:addActionCommand(com.intellij.ide.startup.StartupActionScriptManager$ActionCommand):V
|
||||
- s:addActionCommands(java.util.List):V
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.settings.fileBasedSync
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
|
||||
/**
|
||||
* Allows specifying settings that should be synced between the monolith and backend as raw files
|
||||
*/
|
||||
interface FileBasedSettingsSyncListProvider {
|
||||
|
||||
companion object {
|
||||
val EP_NAME: ExtensionPointName<FileBasedSettingsSyncListProvider> = ExtensionPointName.Companion.create("com.intellij.rdserver.fileBasedSettingsSyncListProvider")
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of the settings files that should be synced.
|
||||
*
|
||||
* **Note**: This method is called when the list is first required and every time the list of extensions changes.
|
||||
*/
|
||||
fun getSettingsFileList(): List<String>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* Allows listening to changes in configuration files.
|
||||
*
|
||||
* See `com.intellij.configurationStore.StateStorageManagerImpl.addOperationListener`.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
interface OperationListener {
|
||||
|
||||
/**
|
||||
* Called when a configuration file is written
|
||||
*
|
||||
* @param fileSpec usually the name of the file
|
||||
* @param content the content of the file
|
||||
*/
|
||||
fun onWrite(fileSpec: String, content: ByteArray)
|
||||
|
||||
/**
|
||||
* Called when a configuration file is deleted
|
||||
*
|
||||
* @param fileSpec usually the name of the file
|
||||
*/
|
||||
fun onDelete(fileSpec: String)
|
||||
|
||||
}
|
||||
@@ -23,6 +23,13 @@ interface StateStorageManager {
|
||||
|
||||
fun removeStreamProvider(aClass: Class<out StreamProvider>)
|
||||
|
||||
/**
|
||||
* Adds a listener for changes in configuration files
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
fun addOperationListener(listener: OperationListener) {}
|
||||
|
||||
fun getOldStorage(component: Any, componentName: String, operation: StateStorageOperation): StateStorage?
|
||||
|
||||
fun expandMacro(collapsedPath: String): Path
|
||||
|
||||
Reference in New Issue
Block a user