[debugger] IDEA-360871 Handle an empty array/list case

The fix resulted in a cleaner API.


(cherry picked from commit e172ca5e833ab01f3c25322b7239971475c0333d)

IJ-CR-147189

GitOrigin-RevId: 692cd2ed978c6d94c73b9578864389a618066cb8
This commit is contained in:
Alexander Kuznetsov
2024-10-17 22:55:14 +02:00
committed by intellij-monorepo-bot
parent d0952cd077
commit 09a1e4b0e1
5 changed files with 64 additions and 50 deletions

View File

@@ -9,7 +9,7 @@ import kotlinx.coroutines.CoroutineScope
import javax.swing.JComponent
interface CollectionVisualizer {
fun applicableFor(collectionClass: String): Boolean
fun applicableFor(evaluationContext: EvaluationContextImpl, descriptor: ValueDescriptor): Boolean
fun createComponent(
project: Project,
@@ -21,8 +21,8 @@ interface CollectionVisualizer {
companion object {
private val EP_NAME = ExtensionPointName.create<CollectionVisualizer>("com.intellij.debugger.collectionVisualizer")
fun findApplicable(collectionClass: String): CollectionVisualizer? {
return EP_NAME.findFirstSafe { it.applicableFor(collectionClass) }
fun findApplicable(evaluationContext: EvaluationContextImpl, descriptor: ValueDescriptor): CollectionVisualizer? {
return EP_NAME.findFirstSafe { it.applicableFor(evaluationContext, descriptor) }
}
}
}

View File

@@ -4,10 +4,9 @@ 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
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl
import com.intellij.debugger.ui.tree.ValueDescriptor
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.FrameWrapper
@@ -20,43 +19,45 @@ import kotlinx.coroutines.cancel
import java.awt.event.MouseEvent
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() ?: "<unknown thread>"
val presenterName = valueDescriptor.type?.name() ?: "<unknown name>"
val mainScopeName = "Collection presentation for $presenterName (thread $threadName)"
val scope = evaluationContext.managerThread.coroutineScope.childScope(mainScopeName)
object CollectionVisualizerEvaluator {
@JvmStatic
fun createFor(evaluationContext: EvaluationContextImpl, valueDescriptor: ValueDescriptor): XFullValueEvaluator? =
CollectionVisualizer.findApplicable(evaluationContext, valueDescriptor)?.let { visualizer ->
val thread = evaluationContext.suspendContext.thread
val threadName = thread?.name() ?: "<unknown thread>"
val presenterName = valueDescriptor.type?.name() ?: "<unknown name>"
val mainScopeName = "Collection presentation for $presenterName (thread $threadName)"
val scope = evaluationContext.managerThread.coroutineScope.childScope(mainScopeName)
return object : CustomComponentEvaluator("CoVi") {
override fun startEvaluation(callback: XFullValueEvaluationCallback) {
callback.evaluated("")
}
return object : CustomComponentEvaluator("CoVi") {
override fun startEvaluation(callback: XFullValueEvaluationCallback) {
callback.evaluated("")
}
override fun createComponent(fullValue: String?): JComponent? {
return visualizer.createComponent(evaluationContext.project, valueDescriptor, evaluationContext, scope)
}
override fun createComponent(fullValue: String?): JComponent? {
return visualizer.createComponent(evaluationContext.project, valueDescriptor, evaluationContext, scope)
}
override fun showValuePopup(event: MouseEvent, project: Project, editor: Editor?, component: JComponent, cancelCallback: Runnable?) {
val frame = FrameWrapper(
project,
dimensionKey = "debugger-collection-visualizer",
isDialog = false,
title = valueDescriptor.name?.let { JavaDebuggerBundle.message("debugger.collection.visualizer.title.0", it) }
?: 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("$mainScopeName (limited to a window)"),
)
frame.apply {
disposeOnResume(evaluationContext, scope)
closeOnEsc()
show()
CollectionVisualizerStatisticsCollector.reportShown(project)
override fun showValuePopup(event: MouseEvent, project: Project, editor: Editor?, component: JComponent, cancelCallback: Runnable?) {
val frame = FrameWrapper(
project,
dimensionKey = "debugger-collection-visualizer",
isDialog = false,
title = valueDescriptor.name?.let { JavaDebuggerBundle.message("debugger.collection.visualizer.title.0", it) }
?: 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("$mainScopeName (limited to a window)"),
)
frame.apply {
disposeOnResume(evaluationContext, scope)
closeOnEsc()
show()
CollectionVisualizerStatisticsCollector.reportShown(project)
}
}
}
}
}
private fun FrameWrapper.disposeOnResume(
evaluationContext: EvaluationContextImpl,
@@ -94,9 +95,4 @@ class CollectionVisualizerEvaluator(private val visualizer: CollectionVisualizer
}
})
}
companion object {
@JvmStatic
fun createFor(collectionClass: String) = CollectionVisualizer.findApplicable(collectionClass)?.let(::CollectionVisualizerEvaluator)
}
}

View File

@@ -4,7 +4,6 @@ package com.intellij.debugger.settings;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.debugger.DebuggerContext;
import com.intellij.debugger.JavaDebuggerBundle;
import com.intellij.debugger.collections.visualizer.CollectionVisualizerEvaluator;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.JavaValuePresentation;
import com.intellij.debugger.engine.evaluation.*;
@@ -567,7 +566,7 @@ public class NodeRendererSettings implements PersistentStateComponent<Element> {
}
}
private static class ListObjectRenderer extends CompoundReferenceRenderer {
private static class ListObjectRenderer extends CollectionReferenceRenderer {
ListObjectRenderer(NodeRendererSettings rendererSettings, ArrayRenderer arrayRenderer) {
super(rendererSettings,
"List",
@@ -575,7 +574,6 @@ public class NodeRendererSettings implements PersistentStateComponent<Element> {
createExpressionArrayChildrenRenderer("toArray()", "!isEmpty()", arrayRenderer));
setClassName(CommonClassNames.JAVA_UTIL_LIST);
setIsApplicableChecker(type -> DebuggerUtilsAsync.instanceOf(type, getClassName()));
setFullValueEvaluator(CollectionVisualizerEvaluator.createFor(getClassName()));
}
@Override

View File

@@ -65,7 +65,6 @@ public class ArrayRenderer extends NodeRendererImpl implements FullValueEvaluato
private static final Logger LOG = Logger.getInstance(ArrayRenderer.class);
public static final @NonNls String UNIQUE_ID = "ArrayRenderer";
public static final @NonNls String VISUALIZER_ID = "%Array%";
public int START_INDEX = 0;
public int END_INDEX = Integer.MAX_VALUE;
@@ -100,11 +99,7 @@ public class ArrayRenderer extends NodeRendererImpl implements FullValueEvaluato
@Override
public @Nullable XFullValueEvaluator getFullValueEvaluator(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ValueDescriptorImpl valueDescriptor) {
CollectionVisualizerEvaluator evaluator = CollectionVisualizerEvaluator.createFor(VISUALIZER_ID);
if (evaluator == null) {
return null;
}
return evaluator.getFullValueEvaluator(evaluationContext, valueDescriptor);
return CollectionVisualizerEvaluator.createFor(evaluationContext, valueDescriptor);
}
@Override

View File

@@ -0,0 +1,25 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.debugger.ui.tree.render
import com.intellij.debugger.collections.visualizer.CollectionVisualizerEvaluator
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.settings.NodeRendererSettings
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl
import com.intellij.xdebugger.frame.XFullValueEvaluator
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Experimental
open class CollectionReferenceRenderer(
rendererSettings: NodeRendererSettings,
name: String,
labelRenderer: ValueLabelRenderer,
childrenRenderer: ChildrenRenderer,
) : CompoundReferenceRenderer(rendererSettings,
name,
labelRenderer,
childrenRenderer) {
override fun getFullValueEvaluator(evaluationContext: EvaluationContextImpl, valueDescriptor: ValueDescriptorImpl): XFullValueEvaluator? {
return super.getFullValueEvaluator(evaluationContext, valueDescriptor)
?: CollectionVisualizerEvaluator.createFor(evaluationContext, valueDescriptor)
}
}