mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[rd debugger] IDEA-370752 Pass isAttached state to FE
GitOrigin-RevId: 6c7beedb9a6dae542b709e4f25ff5435bdf33319
This commit is contained in:
committed by
intellij-monorepo-bot
parent
00751a99ef
commit
6cf266d548
+25
@@ -0,0 +1,25 @@
|
||||
// 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.backend
|
||||
|
||||
import com.intellij.debugger.engine.JavaDebugProcess
|
||||
import com.intellij.debugger.impl.DebuggerSession
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerManagerApi
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerSessionDto
|
||||
import com.intellij.xdebugger.impl.rpc.XDebugSessionId
|
||||
import com.intellij.xdebugger.impl.rpc.models.findValue
|
||||
import fleet.rpc.core.toRpc
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class BackendJavaDebuggerManagerApi : JavaDebuggerManagerApi {
|
||||
override suspend fun getJavaSession(sessionId: XDebugSessionId): JavaDebuggerSessionDto? {
|
||||
val xSession = sessionId.findValue() ?: return null
|
||||
val process = xSession.debugProcess as? JavaDebugProcess ?: return null
|
||||
val debuggerSession = process.debuggerSession
|
||||
|
||||
val sessionStateFlow = debuggerSession.sessionStateFlow
|
||||
return JavaDebuggerSessionDto(
|
||||
isAttachedInitial = debuggerSession.isAttached,
|
||||
isAttachedFlow = sessionStateFlow.map { DebuggerSession.isAttached(it) }.toRpc(),
|
||||
)
|
||||
}
|
||||
}
|
||||
+4
@@ -2,6 +2,7 @@
|
||||
package com.intellij.java.debugger.impl.backend
|
||||
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerLuxActionsApi
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerManagerApi
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerSessionApi
|
||||
import com.intellij.platform.rpc.backend.RemoteApiProvider
|
||||
import fleet.rpc.remoteApiDescriptor
|
||||
@@ -14,5 +15,8 @@ private class BackendJavaDebuggerRemoteApiProvider : RemoteApiProvider {
|
||||
remoteApi(remoteApiDescriptor<JavaDebuggerSessionApi>()) {
|
||||
BackendJavaDebuggerSessionApi()
|
||||
}
|
||||
remoteApi(remoteApiDescriptor<JavaDebuggerManagerApi>()) {
|
||||
BackendJavaDebuggerManagerApi()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.xdebugger.AbstractDebuggerSession;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.impl.CoroutineUtilsKt;
|
||||
import com.intellij.xdebugger.impl.XDebuggerManagerImpl;
|
||||
import com.intellij.xdebugger.impl.actions.XDebuggerActions;
|
||||
import com.intellij.xdebugger.impl.evaluate.ValueLookupManagerController;
|
||||
@@ -55,6 +56,9 @@ import com.sun.jdi.ObjectCollectedException;
|
||||
import com.sun.jdi.ThreadReference;
|
||||
import com.sun.jdi.request.EventRequest;
|
||||
import com.sun.jdi.request.StepRequest;
|
||||
import kotlinx.coroutines.flow.Flow;
|
||||
import kotlinx.coroutines.flow.MutableStateFlow;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -75,7 +79,8 @@ public final class DebuggerSession implements AbstractDebuggerSession {
|
||||
|
||||
private volatile int myIgnoreFiltersFrameCountThreshold = 0;
|
||||
|
||||
private DebuggerSessionState myState;
|
||||
private final MutableStateFlow<DebuggerSessionState> myStateFlow =
|
||||
CoroutineUtilsKt.createMutableStateFlow(new DebuggerSessionState(State.STOPPED, null));
|
||||
|
||||
private final String mySessionName;
|
||||
private final DebugProcessImpl myDebugProcess;
|
||||
@@ -136,6 +141,10 @@ public final class DebuggerSession implements AbstractDebuggerSession {
|
||||
myModifiedClassesScanRequired = modifiedClassesScanRequired;
|
||||
}
|
||||
|
||||
public Flow<State> getSessionStateFlow() {
|
||||
return CoroutineUtilsKt.mapFlow(myStateFlow, (sessionState) -> sessionState.myState);
|
||||
}
|
||||
|
||||
private class MyDebuggerStateManager extends DebuggerStateManager {
|
||||
private DebuggerContextImpl myDebuggerContext;
|
||||
|
||||
@@ -170,7 +179,7 @@ public final class DebuggerSession implements AbstractDebuggerSession {
|
||||
LOG.debug("DebuggerSession state = " + state + ", event = " + event);
|
||||
}
|
||||
|
||||
myState = new DebuggerSessionState(state, description);
|
||||
myStateFlow.setValue(new DebuggerSessionState(state, description));
|
||||
fireStateChanged(context, event);
|
||||
};
|
||||
|
||||
@@ -213,7 +222,6 @@ public final class DebuggerSession implements AbstractDebuggerSession {
|
||||
myDebugProcess = debugProcess;
|
||||
SESSION_EMPTY_CONTEXT = DebuggerContextImpl.createDebuggerContext(this, null, null, null);
|
||||
myContextManager = new MyDebuggerStateManager();
|
||||
myState = new DebuggerSessionState(State.STOPPED, null);
|
||||
myDebugProcess.addDebugProcessListener(new MyDebugProcessListener(debugProcess));
|
||||
ValueLookupManagerController.getInstance(getProject()).startListening();
|
||||
myDebugEnvironment = environment;
|
||||
@@ -263,15 +271,16 @@ public final class DebuggerSession implements AbstractDebuggerSession {
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return myState.myState;
|
||||
return myStateFlow.getValue().myState;
|
||||
}
|
||||
|
||||
public @NlsContexts.Label String getStateDescription() {
|
||||
if (myState.myDescription != null) {
|
||||
return myState.myDescription;
|
||||
String stateDescription = myStateFlow.getValue().myDescription;
|
||||
if (stateDescription != null) {
|
||||
return stateDescription;
|
||||
}
|
||||
|
||||
return switch (myState.myState) {
|
||||
return switch (getState()) {
|
||||
case STOPPED, DISPOSED -> JavaDebuggerBundle.message("status.debug.stopped");
|
||||
case RUNNING -> JavaDebuggerBundle.message("status.app.running");
|
||||
case IN_STEPPING -> JavaDebuggerBundle.message("status.app.stepping");
|
||||
@@ -451,7 +460,12 @@ public final class DebuggerSession implements AbstractDebuggerSession {
|
||||
}
|
||||
|
||||
public boolean isAttached() {
|
||||
return !isStopped() && getState() != State.WAITING_ATTACH;
|
||||
return isAttached(getState());
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static boolean isAttached(State state) {
|
||||
return state != State.STOPPED && state != State.WAITING_ATTACH;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -48,5 +48,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.execution" />
|
||||
<orderEntry type="module" module-name="intellij.platform.execution.impl" />
|
||||
<orderEntry type="module" module-name="intellij.java.frontback.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -6,6 +6,7 @@
|
||||
<rdct.remoteSettingProvider implementation="com.intellij.debugger.settings.JavaDebuggerSettingsProvider"/>
|
||||
<xdebugger.xValueCustomDescriptorSerializerProvider
|
||||
implementation="com.intellij.java.debugger.impl.shared.engine.JavaValueDescriptorSerializerProvider"/>
|
||||
<postStartupActivity implementation="com.intellij.java.debugger.impl.shared.SharedDebuggerInitializationProjectActivity"/>
|
||||
</extensions>
|
||||
<actions resource-bundle="messages.JavaDebuggerSharedBundle">
|
||||
<action id="DumpThreads" class="com.intellij.java.debugger.impl.shared.actions.ThreadDumpAction" icon="AllIcons.Actions.Dump">
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// 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.shared
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
|
||||
private class SharedDebuggerInitializationProjectActivity : ProjectActivity {
|
||||
override suspend fun execute(project: Project) {
|
||||
SharedJavaDebuggerManager.getInstance(project)
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// 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.shared
|
||||
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerManagerApi
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.xdebugger.impl.FrontendXDebuggerManagerListener
|
||||
import com.intellij.xdebugger.impl.frame.XDebugManagerProxy
|
||||
import com.intellij.xdebugger.impl.rpc.XDebugSessionDto
|
||||
import com.intellij.xdebugger.impl.rpc.XDebugSessionId
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
@ApiStatus.Internal
|
||||
class SharedJavaDebuggerManager(private val project: Project, private val cs: CoroutineScope) {
|
||||
private val synchronousExecutor = Channel<suspend () -> Unit>(capacity = Integer.MAX_VALUE)
|
||||
private val debugSessions = hashMapOf<XDebugSessionId, SharedJavaDebuggerSession>()
|
||||
|
||||
init {
|
||||
cs.launch {
|
||||
for (event in synchronousExecutor) {
|
||||
event()
|
||||
}
|
||||
}
|
||||
cs.launch {
|
||||
XDebugManagerProxy.getInstance().getCurrentSessionFlow(project).collectLatest { proxy ->
|
||||
if (proxy == null) return@collectLatest
|
||||
synchronousExecutor.send {
|
||||
val sessionId = proxy.id
|
||||
if (sessionId in debugSessions) return@send
|
||||
requestJavaSession(sessionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
project.messageBus.connect(cs).subscribe(FrontendXDebuggerManagerListener.TOPIC, object : FrontendXDebuggerManagerListener {
|
||||
override fun processStopped(sessionId: XDebugSessionId) {
|
||||
synchronousExecutor.trySend {
|
||||
debugSessions.remove(sessionId)?.close()
|
||||
}
|
||||
}
|
||||
|
||||
override fun processStarted(sessionId: XDebugSessionId, sessionDto: XDebugSessionDto) {
|
||||
synchronousExecutor.trySend {
|
||||
requestJavaSession(sessionId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private suspend fun requestJavaSession(sessionId: XDebugSessionId) {
|
||||
val dto = JavaDebuggerManagerApi.getInstance().getJavaSession(sessionId) ?: return
|
||||
val scope = cs.childScope("Java session $sessionId")
|
||||
debugSessions.put(sessionId, SharedJavaDebuggerSession(dto, scope))?.close()
|
||||
}
|
||||
|
||||
fun getJavaSession(sessionId: XDebugSessionId): SharedJavaDebuggerSession? {
|
||||
return debugSessions[sessionId]
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): SharedJavaDebuggerManager = project.service()
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// 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.shared
|
||||
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerSessionDto
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
class SharedJavaDebuggerSession(dto: JavaDebuggerSessionDto, private val cs: CoroutineScope) {
|
||||
private val isAttachedFlow = dto.isAttachedFlow.toFlow()
|
||||
.stateIn(cs, SharingStarted.Eagerly, dto.isAttachedInitial)
|
||||
|
||||
val isAttached: Boolean get() = isAttachedFlow.value
|
||||
|
||||
|
||||
fun close() {
|
||||
cs.cancel()
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -6,6 +6,7 @@ import com.intellij.frontend.FrontendApplicationInfo
|
||||
import com.intellij.frontend.FrontendType
|
||||
import com.intellij.ide.ui.icons.icon
|
||||
import com.intellij.java.debugger.impl.shared.SharedDebuggerUtils
|
||||
import com.intellij.java.debugger.impl.shared.SharedJavaDebuggerManager
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerSessionApi
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaThreadDumpDto
|
||||
import com.intellij.java.debugger.impl.shared.rpc.JavaThreadDumpItemDto
|
||||
@@ -62,8 +63,8 @@ private class ThreadDumpAction : DumbAwareAction(), ActionRemoteBehaviorSpecific
|
||||
presentation.setEnabled(false)
|
||||
return
|
||||
}
|
||||
// TODO should be isAttached
|
||||
val isAttached = !sessionProxy.isStopped
|
||||
val javaSession = SharedJavaDebuggerManager.getInstance(project).getJavaSession(sessionProxy.id)
|
||||
val isAttached = javaSession != null && javaSession.isAttached
|
||||
presentation.setEnabled(isAttached)
|
||||
}
|
||||
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// 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.shared.rpc
|
||||
|
||||
import com.intellij.platform.rpc.RemoteApiProviderService
|
||||
import com.intellij.xdebugger.impl.rpc.XDebugSessionId
|
||||
import fleet.rpc.RemoteApi
|
||||
import fleet.rpc.Rpc
|
||||
import fleet.rpc.core.RpcFlow
|
||||
import fleet.rpc.remoteApiDescriptor
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Rpc
|
||||
interface JavaDebuggerManagerApi : RemoteApi<Unit> {
|
||||
suspend fun getJavaSession(sessionId: XDebugSessionId): JavaDebuggerSessionDto?
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
suspend fun getInstance(): JavaDebuggerManagerApi {
|
||||
return RemoteApiProviderService.resolve(remoteApiDescriptor<JavaDebuggerManagerApi>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Serializable
|
||||
data class JavaDebuggerSessionDto(
|
||||
val isAttachedInitial: Boolean,
|
||||
val isAttachedFlow: RpcFlow<Boolean>,
|
||||
)
|
||||
@@ -14,9 +14,7 @@ import com.intellij.xdebugger.impl.frame.XDebugManagerProxy
|
||||
import com.intellij.xdebugger.impl.rpc.XDebuggerManagerApi
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@@ -25,6 +23,8 @@ internal fun <T> createMutableStateFlow(initialValue: T): MutableStateFlow<T> {
|
||||
return MutableStateFlow(initialValue)
|
||||
}
|
||||
|
||||
internal fun <T, R> mapFlow(f: Flow<T>, mapper: (T) -> R): Flow<R> = f.map(mapper)
|
||||
|
||||
// Used only for Java code, since MutableSharedFlow function cannot be called there.
|
||||
internal fun <T> createMutableSharedFlow(replay: Int, extraBufferCapacity: Int): MutableSharedFlow<T> {
|
||||
return MutableSharedFlow(replay, extraBufferCapacity)
|
||||
|
||||
Reference in New Issue
Block a user