diff --git a/.idea/modules.xml b/.idea/modules.xml
index 5964b4736333..517d7c4a3b0f 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -946,6 +946,7 @@
+
diff --git a/build/bazel-generated-file-list.txt b/build/bazel-generated-file-list.txt
index 043e83119167..9b8a0eeb0214 100644
--- a/build/bazel-generated-file-list.txt
+++ b/build/bazel-generated-file-list.txt
@@ -264,6 +264,7 @@ idea/customization/base
idea/customization/min
images
images/intellij.platform.images.copyright
+images/intellij.platform.images.xdebugger
java/codeserver/core
java/codeserver/highlighting
java/compiler
diff --git a/images/intellij.platform.images.xdebugger/BUILD.bazel b/images/intellij.platform.images.xdebugger/BUILD.bazel
new file mode 100644
index 000000000000..0529c2014d1a
--- /dev/null
+++ b/images/intellij.platform.images.xdebugger/BUILD.bazel
@@ -0,0 +1,25 @@
+### auto-generated section `build intellij.platform.images.xdebugger` start
+load("@rules_jvm//:jvm.bzl", "jvm_library", "resourcegroup")
+
+resourcegroup(
+ name = "xdebugger_resources",
+ srcs = glob(["resources/**/*"]),
+ strip_prefix = "resources"
+)
+
+jvm_library(
+ name = "xdebugger",
+ module_name = "intellij.platform.images.xdebugger",
+ visibility = ["//visibility:public"],
+ srcs = glob(["src/**/*.kt", "src/**/*.java", "src/**/*.form"], allow_empty = True),
+ resources = [":xdebugger_resources"],
+ deps = [
+ "//images",
+ "//platform/projectModel-api:projectModel",
+ "//platform/util",
+ "//platform/core-api:core",
+ "//platform/xdebugger-impl/frontend",
+ "//platform/ide-core",
+ ]
+)
+### auto-generated section `build intellij.platform.images.xdebugger` end
\ No newline at end of file
diff --git a/images/intellij.platform.images.xdebugger/intellij.platform.images.xdebugger.iml b/images/intellij.platform.images.xdebugger/intellij.platform.images.xdebugger.iml
new file mode 100644
index 000000000000..99d0e0412845
--- /dev/null
+++ b/images/intellij.platform.images.xdebugger/intellij.platform.images.xdebugger.iml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/images/intellij.platform.images.xdebugger/resources/intellij.platform.images.xdebugger.xml b/images/intellij.platform.images.xdebugger/resources/intellij.platform.images.xdebugger.xml
new file mode 100644
index 000000000000..736244b2877c
--- /dev/null
+++ b/images/intellij.platform.images.xdebugger/resources/intellij.platform.images.xdebugger.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/images/intellij.platform.images.xdebugger/src/com/intellij/platform/images/xdebugger/ImageEditorUIProviderImpl.java b/images/intellij.platform.images.xdebugger/src/com/intellij/platform/images/xdebugger/ImageEditorUIProviderImpl.java
new file mode 100644
index 000000000000..aec7a2c92f9f
--- /dev/null
+++ b/images/intellij.platform.images.xdebugger/src/com/intellij/platform/images/xdebugger/ImageEditorUIProviderImpl.java
@@ -0,0 +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.platform.images.xdebugger;
+
+import com.intellij.platform.debugger.impl.frontend.frame.ImageEditorUIProvider;
+import org.intellij.images.editor.impl.ImageEditorManagerImpl;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+class ImageEditorUIProviderImpl implements ImageEditorUIProvider {
+ @Override
+ public @NotNull JComponent createImageEditorUI(byte @NotNull [] imageData) {
+ var icon = new ImageIcon(imageData);
+
+ var w = icon.getIconWidth();
+ var h = icon.getIconHeight();
+ var image = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()
+ .createCompatibleImage(w, h, Transparency.TRANSLUCENT);
+ var g = image.createGraphics();
+ try {
+ icon.paintIcon(null, g, 0, 0);
+ }
+ finally {
+ g.dispose();
+ }
+ return ImageEditorManagerImpl.createImageEditorUI(image);
+ }
+}
\ No newline at end of file
diff --git a/images/plugin-content.yaml b/images/plugin-content.yaml
index 0c278001783c..00e032a6b523 100644
--- a/images/plugin-content.yaml
+++ b/images/plugin-content.yaml
@@ -1,3 +1,6 @@
+- name: lib/modules/intellij.platform.images.xdebugger.jar
+ contentModules:
+ - name: intellij.platform.images.xdebugger
- name: lib/platform-images.jar
modules:
- name: intellij.platform.images
diff --git a/images/resources/META-INF/plugin.xml b/images/resources/META-INF/plugin.xml
index 8100518a6b87..371bb3aa8c14 100644
--- a/images/resources/META-INF/plugin.xml
+++ b/images/resources/META-INF/plugin.xml
@@ -15,6 +15,7 @@
+
diff --git a/java/debugger/impl/BUILD.bazel b/java/debugger/impl/BUILD.bazel
index c8652d2d798d..46fe76d5cb9d 100644
--- a/java/debugger/impl/BUILD.bazel
+++ b/java/debugger/impl/BUILD.bazel
@@ -51,7 +51,6 @@ jvm_library(
"@lib//:jb-jdi",
"//libraries/stream",
"//platform/statistics",
- "//images",
"//platform/core-ui",
"//libraries/fastutil",
"//platform/platform-util-io-impl:ide-util-io-impl",
@@ -74,6 +73,7 @@ jvm_library(
"//platform/xdebugger-impl/rpc",
"//fleet/rpc",
"//platform/kernel/rpc",
+ "//platform/remote-topics/shared:rpc-topics",
]
)
@@ -118,8 +118,6 @@ jvm_library(
"//libraries/stream",
"//platform/statistics",
"//platform/statistics:statistics_test_lib",
- "//images",
- "//images:images_test_lib",
"//platform/core-ui",
"//libraries/fastutil",
"//platform/platform-util-io-impl:ide-util-io-impl",
@@ -144,6 +142,7 @@ jvm_library(
"//fleet/rpc",
"//platform/kernel/rpc",
"//platform/testFramework/common",
+ "//platform/remote-topics/shared:rpc-topics",
]
)
### auto-generated section `build intellij.java.debugger.impl` end
diff --git a/java/debugger/impl/intellij.java.debugger.impl.iml b/java/debugger/impl/intellij.java.debugger.impl.iml
index 58b5a23ddcc8..c9dc56076a38 100644
--- a/java/debugger/impl/intellij.java.debugger.impl.iml
+++ b/java/debugger/impl/intellij.java.debugger.impl.iml
@@ -79,7 +79,6 @@
-
@@ -103,6 +102,7 @@
+
diff --git a/java/debugger/impl/resources/META-INF/java-debugger.xml b/java/debugger/impl/resources/META-INF/java-debugger.xml
index e99cd8a7bd6b..18b80a98e61e 100644
--- a/java/debugger/impl/resources/META-INF/java-debugger.xml
+++ b/java/debugger/impl/resources/META-INF/java-debugger.xml
@@ -150,6 +150,7 @@
+
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/AbstractImageRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/AbstractImageRenderer.java
new file mode 100644
index 000000000000..013a7b3e71cf
--- /dev/null
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/AbstractImageRenderer.java
@@ -0,0 +1,74 @@
+// 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.engine.JavaValue;
+import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
+import com.intellij.debugger.impl.DebuggerUtilsImpl;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.platform.debugger.impl.shared.ShowImagePopupUtil;
+import com.intellij.rt.debugger.ImageSerializer;
+import com.intellij.xdebugger.XDebuggerManager;
+import com.intellij.xdebugger.frame.XFullValueEvaluator;
+import com.intellij.xdebugger.impl.XDebuggerManagerImpl;
+import com.sun.jdi.StringReference;
+import com.sun.jdi.Value;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+
+abstract class AbstractImageRenderer extends CompoundRendererProvider {
+ private static final Logger LOG = Logger.getInstance(AbstractImageRenderer.class);
+
+ @Override
+ public boolean isApplicable(Project project) {
+ if (!super.isApplicable(project)) return false;
+
+ var manager = (XDebuggerManagerImpl)XDebuggerManager.getInstance(project);
+ return manager.getFrontendCapabilities().getCanShowImages();
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return true;
+ }
+
+ static byte @Nullable [] getImageBytes(EvaluationContextImpl evaluationContext, Value obj, String methodName) {
+ try {
+ EvaluationContextImpl copyContext = evaluationContext.createEvaluationContext(obj);
+ StringReference bytes =
+ (StringReference)DebuggerUtilsImpl.invokeHelperMethod(copyContext, ImageSerializer.class, methodName, Collections.singletonList(obj));
+ if (bytes != null) {
+ return bytes.value().getBytes(StandardCharsets.ISO_8859_1);
+ }
+ }
+ catch (Exception e) {
+ DebuggerUtilsImpl.logError("Exception while getting image data", e);
+ }
+ return null;
+ }
+
+ protected XFullValueEvaluator createImagePopupEvaluator(@NotNull @Nls String message,
+ @NotNull EvaluationContextImpl evaluationContext,
+ Value imageObj,
+ String getBytesMethodName) {
+ return new JavaValue.JavaFullValueEvaluator(message, evaluationContext) {
+ {
+ setShowValuePopup(false);
+ }
+
+ @Override
+ public void evaluate(@NotNull XFullValueEvaluationCallback callback) {
+ if (callback.isObsolete()) return;
+
+ var imageData = getImageBytes(getEvaluationContext(), imageObj, getBytesMethodName);
+
+ ShowImagePopupUtil.showOnFrontend(getEvaluationContext().getProject(), imageData);
+ callback.evaluated("");
+ }
+ };
+ }
+}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/GraphicsObjectRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/GraphicsObjectRenderer.java
index 4546fa9e6262..5aab93b5fbcb 100644
--- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/GraphicsObjectRenderer.java
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/GraphicsObjectRenderer.java
@@ -6,9 +6,7 @@ import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.FullValueEvaluatorProvider;
import com.sun.jdi.*;
-import javax.swing.*;
-
-public final class GraphicsObjectRenderer extends CompoundRendererProvider {
+public final class GraphicsObjectRenderer extends AbstractImageRenderer {
@Override
protected String getName() {
return "Graphics";
@@ -19,11 +17,6 @@ public final class GraphicsObjectRenderer extends CompoundRendererProvider {
return "sun.java2d.SunGraphics2D";
}
- @Override
- protected boolean isEnabled() {
- return true;
- }
-
@Override
protected FullValueEvaluatorProvider getFullValueEvaluatorProvider() {
return (evaluationContext, valueDescriptor) -> {
@@ -45,12 +38,8 @@ public final class GraphicsObjectRenderer extends CompoundRendererProvider {
if (!(type instanceof ReferenceType) || !DebuggerUtils.instanceOf(type, "java.awt.Image")) {
return null;
}
- return new ImageObjectRenderer.IconPopupEvaluator(JavaDebuggerBundle.message("message.node.show.image"), evaluationContext) {
- @Override
- protected Icon getData() {
- return ImageObjectRenderer.getIcon(getEvaluationContext(), bufImgValue, "imageToBytes");
- }
- };
+ return createImagePopupEvaluator(JavaDebuggerBundle.message("message.node.show.image"), evaluationContext,
+ bufImgValue, "imageToBytes");
}
catch (Exception ignored) {
}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/IconObjectRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/IconObjectRenderer.java
index b48d951e067f..4dff88a395f7 100644
--- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/IconObjectRenderer.java
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/IconObjectRenderer.java
@@ -10,11 +10,13 @@ import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.impl.DebuggerUtilsImpl;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.util.registry.Registry;
+import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
-final class IconObjectRenderer extends CompoundRendererProvider {
+final class IconObjectRenderer extends AbstractImageRenderer {
@Override
protected String getName() {
return "Icon";
@@ -25,11 +27,6 @@ final class IconObjectRenderer extends CompoundRendererProvider {
return "javax.swing.Icon";
}
- @Override
- protected boolean isEnabled() {
- return true;
- }
-
@Override
protected ValueIconRenderer getIconRenderer() {
return (descriptor, evaluationContext, listener) -> {
@@ -42,7 +39,7 @@ final class IconObjectRenderer extends CompoundRendererProvider {
@Override
public void contextAction(@NotNull SuspendContextImpl suspendContext) {
String getterName = AllIcons.Debugger.Value.getIconHeight() <= 16 ? "iconToBytesPreviewNormal" : "iconToBytesPreviewRetina";
- descriptor.setValueIcon(ImageObjectRenderer.getIcon(evalContext, descriptor.getValue(), getterName));
+ descriptor.setValueIcon(getImageIcon(evalContext, descriptor.getValue(), getterName));
listener.labelChanged();
}
});
@@ -53,12 +50,13 @@ final class IconObjectRenderer extends CompoundRendererProvider {
@Override
protected FullValueEvaluatorProvider getFullValueEvaluatorProvider() {
return (evaluationContext, valueDescriptor) -> {
- return new ImageObjectRenderer.IconPopupEvaluator(JavaDebuggerBundle.message("message.node.show.icon"), evaluationContext) {
- @Override
- protected Icon getData() {
- return ImageObjectRenderer.getIcon(getEvaluationContext(), valueDescriptor.getValue(), "iconToBytes");
- }
- };
+ return createImagePopupEvaluator(JavaDebuggerBundle.message("message.node.show.icon"), evaluationContext,
+ valueDescriptor.getValue(), "iconToBytes");
};
}
+
+ static @Nullable ImageIcon getImageIcon(EvaluationContextImpl evaluationContext, Value obj, String methodName) {
+ byte[] data = getImageBytes(evaluationContext, obj, methodName);
+ return data != null ? new ImageIcon(data) : null;
+ }
}
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ImageObjectRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ImageObjectRenderer.java
index 2d04f602992f..864c2db62086 100644
--- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ImageObjectRenderer.java
+++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ImageObjectRenderer.java
@@ -1,30 +1,10 @@
// 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.CommonBundle;
import com.intellij.debugger.JavaDebuggerBundle;
import com.intellij.debugger.engine.FullValueEvaluatorProvider;
-import com.intellij.debugger.engine.evaluation.EvaluateException;
-import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
-import com.intellij.debugger.impl.DebuggerUtilsImpl;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.rt.debugger.ImageSerializer;
-import com.sun.jdi.StringReference;
-import com.sun.jdi.Value;
-import org.intellij.images.editor.impl.ImageEditorManagerImpl;
-import org.jetbrains.annotations.Nls;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-
-final class ImageObjectRenderer extends CompoundRendererProvider {
- private static final Logger LOG = Logger.getInstance(ImageObjectRenderer.class);
+final class ImageObjectRenderer extends AbstractImageRenderer {
@Override
protected String getName() {
return "Image";
@@ -35,67 +15,10 @@ final class ImageObjectRenderer extends CompoundRendererProvider {
return "java.awt.Image";
}
- @Override
- protected boolean isEnabled() {
- return true;
- }
-
@Override
protected FullValueEvaluatorProvider getFullValueEvaluatorProvider() {
return (evaluationContext, valueDescriptor) ->
- new IconPopupEvaluator(JavaDebuggerBundle.message("message.node.show.image"), evaluationContext) {
- @Override
- protected Icon getData() {
- return getIcon(getEvaluationContext(), valueDescriptor.getValue(), "imageToBytes");
- }
- };
- }
-
- static JComponent createIconViewer(@Nullable Icon icon) {
- if (icon == null) return new JLabel(CommonBundle.message("label.no.data"), SwingConstants.CENTER);
- final int w = icon.getIconWidth();
- final int h = icon.getIconHeight();
- final BufferedImage image = GraphicsEnvironment.getLocalGraphicsEnvironment()
- .getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(w, h, Transparency.TRANSLUCENT);
- final Graphics2D g = image.createGraphics();
- icon.paintIcon(null, g, 0, 0);
- g.dispose();
-
- return ImageEditorManagerImpl.createImageEditorUI(image);
- }
-
- static @Nullable ImageIcon getIcon(EvaluationContextImpl evaluationContext, Value obj, String methodName) {
- try {
- byte[] data = getImageBytes(evaluationContext, obj, methodName);
- if (data != null) {
- return new ImageIcon(data);
- }
- }
- catch (Exception e) {
- LOG.info("Exception while getting image data", e);
- }
- return null;
- }
-
- private static byte @Nullable [] getImageBytes(EvaluationContextImpl evaluationContext, Value obj, String methodName)
- throws EvaluateException {
- EvaluationContextImpl copyContext = evaluationContext.createEvaluationContext(obj);
- StringReference bytes =
- (StringReference)DebuggerUtilsImpl.invokeHelperMethod(copyContext, ImageSerializer.class, methodName, Collections.singletonList(obj));
- if (bytes != null) {
- return bytes.value().getBytes(StandardCharsets.ISO_8859_1);
- }
- return null;
- }
-
- abstract static class IconPopupEvaluator extends CustomPopupFullValueEvaluator {
- IconPopupEvaluator(@NotNull @Nls String linkText, @NotNull EvaluationContextImpl evaluationContext) {
- super(linkText, evaluationContext);
- }
-
- @Override
- protected JComponent createComponent(Icon data) {
- return createIconViewer(data);
- }
+ createImagePopupEvaluator(JavaDebuggerBundle.message("message.node.show.image"), evaluationContext,
+ valueDescriptor.getValue(), "imageToBytes");
}
}
diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml
index 1de291b91bce..a550925a6548 100644
--- a/java/java-impl/resources/META-INF/JavaPlugin.xml
+++ b/java/java-impl/resources/META-INF/JavaPlugin.xml
@@ -2,7 +2,7 @@
- com.intellij.platform.images
+ com.intellij.platform.images
com.intellij.modules.idea.community
diff --git a/java/java-impl/resources/META-INF/images-integration.xml b/java/java-impl/resources/META-INF/images-integration.xml
deleted file mode 100644
index 93c7155a06bf..000000000000
--- a/java/java-impl/resources/META-INF/images-integration.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/platform/testFramework/monorepo/tests/api/PlatformApi.kt b/platform/testFramework/monorepo/tests/api/PlatformApi.kt
index e7cab4cd5e3e..fae564e19726 100644
--- a/platform/testFramework/monorepo/tests/api/PlatformApi.kt
+++ b/platform/testFramework/monorepo/tests/api/PlatformApi.kt
@@ -14,6 +14,7 @@ internal object PlatformApi {
"intellij.platform.uast.ide", // IDEA
"intellij.platform.images", // plugin
"intellij.platform.images.copyright", // plugin
+ "intellij.platform.images.xdebugger", // plugin
"intellij.platform.images.build", // build scripts
)
diff --git a/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXDebuggerManagerApi.kt b/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXDebuggerManagerApi.kt
index 0e902fbea266..9cf94f892028 100644
--- a/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXDebuggerManagerApi.kt
+++ b/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXDebuggerManagerApi.kt
@@ -7,6 +7,7 @@ import com.intellij.openapi.application.EDT
import com.intellij.openapi.editor.impl.EditorId
import com.intellij.openapi.editor.impl.findEditorOrNull
import com.intellij.platform.debugger.impl.rpc.*
+import com.intellij.platform.debugger.impl.rpc.XFrontendDebuggerCapabilities
import com.intellij.platform.execution.impl.backend.createProcessHandlerDto
import com.intellij.platform.project.ProjectId
import com.intellij.platform.project.findProject
@@ -31,6 +32,17 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
internal class BackendXDebuggerManagerApi : XDebuggerManagerApi {
+ override suspend fun initialize(projectId: ProjectId, capabilities: XFrontendDebuggerCapabilities) {
+ val project = projectId.findProject()
+ val manager = XDebuggerManager.getInstance(project) as XDebuggerManagerImpl
+ val old = manager.frontendCapabilities
+ val new = XFrontendDebuggerCapabilities(
+ // _any_ of clients can show images
+ canShowImages = old.canShowImages || capabilities.canShowImages,
+ )
+ manager.frontendCapabilities = new
+ }
+
@OptIn(ExperimentalCoroutinesApi::class)
override suspend fun currentSession(projectId: ProjectId): Flow {
val project = projectId.findProject()
diff --git a/platform/xdebugger-impl/frontend/resources/intellij.platform.debugger.impl.frontend.xml b/platform/xdebugger-impl/frontend/resources/intellij.platform.debugger.impl.frontend.xml
index 99cefc1817fe..6340b708d67b 100644
--- a/platform/xdebugger-impl/frontend/resources/intellij.platform.debugger.impl.frontend.xml
+++ b/platform/xdebugger-impl/frontend/resources/intellij.platform.debugger.impl.frontend.xml
@@ -10,6 +10,8 @@
+
@@ -26,5 +28,6 @@
+
diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/FrontendXDebuggerManager.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/FrontendXDebuggerManager.kt
index a1910049a6c1..56948f674ca7 100644
--- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/FrontendXDebuggerManager.kt
+++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/FrontendXDebuggerManager.kt
@@ -13,10 +13,8 @@ import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.openapi.wm.ex.ToolWindowManagerListener
import com.intellij.platform.debugger.impl.frontend.editor.BreakpointPromoterEditorListener
import com.intellij.platform.debugger.impl.frontend.evaluate.quick.common.ValueLookupManager
-import com.intellij.platform.debugger.impl.rpc.XDebugSessionDto
-import com.intellij.platform.debugger.impl.rpc.XDebuggerManagerApi
-import com.intellij.platform.debugger.impl.rpc.XDebuggerManagerSessionEvent
-import com.intellij.platform.debugger.impl.rpc.XDebuggerValueLookupHintsRemoteApi
+import com.intellij.platform.debugger.impl.frontend.frame.ImageEditorUIUtil
+import com.intellij.platform.debugger.impl.rpc.*
import com.intellij.platform.project.projectId
import com.intellij.ui.content.ContentManagerEvent
import com.intellij.ui.content.ContentManagerListener
@@ -64,6 +62,9 @@ class FrontendXDebuggerManager(private val project: Project, private val cs: Cor
internal val sessions get() = sessionsFlow.value
init {
+ initCapabilities()
+ // TODO: make sure that capabilities are send before anything else
+
cs.launch {
for (event in synchronousExecutor) {
event()
@@ -81,6 +82,12 @@ class FrontendXDebuggerManager(private val project: Project, private val cs: Cor
}
}
+ private fun initCapabilities() = cs.launch {
+ XDebuggerManagerApi.getInstance().initialize(project.projectId(), XFrontendDebuggerCapabilities(
+ canShowImages = ImageEditorUIUtil.canCreateImageEditor(),
+ ))
+ }
+
private fun initSessions() = cs.launch {
// When the registry flag is not set, we would prefer to have XDebugSessionProxy.Monolith in a listener
// see com.intellij.xdebugger.impl.MonolithListenerAdapter
diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/ImagePopupUtil.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/ImagePopupUtil.kt
new file mode 100644
index 000000000000..86d818394f45
--- /dev/null
+++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/ImagePopupUtil.kt
@@ -0,0 +1,60 @@
+// 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.platform.debugger.impl.frontend.frame
+
+import com.intellij.CommonBundle
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.runInEdt
+import com.intellij.openapi.extensions.ExtensionPointName
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.wm.WindowManager
+import com.intellij.platform.debugger.impl.shared.ShowImagePopupUtil
+import com.intellij.platform.rpc.topics.ProjectRemoteTopic
+import com.intellij.platform.rpc.topics.ProjectRemoteTopicListener
+import com.intellij.ui.awt.RelativePoint
+import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
+import org.jetbrains.annotations.ApiStatus
+import java.awt.Dimension
+import java.awt.Point
+import javax.swing.JComponent
+import javax.swing.JLabel
+import javax.swing.SwingConstants
+
+@ApiStatus.Internal
+interface ImageEditorUIProvider {
+ fun createImageEditorUI(imageData: ByteArray): JComponent
+}
+
+@ApiStatus.Internal // might be public in theory
+object ImageEditorUIUtil {
+ private val EP_NAME = ExtensionPointName("com.intellij.xdebugger.imageEditorUIProvider")
+
+ fun canCreateImageEditor(): Boolean = EP_NAME.hasAnyExtensions()
+
+ fun createImageEditorUI(imageData: ByteArray): JComponent? = EP_NAME.extensionList.firstOrNull()?.createImageEditorUI(imageData)
+}
+
+private class ShowImagePopupRemoteTopicListener : ProjectRemoteTopicListener {
+ override val topic: ProjectRemoteTopic = ShowImagePopupUtil.REMOTE_TOPIC
+
+ override fun handleEvent(project: Project, event: ShowImagePopupUtil.Request) {
+ runInEdt {
+ val frame = WindowManager.getInstance().getFrame(project)
+ if (frame == null) return@runInEdt
+ val popupSize = Dimension(frame.size.width / 2, frame.size.height / 2)
+
+ val imageData = event.imageData
+ val content =
+ if (imageData != null) ImageEditorUIUtil.createImageEditorUI(imageData) ?: return@runInEdt
+ else JLabel(CommonBundle.message("label.no.data"), SwingConstants.CENTER)
+
+ val popup = DebuggerUIUtil.createValuePopup(project, content, null)
+ if (content is Disposable) {
+ Disposer.register(popup, content)
+ }
+
+ popup.setSize(popupSize)
+ popup.show(RelativePoint(frame, Point(popupSize.width / 2, popupSize.height / 2)))
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XDebuggerManagerApi.kt b/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XDebuggerManagerApi.kt
index 4ad7190fe289..5060e8d67f59 100644
--- a/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XDebuggerManagerApi.kt
+++ b/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XDebuggerManagerApi.kt
@@ -19,6 +19,8 @@ import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
@Rpc
interface XDebuggerManagerApi : RemoteApi {
+ suspend fun initialize(projectId: ProjectId, capabilities: XFrontendDebuggerCapabilities)
+
suspend fun currentSession(projectId: ProjectId): Flow
suspend fun sessions(projectId: ProjectId): XDebugSessionsList
@@ -41,6 +43,12 @@ interface XDebuggerManagerApi : RemoteApi {
}
}
+@ApiStatus.Internal
+@Serializable
+data class XFrontendDebuggerCapabilities(
+ val canShowImages: Boolean,
+)
+
@ApiStatus.Internal
@Serializable
data class XBreakpointsSetDto(
diff --git a/platform/xdebugger-impl/shared/BUILD.bazel b/platform/xdebugger-impl/shared/BUILD.bazel
index a41bb900ce30..586dc106d8da 100644
--- a/platform/xdebugger-impl/shared/BUILD.bazel
+++ b/platform/xdebugger-impl/shared/BUILD.bazel
@@ -24,6 +24,7 @@ jvm_library(
"//platform/platform-impl/rpc",
"//platform/project/shared:project",
"//platform/editor-ui-api:editor-ui",
+ "//platform/remote-topics/shared:rpc-topics",
]
)
### auto-generated section `build intellij.platform.debugger.impl.shared` end
\ No newline at end of file
diff --git a/platform/xdebugger-impl/shared/api-dump-experimental.txt b/platform/xdebugger-impl/shared/api-dump-experimental.txt
new file mode 100644
index 000000000000..20e303709a14
--- /dev/null
+++ b/platform/xdebugger-impl/shared/api-dump-experimental.txt
@@ -0,0 +1,3 @@
+*f:com.intellij.platform.debugger.impl.shared.ShowImagePopupUtil
+- sf:INSTANCE:com.intellij.platform.debugger.impl.shared.ShowImagePopupUtil
+- sf:showOnFrontend(com.intellij.openapi.project.Project,B[]):V
diff --git a/platform/xdebugger-impl/shared/intellij.platform.debugger.impl.shared.iml b/platform/xdebugger-impl/shared/intellij.platform.debugger.impl.shared.iml
index 475ffadc9f1c..1cb4841c2214 100644
--- a/platform/xdebugger-impl/shared/intellij.platform.debugger.impl.shared.iml
+++ b/platform/xdebugger-impl/shared/intellij.platform.debugger.impl.shared.iml
@@ -43,5 +43,6 @@
+
\ No newline at end of file
diff --git a/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/ShowImagePopupUtil.kt b/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/ShowImagePopupUtil.kt
new file mode 100644
index 000000000000..c1a8d7f93d0d
--- /dev/null
+++ b/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/ShowImagePopupUtil.kt
@@ -0,0 +1,23 @@
+// 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.platform.debugger.impl.shared
+
+import com.intellij.openapi.project.Project
+import com.intellij.platform.rpc.topics.ProjectRemoteTopic
+import com.intellij.platform.rpc.topics.sendToClient
+import kotlinx.serialization.Serializable
+import org.jetbrains.annotations.ApiStatus
+
+@ApiStatus.Experimental
+object ShowImagePopupUtil {
+ @ApiStatus.Internal
+ @Serializable
+ class Request(val imageData: ByteArray?)
+
+ @ApiStatus.Internal
+ val REMOTE_TOPIC: ProjectRemoteTopic = ProjectRemoteTopic("xdebugger.show.image.popup", Request.serializer())
+
+ @JvmStatic
+ fun showOnFrontend(project: Project, imageData: ByteArray?) {
+ REMOTE_TOPIC.sendToClient(project, Request(imageData))
+ }
+}
diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java
index e66c36a690a8..0f4a125d31ba 100644
--- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java
+++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java
@@ -39,6 +39,7 @@ import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.wm.IdeGlassPaneUtil;
import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.platform.debugger.impl.rpc.XFrontendDebuggerCapabilities;
import com.intellij.ui.ExperimentalUI;
import com.intellij.ui.HintHint;
import com.intellij.ui.awt.RelativePoint;
@@ -95,6 +96,11 @@ public final class XDebuggerManagerImpl extends XDebuggerManager implements Pers
private InlayRunToCursorEditorListener myNewRunToCursorListener = null;
+ private @NotNull XFrontendDebuggerCapabilities myFrontendCapabilities =
+ new XFrontendDebuggerCapabilities(
+ false
+ );
+
XDebuggerManagerImpl(@NotNull Project project, @NotNull CoroutineScope coroutineScope) {
myProject = project;
myCoroutineScope = coroutineScope;
@@ -368,6 +374,15 @@ public final class XDebuggerManagerImpl extends XDebuggerManager implements Pers
private static final TooltipGroup RUN_TO_CURSOR_TOOLTIP_GROUP = new TooltipGroup("RUN_TO_CURSOR_TOOLTIP_GROUP", 0);
+ @ApiStatus.Internal
+ public void setFrontendCapabilities(@NotNull XFrontendDebuggerCapabilities capabilities) {
+ myFrontendCapabilities = capabilities;
+ }
+
+ public @NotNull XFrontendDebuggerCapabilities getFrontendCapabilities() {
+ return myFrontendCapabilities;
+ }
+
public static @NotNull NotificationGroup getNotificationGroup() {
return NotificationGroupManager.getInstance().getNotificationGroup("Debugger messages");
}