diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/hotswap/HotSwapDebugSessionListener.kt b/java/debugger/impl/src/com/intellij/debugger/impl/hotswap/HotSwapDebugSessionListener.kt index bf8acd37e322..0e75a14e5a69 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/hotswap/HotSwapDebugSessionListener.kt +++ b/java/debugger/impl/src/com/intellij/debugger/impl/hotswap/HotSwapDebugSessionListener.kt @@ -3,34 +3,68 @@ package com.intellij.debugger.impl.hotswap import com.intellij.debugger.impl.DebuggerManagerListener 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.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.registry.Registry +import com.intellij.xdebugger.impl.hotswap.HotSwapResultListener +import com.intellij.xdebugger.impl.hotswap.HotSwapSession import com.intellij.xdebugger.impl.hotswap.HotSwapSessionManager import java.util.concurrent.ConcurrentHashMap @Service(Service.Level.PROJECT) -internal class HotSwapDebugSessionListener : DebuggerManagerListener { - private val sessions = ConcurrentHashMap() +internal class HotSwapDebugSessionManager { + private val disposables = ConcurrentHashMap() + private val sessions = ConcurrentHashMap>() - override fun sessionCreated(session: DebuggerSession?) { - if (session == null) return + fun createSessionListenerOrNull(session: DebuggerSession): HotSwapStatusListener? = sessions[session] + ?.let { HotSwapStatusListenerAdapter(it.startHotSwapListening()) } + + companion object { + @JvmStatic + fun getInstance(project: Project): HotSwapDebugSessionManager = project.service() + } + + internal fun createSession(session: DebuggerSession) { if (!Registry.`is`("debugger.hotswap.floating.toolbar")) return val disposable = Disposer.newDisposable() - sessions[session] = disposable - HotSwapSessionManager.getInstance(session.project).createSession(JvmHotSwapProvider(session), disposable) + disposables[session] = disposable + val hotSwapSession = HotSwapSessionManager.getInstance(session.project).createSession(JvmHotSwapProvider(session), disposable) + sessions[session] = hotSwapSession + } + + internal fun removeSession(session: DebuggerSession) { + disposables.remove(session)?.let { Disposer.dispose(it) } + sessions.remove(session) + } +} + + +internal class HotSwapDebugSessionListener : DebuggerManagerListener { + override fun sessionCreated(session: DebuggerSession?) { + if (session == null) return + HotSwapDebugSessionManager.getInstance(session.project).createSession(session) } override fun sessionRemoved(session: DebuggerSession?) { if (session == null) return - sessions.remove(session)?.let { Disposer.dispose(it) } + HotSwapDebugSessionManager.getInstance(session.project).removeSession(session) } +} - companion object { - @JvmStatic - fun getInstance(project: Project): HotSwapDebugSessionListener = project.service() +private class HotSwapStatusListenerAdapter(private val listener: HotSwapResultListener) : HotSwapStatusListener { + override fun onSuccess(sessions: MutableList?) { + listener.onCompleted() + } + + override fun onFailure(sessions: MutableList?) { + listener.onFailed() + } + + override fun onCancel(sessions: MutableList?) { + listener.onCanceled() } } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/hotswap/JvmHotSwapProvider.kt b/java/debugger/impl/src/com/intellij/debugger/impl/hotswap/JvmHotSwapProvider.kt index 4d25aea5639e..1b3a3507eda6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/hotswap/JvmHotSwapProvider.kt +++ b/java/debugger/impl/src/com/intellij/debugger/impl/hotswap/JvmHotSwapProvider.kt @@ -26,23 +26,7 @@ internal class JvmHotSwapProvider(private val debuggerSession: DebuggerSession) override fun performHotSwap(context: DataContext, session: HotSwapSession) { val project = context.getData(CommonDataKeys.PROJECT) ?: return - val listener = session.createStatusListener() - HotSwapUI.getInstance(project).compileAndReload(debuggerSession, HotSwapStatusListenerAdapter(listener), - *session.getChanges().toTypedArray()) - } -} - -private class HotSwapStatusListenerAdapter(private val listener: HotSwapResultListener) : HotSwapStatusListener { - override fun onSuccess(sessions: MutableList?) { - listener.onCompleted() - } - - override fun onFailure(sessions: MutableList?) { - listener.onFailed() - } - - override fun onCancel(sessions: MutableList?) { - listener.onCanceled() + HotSwapUI.getInstance(project).compileAndReload(debuggerSession, *session.getChanges().toTypedArray()) } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUI.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUI.java index ccaafbb9582c..b4bfca2e9395 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUI.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUI.java @@ -19,7 +19,6 @@ public abstract class HotSwapUI { @Nullable HotSwapStatusListener callback); public abstract void compileAndReload(@NotNull DebuggerSession session, VirtualFile @NotNull ... files); - public abstract void compileAndReload(@NotNull DebuggerSession session, @Nullable HotSwapStatusListener callback, VirtualFile @NotNull ... files); public abstract void addListener(HotSwapVetoableListener listener); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java index d9a30f5f590b..7d7887064e59 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java @@ -7,6 +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.settings.DebuggerSettings; import com.intellij.notification.NotificationType; import com.intellij.openapi.application.ApplicationManager; @@ -29,7 +30,6 @@ import com.intellij.ui.UIBundle; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FileCollectionFactory; import com.intellij.util.messages.MessageBusConnection; -import com.intellij.util.ui.MessageCategory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.util.JpsPathUtil; @@ -276,8 +276,9 @@ public final class HotSwapUIImpl extends HotSwapUI { boolean compileBeforeHotswap, @Nullable HotSwapStatusListener callback) { dontAskHotswapAfterThisCompilation(); + Project project = session.getProject(); + callback = mergeCallbacksIfNeeded(callback, HotSwapDebugSessionManager.getInstance(project).createSessionListenerOrNull(session)); if (compileBeforeHotswap) { - Project project = session.getProject(); ProjectTaskManagerImpl.putBuildOriginator(project, this.getClass()); ProjectTaskManager projectTaskManager = ProjectTaskManager.getInstance(project); if (callback == null) { @@ -300,15 +301,11 @@ public final class HotSwapUIImpl extends HotSwapUI { @Override public void compileAndReload(@NotNull DebuggerSession session, VirtualFile @NotNull ... files) { - compileAndReload(session, null, files); - } - - @Override - public void compileAndReload(@NotNull DebuggerSession session, @Nullable HotSwapStatusListener callback, VirtualFile @NotNull ... files) { dontAskHotswapAfterThisCompilation(); Project project = session.getProject(); ProjectTaskManagerImpl.putBuildOriginator(project, this.getClass()); + HotSwapStatusListener callback = HotSwapDebugSessionManager.getInstance(project).createSessionListenerOrNull(session); if (callback == null) { ProjectTaskManager.getInstance(project).compile(files); } else { @@ -323,6 +320,30 @@ public final class HotSwapUIImpl extends HotSwapUI { return new ProjectTaskContext(callback).withUserData(HOT_SWAP_CALLBACK_KEY, callback); } + private static @Nullable HotSwapStatusListener mergeCallbacksIfNeeded(@Nullable HotSwapStatusListener callback1, @Nullable HotSwapStatusListener callback2) { + if (callback1 == null) return callback2; + if (callback2 == null) return callback1; + return new HotSwapStatusListener() { + @Override + public void onSuccess(@NotNull List sessions) { + callback1.onSuccess(sessions); + callback2.onSuccess(sessions); + } + + @Override + public void onCancel(List sessions) { + callback1.onCancel(sessions); + callback2.onCancel(sessions); + } + + @Override + public void onFailure(List sessions) { + callback1.onFailure(sessions); + callback2.onFailure(sessions); + } + }; + } + public void dontAskHotswapAfterThisCompilation() { myAskBeforeHotswap = false; } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/hotswap/HotSwapFloatingToolbarProvider.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/hotswap/HotSwapFloatingToolbarProvider.kt index 0461787d5ee1..090b0dd34cbc 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/hotswap/HotSwapFloatingToolbarProvider.kt +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/hotswap/HotSwapFloatingToolbarProvider.kt @@ -29,7 +29,6 @@ private class HotSwapWithRebuildAction : AnAction(), CustomComponentAction { override fun actionPerformed(e: AnActionEvent) { val session = session ?: return - session.startHotSwap() callWithTemplate(e.dataContext, session) } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/hotswap/HotSwapSessionManager.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/hotswap/HotSwapSessionManager.kt index 214787bae213..1ffaaace3811 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/hotswap/HotSwapSessionManager.kt +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/hotswap/HotSwapSessionManager.kt @@ -19,11 +19,12 @@ class HotSwapSessionManager(private val project: Project, private val parentScop private val listeners = DisposableWrapperList() private val sessions = DisposableWrapperList>() - fun createSession(provider: HotSwapProvider, disposable: Disposable) { + fun createSession(provider: HotSwapProvider, disposable: Disposable): HotSwapSession { val hotSwapSession = HotSwapSession(project, provider, parentScope) Disposer.register(disposable, hotSwapSession) sessions.add(hotSwapSession, disposable) hotSwapSession.init() + return hotSwapSession } internal fun addListener(listener: HotSwapChangesListener, disposable: Disposable) { @@ -84,24 +85,23 @@ class HotSwapSession(val project: Project, internal val provider: HotSwapProv } } - internal fun startHotSwap() { - currentStatus = HotSwapVisibleStatus.IN_PROGRESS - } - fun getChanges() = changesCollector.getChanges() - fun createStatusListener() = object : HotSwapResultListener { - override fun onCompleted() { - completeHotSwap() - HotSwapStatusNotificationManager.getInstance(project).showSuccessNotification(coroutineScope) - } + fun startHotSwapListening(): HotSwapResultListener { + currentStatus = HotSwapVisibleStatus.IN_PROGRESS + return object : HotSwapResultListener { + override fun onCompleted() { + completeHotSwap() + HotSwapStatusNotificationManager.getInstance(project).showSuccessNotification(coroutineScope) + } - override fun onFailed() { - completeHotSwap() - } + override fun onFailed() { + completeHotSwap() + } - override fun onCanceled() { - currentStatus = HotSwapVisibleStatus.CHANGES_READY + override fun onCanceled() { + currentStatus = HotSwapVisibleStatus.CHANGES_READY + } } } }