IDEA-291623 Show a warning if the settings pack exceeds some reasonably big limit

Post code review improvements

GitOrigin-RevId: c3875ea81f130b69e8435c89a8cc906648340638
This commit is contained in:
Filipp Vakhitov
2023-07-16 22:16:07 +03:00
committed by intellij-monorepo-bot
parent 4237f837f1
commit 056294550b
7 changed files with 85 additions and 55 deletions

View File

@@ -65,9 +65,20 @@ subcategory.config.link=Configure
settings.category.ui.editor.font=Editor font
settings.sync.info.message=Sync UI, Code and System settings, Keymaps, Plugins, and Tools.
sync.restart.notification.title=Restart required after syncing settings
sync.restart.notification.message=Restart the IDE to {0}
sync.restart.notification.message.subtitle=Restart the IDE to complete the following actions:
sync.restart.notification.submessage.registry=apply the new UI
# {0} - count of plugins, {1} - coma separated list of plugins (max two plugins)
sync.notification.restart.message.plugin.install=Restart the IDE to install {0,choice,1#plugin|2#plugins}: {1}{0,choice,3#...}
sync.notification.restart.message.plugin.enable=Restart the IDE to enable {0,choice,1#plugin|2#plugins}: {1}{0,choice,3#...}
sync.notification.restart.message.plugin.disable=Restart the IDE to disable {0,choice,1#plugin|2#plugins}: {1}{0,choice,3#...}
sync.notification.restart.message.registry=Restart the IDE to enable the new UI
sync.notification.restart.message.list.title=Restart the IDE to complete the following actions:
# {0} - count of plugins, {1} - coma separated list of plugins (max two plugins)
sync.notification.restart.message.list.entry.plugin.install=Install {0,choice,1#plugin|2#plugins}: {1}{0,choice,3#...}
sync.notification.restart.message.list.entry.plugin.enable=Enable {0,choice,1#plugin|2#plugins}: {1}{0,choice,3#...}
sync.notification.restart.message.list.entry.plugin.disable=Disable {0,choice,1#plugin|2#plugins}: {1}{0,choice,3#...}
sync.notification.restart.message.list.entry.registry=Enable the new UI
# {0} - action (install, enable, disable), {1} - comma separated list of plugins
sync.restart.notification.submessage.plugins={0} plugin(s): {1}...
# {0} - IDE name, i.e. Android Studio, MPS, etc.
@@ -85,8 +96,9 @@ sync.status.login.not.available=To enable Setting Sync, install
sync.status.restart.required=To enable Setting Sync, restart {0}
sync.status.restart.ide.button=Restart
sync.notification.size.exceed.title=Settings pack exceeds maximum allowed limit
sync.notification.size.exceed.text=Please reach out to us at https://youtrack.jetbrains.com/ and attach your logs and settings to address the issue
sync.notification.size.exceed.title=Settings archive exceeds size limit
sync.notification.size.exceed.text=Please reach out to us at <a href="https://youtrack.jetbrains.com/IDEA/">YouTrack</a> and attach your <a href="https://intellij-support.jetbrains.com/hc/en-us/articles/9102205110546">logs and settings</a> to address the issue
sync.notification.do.not.ask.again=Don't ask again
settings.cross.product.sync=Sync settings across:
# {0} is the current product name, e.g. 'IntelliJ IDEA Community Edition' or 'WebStorm'

View File

@@ -189,11 +189,6 @@ internal open class CloudConfigServerCommunicator(serverUrl: String? = null) : S
val zip = try {
SettingsSnapshotZipSerializer.serializeToZip(snapshot)
}
catch (e: SettingsSnapshotZipSerializer.ZipSizeExceedException) {
LOG.warn(e)
NotificationService.getInstance().notifyZipSizeExceed()
return SettingsSyncPushResult.Error(e.message ?: "Couldn't prepare zip file")
}
catch (e: Throwable) {
LOG.warn(e)
return SettingsSyncPushResult.Error(e.message ?: "Couldn't prepare zip file")

View File

@@ -5,10 +5,12 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.util.BuildNumber
import com.intellij.openapi.util.io.FileUtil
import com.intellij.settingsSync.notification.NotificationService
import com.intellij.settingsSync.plugins.SettingsSyncPluginsState
import com.intellij.util.io.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
import java.io.OutputStream
import java.lang.RuntimeException
import java.nio.file.Files
@@ -28,7 +30,7 @@ internal object SettingsSnapshotZipSerializer {
private const val INFO = "info.json"
private const val PLUGINS = "plugins.json"
private const val MAX_ZIP_SIZE = 524288 // bytes
private const val ZIP_SIZE_SOFT_LIMIT = 524288 // bytes
private val LOG = logger<SettingsSnapshotZipSerializer>()
@@ -37,8 +39,8 @@ internal object SettingsSnapshotZipSerializer {
fun serializeToZip(snapshot: SettingsSnapshot): Path {
val file = FileUtil.createTempFile(SETTINGS_SYNC_SNAPSHOT_ZIP, null)
serialize(snapshot, Compressor.Zip(file))
if (file.length() > MAX_ZIP_SIZE) {
throw ZipSizeExceedException(file.length())
if (file.length() > ZIP_SIZE_SOFT_LIMIT) {
NotificationService.getInstance().notifyZipSizeExceed()
}
return file.toPath()
}
@@ -189,10 +191,4 @@ internal object SettingsSnapshotZipSerializer {
var configFolder: String = ""
var isDeleted: Boolean = false
}
class ZipSizeExceedException(private val size: Long): RuntimeException() {
override fun toString(): String {
return "Zip size $size excesses maximum allowed zip size"
}
}
}

View File

@@ -60,51 +60,61 @@ interface SettingsSyncEventListener : EventListener {
sealed class RestartReason: Comparable<RestartReason> {
abstract val sortingPriority: Int
abstract fun getNotificationSubMessage(): String
@NlsSafe
fun getSingleReasonNotificationMessage(): String {
return SettingsSyncBundle.message("sync.restart.notification.message", getNotificationSubMessage())
}
abstract fun getSingleReasonNotificationMessage(): String
@NlsSafe
fun getMultiReasonNotificationListEntry(number: Int): String {
return "$number. ${getNotificationSubMessage().capitalize()}\n"
}
private fun String.capitalize() : String {
return this.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
}
abstract fun getMultiReasonNotificationListEntry(number: Int): String
override fun compareTo(other: RestartReason): Int {
return this.sortingPriority.compareTo(other.sortingPriority)
}
}
internal class RestartForPluginInstall(private val plugins: Collection<String>) : RestartReason() {
internal class RestartForPluginInstall(val plugins: Collection<String>) : RestartReason() {
override val sortingPriority = 0
override fun getNotificationSubMessage(): String {
return SettingsSyncBundle.message("sync.restart.notification.submessage.plugins", "install", plugins.joinToString(", "))
override fun getSingleReasonNotificationMessage(): String {
return SettingsSyncBundle.message("sync.notification.restart.message.plugin.install", plugins.size, plugins.take(2).joinToString(", "))
}
override fun getMultiReasonNotificationListEntry(number: Int): String {
return "$number. " + SettingsSyncBundle.message("sync.notification.restart.message.list.entry.plugin.install", plugins.size, plugins.take(2).joinToString(", "))
}
}
internal class RestartForPluginEnable(private val plugins: Collection<String>) : RestartReason() {
internal class RestartForPluginEnable(val plugins: Collection<String>) : RestartReason() {
override val sortingPriority = 1
override fun getNotificationSubMessage(): String {
return SettingsSyncBundle.message("sync.restart.notification.submessage.plugins", "enable", plugins.joinToString(", "))
override fun getSingleReasonNotificationMessage(): String {
return SettingsSyncBundle.message("sync.notification.restart.message.plugin.enable", plugins.size, plugins.take(2).joinToString(", "))
}
override fun getMultiReasonNotificationListEntry(number: Int): String {
return "$number. " + SettingsSyncBundle.message("sync.notification.restart.message.list.entry.plugin.enable", plugins.size, plugins.take(2).joinToString(", "))
}
}
internal class RestartForPluginDisable(private val plugins: Collection<String>) : RestartReason() {
internal class RestartForPluginDisable(val plugins: Collection<String>) : RestartReason() {
override val sortingPriority = 2
override fun getNotificationSubMessage(): String {
return SettingsSyncBundle.message("sync.restart.notification.submessage.plugins", "disable", plugins.joinToString(", "))
override fun getSingleReasonNotificationMessage(): String {
return SettingsSyncBundle.message("sync.notification.restart.message.plugin.disable", plugins.size, plugins.take(2).joinToString(", "))
}
override fun getMultiReasonNotificationListEntry(number: Int): String {
return "$number. " + SettingsSyncBundle.message("sync.notification.restart.message.list.entry.plugin.disable", plugins.size, plugins.take(2).joinToString(", "))
}
}
internal object RestartForNewUI : RestartReason() {
override val sortingPriority = 3
override fun getNotificationSubMessage(): String {
return SettingsSyncBundle.message("sync.restart.notification.submessage.registry")
override fun getSingleReasonNotificationMessage(): String {
return SettingsSyncBundle.message("sync.notification.restart.message.registry")
}
override fun getMultiReasonNotificationListEntry(number: Int): String {
return "$number. " + SettingsSyncBundle.message("sync.notification.restart.message.list.entry.registry")
}
}

View File

@@ -96,8 +96,21 @@ internal class SettingsSyncIdeMediatorImpl(private val componentStore: Component
}
private fun notifyRestartNeeded() {
if (restartRequiredReasons.isEmpty()) return
NotificationService.getInstance().notifyRestartNeeded(restartRequiredReasons)
val mergedReasons = mergeRestartReasons()
if (mergedReasons.isEmpty()) return
NotificationService.getInstance().notifyRestartNeeded(mergedReasons)
}
private fun mergeRestartReasons(): List<RestartReason> {
return restartRequiredReasons.groupBy { it::class.java }.mapNotNull { (clazz, reasons) ->
when (clazz) {
RestartForPluginInstall::class.java -> RestartForPluginInstall(reasons.flatMap { (it as RestartForPluginInstall).plugins })
RestartForPluginEnable::class.java -> RestartForPluginEnable(reasons.flatMap { (it as RestartForPluginEnable).plugins })
RestartForPluginDisable::class.java -> RestartForPluginDisable(reasons.flatMap { (it as RestartForPluginDisable).plugins })
RestartForNewUI::class.java -> RestartForNewUI
else -> null
}
}
}
override fun activateStreamProvider() {

View File

@@ -1,6 +1,5 @@
package com.intellij.settingsSync.notification
import com.intellij.notification.Notification
import com.intellij.openapi.components.service
import com.intellij.settingsSync.RestartReason
@@ -10,7 +9,5 @@ internal interface NotificationService {
}
fun notifyZipSizeExceed()
fun buildZipSizeExceedNotification(): Notification
fun notifyRestartNeeded(reasons: Collection<RestartReason>)
fun buildRestartNeededNotification(reasons: Collection<RestartReason>): Notification
}

View File

@@ -1,5 +1,6 @@
package com.intellij.settingsSync.notification
import com.intellij.ide.util.propComponentProperty
import com.intellij.notification.Notification
import com.intellij.notification.NotificationAction
import com.intellij.notification.NotificationGroupManager
@@ -10,19 +11,26 @@ import com.intellij.openapi.application.ex.ApplicationEx
import com.intellij.settingsSync.NOTIFICATION_GROUP
import com.intellij.settingsSync.RestartReason
import com.intellij.settingsSync.SettingsSyncBundle
import java.lang.RuntimeException
internal class NotificationServiceImpl: NotificationService {
override fun notifyZipSizeExceed() {
val notification = buildZipSizeExceedNotification()
val notification = buildZipSizeExceedNotification() ?: return
notification.notify(null)
}
override fun buildZipSizeExceedNotification(): Notification {
return NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
private fun buildZipSizeExceedNotification(): Notification? {
var showNotification: Boolean by propComponentProperty(null, "sync.notification.zip.size.exceed.show", defaultValue = true)
if (!showNotification) return null
val notification = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
.createNotification(SettingsSyncBundle.message("sync.notification.size.exceed.title"),
SettingsSyncBundle.message("sync.notification.size.exceed.text"),
NotificationType.ERROR)
notification.addAction(
NotificationAction.createSimpleExpiring(SettingsSyncBundle.message("sync.notification.do.not.ask.again")) {
showNotification = false
}
)
return notification
}
override fun notifyRestartNeeded(reasons: Collection<RestartReason>) {
@@ -36,22 +44,21 @@ internal class NotificationServiceImpl: NotificationService {
notification.notify(null)
}
override fun buildRestartNeededNotification(reasons: Collection<RestartReason>): Notification {
private fun buildRestartNeededNotification(reasons: Collection<RestartReason>): Notification {
fun getMultiReasonRestartMessage(): String {
assert(reasons.size > 1)
val message = StringBuilder(SettingsSyncBundle.message("sync.restart.notification.message.subtitle")).append('\n')
val message = StringBuilder(SettingsSyncBundle.message("sync.notification.restart.message.list.title")).append("<br/>")
val sortedRestartReasons = reasons.sorted()
for ((counter, reason) in sortedRestartReasons.withIndex()) {
message.append(reason.getMultiReasonNotificationListEntry(counter + 1))
if (counter < sortedRestartReasons.lastIndex) message.append("<br/>")
}
message.dropLast(0) // we do not need the new line in the end
return message.toString()
}
val message = when {
reasons.isEmpty() -> throw RuntimeException("No restart reasons provided")
reasons.isEmpty() -> ""
reasons.size == 1 -> reasons.first().getSingleReasonNotificationMessage()
else -> getMultiReasonRestartMessage()
}