mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[rd debugger] IDEA-377734 Implement JavaValueDescriptor state
GitOrigin-RevId: fd6aa8ba21aae57121cda5f2fea3e22994ab42b9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d05e2ad41e
commit
b5f40fabac
@@ -5,4 +5,7 @@
|
||||
</dependencies>
|
||||
<actions resource-bundle="messages.JavaDebuggerImplFrontendBundle">
|
||||
</actions>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<xdebugger.descriptorStateManager implementation="com.intellij.java.debugger.impl.frontend.JavaValueDescriptorStateExtension"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.debugger.impl.frontend
|
||||
|
||||
import com.intellij.java.debugger.impl.shared.engine.JavaValueDescriptor
|
||||
import com.intellij.java.debugger.impl.shared.engine.JavaValueDescriptorState
|
||||
import com.intellij.platform.debugger.impl.frontend.FrontendDescriptorStateManagerExtension
|
||||
import com.intellij.xdebugger.frame.XDescriptor
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
private class JavaValueDescriptorStateExtension : FrontendDescriptorStateManagerExtension {
|
||||
override fun createState(descriptor: XDescriptor, cs: CoroutineScope): Any? {
|
||||
if (descriptor !is JavaValueDescriptor) return null
|
||||
return JavaValueDescriptorState(descriptor, cs)
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ jvm_library(
|
||||
"//xml/xml-psi-api:psi",
|
||||
"//platform/xdebugger-impl/shared",
|
||||
"//platform/xdebugger-impl/rpc",
|
||||
"//fleet/rpc",
|
||||
],
|
||||
runtime_deps = [":impl_resources"]
|
||||
)
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
<orderEntry type="module" module-name="intellij.xml.psi" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger.impl.shared" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger.impl.rpc" />
|
||||
<orderEntry type="module" module-name="fleet.rpc" />
|
||||
</component>
|
||||
<component name="copyright">
|
||||
<Base>
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
|
||||
import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase;
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -89,8 +90,8 @@ public class ViewAsGroup extends ActionGroup implements DumbAware {
|
||||
public void threadAction(@NotNull SuspendContextImpl suspendContext) {
|
||||
for (XValueNodeImpl node : selectedNodes) {
|
||||
XValue container = node.getValueContainer();
|
||||
if (container instanceof JavaValue) {
|
||||
((JavaValue)container).setRenderer(myNodeRenderer, node);
|
||||
if (container instanceof JavaValue javaValue) {
|
||||
javaValue.setRenderer(myNodeRenderer, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,14 +159,8 @@ public class ViewAsGroup extends ActionGroup implements DumbAware {
|
||||
private static @NotNull CompletableFuture<List<NodeRenderer>> getApplicableRenderers(List<JavaValue> values, DebugProcessImpl process) {
|
||||
List<CompletableFuture<List<NodeRenderer>>> futures = new ArrayList<>(values.size());
|
||||
for (JavaValue value : values) {
|
||||
if (value instanceof JavaReferringObjectsValue) { // disable for any referrers at all
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
ValueDescriptorImpl valueDescriptor = value.getDescriptor();
|
||||
if (!valueDescriptor.isValueValid()) {
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
futures.add(process.getApplicableRenderers(valueDescriptor.getType()));
|
||||
CompletableFuture<List<NodeRenderer>> completedFuture = getApplicableNodeRenderers(value);
|
||||
futures.add(completedFuture);
|
||||
}
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApply(__ -> {
|
||||
@@ -183,6 +178,19 @@ public class ViewAsGroup extends ActionGroup implements DumbAware {
|
||||
});
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static CompletableFuture<List<NodeRenderer>> getApplicableNodeRenderers(JavaValue value) {
|
||||
if (value instanceof JavaReferringObjectsValue) { // disable for any referrers at all
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
ValueDescriptorImpl valueDescriptor = value.getDescriptor();
|
||||
if (!valueDescriptor.isValueValid()) {
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
DebugProcessImpl process = value.getEvaluationContext().getDebugProcess();
|
||||
return process.getApplicableRenderers(valueDescriptor.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final @NotNull AnActionEvent event) {
|
||||
DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(event.getDataContext());
|
||||
|
||||
@@ -81,6 +81,7 @@ import com.intellij.xdebugger.XDebuggerBundle;
|
||||
import com.intellij.xdebugger.XDebuggerManager;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.frame.XExecutionStack;
|
||||
import com.intellij.xdebugger.impl.CoroutineUtilsKt;
|
||||
import com.intellij.xdebugger.impl.XDebugSessionImpl;
|
||||
import com.intellij.xdebugger.impl.XDebuggerManagerImpl;
|
||||
import com.intellij.xdebugger.impl.actions.XDebuggerActions;
|
||||
@@ -94,10 +95,13 @@ import com.sun.jdi.event.LocatableEvent;
|
||||
import com.sun.jdi.request.EventRequest;
|
||||
import com.sun.jdi.request.EventRequestManager;
|
||||
import com.sun.jdi.request.StepRequest;
|
||||
import kotlin.Unit;
|
||||
import kotlin.coroutines.EmptyCoroutineContext;
|
||||
import kotlinx.coroutines.CoroutineScope;
|
||||
import kotlinx.coroutines.CoroutineScopeKt;
|
||||
import kotlinx.coroutines.Job;
|
||||
import kotlinx.coroutines.flow.Flow;
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
@@ -146,6 +150,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
||||
private volatile Map<String, Connector.Argument> myArguments;
|
||||
|
||||
private final List<NodeRenderer> myRenderers = new ArrayList<>();
|
||||
private final MutableSharedFlow<Unit> myRenderersUpdated = CoroutineUtilsKt.createMutableSharedFlow(1, 1);
|
||||
|
||||
// we use null key here
|
||||
private final Map<Type, Object> myNodeRenderersMap = Collections.synchronizedMap(new HashMap<>());
|
||||
@@ -220,6 +225,11 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
||||
return new DebuggerManagerThreadImpl(disposable, projectScope, this);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public Flow<Unit> getRenderersUpdatedFlow() {
|
||||
return myRenderersUpdated;
|
||||
}
|
||||
|
||||
private void reloadRenderers() {
|
||||
getManagerThread().schedule(new DebuggerCommandImpl(PrioritizedTask.Priority.HIGH) {
|
||||
@Override
|
||||
@@ -230,6 +240,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
||||
myRenderers.addAll(NodeRendererSettings.getInstance().getAllRenderers(project));
|
||||
}
|
||||
finally {
|
||||
myRenderersUpdated.tryEmit(Unit.INSTANCE);
|
||||
DebuggerInvocationUtil.invokeLaterAnyModality(project, () -> {
|
||||
final DebuggerSession session = mySession;
|
||||
if (session != null && session.isAttached()) {
|
||||
|
||||
@@ -523,17 +523,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
|
||||
|
||||
@Override
|
||||
public @Nullable CompletableFuture<XDescriptor> getXValueDescriptorAsync() {
|
||||
return myValueDescriptor.getInitFuture().thenApply(ignored -> {
|
||||
Value value = myValueDescriptor.getValue();
|
||||
JavaValueObjectReferenceInfo objectReferenceInfo = null;
|
||||
if (value instanceof ObjectReference ref) {
|
||||
objectReferenceInfo = new JavaValueObjectReferenceInfo(ref.referenceType().name(), ref.virtualMachine().canGetInstanceInfo());
|
||||
}
|
||||
return new JavaValueDescriptor(
|
||||
myValueDescriptor.isString(),
|
||||
objectReferenceInfo
|
||||
);
|
||||
});
|
||||
return myValueDescriptor.getInitFuture().thenCompose(ignored -> JavaValueUtilsKt.getJavaValueXDescriptor(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.debugger.engine
|
||||
|
||||
import com.intellij.debugger.actions.ViewAsGroup
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||
import com.intellij.debugger.impl.PrioritizedTask
|
||||
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl
|
||||
import com.intellij.debugger.ui.tree.render.NodeRenderer
|
||||
import com.intellij.debugger.ui.tree.render.Renderer
|
||||
import com.intellij.java.debugger.impl.shared.engine.JavaValueDescriptor
|
||||
import com.intellij.java.debugger.impl.shared.engine.JavaValueObjectReferenceInfo
|
||||
import com.intellij.java.debugger.impl.shared.engine.NodeRendererDto
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.xdebugger.XSourcePosition
|
||||
import com.intellij.xdebugger.frame.XDescriptor
|
||||
import com.sun.jdi.ObjectReference
|
||||
import fleet.rpc.core.toRpc
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.future.await
|
||||
import kotlinx.coroutines.future.future
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@OptIn(InternalCoroutinesApi::class)
|
||||
@@ -34,4 +50,37 @@ internal fun scheduleSourcePositionCompute(
|
||||
positionCallback(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getJavaValueXDescriptor(javaValue: JavaValue): CompletableFuture<XDescriptor> {
|
||||
val valueDescriptor = javaValue.descriptor
|
||||
val value = valueDescriptor.getValue()
|
||||
var objectReferenceInfo: JavaValueObjectReferenceInfo? = null
|
||||
if (value is ObjectReference) {
|
||||
objectReferenceInfo = JavaValueObjectReferenceInfo(value.referenceType().name(), value.virtualMachine().canGetInstanceInfo())
|
||||
}
|
||||
val cs = valueDescriptor.project.service<JavaValueDescriptorCreationService>().cs
|
||||
return cs.future {
|
||||
val renderersUpdatedFlow = javaValue.evaluationContext.debugProcess.renderersUpdatedFlow
|
||||
JavaValueDescriptor(
|
||||
valueDescriptor.isString(),
|
||||
objectReferenceInfo,
|
||||
valueDescriptor.lastRenderer?.toRpc(),
|
||||
valueDescriptor.lastRendererFlow.map { it.toRpc() }.toRpc(),
|
||||
renderersUpdatedFlow.map { fetchApplicableNodeRenderers(javaValue).map { it.toRpc() } }.toRpc()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchApplicableNodeRenderers(javaValue: JavaValue): List<NodeRenderer> {
|
||||
val renderersFuture = withDebugContext(javaValue.evaluationContext.suspendContext) {
|
||||
ViewAsGroup.getApplicableNodeRenderers(javaValue)
|
||||
}
|
||||
return renderersFuture.await()
|
||||
}
|
||||
|
||||
private fun Renderer.toRpc() = (this as? NodeRenderer)?.toRpc()
|
||||
private fun NodeRenderer.toRpc() = NodeRendererDto(uniqueId, name)
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
private class JavaValueDescriptorCreationService(val cs: CoroutineScope)
|
||||
|
||||
+17
-2
@@ -39,9 +39,13 @@ import com.intellij.util.concurrency.Semaphore;
|
||||
import com.intellij.xdebugger.frame.XValueModifier;
|
||||
import com.intellij.xdebugger.frame.XValueNode;
|
||||
import com.intellij.xdebugger.frame.presentation.XRegularValuePresentation;
|
||||
import com.intellij.xdebugger.impl.CoroutineUtilsKt;
|
||||
import com.intellij.xdebugger.impl.frame.XValueMarkers;
|
||||
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup;
|
||||
import com.sun.jdi.*;
|
||||
import kotlin.Unit;
|
||||
import kotlinx.coroutines.flow.Flow;
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -60,8 +64,8 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
|
||||
private final CompletableFuture<Void> myInitFuture;
|
||||
|
||||
NodeRenderer myRenderer = null;
|
||||
|
||||
NodeRenderer myAutoRenderer = null;
|
||||
private final MutableSharedFlow<Unit> myRenderersChangedFlow = CoroutineUtilsKt.createMutableSharedFlow(0, 1);
|
||||
|
||||
private Value myValue;
|
||||
|
||||
@@ -497,10 +501,16 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
|
||||
public void displayAs(NodeDescriptor descriptor) {
|
||||
if (descriptor instanceof ValueDescriptorImpl valueDescriptor) {
|
||||
myRenderer = valueDescriptor.myRenderer;
|
||||
myRenderersChangedFlow.tryEmit(Unit.INSTANCE);
|
||||
}
|
||||
super.displayAs(descriptor);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public Flow<Renderer> getLastRendererFlow() {
|
||||
return CoroutineUtilsKt.mapFlow(myRenderersChangedFlow, __ -> getLastRenderer());
|
||||
}
|
||||
|
||||
public Renderer getLastRenderer() {
|
||||
return myRenderer != null ? myRenderer : myAutoRenderer;
|
||||
}
|
||||
@@ -539,7 +549,11 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
|
||||
}
|
||||
else {
|
||||
return DebuggerUtilsAsync.reschedule(debugProcess.getAutoRendererAsync(type))
|
||||
.thenApply(r -> myAutoRenderer = r);
|
||||
.thenApply(r -> {
|
||||
myAutoRenderer = r;
|
||||
myRenderersChangedFlow.tryEmit(Unit.INSTANCE);
|
||||
return r;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -548,6 +562,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
|
||||
DebuggerManagerThreadImpl.assertIsManagerThread();
|
||||
myRenderer = renderer;
|
||||
myAutoRenderer = null;
|
||||
myRenderersChangedFlow.tryEmit(Unit.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+34
@@ -3,6 +3,11 @@ package com.intellij.java.debugger.impl.shared.engine
|
||||
|
||||
import com.intellij.xdebugger.frame.CustomXDescriptorSerializerProvider
|
||||
import com.intellij.xdebugger.frame.XDescriptor
|
||||
import fleet.rpc.core.RpcFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -17,10 +22,17 @@ const val JAVA_VALUE_KIND: String = "JavaValue"
|
||||
data class JavaValueDescriptor(
|
||||
val isString: Boolean,
|
||||
val objectReferenceInfo: JavaValueObjectReferenceInfo?,
|
||||
val initialRenderer: NodeRendererDto?,
|
||||
val rendererFlow: RpcFlow<NodeRendererDto?>,
|
||||
val applicableRenderersFlow: RpcFlow<List<NodeRendererDto>>,
|
||||
) : XDescriptor {
|
||||
override val kind: String = JAVA_VALUE_KIND
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Serializable
|
||||
data class NodeRendererDto(val id: String, val name: String)
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Serializable
|
||||
data class JavaValueObjectReferenceInfo(
|
||||
@@ -35,4 +47,26 @@ private class JavaValueDescriptorSerializerProvider : CustomXDescriptorSerialize
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
class JavaValueDescriptorState(val descriptor: JavaValueDescriptor, cs: CoroutineScope) {
|
||||
private val _lastRenderer = MutableStateFlow(descriptor.initialRenderer)
|
||||
private val _applicableRenderers = MutableStateFlow(emptyList<NodeRendererDto>())
|
||||
|
||||
init {
|
||||
cs.launch {
|
||||
descriptor.rendererFlow.toFlow().collectLatest {
|
||||
_lastRenderer.value = it
|
||||
}
|
||||
}
|
||||
cs.launch {
|
||||
descriptor.applicableRenderersFlow.toFlow().collectLatest {
|
||||
_applicableRenderers.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val lastRenderer: NodeRendererDto? get() = _lastRenderer.value
|
||||
val applicableRenderers: List<NodeRendererDto> get() = _applicableRenderers.value
|
||||
}
|
||||
Reference in New Issue
Block a user