From 30fcd456eed2c02108c41ce2f97f6ce1e84efbae Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Thu, 20 Nov 2025 17:25:54 +0200 Subject: [PATCH] [terminal] IJPL-215159 Try to fix race of terminal editors disposing on project closing Editors can be disposed before the terminal tool window tab is hidden. Because terminal coroutine scope is bound to the project and is canceled concurrently with other things, like tool window tabs disposing. Let's try to hide the tool window tab right when terminal coroutine scope is canceled. GitOrigin-RevId: 47123bb56714c0717e4e528a8e227bbb76ebbff9 --- .../impl/TerminalToolWindowTabsManagerImpl.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/toolwindow/impl/TerminalToolWindowTabsManagerImpl.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/toolwindow/impl/TerminalToolWindowTabsManagerImpl.kt index b9f2f5776d1e..52999d712890 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/toolwindow/impl/TerminalToolWindowTabsManagerImpl.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/toolwindow/impl/TerminalToolWindowTabsManagerImpl.kt @@ -46,6 +46,7 @@ import org.jetbrains.plugins.terminal.fus.ReworkedTerminalUsageCollector import org.jetbrains.plugins.terminal.fus.TerminalOpeningWay import org.jetbrains.plugins.terminal.fus.TerminalStartupFusInfo import org.jetbrains.plugins.terminal.util.fireListenersAndLogAllExceptions +import java.lang.ref.WeakReference import kotlin.time.Duration.Companion.seconds internal class TerminalToolWindowTabsManagerImpl( @@ -129,6 +130,7 @@ internal class TerminalToolWindowTabsManagerImpl( return tab } + @OptIn(AwaitCancellationAndInvoke::class) private fun doCreateTab(terminal: TerminalView): TerminalToolWindowTab { val panel = TerminalToolWindowPanel() panel.setContent(terminal.component) @@ -163,6 +165,21 @@ internal class TerminalToolWindowTabsManagerImpl( } } + // In case of project closing there can be a race between terminal coroutine scope cancellation + // and removing the content from the tool window. + // If the terminal coroutine scope is canceled before the content is removed, the editor may be shown green for a moment. + // The ideal solution is to cancel the terminal scope only after the content is removed. + // However, the terminal component has a broader lifecycle than the tool window tab (to be able to detach it), + // so it can't be tied to the content removal directly. + // Let's try to hide the tool window tab right on terminal scope cancellation, + // but do not store strong reference to the content to avoid leaks. + val tabReference = WeakReference(content) + terminal.coroutineScope.awaitCancellationAndInvoke(Dispatchers.EDT) { + val content = tabReference.get() ?: return@awaitCancellationAndInvoke + val manager = content.manager ?: return@awaitCancellationAndInvoke + manager.removeContent(content, true) + } + return TerminalToolWindowTabImpl(terminal, content) }