From f4d40112b92d621495b33f43e7eca4b6c947e41c Mon Sep 17 00:00:00 2001 From: Artem Orlov Date: Wed, 18 Feb 2026 14:09:46 +0300 Subject: [PATCH 1/2] ASPR-3010 Recommended plugin installation on IDEA startup Fixed bugs --- .idea/dbnavigator.xml | 460 ------------------ .../messages/ImportSettingsBundle.properties | 2 + .../chooser/ui/OpenIdeStartupWizardService.kt | 92 ++-- .../openide/utils/PluginInstallationUtil.kt | 119 +++++ .../wizard/pluginChooser/WizardPluginsPage.kt | 2 +- 5 files changed, 182 insertions(+), 493 deletions(-) delete mode 100644 .idea/dbnavigator.xml create mode 100644 plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/openide/utils/PluginInstallationUtil.kt diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml deleted file mode 100644 index d26b689040a5..000000000000 --- a/.idea/dbnavigator.xml +++ /dev/null @@ -1,460 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/ide-startup/importSettings/resources/messages/ImportSettingsBundle.properties b/plugins/ide-startup/importSettings/resources/messages/ImportSettingsBundle.properties index f2ddce7c72a6..4fcb7eb68b9e 100644 --- a/plugins/ide-startup/importSettings/resources/messages/ImportSettingsBundle.properties +++ b/plugins/ide-startup/importSettings/resources/messages/ImportSettingsBundle.properties @@ -46,6 +46,7 @@ onboarding.wizard.getting-ready=Getting Ready\u2026 plugin-installation.progress.text-with-details={0} / {1} plugin-installation.progress.determining-plugins-to-download=Determining plugins to download +plugin-installation.progress.downloading=Установка {0}... settings.category.ui.name=UI settings settings.category.ui.description=Includes theme, editor font, toolbar, and notifications @@ -66,6 +67,7 @@ transfer.settings.message=Importing from {0}\u2026 import.settings.title = Importing settings\u2026 import.settings.ok = Import Settings import.settings.back = Back +import.settings.back.ru = Назад import.settings.sync.ok = Sync Settings import.settings.sync.import.once = Import Once diff --git a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/chooser/ui/OpenIdeStartupWizardService.kt b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/chooser/ui/OpenIdeStartupWizardService.kt index 7b3e0f5edc05..0fb95bd20046 100644 --- a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/chooser/ui/OpenIdeStartupWizardService.kt +++ b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/chooser/ui/OpenIdeStartupWizardService.kt @@ -16,21 +16,26 @@ package com.intellij.ide.startup.importSettings.chooser.ui import com.intellij.icons.AllIcons import com.intellij.ide.startup.importSettings.ImportSettingsBundle import com.intellij.ide.startup.importSettings.OpenIdePluginIcons -import com.intellij.ide.startup.importSettings.StartupImportIcons import com.intellij.ide.startup.importSettings.data.* +import com.intellij.ide.startup.importSettings.openide.utils.PluginInstallationUtil +import com.intellij.ide.startup.importSettings.transfer.ProgressIndicatorAdapter +import com.intellij.ide.startup.importSettings.transfer.TransferSettingsProgressIndicator import com.intellij.java.ui.icons.JavaUIIcons +import com.intellij.openapi.application.EDT +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.asContextElement +import com.intellij.openapi.application.ex.ApplicationManagerEx +import com.intellij.util.IconUtil import com.jetbrains.rd.util.lifetime.Lifetime -import com.jetbrains.rd.util.reactive.IVoidSource -import com.jetbrains.rd.util.reactive.OptProperty import com.jetbrains.rd.util.reactive.Property +import com.jetbrains.rd.util.reactive.Signal +import kotlinx.coroutines.* -class OpenIdeStartupWizardService : StartupWizardService { +class OpenIdeStartupWizardService(private val coroutineScope: CoroutineScope) : StartupWizardService { override val isActive = true - override val shouldClose: IVoidSource - get() = object : IVoidSource { - override fun advise(lifetime: Lifetime, handler: (Unit) -> Unit) { - } - } + private val _shouldClose = Signal() + override val shouldClose: Signal + get() = _shouldClose override fun getKeymapService(): KeymapService { TODO("Not yet implemented") @@ -45,6 +50,25 @@ class OpenIdeStartupWizardService : StartupWizardService { return object : PluginService { override val pluginGroups: List get() = listOf( + WizardPluginGroupImpl( + id = "ai", + name = "AI", + icon = AllIcons.Actions.Lightning, + plugins = listOf( + WizardPluginImpl( + id = "ai.kilocode.jetbrains", + icon = AllIcons.Plugins.PluginLogo, + name = "Kilo Code", + description = ImportSettingsBundle.message("plugin.description.kilocode"), + ), + WizardPluginImpl( + id = "com.github.continuedev.continueintellijextension", + icon = AllIcons.Plugins.PluginLogo, + name = "Continue", + description = ImportSettingsBundle.message("plugin.description.continue"), + ), + ) + ), WizardPluginGroupImpl( id = "java-kotlin", name = "Java / Kotlin", @@ -172,25 +196,6 @@ class OpenIdeStartupWizardService : StartupWizardService { ), ) ), - WizardPluginGroupImpl( - id = "ai", - name = "AI", - icon = AllIcons.Actions.Lightning, - plugins = listOf( - WizardPluginImpl( - id = "ai.kilocode.jetbrains", - icon = AllIcons.Plugins.PluginLogo, - name = "Kilo Code", - description = ImportSettingsBundle.message("plugin.description.kilocode"), - ), - WizardPluginImpl( - id = "com.github.continuedev.continueintellijextension", - icon = AllIcons.Plugins.PluginLogo, - name = "Continue", - description = ImportSettingsBundle.message("plugin.description.continue"), - ), - ) - ), WizardPluginGroupImpl( id = "deployment", name = "Deployment", @@ -210,11 +215,34 @@ class OpenIdeStartupWizardService : StartupWizardService { } override fun install(lifetime: Lifetime, ids: List): PluginImportProgress { - return object : PluginImportProgress { - override val icon = Property(AllIcons.Plugins.PluginLogo) - override val progressMessage = Property(null) - override val progress = OptProperty() + val progressIndicator = TransferSettingsProgressIndicator() + val progressAdapter = ProgressIndicatorAdapter(progressIndicator) + val iconProperty = Property(AllIcons.Plugins.PluginLogo) + coroutineScope.launch { + var needRestart = false + try { + needRestart = PluginInstallationUtil.installPlugins(ids, progressAdapter) { icon -> + iconProperty.set(IconUtil.resizeSquared(icon, 64)) + } + } catch (e: CancellationException) { + throw e + } catch (_: Exception) { + } finally { + withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) { + if (needRestart) { + ApplicationManagerEx.getApplicationEx().restart(true) + } else { + _shouldClose.fire(Unit) + } + } + } + } + + return object : PluginImportProgress { + override val icon = iconProperty + override val progressMessage = progressIndicator.progressMessage + override val progress = progressIndicator.progress } } diff --git a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/openide/utils/PluginInstallationUtil.kt b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/openide/utils/PluginInstallationUtil.kt new file mode 100644 index 000000000000..1cfbe9ae6ccd --- /dev/null +++ b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/openide/utils/PluginInstallationUtil.kt @@ -0,0 +1,119 @@ +// OpenIDE Project +// Copyright (C) 2026 “Open Development Platform” Ltd. (https://openide.ru) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License version 3 or later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +package com.intellij.ide.startup.importSettings.openide.utils + +import com.intellij.ide.plugins.IdeaPluginDescriptor +import com.intellij.ide.plugins.PluginManagerCore +import com.intellij.ide.plugins.marketplace.MarketplaceRequests +import com.intellij.ide.startup.importSettings.ImportSettingsBundle +import com.intellij.ide.startup.importSettings.openide.service.PluginIconService +import com.intellij.openapi.application.EDT +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.asContextElement +import com.intellij.openapi.extensions.PluginId +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.updateSettings.impl.PluginDownloader +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import javax.swing.Icon + +object PluginInstallationUtil { + + /** + * Installs plugins by their IDs. + * @param onIconLoaded callback to update icon when plugin icon is loaded + * @return true if IDE restart is required after installation + */ + @Suppress("DEPRECATION") + suspend fun installPlugins( + pluginIds: List, + progressIndicator: ProgressIndicator, + onIconLoaded: ((Icon) -> Unit)? = null, + ): Boolean { + if (progressIndicator.isCanceled || pluginIds.isEmpty()) { + return false + } + + val idsToInstall = pluginIds + .map { PluginId.Companion.getId(it) } + .filter { !PluginManagerCore.isPluginInstalled(it) } + + if (idsToInstall.isEmpty()) { + progressIndicator.fraction = 1.0 + return false + } + + progressIndicator.text = ImportSettingsBundle.message("plugin-installation.progress.determining-plugins-to-download") + progressIndicator.fraction = 0.05 + + val pluginsToInstall: List = withContext(Dispatchers.IO) { + MarketplaceRequests.Companion.loadLastCompatiblePluginDescriptors(idsToInstall.toSet(), null, true) + } + + if (pluginsToInstall.isEmpty()) { + progressIndicator.fraction = 1.0 + return false + } + + var shouldRestart = false + val progressPerPlugin = 0.9 / pluginsToInstall.size.coerceAtLeast(1) + var currentProgress = 0.1 + + for (plugin in pluginsToInstall) { + if (progressIndicator.isCanceled) break + + progressIndicator.text = ImportSettingsBundle.message("plugin-installation.progress.downloading", plugin.name) + + // Load and set plugin icon + if (onIconLoaded != null) { + try { + val icon = PluginIconService.Companion.getInstance().loadIcon(plugin.pluginId.idString) + if (icon != null) { + withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) { + onIconLoaded(icon) + } + } + } + catch (_: Exception) { + } + } + + try { + val downloader = PluginDownloader.createDownloader(plugin).withErrorsConsumer { problem -> + } + + withContext(Dispatchers.IO) { + downloader.prepareToInstall(null) + } + + val appliedWithoutRestart = withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) { + downloader.installDynamically(null) + } + + if (!appliedWithoutRestart) { + shouldRestart = true + } + } + catch (_: Exception) { + } + + currentProgress += progressPerPlugin + progressIndicator.fraction = currentProgress.coerceAtMost(0.99) + } + + progressIndicator.fraction = 1.0 + progressIndicator.text = null + return shouldRestart + } +} \ No newline at end of file diff --git a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/wizard/pluginChooser/WizardPluginsPage.kt b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/wizard/pluginChooser/WizardPluginsPage.kt index d16f6e407b89..9fa5b1c475d5 100644 --- a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/wizard/pluginChooser/WizardPluginsPage.kt +++ b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/wizard/pluginChooser/WizardPluginsPage.kt @@ -103,7 +103,7 @@ internal class WizardPluginsPage( }) } - private val backAction = controller.createButton(ImportSettingsBundle.message("import.settings.back"), goBackAction ?: {}) + private val backAction = controller.createButton(ImportSettingsBundle.message("import.settings.back.ru"), goBackAction ?: {}) private val continueAction = controller.createDefaultButton(continueButtonTextOverride ?: ImportSettingsBundle.message("plugins.page.ok.button.continue.without")) { val ids = getSelected().map { it.plugin.id }.toList() From 3c0ff6204fab27ad35c870c503b0b10b97ab1a15 Mon Sep 17 00:00:00 2001 From: Artem Orlov Date: Wed, 18 Feb 2026 15:06:52 +0300 Subject: [PATCH 2/2] ASPR-3010 Recommended plugin installation on IDEA startup Add easyp plugin --- .../resources/messages/ImportSettingsBundle.properties | 1 + .../chooser/ui/OpenIdeStartupWizardService.kt | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/plugins/ide-startup/importSettings/resources/messages/ImportSettingsBundle.properties b/plugins/ide-startup/importSettings/resources/messages/ImportSettingsBundle.properties index 4fcb7eb68b9e..6a393f91ecaf 100644 --- a/plugins/ide-startup/importSettings/resources/messages/ImportSettingsBundle.properties +++ b/plugins/ide-startup/importSettings/resources/messages/ImportSettingsBundle.properties @@ -114,6 +114,7 @@ plugin.description.go = Поддержка языка Go plugin.description.fileWatchers = Автоматический запуск внешних инструментов при изменении файлов plugin.description.makefile = Подсветка синтаксиса и навигация для Makefile plugin.description.protocolBuffers = Поддержка Protocol Buffers для сериализации данных +plugin.description.easyp = Линтинг и форматирование proto файлов с помощью easyp plugin.description.kilocode = Kilo Code AI Агент plugin.description.continue = Open-source AI ассистент для кода plugin.description.docker = Поддержка Docker для управления контейнерами diff --git a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/chooser/ui/OpenIdeStartupWizardService.kt b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/chooser/ui/OpenIdeStartupWizardService.kt index 0fb95bd20046..6e4101dd7602 100644 --- a/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/chooser/ui/OpenIdeStartupWizardService.kt +++ b/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/chooser/ui/OpenIdeStartupWizardService.kt @@ -194,6 +194,12 @@ class OpenIdeStartupWizardService(private val coroutineScope: CoroutineScope) : name = "Protocol Buffers", description = ImportSettingsBundle.message("plugin.description.protocolBuffers"), ), + WizardPluginImpl( + id = "com.github.easyptech.easyp", + icon = AllIcons.Plugins.PluginLogo, + name = "easyp", + description = ImportSettingsBundle.message("plugin.description.easyp"), + ), ) ), WizardPluginGroupImpl(