mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[debugger hotswap] Move HotSwapDebugSessionManager to platform
GitOrigin-RevId: 736c5f99fbcbc7c5bfcec59157f69db1e5fc184d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9f1bb76ee7
commit
49d5049d30
@@ -95,7 +95,7 @@
|
||||
<applicationService serviceImplementation="com.intellij.debugger.settings.ThreadsViewSettings"/>
|
||||
<applicationService serviceImplementation="com.intellij.debugger.settings.TraceSettings"/>
|
||||
<projectService serviceInterface="com.intellij.debugger.ui.HotSwapUI" serviceImplementation="com.intellij.debugger.ui.HotSwapUIImpl"/>
|
||||
<postStartupActivity implementation="com.intellij.debugger.impl.hotswap.HotSwapManagerInitActivity"/>
|
||||
<xdebugger.hotSwapInDebugSessionEnabler implementation="com.intellij.debugger.impl.hotswap.JvmHotSwapInDebugSessionEnabler"/>
|
||||
<projectService serviceInterface="com.intellij.debugger.DebuggerManager"
|
||||
serviceImplementation="com.intellij.debugger.impl.DebuggerManagerImpl"/>
|
||||
<debugger.additionalContextProvider implementation="com.intellij.debugger.engine.evaluation.MarkedObjectAdditionalContextProvider"/>
|
||||
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
// 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.impl.hotswap
|
||||
|
||||
import com.intellij.debugger.engine.JavaDebugProcess
|
||||
import com.intellij.debugger.impl.DebuggerSession
|
||||
import com.intellij.debugger.ui.HotSwapStatusListener
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.util.asDisposable
|
||||
import com.intellij.xdebugger.XDebugProcess
|
||||
import com.intellij.xdebugger.XDebugSession
|
||||
import com.intellij.xdebugger.XDebuggerManager
|
||||
import com.intellij.xdebugger.XDebuggerManagerListener
|
||||
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
||||
import com.intellij.xdebugger.impl.hotswap.HotSwapResultListener
|
||||
import com.intellij.xdebugger.impl.hotswap.HotSwapSession
|
||||
import com.intellij.xdebugger.impl.hotswap.HotSwapSessionManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
private data class JavaHotSwapSessionEntry(val hotSwapSession: HotSwapSession<*>, val disposable: Disposable)
|
||||
|
||||
private class HotSwapManagerInitActivity : ProjectActivity {
|
||||
override suspend fun execute(project: Project) {
|
||||
project.serviceAsync<HotSwapDebugSessionManager>()
|
||||
}
|
||||
}
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class HotSwapDebugSessionManager(project: Project, cs: CoroutineScope) : XDebuggerManagerListener {
|
||||
private val sessions = ConcurrentHashMap<DebuggerSession, JavaHotSwapSessionEntry>()
|
||||
|
||||
init {
|
||||
project.getMessageBus().connect(cs).subscribe(XDebuggerManager.TOPIC, this)
|
||||
}
|
||||
|
||||
fun createSessionListenerOrNull(session: DebuggerSession): HotSwapStatusListener? {
|
||||
val hotSwapSession = findHotSwapSession(session) ?: return null
|
||||
return HotSwapStatusListenerAdapter(session, hotSwapSession.startHotSwapListening())
|
||||
}
|
||||
|
||||
internal fun findHotSwapSession(session: DebuggerSession): HotSwapSession<*>? = sessions[session]?.hotSwapSession
|
||||
|
||||
override fun processStarted(debugProcess: XDebugProcess) {
|
||||
val session = (debugProcess as? JavaDebugProcess)?.debuggerSession ?: return
|
||||
if (!Registry.`is`("debugger.hotswap.floating.toolbar")) return
|
||||
val disposable = (debugProcess.session as XDebugSessionImpl).coroutineScope.asDisposable()
|
||||
val hotSwapSession = HotSwapSessionManager.getInstance(session.project).createSession(JvmHotSwapProvider(session), disposable)
|
||||
sessions[session] = JavaHotSwapSessionEntry(hotSwapSession, disposable)
|
||||
}
|
||||
|
||||
override fun processStopped(debugProcess: XDebugProcess) {
|
||||
val javaDebugSession = (debugProcess as? JavaDebugProcess)?.debuggerSession ?: return
|
||||
val sessionEntry = sessions.remove(javaDebugSession) ?: return
|
||||
Disposer.dispose(sessionEntry.disposable)
|
||||
}
|
||||
|
||||
override fun currentSessionChanged(previousSession: XDebugSession?, currentSession: XDebugSession?) {
|
||||
if (currentSession == null) return
|
||||
val (_, entry) = sessions.entries.firstOrNull { (session, _) -> session.xDebugSession === currentSession } ?: return
|
||||
HotSwapSessionManager.getInstance(currentSession.project).onSessionSelected(entry.hotSwapSession)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): HotSwapDebugSessionManager = project.service()
|
||||
}
|
||||
}
|
||||
|
||||
private class HotSwapStatusListenerAdapter(private val originalSession: DebuggerSession, private val listener: HotSwapResultListener) : HotSwapStatusListener {
|
||||
override fun onSuccess(sessions: MutableList<DebuggerSession>?) {
|
||||
sessions?.createListeners(originalSession)?.forEach { it.onSuccessfulReload() }
|
||||
listener.onSuccessfulReload()
|
||||
}
|
||||
|
||||
override fun onFailure(sessions: MutableList<DebuggerSession>?) {
|
||||
sessions?.createListeners(originalSession)?.forEach { it.onFailure() }
|
||||
listener.onFailure()
|
||||
}
|
||||
|
||||
override fun onCancel(sessions: MutableList<DebuggerSession>?) {
|
||||
sessions?.createListeners(originalSession)?.forEach { it.onCanceled() }
|
||||
listener.onCanceled()
|
||||
}
|
||||
|
||||
override fun onNothingToReload(sessions: MutableList<DebuggerSession>?) {
|
||||
sessions?.createListeners(originalSession)?.forEach { it.onFinish() }
|
||||
listener.onFinish()
|
||||
}
|
||||
|
||||
private fun List<DebuggerSession>.createListeners(except: DebuggerSession) = filter { it !== except }
|
||||
.mapNotNull { HotSwapDebugSessionManager.getInstance(except.project).findHotSwapSession(it) }
|
||||
.map { it.startHotSwapListening() }
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// 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.impl.hotswap
|
||||
|
||||
import com.intellij.debugger.engine.JavaDebugProcess
|
||||
import com.intellij.debugger.impl.DebuggerSession
|
||||
import com.intellij.debugger.ui.HotSwapStatusListener
|
||||
import com.intellij.xdebugger.XDebugProcess
|
||||
import com.intellij.xdebugger.impl.hotswap.HotSwapDebugSessionManager
|
||||
import com.intellij.xdebugger.impl.hotswap.HotSwapInDebugSessionEnabler
|
||||
import com.intellij.xdebugger.impl.hotswap.HotSwapProvider
|
||||
import com.intellij.xdebugger.impl.hotswap.HotSwapResultListener
|
||||
|
||||
private class JvmHotSwapInDebugSessionEnabler : HotSwapInDebugSessionEnabler {
|
||||
override fun createProvider(process: XDebugProcess): HotSwapProvider<*>? {
|
||||
val session = (process as? JavaDebugProcess)?.debuggerSession ?: return null
|
||||
return JvmHotSwapProvider(session)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createHotSwapSessionListenerOrNull(session: DebuggerSession): HotSwapStatusListener? {
|
||||
val process = session.xDebugSession?.debugProcess ?: return null
|
||||
val hotSwapSession = HotSwapDebugSessionManager.getInstance(session.project).findHotSwapSession(process) ?: return null
|
||||
return HotSwapStatusListenerAdapter(session, hotSwapSession.startHotSwapListening())
|
||||
}
|
||||
|
||||
private class HotSwapStatusListenerAdapter(private val originalSession: DebuggerSession, private val listener: HotSwapResultListener) : HotSwapStatusListener {
|
||||
override fun onSuccess(sessions: MutableList<DebuggerSession>?) {
|
||||
sessions?.createListeners(originalSession)?.forEach { it.onSuccessfulReload() }
|
||||
listener.onSuccessfulReload()
|
||||
}
|
||||
|
||||
override fun onFailure(sessions: MutableList<DebuggerSession>?) {
|
||||
sessions?.createListeners(originalSession)?.forEach { it.onFailure() }
|
||||
listener.onFailure()
|
||||
}
|
||||
|
||||
override fun onCancel(sessions: MutableList<DebuggerSession>?) {
|
||||
sessions?.createListeners(originalSession)?.forEach { it.onCanceled() }
|
||||
listener.onCanceled()
|
||||
}
|
||||
|
||||
override fun onNothingToReload(sessions: MutableList<DebuggerSession>?) {
|
||||
sessions?.createListeners(originalSession)?.forEach { it.onFinish() }
|
||||
listener.onFinish()
|
||||
}
|
||||
|
||||
private fun List<DebuggerSession>.createListeners(except: DebuggerSession) = filter { it !== except }
|
||||
.mapNotNull { HotSwapDebugSessionManager.getInstance(except.project).findHotSwapSession(it.xDebugSession!!.debugProcess) }
|
||||
.map { it.startHotSwapListening() }
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import com.intellij.debugger.impl.DebuggerManagerListener;
|
||||
import com.intellij.debugger.impl.DebuggerSession;
|
||||
import com.intellij.debugger.impl.HotSwapFile;
|
||||
import com.intellij.debugger.impl.HotSwapManager;
|
||||
import com.intellij.debugger.impl.hotswap.HotSwapDebugSessionManager;
|
||||
import com.intellij.debugger.impl.hotswap.JvmHotSwapInDebugSessionEnablerKt;
|
||||
import com.intellij.debugger.settings.DebuggerSettings;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
@@ -295,7 +295,7 @@ public final class HotSwapUIImpl extends HotSwapUI {
|
||||
@Nullable HotSwapStatusListener callback) {
|
||||
dontAskHotswapAfterThisCompilation();
|
||||
Project project = session.getProject();
|
||||
callback = mergeCallbacksIfNeeded(callback, HotSwapDebugSessionManager.getInstance(project).createSessionListenerOrNull(session));
|
||||
callback = mergeCallbacksIfNeeded(callback, JvmHotSwapInDebugSessionEnablerKt.createHotSwapSessionListenerOrNull(session));
|
||||
if (compileBeforeHotswap) {
|
||||
ProjectTaskManagerImpl.putBuildOriginator(project, this.getClass());
|
||||
ProjectTaskManager projectTaskManager = ProjectTaskManager.getInstance(project);
|
||||
@@ -323,7 +323,7 @@ public final class HotSwapUIImpl extends HotSwapUI {
|
||||
Project project = session.getProject();
|
||||
ProjectTaskManagerImpl.putBuildOriginator(project, this.getClass());
|
||||
|
||||
HotSwapStatusListener callback = HotSwapDebugSessionManager.getInstance(project).createSessionListenerOrNull(session);
|
||||
HotSwapStatusListener callback = JvmHotSwapInDebugSessionEnablerKt.createHotSwapSessionListenerOrNull(session);
|
||||
if (callback == null) {
|
||||
ProjectTaskManager.getInstance(project).compile(files);
|
||||
}
|
||||
@@ -399,9 +399,8 @@ public final class HotSwapUIImpl extends HotSwapUI {
|
||||
boolean alwaysDoHotSwapOnRebuild =
|
||||
instance.myAskBeforeHotswap && DebuggerSettings.RUN_HOTSWAP_ALWAYS.equals(DebuggerSettings.getInstance().RUN_HOTSWAP_AFTER_COMPILE);
|
||||
List<DebuggerSession> sessions = getHotSwappableDebugSessions(myProject);
|
||||
HotSwapDebugSessionManager manager = HotSwapDebugSessionManager.getInstance(myProject);
|
||||
for (DebuggerSession session : sessions) {
|
||||
HotSwapStatusListener listener = manager.createSessionListenerOrNull(session);
|
||||
HotSwapStatusListener listener = JvmHotSwapInDebugSessionEnablerKt.createHotSwapSessionListenerOrNull(session);
|
||||
if (listener == null) continue;
|
||||
context.putUserData(HOT_SWAP_CALLBACK_KEY, listener);
|
||||
if (alwaysDoHotSwapOnRebuild) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<extensionPoint name="xdebugger.controlExceptionBreakpointSupport" interface="com.intellij.xdebugger.codeinsight.ControlExceptionBreakpointSupport" dynamic="true"/>
|
||||
|
||||
<extensionPoint name="xdebugger.hotSwapUiExtension" interface="com.intellij.xdebugger.impl.hotswap.HotSwapUiExtension" dynamic="true"/>
|
||||
<extensionPoint name="xdebugger.hotSwapInDebugSessionEnabler" interface="com.intellij.xdebugger.impl.hotswap.HotSwapInDebugSessionEnabler" dynamic="true"/>
|
||||
|
||||
<extensionPoint name="xdebugger.customEvaluateHandler"
|
||||
interface="com.intellij.xdebugger.impl.actions.handlers.XDebuggerCustomEvaluateHandler" dynamic="true"/>
|
||||
@@ -121,6 +122,7 @@
|
||||
<projectService serviceImplementation="com.intellij.xdebugger.memory.component.InstancesTracker" />
|
||||
|
||||
<projectService serviceImplementation="com.intellij.xdebugger.impl.frame.XDebuggerExecutionStackDescriptionService" />
|
||||
<postStartupActivity implementation="com.intellij.xdebugger.impl.hotswap.HotSwapManagerInitActivity"/>
|
||||
|
||||
<titleInfoProvider implementation="com.intellij.xdebugger.impl.ui.DebuggerTitleInfoProvider"/>
|
||||
<notificationGroup id="Debugger messages" displayType="TOOL_WINDOW" toolWindowId="Debug" isLogByDefault="false" bundle="messages.XDebuggerBundle" key="notification.group.debugger.messages"/>
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// 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.xdebugger.impl.hotswap
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.util.asDisposable
|
||||
import com.intellij.xdebugger.XDebugProcess
|
||||
import com.intellij.xdebugger.XDebugSession
|
||||
import com.intellij.xdebugger.XDebuggerManager
|
||||
import com.intellij.xdebugger.XDebuggerManagerListener
|
||||
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Service(Service.Level.PROJECT)
|
||||
class HotSwapDebugSessionManager(project: Project, cs: CoroutineScope) : XDebuggerManagerListener {
|
||||
private val sessions = ConcurrentHashMap<XDebugProcess, HotSwapSessionEntry>()
|
||||
|
||||
init {
|
||||
project.getMessageBus().connect(cs).subscribe(XDebuggerManager.TOPIC, this)
|
||||
}
|
||||
|
||||
fun findHotSwapSession(process: XDebugProcess): HotSwapSession<*>? = sessions[process]?.hotSwapSession
|
||||
|
||||
override fun processStarted(debugProcess: XDebugProcess) {
|
||||
if (!Registry.`is`("debugger.hotswap.floating.toolbar")) return
|
||||
val provider = HotSwapInDebugSessionEnabler.createProviderForProcess(debugProcess) ?: return
|
||||
val xDebugSession = debugProcess.session as XDebugSessionImpl
|
||||
val disposable = xDebugSession.coroutineScope.asDisposable()
|
||||
val hotSwapSession = HotSwapSessionManager.getInstance(xDebugSession.project).createSession(provider, disposable)
|
||||
sessions[debugProcess] = HotSwapSessionEntry(hotSwapSession, disposable)
|
||||
}
|
||||
|
||||
override fun processStopped(debugProcess: XDebugProcess) {
|
||||
val sessionEntry = sessions.remove(debugProcess) ?: return
|
||||
Disposer.dispose(sessionEntry.disposable)
|
||||
}
|
||||
|
||||
override fun currentSessionChanged(previousSession: XDebugSession?, currentSession: XDebugSession?) {
|
||||
if (currentSession == null) return
|
||||
val (_, entry) = sessions.entries.firstOrNull { (process, _) -> process.session === currentSession } ?: return
|
||||
HotSwapSessionManager.getInstance(currentSession.project).onSessionSelected(entry.hotSwapSession)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): HotSwapDebugSessionManager = project.service()
|
||||
}
|
||||
}
|
||||
|
||||
private data class HotSwapSessionEntry(val hotSwapSession: HotSwapSession<*>, val disposable: Disposable)
|
||||
|
||||
private class HotSwapManagerInitActivity : ProjectActivity {
|
||||
override suspend fun execute(project: Project) {
|
||||
project.serviceAsync<HotSwapDebugSessionManager>()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
package com.intellij.xdebugger.impl.hotswap
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.xdebugger.XDebugProcess
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@@ -9,6 +11,8 @@ import org.jetbrains.annotations.ApiStatus
|
||||
* Provides platform-specific implementation for the hot swap process.
|
||||
*
|
||||
* The provider should be passed to [HotSwapSessionManager.createSession] on the start of a session where hot swapping is possible.
|
||||
*
|
||||
* @see [HotSwapInDebugSessionEnabler]
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
interface HotSwapProvider<T> {
|
||||
@@ -34,6 +38,22 @@ interface HotSwapProvider<T> {
|
||||
fun performHotSwap(session: HotSwapSession<T>)
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this to enable hotswap in debug sessions.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
interface HotSwapInDebugSessionEnabler {
|
||||
fun createProvider(process: XDebugProcess): HotSwapProvider<*>?
|
||||
|
||||
companion object {
|
||||
private val EP_NAME = ExtensionPointName<HotSwapInDebugSessionEnabler>("com.intellij.xdebugger.hotSwapInDebugSessionEnabler")
|
||||
|
||||
internal fun createProviderForProcess(process: XDebugProcess): HotSwapProvider<*>? {
|
||||
return EP_NAME.computeSafeIfAny { it.createProvider(process) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to report the hot swap status.
|
||||
* @see HotSwapSession.startHotSwapListening
|
||||
|
||||
Reference in New Issue
Block a user