diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsChangeListener.kt b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsChangeListener.kt index 17f636511c52..bf5247cf7eec 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsChangeListener.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsChangeListener.kt @@ -2,7 +2,6 @@ package com.intellij.settingsSync import com.intellij.openapi.util.NlsSafe import org.jetbrains.annotations.ApiStatus.Internal -import java.util.* @Internal @@ -18,7 +17,8 @@ sealed class SyncSettingsEvent { sealed class ExclusiveEvent : SyncSettingsEvent() class IdeChange(snapshot: SettingsSnapshot) : EventWithSnapshot(snapshot) - class CloudChange(snapshot: SettingsSnapshot, val serverVersionId: String?) : EventWithSnapshot(snapshot) + class CloudChange(snapshot: SettingsSnapshot, val serverVersionId: String?, val syncSettings: SettingsSyncState? = null) + : EventWithSnapshot(snapshot) object MustPushRequest : StandardEvent() object LogCurrentSettings : StandardEvent() diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSnapshot.kt b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSnapshot.kt index 5dbd020ef851..d04007308cef 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSnapshot.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSnapshot.kt @@ -2,10 +2,13 @@ package com.intellij.settingsSync import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.application.PathManager +import com.intellij.openapi.components.SettingsCategory import com.intellij.openapi.components.service import com.intellij.openapi.util.BuildNumber +import com.intellij.openapi.util.JDOMUtil import com.intellij.settingsSync.plugins.SettingsSyncPluginsState import com.intellij.util.SystemProperties +import com.intellij.util.xmlb.XmlSerializer import org.jetbrains.annotations.ApiStatus import java.time.Instant import java.util.* @@ -53,6 +56,29 @@ data class SettingsSnapshot(val metaInfo: MetaInfo, fun isDeleted(): Boolean { return metaInfo.isDeleted } + + fun getState(): SettingsSyncState { + val fileState = fileStates.firstOrNull { + it.file == ("${PathManager.OPTIONS_DIRECTORY}/${SettingsSyncSettings.FILE_SPEC}") + } ?: return SettingsSyncStateHolder() + + if (fileState !is FileState.Modified) { + return SettingsSyncStateHolder() + } + try { + val componentElement = JDOMUtil.load(fileState.content).getChildren("component") + .firstOrNull { it.getAttributeValue("name") == SettingsSyncSettings.COMPONENT_NAME } + ?: return SettingsSyncStateHolder() + val state = XmlSerializer.deserialize(componentElement, SettingsSyncSettings.State::class.java) + return SettingsSyncStateHolder( + state + ) + } + catch (ex: Throwable) { + SettingsSyncSettings.LOG.error("Unable to deserialize content of ${SettingsSyncSettings.FILE_SPEC} into object", ex) + return SettingsSyncStateHolder() + } + } } @ApiStatus.Internal diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncBridge.kt b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncBridge.kt index 1bb208c9a2bf..fb24c837c925 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncBridge.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncBridge.kt @@ -102,7 +102,7 @@ class SettingsSyncBridge(parentDisposable: Disposable, settingsLog.advanceMaster() // merge (preserve) 'ide' changes made by logging existing settings val masterPosition = settingsLog.forceWriteToMaster(cloudEvent.snapshot, "Remote changes to initialize settings by data from cloud") - pushToIde(settingsLog.collectCurrentSnapshot(), masterPosition) + pushToIde(settingsLog.collectCurrentSnapshot(), masterPosition, cloudEvent.syncSettings) // normally we set cloud position only after successful push to cloud, but in this case we already take all settings from the cloud, // so no push is needed, and we know the cloud settings state. @@ -127,7 +127,7 @@ class SettingsSyncBridge(parentDisposable: Disposable, SettingsSyncLocalSettings.getInstance().knownAndAppliedServerId = updateResult.serverVersionId SettingsSyncSettings.getInstance().syncEnabled = true - pushToIde(settingsLog.collectCurrentSnapshot(), masterPosition) + pushToIde(settingsLog.collectCurrentSnapshot(), masterPosition, null) } is UpdateResult.FileDeletedFromServer -> { SettingsSyncSettings.getInstance().syncEnabled = false @@ -144,7 +144,7 @@ class SettingsSyncBridge(parentDisposable: Disposable, settingsLog.setCloudPosition(masterPosition) SettingsSyncSettings.getInstance().syncEnabled = true - pushToIde(settingsLog.collectCurrentSnapshot(), masterPosition) + pushToIde(settingsLog.collectCurrentSnapshot(), masterPosition, null) migration.migrateCategoriesSyncStatus(appConfigPath, SettingsSyncSettings.getInstance()) saveIdeSettings() } @@ -333,7 +333,7 @@ class SettingsSyncBridge(parentDisposable: Disposable, } if (newIdePosition != masterPosition) { // master has advanced further that ide => the ide needs to be updated - pushToIde(settingsLog.collectCurrentSnapshot(), masterPosition) + pushToIde(settingsLog.collectCurrentSnapshot(), masterPosition, null) } if (newCloudPosition != masterPosition || pushRequestMode == MUST_PUSH || pushRequestMode == FORCE_PUSH) { @@ -399,8 +399,8 @@ class SettingsSyncBridge(parentDisposable: Disposable, } } - private fun pushToIde(settingsSnapshot: SettingsSnapshot, targetPosition: SettingsLog.Position) { - ideMediator.applyToIde(settingsSnapshot) + private fun pushToIde(settingsSnapshot: SettingsSnapshot, targetPosition: SettingsLog.Position, syncSettings: SettingsSyncState?) { + ideMediator.applyToIde(settingsSnapshot, syncSettings) settingsLog.setIdePosition(targetPosition) LOG.info("Applied settings to the IDE.") } diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediator.kt b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediator.kt index 9c93aff1f8b2..08760fb6cbd1 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediator.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediator.kt @@ -11,7 +11,10 @@ import java.nio.file.Path @ApiStatus.Internal interface SettingsSyncIdeMediator { - fun applyToIde(snapshot: SettingsSnapshot) + /** + * @param settings if not null, SettingsSync settings will be taken from this object rather than snapshot + */ + fun applyToIde(snapshot: SettingsSnapshot, settings: SettingsSyncState?) fun activateStreamProvider() diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediatorImpl.kt b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediatorImpl.kt index 86ad147b0e61..8bc04a1b2cc7 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediatorImpl.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncIdeMediatorImpl.kt @@ -22,6 +22,7 @@ import com.intellij.openapi.util.registry.Registry import com.intellij.settingsSync.SettingsSnapshot.MetaInfo import com.intellij.settingsSync.plugins.SettingsSyncPluginManager import com.intellij.util.io.* +import org.jdom.Element import java.io.InputStream import java.nio.file.FileVisitResult import java.nio.file.Files @@ -64,11 +65,17 @@ internal class SettingsSyncIdeMediatorImpl(private val componentStore: Component return roamingType != RoamingType.DISABLED } - override fun applyToIde(snapshot: SettingsSnapshot) { + override fun applyToIde(snapshot: SettingsSnapshot, settings: SettingsSyncState?) { // 1. update SettingsSyncSettings first to apply changes in categories val settingsSyncFileState = snapshot.fileStates.find { it.file == "$OPTIONS_DIRECTORY/${SettingsSyncSettings.FILE_SPEC}" } - if (settingsSyncFileState != null) { - writeStatesToAppConfig(listOf(settingsSyncFileState)) + if (settings != null) { + LOG.info("applying sync settings from SettingsSyncState") + SettingsSyncSettings.getInstance().applyFromState(settings) + } + else { + if (settingsSyncFileState != null) { + writeStatesToAppConfig(listOf(settingsSyncFileState)) + } } // 2. update plugins @@ -283,10 +290,9 @@ internal class SettingsSyncIdeMediatorImpl(private val componentStore: Component invokeAndWaitIfNeeded { reloadComponents(changedFileSpecs, deletedFileSpecs) - if (Registry.getInstance().isRestartNeeded){ + if (Registry.getInstance().isRestartNeeded) { SettingsSyncEvents.getInstance().fireRestartRequired("registry", SettingsSyncBundle.message("sync.registry.update.message")) } - } } diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncSettings.kt b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncSettings.kt index 44d4bae463ef..809835fa6d8c 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncSettings.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncSettings.kt @@ -2,34 +2,41 @@ package com.intellij.settingsSync import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.* +import com.intellij.openapi.diagnostic.logger +import com.intellij.settingsSync.SettingsSyncSettings.Companion.COMPONENT_NAME import com.intellij.settingsSync.SettingsSyncSettings.Companion.FILE_SPEC +import com.intellij.util.xmlb.annotations.Property import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.TestOnly import java.util.* +import kotlin.collections.ArrayList -@State(name = "SettingsSyncSettings", storages = [Storage(FILE_SPEC)]) +@State(name = COMPONENT_NAME, storages = [Storage(FILE_SPEC)]) @ApiStatus.Internal -class SettingsSyncSettings : - SimplePersistentStateComponent(SettingsSyncSettingsState()) -{ +class SettingsSyncSettings : SettingsSyncState, SerializablePersistentStateComponent(State()) { companion object { - fun getInstance() = ApplicationManager.getApplication().getService(SettingsSyncSettings::class.java) - + fun getInstance(): SettingsSyncSettings = ApplicationManager.getApplication().getService(SettingsSyncSettings::class.java) + val LOG = logger() const val FILE_SPEC = "settingsSync.xml" + const val COMPONENT_NAME = "SettingsSyncSettings" } - var migrationFromOldStorageChecked: Boolean + override var migrationFromOldStorageChecked: Boolean get() = state.migrationFromOldStorageChecked set(value) { - state.migrationFromOldStorageChecked = value + updateState { + it.withMigrationFromOldStorageChecked(value) + } } - var syncEnabled + override var syncEnabled get() = state.syncEnabled set(value) { - state.syncEnabled = value + updateState { + it.withSyncEnabled(value) + } fireSettingsStateChanged(value) } @@ -37,65 +44,145 @@ class SettingsSyncSettings : SettingsSyncEvents.getInstance().fireEnabledStateChanged(syncEnabled) } - fun isCategoryEnabled(category: SettingsCategory) = !state.disabledCategories.contains(category) + override fun isCategoryEnabled(category: SettingsCategory) = state.isCategoryEnabled(category) - fun setCategoryEnabled(category: SettingsCategory, isEnabled: Boolean) { - if (isEnabled) { - state.disabledCategories.remove(category) - } - else { - if (!state.disabledCategories.contains(category)) { - state.disabledCategories.add(category) - state.disabledCategories.sort() - } + override fun setCategoryEnabled(category: SettingsCategory, isEnabled: Boolean) { + updateState { + it.withCategoryEnabled(category, isEnabled) } } - fun isSubcategoryEnabled(category: SettingsCategory, subcategoryId: String): Boolean { - val disabled = state.disabledSubcategories[category] - return disabled == null || !disabled.contains(subcategoryId) + override fun isSubcategoryEnabled(category: SettingsCategory, subcategoryId: String) = state.isSubcategoryEnabled(category, subcategoryId) + override fun setSubcategoryEnabled(category: SettingsCategory, subcategoryId: String, isEnabled: Boolean) { + updateState { + it.withSubcategoryEnabled(category, subcategoryId, isEnabled) + } } - fun setSubcategoryEnabled(category: SettingsCategory, subcategoryId: String, isEnabled: Boolean) { - val disabledList = state.disabledSubcategories[category] - if (isEnabled) { - if (disabledList != null) { - disabledList.remove(subcategoryId) - if (disabledList.isEmpty()) { - state.disabledSubcategories.remove(category) - } - } + override val disabledCategories: List + get() = state.disabledCategories + override val disabledSubcategories: Map> + get() = state.disabledSubcategories + + fun applyFromState(state: SettingsSyncState) { + updateState { + State(state.disabledCategories, state.disabledSubcategories, state.migrationFromOldStorageChecked, state.syncEnabled) } - else { - if (disabledList == null) { - val newList = ArrayList() - newList.add(subcategoryId) - state.disabledSubcategories.put(category, newList) + } + + data class State(@JvmField val disabledCategories: List = emptyList(), + @JvmField val disabledSubcategories: Map> = emptyMap(), + @JvmField @field:Property val migrationFromOldStorageChecked: Boolean = false, + @JvmField @field:Property val syncEnabled: Boolean = false) { + fun withSyncEnabled(enabled: Boolean): State { + return State(disabledCategories, disabledSubcategories, migrationFromOldStorageChecked, enabled) + } + + fun withMigrationFromOldStorageChecked(checked: Boolean): State { + return State(disabledCategories, disabledSubcategories, checked, syncEnabled) + } + + private fun withDisabledCategories(newCategories: List): State { + return State(newCategories, disabledSubcategories, migrationFromOldStorageChecked, syncEnabled) + } + + fun withCategoryEnabled(category: SettingsCategory, isEnabled: Boolean): State { + val newCategories = ArrayList(disabledCategories) + if (isEnabled) { + newCategories -= category } else { - if (!disabledList.contains(subcategoryId)) { - disabledList.add(subcategoryId) - Collections.sort(disabledList) + if (!newCategories.contains(category)) newCategories += category + } + newCategories.sort() + return withDisabledCategories(newCategories) + } + + + private fun withDisabledSubcategories(newSubcategoriesMap: Map>): State { + return State(disabledCategories, newSubcategoriesMap, migrationFromOldStorageChecked, syncEnabled) + } + + fun withSubcategoryEnabled(category: SettingsCategory, subcategoryId: String, isEnabled: Boolean): State { + val newSubcategoriesMap = HashMap(disabledSubcategories) + val subcategoriesList = newSubcategoriesMap[category] + if (isEnabled) { + if (subcategoriesList != null) { + val newSubcategories = ArrayList(subcategoriesList) + newSubcategories -= subcategoryId + if (newSubcategories.isEmpty()) { + newSubcategoriesMap.remove(category) + } + else { + newSubcategoriesMap[category] = newSubcategories + } } } + else { + val newSubcategories = if (subcategoriesList == null) { + ArrayList() + } + else { + ArrayList(subcategoriesList) + } + if (!newSubcategories.contains(subcategoryId)) { + newSubcategories += subcategoryId + } + newSubcategories.sort() + newSubcategoriesMap[category] = newSubcategories + } + return withDisabledSubcategories(newSubcategoriesMap) } - state.intIncrementModificationCount() - } - class SettingsSyncSettingsState : BaseState() { - var syncEnabled by property(false) + fun isCategoryEnabled(category: SettingsCategory) = !disabledCategories.contains(category) - var disabledCategories by list() - var disabledSubcategories by map>() - - var migrationFromOldStorageChecked by property(false) - - @TestOnly - internal fun reset() { - syncEnabled = false - disabledCategories = mutableListOf() - disabledSubcategories = mutableMapOf() - migrationFromOldStorageChecked = false + fun isSubcategoryEnabled(category: SettingsCategory, subcategoryId: String): Boolean { + val disabled = disabledSubcategories[category] + return disabled == null || !disabled.contains(subcategoryId) } } +} + +interface SettingsSyncState { + fun isCategoryEnabled(category: SettingsCategory): Boolean + fun setCategoryEnabled(category: SettingsCategory, isEnabled: Boolean) + fun isSubcategoryEnabled(category: SettingsCategory, subcategoryId: String): Boolean + fun setSubcategoryEnabled(category: SettingsCategory, subcategoryId: String, isEnabled: Boolean) + + val disabledCategories: List + val disabledSubcategories: Map> + + var syncEnabled: Boolean + var migrationFromOldStorageChecked: Boolean +} + +class SettingsSyncStateHolder(initState: SettingsSyncSettings.State = SettingsSyncSettings.State()) : SettingsSyncState { + @Volatile + private var state = initState + override fun isCategoryEnabled(category: SettingsCategory) = state.isCategoryEnabled(category) + + override fun setCategoryEnabled(category: SettingsCategory, isEnabled: Boolean) { + state = state.withCategoryEnabled(category, isEnabled) + } + + override fun isSubcategoryEnabled(category: SettingsCategory, subcategoryId: String) = state.isSubcategoryEnabled(category, subcategoryId) + + override fun setSubcategoryEnabled(category: SettingsCategory, subcategoryId: String, isEnabled: Boolean) { + state = state.withSubcategoryEnabled(category, subcategoryId, isEnabled) + } + + override val disabledCategories: List + get() = state.disabledCategories + override val disabledSubcategories: Map> + get() = state.disabledSubcategories + override var syncEnabled: Boolean + get() = state.syncEnabled + set(value) { + state = state.withSyncEnabled(value) + } + override var migrationFromOldStorageChecked: Boolean + get() = state.migrationFromOldStorageChecked + set(value) { + state = state.withMigrationFromOldStorageChecked(value) + } } \ No newline at end of file diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/EnableSettingsSyncDialog.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/EnableSettingsSyncDialog.kt index 628270ccf426..20d6fef9233d 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/config/EnableSettingsSyncDialog.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/config/EnableSettingsSyncDialog.kt @@ -3,17 +3,19 @@ package com.intellij.settingsSync.config import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.DialogWrapper import com.intellij.settingsSync.SettingsSyncBundle.message -import org.jetbrains.annotations.Nls +import com.intellij.settingsSync.SettingsSyncState +import com.intellij.settingsSync.SettingsSyncStateHolder import java.awt.event.ActionEvent import javax.swing.AbstractAction import javax.swing.Action import javax.swing.JComponent -internal class EnableSettingsSyncDialog - private constructor(parent: JComponent, private val remoteSettingsFound: Boolean) : DialogWrapper(parent, false) { +internal class EnableSettingsSyncDialog(parent: JComponent, remoteSettings: SettingsSyncState?) : DialogWrapper(parent, false) { private lateinit var configPanel: DialogPanel private var dialogResult: Result? = null + val syncSettings: SettingsSyncState = remoteSettings ?: SettingsSyncStateHolder() + private val remoteSettingsExist: Boolean = remoteSettings != null init { title = message("title.settings.sync") @@ -25,22 +27,14 @@ internal class EnableSettingsSyncDialog GET_FROM_SERVER } - companion object { - fun showAndGetResult(parent: JComponent, remoteSettingsFound: Boolean) : Result? { - val dialog = EnableSettingsSyncDialog(parent, remoteSettingsFound) - dialog.show() - return dialog.getResult() - } - } - override fun createCenterPanel(): JComponent { - configPanel = SettingsSyncPanelFactory.createPanel(message("enable.dialog.select.what.to.sync")) + configPanel = SettingsSyncPanelFactory.createPanel(message("enable.dialog.select.what.to.sync"), syncSettings) configPanel.reset() return configPanel } override fun createActions(): Array = - if (remoteSettingsFound) arrayOf(cancelAction, SyncLocalSettingsAction(), GetSettingsFromAccountAction()) + if (remoteSettingsExist) arrayOf(cancelAction, SyncLocalSettingsAction(), GetSettingsFromAccountAction()) else { val enableSyncAction = EnableSyncAction() enableSyncAction.putValue(DEFAULT_ACTION, true) diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsCategoryDescriptor.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsCategoryDescriptor.kt deleted file mode 100644 index 95e8bff8fa69..000000000000 --- a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsCategoryDescriptor.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.intellij.settingsSync.config - -import com.intellij.openapi.components.SettingsCategory -import com.intellij.openapi.components.SettingsCategory.* -import com.intellij.settingsSync.SettingsSyncBundle.message -import com.intellij.settingsSync.SettingsSyncSettings -import org.jetbrains.annotations.Nls -import java.util.* - -internal class SettingsCategoryDescriptor( - private val category : SettingsCategory, - val secondaryGroup: SettingsSyncSubcategoryGroup? = null -) { - - companion object { - private val DESCRIPTORS : List = listOf( - SettingsCategoryDescriptor(UI, SettingsSyncUiGroup()), - SettingsCategoryDescriptor(KEYMAP), - SettingsCategoryDescriptor(CODE), - SettingsCategoryDescriptor(PLUGINS, SettingsSyncPluginsGroup()), - SettingsCategoryDescriptor(TOOLS), - SettingsCategoryDescriptor(SYSTEM), - ) - - fun listAll() : List { - return DESCRIPTORS - } - } - - var isSynchronized: Boolean = true - - fun reset() { - isSynchronized = SettingsSyncSettings.getInstance().isCategoryEnabled(category) - if (secondaryGroup != null) { - secondaryGroup.getDescriptors().forEach { - it.isSelected = isSynchronized && SettingsSyncSettings.getInstance().isSubcategoryEnabled(category, it.id) - } - } - } - - fun apply() { - if (secondaryGroup != null) { - secondaryGroup.getDescriptors().forEach { - // !isSynchronized not store disabled states individually - SettingsSyncSettings.getInstance().setSubcategoryEnabled(category, it.id, !isSynchronized || it.isSelected) - } - } - SettingsSyncSettings.getInstance().setCategoryEnabled(category, isSynchronized) - } - - fun isModified() : Boolean { - if (isSynchronized != SettingsSyncSettings.getInstance().isCategoryEnabled(category)) return true - if (secondaryGroup != null && isSynchronized) { - secondaryGroup.getDescriptors().forEach { - if (it.isSelected != SettingsSyncSettings.getInstance().isSubcategoryEnabled(category, it.id)) return true - } - } - return false - } - - - val name: @Nls String - get() { - return message("${categoryKey}.name") - } - - val description: @Nls String - get() { - return message("${categoryKey}.description") - } - - private val categoryKey: String - get() { - return "settings.category." + category.name.lowercase(Locale.getDefault()) - } -} \ No newline at end of file diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncConfigurable.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncConfigurable.kt index 034eae27c0eb..6fac89f22064 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncConfigurable.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncConfigurable.kt @@ -73,6 +73,7 @@ internal class SettingsSyncConfigurable : BoundConfigurable(message("title.setti SettingsSyncEvents.getInstance().addListener(object : SettingsSyncEventListener { override fun enabledStateChanged(syncEnabled: Boolean) { listener(invoke()) + configPanel.reset() } }, disposable!!) } @@ -126,7 +127,7 @@ internal class SettingsSyncConfigurable : BoundConfigurable(message("title.setti } override fun createPanel(): DialogPanel { - val categoriesPanel = SettingsSyncPanelFactory.createPanel(message("configurable.what.to.sync.label")) + val categoriesPanel = SettingsSyncPanelFactory.createPanel(message("configurable.what.to.sync.label"), SettingsSyncSettings.getInstance()) val authService = SettingsSyncAuthService.getInstance() val authAvailable = authService.isLoginAvailable() configPanel = panel { @@ -252,8 +253,8 @@ internal class SettingsSyncConfigurable : BoundConfigurable(message("title.setti override fun serverStateCheckFinished(updateResult: UpdateResult) { when (updateResult) { - NoFileOnServer, FileDeletedFromServer -> showEnableSyncDialog(false) - is Success -> showEnableSyncDialog(true) + NoFileOnServer, FileDeletedFromServer -> showEnableSyncDialog(null) + is Success -> showEnableSyncDialog(updateResult.settingsSnapshot.getState()) is Error -> { if (updateResult != SettingsSyncEnabler.State.CANCELLED) { showError(message("notification.title.update.error"), updateResult.message) @@ -278,19 +279,20 @@ internal class SettingsSyncConfigurable : BoundConfigurable(message("title.setti updateStatusInfo() } - private fun showEnableSyncDialog(remoteSettingsFound: Boolean) { - val dialogResult = EnableSettingsSyncDialog.showAndGetResult(configPanel, remoteSettingsFound) + private fun showEnableSyncDialog(remoteSettings: SettingsSyncState?) { + val dialog = EnableSettingsSyncDialog(configPanel, remoteSettings) + dialog.show() + val dialogResult = dialog.getResult() if (dialogResult != null) { - reset() when (dialogResult) { EnableSettingsSyncDialog.Result.GET_FROM_SERVER -> { - syncEnabler.getSettingsFromServer() + syncEnabler.getSettingsFromServer(dialog.syncSettings) SettingsSyncEventsStatistics.ENABLED_MANUALLY.log(SettingsSyncEventsStatistics.EnabledMethod.GET_FROM_SERVER) } EnableSettingsSyncDialog.Result.PUSH_LOCAL -> { SettingsSyncSettings.getInstance().syncEnabled = true syncEnabler.pushSettingsToServer() - if (remoteSettingsFound) { + if (remoteSettings != null) { SettingsSyncEventsStatistics.ENABLED_MANUALLY.log(SettingsSyncEventsStatistics.EnabledMethod.PUSH_LOCAL) } else { @@ -302,6 +304,8 @@ internal class SettingsSyncConfigurable : BoundConfigurable(message("title.setti else { SettingsSyncEventsStatistics.ENABLED_MANUALLY.log(SettingsSyncEventsStatistics.EnabledMethod.CANCELED) } + reset() + configPanel.reset() } companion object DisableResult { diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncEnabler.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncEnabler.kt index fa8763686535..28b1615aa85f 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncEnabler.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncEnabler.kt @@ -34,7 +34,7 @@ internal class SettingsSyncEnabler { } - fun getSettingsFromServer() { + fun getSettingsFromServer(syncSettings: SettingsSyncState? = null) { eventDispatcher.multicaster.updateFromServerStarted() val settingsSyncControls = SettingsSyncMain.getInstance().controls object : Task.Modal(null, SettingsSyncBundle.message("enable.sync.get.from.server.progress"), false) { @@ -44,7 +44,7 @@ internal class SettingsSyncEnabler { val result = settingsSyncControls.remoteCommunicator.receiveUpdates() updateResult = result if (result is UpdateResult.Success) { - val cloudEvent = SyncSettingsEvent.CloudChange(result.settingsSnapshot, result.serverVersionId) + val cloudEvent = SyncSettingsEvent.CloudChange(result.settingsSnapshot, result.serverVersionId, syncSettings) settingsSyncControls.bridge.initialize(SettingsSyncBridge.InitMode.TakeFromServer(cloudEvent)) } } @@ -58,7 +58,7 @@ internal class SettingsSyncEnabler { fun pushSettingsToServer() { val settingsSyncControls = SettingsSyncMain.getInstance().controls - object: Task.Modal(null, SettingsSyncBundle.message("enable.sync.push.to.server.progress"), false) { + object : Task.Modal(null, SettingsSyncBundle.message("enable.sync.push.to.server.progress"), false) { override fun run(indicator: ProgressIndicator) { // todo initialization must be modal but pushing to server can be made later settingsSyncControls.bridge.initialize(SettingsSyncBridge.InitMode.PushToServer) diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncPanelFactory.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncPanelFactory.kt index 94dc3bfdbc97..b3d78fce4478 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncPanelFactory.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncPanelFactory.kt @@ -3,6 +3,7 @@ package com.intellij.settingsSync.config import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.settingsSync.SettingsSyncBundle.message +import com.intellij.settingsSync.SettingsSyncState import com.intellij.ui.CheckBoxList import com.intellij.ui.CheckBoxListListener import com.intellij.ui.SeparatorComponent @@ -21,52 +22,51 @@ import javax.swing.JComponent import javax.swing.JPanel internal object SettingsSyncPanelFactory { - fun createPanel(syncLabel: @Nls String): DialogPanel { + fun createPanel(syncLabel: @Nls String, state: SettingsSyncState): DialogPanel { return panel { row { label(syncLabel) } - - SettingsCategoryDescriptor.listAll().forEach { descriptor -> - descriptor.reset() + val categoryHolders = SyncCategoryHolder.createAllForState(state) + for (holder in categoryHolders) { indent { row { - if (descriptor.secondaryGroup == null) { + if (holder.secondaryGroup == null) { checkBox( - descriptor.name + holder.name ) - .bindSelected(descriptor::isSynchronized) - .onReset { descriptor.reset() } - .onApply { descriptor.apply() } - .onIsModified { descriptor.isModified() } - comment(descriptor.description) + .bindSelected(holder::isSynchronized) + .onReset { holder.reset() } + .onApply { holder.apply() } + .onIsModified { holder.isModified() } + comment(holder.description) } else { - val topCheckBox = ThreeStateCheckBox(descriptor.name) + val topCheckBox = ThreeStateCheckBox(holder.name) topCheckBox.isThirdStateEnabled = false cell(topCheckBox) .onReset { - descriptor.reset() - topCheckBox.state = getGroupState(descriptor) + holder.reset() + topCheckBox.state = getGroupState(holder) } .onApply { - descriptor.isSynchronized = topCheckBox.state != State.NOT_SELECTED - descriptor.apply() + holder.isSynchronized = topCheckBox.state != State.NOT_SELECTED + holder.apply() } - .onIsModified { descriptor.isModified() } - val c = comment(descriptor.description).visible(!descriptor.description.isEmpty()) - val subcategoryLink = configureLink(descriptor.secondaryGroup, c.component.font.size2D) { - topCheckBox.state = getGroupState(descriptor) - descriptor.isSynchronized = topCheckBox.state != State.NOT_SELECTED + .onIsModified { holder.isModified() } + val c = comment(holder.description).visible(!holder.description.isEmpty()) + val subcategoryLink = configureLink(holder.secondaryGroup!!, c.component.font.size2D) { + topCheckBox.state = getGroupState(holder) + holder.isSynchronized = topCheckBox.state != State.NOT_SELECTED } cell(subcategoryLink) - .visible(descriptor.secondaryGroup.getDescriptors().size > 1 || !descriptor.secondaryGroup.isComplete()) + .visible(holder.secondaryGroup!!.getDescriptors().size > 1 || !holder.secondaryGroup!!.isComplete()) topCheckBox.addActionListener { - descriptor.isSynchronized = topCheckBox.state != State.NOT_SELECTED - descriptor.secondaryGroup.getDescriptors().forEach { - it.isSelected = descriptor.isSynchronized + holder.isSynchronized = topCheckBox.state != State.NOT_SELECTED + holder.secondaryGroup!!.getDescriptors().forEach { + it.isSelected = holder.isSynchronized } - subcategoryLink.isEnabled = descriptor.secondaryGroup.isComplete() || descriptor.isSynchronized + subcategoryLink.isEnabled = holder.secondaryGroup!!.isComplete() || holder.isSynchronized } } @@ -76,7 +76,7 @@ internal object SettingsSyncPanelFactory { } } - private fun getGroupState(descriptor: SettingsCategoryDescriptor): State { + private fun getGroupState(descriptor: SyncCategoryHolder): State { val group = descriptor.secondaryGroup if (group == null) { return if (descriptor.isSynchronized) State.SELECTED else State.NOT_SELECTED @@ -95,7 +95,7 @@ internal object SettingsSyncPanelFactory { } } - private fun configureLink(group: SettingsSyncSubcategoryGroup, + private fun configureLink(group: SyncSubcategoryGroup, fontSize: Float, onCheckBoxChange: () -> Unit): JComponent { val actionLink = ActionLink(message("subcategory.config.link")) {} diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncSubcatigories.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncSubcategories.kt similarity index 93% rename from plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncSubcatigories.kt rename to plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncSubcategories.kt index c00f9e4503f0..c31412149114 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncSubcatigories.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncSubcategories.kt @@ -2,7 +2,7 @@ package com.intellij.settingsSync.config import org.jetbrains.annotations.Nls -internal interface SettingsSyncSubcategoryGroup { +internal interface SyncSubcategoryGroup { fun getDescriptors() : List /** diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/SyncCategoryHolder.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/SyncCategoryHolder.kt new file mode 100644 index 000000000000..ed0cb7553b59 --- /dev/null +++ b/plugins/settings-sync/src/com/intellij/settingsSync/config/SyncCategoryHolder.kt @@ -0,0 +1,102 @@ +package com.intellij.settingsSync.config + +import com.intellij.openapi.components.SettingsCategory +import com.intellij.openapi.components.SettingsCategory.* +import com.intellij.settingsSync.SettingsSyncState +import com.intellij.settingsSync.SettingsSyncBundle.message +import org.jetbrains.annotations.Nls +import java.util.* + +internal class SyncCategoryHolder( + val descriptor: Category, + private val state: SettingsSyncState +) { + var isSynchronized: Boolean = state.isCategoryEnabled(descriptor.category) + + val name: @Nls String + get() = descriptor.name + + val description: @Nls String + get() = descriptor.description + + val secondaryGroup: SyncSubcategoryGroup? + get() = descriptor.secondaryGroup + + fun reset() { + with(descriptor) { + isSynchronized = state.isCategoryEnabled(category) + if (secondaryGroup != null) { + secondaryGroup.getDescriptors().forEach { + it.isSelected = isSynchronized && state.isSubcategoryEnabled(category, it.id) + } + } + } + } + + fun apply() { + with(descriptor) { + if (secondaryGroup != null) { + secondaryGroup.getDescriptors().forEach { + // !isSynchronized not store disabled states individually + state.setSubcategoryEnabled(category, it.id, !isSynchronized || it.isSelected) + } + } + state.setCategoryEnabled(category, isSynchronized) + } + } + + fun isModified(): Boolean { + with(descriptor) { + if (isSynchronized != state.isCategoryEnabled(category)) return true + if (secondaryGroup != null && isSynchronized) { + secondaryGroup.getDescriptors().forEach { + if (it.isSelected != state.isSubcategoryEnabled(category, it.id)) return true + } + } + return false + } + } + + companion object { + fun createAllForState(state: SettingsSyncState): List { + val retval = arrayListOf() + for (descriptor in Category.DESCRIPTORS) { + retval.add(SyncCategoryHolder(descriptor, state)) + } + return retval + } + } + + internal class Category( + val category: SettingsCategory, + val secondaryGroup: SyncSubcategoryGroup? = null + ) { + + val name: @Nls String + get() { + return message("${categoryKey}.name") + } + + val description: @Nls String + get() { + return message("${categoryKey}.description") + } + + private val categoryKey: String + get() { + return "settings.category." + category.name.lowercase(Locale.getDefault()) + } + + companion object { + internal val DESCRIPTORS: List = listOf( + Category(UI, SyncUiGroup()), + Category(KEYMAP), + Category(CODE), + Category(PLUGINS, SyncPluginsGroup()), + Category(TOOLS), + Category(SYSTEM), + ) + } + } + +} \ No newline at end of file diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncPluginsGroup.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/SyncPluginsGroup.kt similarity index 95% rename from plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncPluginsGroup.kt rename to plugins/settings-sync/src/com/intellij/settingsSync/config/SyncPluginsGroup.kt index 8327cc49600c..4535051f4e5f 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncPluginsGroup.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/config/SyncPluginsGroup.kt @@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nls internal const val BUNDLED_PLUGINS_ID = "bundled" -internal class SettingsSyncPluginsGroup : SettingsSyncSubcategoryGroup { +internal class SyncPluginsGroup : SyncSubcategoryGroup { private val storedDescriptors = HashMap() override fun getDescriptors(): List { diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncUiGroup.kt b/plugins/settings-sync/src/com/intellij/settingsSync/config/SyncUiGroup.kt similarity index 88% rename from plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncUiGroup.kt rename to plugins/settings-sync/src/com/intellij/settingsSync/config/SyncUiGroup.kt index 0057b301152e..f3f253911832 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/config/SettingsSyncUiGroup.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/config/SyncUiGroup.kt @@ -4,7 +4,7 @@ import com.intellij.settingsSync.SettingsSyncBundle const val EDITOR_FONT_SUBCATEGORY_ID = "editorFont" -internal class SettingsSyncUiGroup : SettingsSyncSubcategoryGroup { +internal class SyncUiGroup : SyncSubcategoryGroup { private val descriptors = listOf(SettingsSyncSubcategoryDescriptor(SettingsSyncBundle.message("settings.category.ui.editor.font"), EDITOR_FONT_SUBCATEGORY_ID, false, false)) diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/migration/SettingsRepositoryToSettingsSyncMigration.kt b/plugins/settings-sync/src/com/intellij/settingsSync/migration/SettingsRepositoryToSettingsSyncMigration.kt index 0eb2dcf2aea2..98cea4df3e6d 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/migration/SettingsRepositoryToSettingsSyncMigration.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/migration/SettingsRepositoryToSettingsSyncMigration.kt @@ -145,7 +145,7 @@ internal class SettingsRepositoryToSettingsSyncMigration { TemplateSettings.getInstance() // Required for live templates to be migrated correctly, see IDEA-303831 SettingsSyncIdeMediatorImpl(ApplicationManager.getApplication().stateStore as ComponentStoreImpl, - PathManager.getConfigDir(), { false }).applyToIde(snapshot) + PathManager.getConfigDir(), { false }).applyToIde(snapshot, null) settingsRepositoryMigration.showNotificationAboutUnbundling(executorService) SettingsSyncEventsStatistics.MIGRATED_FROM_SETTINGS_REPOSITORY.log() } diff --git a/plugins/settings-sync/tests/com/intellij/settingsSync/BasePluginManagerTest.kt b/plugins/settings-sync/tests/com/intellij/settingsSync/BasePluginManagerTest.kt index 0cadf6adb82c..54091232160c 100644 --- a/plugins/settings-sync/tests/com/intellij/settingsSync/BasePluginManagerTest.kt +++ b/plugins/settings-sync/tests/com/intellij/settingsSync/BasePluginManagerTest.kt @@ -13,14 +13,11 @@ import com.intellij.testFramework.junit5.TestApplication import com.intellij.testFramework.junit5.TestDisposable import com.intellij.testFramework.replaceService import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.test.* import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.BeforeEach -import kotlin.coroutines.CoroutineContext @OptIn(ExperimentalCoroutinesApi::class) @TestApplication @@ -76,7 +73,7 @@ abstract class BasePluginManagerTest { @BeforeEach fun setUp() { SettingsSyncSettings.getInstance().syncEnabled = true - SettingsSyncSettings.getInstance().loadState(SettingsSyncSettings.SettingsSyncSettingsState()) + SettingsSyncSettings.getInstance().loadState(SettingsSyncSettings.State()) testPluginManager = TestPluginManager() ApplicationManager.getApplication().replaceService(PluginManagerProxy::class.java, testPluginManager, testRootDisposable) testScheduler = TestCoroutineScheduler() diff --git a/plugins/settings-sync/tests/com/intellij/settingsSync/MockSettingsSyncIdeMediator.kt b/plugins/settings-sync/tests/com/intellij/settingsSync/MockSettingsSyncIdeMediator.kt index f1fc46d194ed..76776db82347 100644 --- a/plugins/settings-sync/tests/com/intellij/settingsSync/MockSettingsSyncIdeMediator.kt +++ b/plugins/settings-sync/tests/com/intellij/settingsSync/MockSettingsSyncIdeMediator.kt @@ -13,7 +13,7 @@ internal class MockSettingsSyncIdeMediator : SettingsSyncIdeMediator { private var exceptionToThrowOnApply: Exception? = null - override fun applyToIde(snapshot: SettingsSnapshot) { + override fun applyToIde(snapshot: SettingsSnapshot, settings: SettingsSyncState?) { if (exceptionToThrowOnApply != null) { throw exceptionToThrowOnApply!! } diff --git a/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSnapshotTest.kt b/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSnapshotTest.kt new file mode 100644 index 000000000000..fc757b3bf8c5 --- /dev/null +++ b/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSnapshotTest.kt @@ -0,0 +1,87 @@ +package com.intellij.settingsSync + +import com.intellij.openapi.components.SettingsCategory +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.time.Instant + +class SettingsSnapshotTest { + + @Test + fun `extract dialog state`() { + val metaInfo = SettingsSnapshot.MetaInfo(Instant.now(), null) + val settingsSyncXmlState = FileState.Modified("options/settingsSync.xml", """ + + + + + + + """.trimIndent().toByteArray()) + val snapshot = SettingsSnapshot(metaInfo, setOf(settingsSyncXmlState), null, emptyMap(), emptySet()) + val state = snapshot.getState() + Assertions.assertTrue(state.disabledCategories.containsAll(listOf(SettingsCategory.SYSTEM, SettingsCategory.TOOLS))) + Assertions.assertEquals(2,state.disabledCategories.size) + Assertions.assertTrue(state.isCategoryEnabled(SettingsCategory.PLUGINS)) + Assertions.assertTrue(state.isCategoryEnabled(SettingsCategory.UI)) + Assertions.assertFalse(state.isSubcategoryEnabled(SettingsCategory.UI, "editorFont")) + Assertions.assertTrue(state.disabledSubcategories.keys.containsAll(listOf(SettingsCategory.UI, SettingsCategory.PLUGINS))) + Assertions.assertTrue(state.syncEnabled) + Assertions.assertTrue(state.migrationFromOldStorageChecked) + } + + @Test + fun `extract dialog state 2`() { + val metaInfo = SettingsSnapshot.MetaInfo(Instant.now(), null) + val settingsSyncXmlState = FileState.Modified("options/settingsSync.xml", """ + + + + + + """.trimIndent().toByteArray()) + val snapshot = SettingsSnapshot(metaInfo, setOf(settingsSyncXmlState), null, emptyMap(), emptySet()) + val state = snapshot.getState() + Assertions.assertTrue(state.isCategoryEnabled(SettingsCategory.TOOLS)) + Assertions.assertTrue(state.isCategoryEnabled(SettingsCategory.PLUGINS)) + Assertions.assertTrue(state.isCategoryEnabled(SettingsCategory.UI)) + Assertions.assertTrue(state.isSubcategoryEnabled(SettingsCategory.UI, "editorFont")) + Assertions.assertFalse(state.syncEnabled) + Assertions.assertFalse(state.migrationFromOldStorageChecked) + } + +} \ No newline at end of file diff --git a/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncIdeMediatorTest.kt b/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncIdeMediatorTest.kt index add7c77ae2d7..2f0590016352 100644 --- a/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncIdeMediatorTest.kt +++ b/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncIdeMediatorTest.kt @@ -2,21 +2,26 @@ package com.intellij.settingsSync import com.intellij.configurationStore.ChildlessComponentStore import com.intellij.configurationStore.StateStorageManager +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.components.SettingsCategory +import com.intellij.openapi.components.stateStore +import com.intellij.testFramework.fixtures.BasePlatformTestCase import com.intellij.testFramework.rules.InMemoryFsRule -import org.junit.Assert.assertEquals +import org.junit.Assert import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import java.nio.file.Path +import java.time.Instant import kotlin.io.path.createDirectories import kotlin.io.path.createFile import kotlin.io.path.div import kotlin.io.path.pathString @RunWith(JUnit4::class) -class SettingsSyncIdeMediatorTest { +class SettingsSyncIdeMediatorTest : BasePlatformTestCase() { @JvmField @Rule val memoryFs = InMemoryFsRule() @@ -46,4 +51,50 @@ true assertEquals(setOf("mytemplate.kt"), visited) } + + @Test + fun `respect SettingSyncState`() { + val rootConfig = memoryFs.fs.getPath("/appconfig") + val componentStore = object : ChildlessComponentStore() { + override val storageManager: StateStorageManager + get() = ApplicationManager.getApplication().stateStore.storageManager + + override fun setPath(path: Path) { + TODO("Not yet implemented") + } + } + val mediator = SettingsSyncIdeMediatorImpl(componentStore, rootConfig, { true }) + val metaInfo = SettingsSnapshot.MetaInfo(Instant.now(), null) + val settingsSyncXmlState = FileState.Modified("options/settingsSync.xml", """ + + + + + + """.trimIndent().toByteArray()) + val snapshot = SettingsSnapshot(metaInfo, setOf(settingsSyncXmlState), null, emptyMap(), emptySet()) + val syncState = SettingsSyncStateHolder(SettingsSyncSettings.State()) + syncState.syncEnabled = true + syncState.setCategoryEnabled(SettingsCategory.CODE, false) + syncState.setSubcategoryEnabled(SettingsCategory.PLUGINS, "IdeaVIM", false) + mediator.applyToIde(snapshot, syncState) + Assert.assertTrue(SettingsSyncSettings.getInstance().syncEnabled) + Assert.assertFalse(SettingsSyncSettings.getInstance().migrationFromOldStorageChecked) + Assert.assertFalse(SettingsSyncSettings.getInstance().isCategoryEnabled(SettingsCategory.CODE)) + Assert.assertTrue(SettingsSyncSettings.getInstance().isCategoryEnabled(SettingsCategory.UI)) + Assert.assertTrue(SettingsSyncSettings.getInstance().isCategoryEnabled(SettingsCategory.SYSTEM)) + + Assert.assertTrue(SettingsSyncSettings.getInstance().isSubcategoryEnabled(SettingsCategory.PLUGINS, "org.vlang")) + Assert.assertFalse(SettingsSyncSettings.getInstance().isSubcategoryEnabled(SettingsCategory.PLUGINS, "IdeaVIM")) + } } \ No newline at end of file diff --git a/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncTestBase.kt b/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncTestBase.kt index febca87de2ca..79d776b07e30 100644 --- a/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncTestBase.kt +++ b/plugins/settings-sync/tests/com/intellij/settingsSync/SettingsSyncTestBase.kt @@ -52,7 +52,7 @@ internal abstract class SettingsSyncTestBase { configDir = mainDir.resolve("rootconfig").createDirectories() SettingsSyncLocalSettings.getInstance().state.reset() - SettingsSyncSettings.getInstance().state.reset() + SettingsSyncSettings.getInstance().state = SettingsSyncSettings.State() remoteCommunicator = if (isTestingAgainstRealCloudServer()) { TestRemoteCommunicator()