From 97111e7f7c9b5d800bcbc6dfe045b8d0b2d33a0f Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 28 Aug 2024 11:44:59 +0200 Subject: [PATCH] [intention-preview] Do not cache NO_PREVIEW and LOADING_PREVIEW UI components Later the listener could be added to these components, so memory leak through the listener is possible Fixes IJPL-161082 Intention preview leaks project GitOrigin-RevId: 28c0b15b9ee35ebd19c6d300b52e5aad9ec97093 --- .../impl/preview/IntentionPreviewComponent.kt | 13 ++++++++++--- .../preview/IntentionPreviewPopupUpdateProcessor.kt | 7 ++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewComponent.kt b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewComponent.kt index e3bb78d632f1..1ba66584d2e0 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewComponent.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewComponent.kt @@ -2,6 +2,7 @@ package com.intellij.codeInsight.intention.impl.preview import com.intellij.codeInsight.CodeInsightBundle +import com.intellij.codeInsight.CodeInsightBundle.message import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo import com.intellij.ide.plugins.MultiPanel import com.intellij.openapi.Disposable @@ -24,7 +25,7 @@ internal class IntentionPreviewComponent(parent: Disposable) : val multiPanel: MultiPanel = object : MultiPanel() { override fun create(key: Int): JComponent { return when (key) { - LOADING_PREVIEW -> LOADING_LABEL + LOADING_PREVIEW -> createHtmlPanel(IntentionPreviewInfo.Html(message("intention.preview.loading.preview"))) else -> previewComponent!! // It's set in IntentionPreviewPopupUpdateProcessor#select } } @@ -40,8 +41,14 @@ internal class IntentionPreviewComponent(parent: Disposable) : const val NO_PREVIEW: Int = -1 const val LOADING_PREVIEW: Int = -2 private val BORDER: JBEmptyBorder = JBUI.Borders.empty(6, 10) - internal var NO_PREVIEW_LABEL = createHtmlPanel(IntentionPreviewInfo.Html(CodeInsightBundle.message("intention.preview.no.available.text"))) - private var LOADING_LABEL = createHtmlPanel(IntentionPreviewInfo.Html(CodeInsightBundle.message("intention.preview.loading.preview"))) + + internal fun createNoPreviewPanel(): JPanel { + val panel = createHtmlPanel(IntentionPreviewInfo.Html(message("intention.preview.no.available.text"))) + panel.putClientProperty("NO_PREVIEW", true) + return panel + } + + internal fun JComponent.isNoPreviewPanel(): Boolean = this.getClientProperty("NO_PREVIEW") != null internal fun createHtmlPanel(htmlInfo: IntentionPreviewInfo.Html): JPanel { val targetSize = IntentionPreviewPopupUpdateProcessor.MIN_WIDTH * UIUtil.getLabelFont().size.coerceAtMost(24) / 12 diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewPopupUpdateProcessor.kt b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewPopupUpdateProcessor.kt index 86560e09899c..decff18fdaa7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewPopupUpdateProcessor.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewPopupUpdateProcessor.kt @@ -3,6 +3,7 @@ package com.intellij.codeInsight.intention.impl.preview import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewComponent.Companion.LOADING_PREVIEW +import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewComponent.Companion.isNoPreviewPanel import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo.Html import com.intellij.openapi.actionSystem.IdeActions @@ -171,7 +172,7 @@ class IntentionPreviewPopupUpdateProcessor internal constructor( is IntentionPreviewDiffResult -> { val editors = IntentionPreviewEditorsPanel.createEditors(project, result) if (editors.isEmpty()) { - IntentionPreviewComponent.NO_PREVIEW_LABEL + IntentionPreviewComponent.createNoPreviewPanel() } else { val location = popup.locationOnScreen @@ -211,7 +212,7 @@ class IntentionPreviewPopupUpdateProcessor internal constructor( } } is Html -> IntentionPreviewComponent.createHtmlPanel(result) - else -> IntentionPreviewComponent.NO_PREVIEW_LABEL + else -> IntentionPreviewComponent.createNoPreviewPanel() } } @@ -249,7 +250,7 @@ class IntentionPreviewPopupUpdateProcessor internal constructor( private fun select(index: Int, previewComponent: JComponent? = null) { val selectedComponent = previewComponent ?: component.multiPanel.getValue(index, false) - getPopupWindow()?.isVisible = selectedComponent != IntentionPreviewComponent.NO_PREVIEW_LABEL || justActivated + getPopupWindow()?.isVisible = !selectedComponent.isNoPreviewPanel() || justActivated justActivated = false component.stopLoading() // need to set previewComponent before select, as multiPanel.create expects previewComponent to be initialized