diff --git a/java/debugger/impl/src/com/intellij/debugger/collections/visualizer/CollectionVisualizerEvaluator.kt b/java/debugger/impl/src/com/intellij/debugger/collections/visualizer/CollectionVisualizerEvaluator.kt index e49e80cde724..e5815b5f8a72 100644 --- a/java/debugger/impl/src/com/intellij/debugger/collections/visualizer/CollectionVisualizerEvaluator.kt +++ b/java/debugger/impl/src/com/intellij/debugger/collections/visualizer/CollectionVisualizerEvaluator.kt @@ -2,6 +2,7 @@ package com.intellij.debugger.collections.visualizer import com.intellij.debugger.JavaDebuggerBundle +import com.intellij.debugger.engine.DebugProcess import com.intellij.debugger.engine.DebugProcessListener import com.intellij.debugger.engine.FullValueEvaluatorProvider import com.intellij.debugger.engine.SuspendContext @@ -13,6 +14,7 @@ import com.intellij.openapi.ui.FrameWrapper import com.intellij.platform.util.coroutines.childScope import com.intellij.xdebugger.frame.XFullValueEvaluator import com.intellij.xdebugger.impl.ui.CustomComponentEvaluator +import com.sun.jdi.request.EventRequest import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import java.awt.event.MouseEvent @@ -20,8 +22,11 @@ import javax.swing.JComponent class CollectionVisualizerEvaluator(private val visualizer: CollectionVisualizer) : FullValueEvaluatorProvider { override fun getFullValueEvaluator(evaluationContext: EvaluationContextImpl, valueDescriptor: ValueDescriptorImpl): XFullValueEvaluator? { + val thread = evaluationContext.suspendContext.thread + val threadName = thread?.name() ?: "" val presenterName = valueDescriptor.type?.name() ?: "" - val scope = evaluationContext.managerThread.coroutineScope.childScope("Collection presentation for $presenterName") + val mainScopeName = "Collection presentation for $presenterName (thread $threadName)" + val scope = evaluationContext.managerThread.coroutineScope.childScope(mainScopeName) return object : CustomComponentEvaluator("CoVi") { override fun startEvaluation(callback: XFullValueEvaluationCallback) { @@ -40,7 +45,7 @@ class CollectionVisualizerEvaluator(private val visualizer: CollectionVisualizer title = JavaDebuggerBundle.message("debugger.collection.visualizer.title"), component, // don't cancel the whole scope when the window is closed -- otherwise it wouldn't be possible to reopen - scope.childScope("Collection presentation for $presenterName (limited to a window)"), + scope.childScope("$mainScopeName (limited to a window)"), ) frame.apply { disposeOnResume(evaluationContext, scope) @@ -57,9 +62,27 @@ class CollectionVisualizerEvaluator(private val visualizer: CollectionVisualizer ) { evaluationContext.debugProcess.addDebugProcessListener(object : DebugProcessListener { override fun resumed(suspendContext: SuspendContext?) { + if (affectedByResume(suspendContext)) { + close() + scope.cancel() + } + } + + override fun processDetached(process: DebugProcess, closedByUser: Boolean) { close() scope.cancel() } + + // maximum conservatism -- better close more popups than make them leak + private fun affectedByResume(suspendContext: SuspendContext?): Boolean { + if (suspendContext == null) return true + if (evaluationContext.suspendContext.suspendPolicy == EventRequest.SUSPEND_ALL) { + return true + } + val visualizerRelatedThread = evaluationContext.suspendContext.thread ?: return true + val resumeOnThread = suspendContext.thread ?: return true + return visualizerRelatedThread === resumeOnThread + } }) }