diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java index 7bb98d77793a..93c63f1760da 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java @@ -100,6 +100,10 @@ public abstract class Breakpoint

implements return myProject; } + private @NotNull BreakpointManager getBreakpointManager() { + return DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager(); + } + protected @NotNull P getProperties() { return myXBreakpoint.getProperties(); } @@ -395,7 +399,9 @@ public abstract class Breakpoint

implements buf.append("\n"); } if (!buf.isEmpty()) { - debugProcess.printToConsole(buf.toString()); + var msg = buf.toString(); + getBreakpointManager().multicastLogMessage(this, msg, debugProcess); + debugProcess.printToConsole(msg); } } if (isRemoveAfterHit()) { @@ -623,7 +629,7 @@ public abstract class Breakpoint

implements } private void removeBreakpoint() { - AppUIUtil.invokeOnEdt(() -> DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().removeBreakpoint(Breakpoint.this)); + AppUIUtil.invokeOnEdt(() -> getBreakpointManager().removeBreakpoint(Breakpoint.this)); debugProcess.removeDebugProcessListener(this); } }); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointLogMessageListener.kt b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointLogMessageListener.kt new file mode 100644 index 000000000000..80625a850a74 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointLogMessageListener.kt @@ -0,0 +1,14 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.debugger.ui.breakpoints + +import com.intellij.debugger.engine.DebugProcessImpl +import org.jetbrains.annotations.ApiStatus +import java.util.EventListener + +/** + * Listener for breakpoint messages: evaluate and log, stack trace, "breakpoint hit", etc. + */ +@ApiStatus.Internal +fun interface BreakpointLogMessageListener : EventListener { + fun onLogMessage(breakpoint: Breakpoint<*>, message: String, debugProcess: DebugProcessImpl) +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java index 1c136686b1c3..185fa31b8d99 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java @@ -14,6 +14,7 @@ import com.intellij.debugger.engine.LightOrRealThreadInfo; import com.intellij.debugger.engine.RealThreadInfo; import com.intellij.debugger.engine.requests.RequestManagerImpl; import com.intellij.debugger.impl.*; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.application.WriteAction; @@ -33,6 +34,8 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.PsiField; +import com.intellij.util.CoroutineScopeKt; +import com.intellij.util.EventDispatcher; import com.intellij.util.concurrency.AppExecutorUtil; import com.intellij.util.concurrency.ThreadingAssertions; import com.intellij.util.containers.ContainerUtil; @@ -55,6 +58,7 @@ import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.request.EventRequest; import com.sun.jdi.request.EventRequestManager; import com.sun.jdi.request.InvalidRequestStateException; +import kotlinx.coroutines.CoroutineScope; import one.util.streamex.StreamEx; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -86,6 +90,8 @@ public class BreakpointManager { private final Project myProject; private final Map myUIProperties = new LinkedHashMap<>(); + private final EventDispatcher myLogMessageDispatcher = EventDispatcher.create(BreakpointLogMessageListener.class); + public BreakpointManager(@NotNull Project project, @NotNull DebuggerManagerImpl debuggerManager) { myProject = project; debuggerManager.getContextManager().addListener(new DebuggerContextListener() { @@ -665,4 +671,16 @@ public class BreakpointManager { public String setProperty(String name, String value) { return myUIProperties.put(name, value); } + + public void addLogMessageListener(CoroutineScope scope, BreakpointLogMessageListener listener) { + addLogMessageListener(listener, CoroutineScopeKt.asDisposable(scope)); + } + + private void addLogMessageListener(BreakpointLogMessageListener listener, Disposable parentDisposable) { + myLogMessageDispatcher.addListener(listener, parentDisposable); + } + + void multicastLogMessage(Breakpoint breakpoint, String message, DebugProcessImpl debugProcess) { + myLogMessageDispatcher.getMulticaster().onLogMessage(breakpoint, message, debugProcess); + } }