mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[terminal] IJPL-212254 Refactor: extract terminal view creation out of TerminalToolWindowTabsManager
(cherry picked from commit 67a1e12d50598049783f9c999646477e2320be5a) IJ-CR-214214 GitOrigin-RevId: 071aa77754023dcc0b411a19bce3b28bc61c06af
This commit is contained in:
committed by
intellij-monorepo-bot
parent
00b15eb7b0
commit
74539bdc48
+2
-121
@@ -6,7 +6,6 @@ import com.intellij.openapi.actionSystem.ActionGroup
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.UI
|
||||
import com.intellij.openapi.application.asContextElement
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
@@ -21,7 +20,6 @@ import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.terminal.frontend.action.TerminalAgentsAvailabilityService
|
||||
import com.intellij.terminal.frontend.action.TerminalRenameTabAction
|
||||
import com.intellij.terminal.frontend.fus.TerminalFocusFusService
|
||||
import com.intellij.terminal.frontend.session.TerminalSessionsManager
|
||||
import com.intellij.terminal.frontend.session.TerminalTabsManager
|
||||
import com.intellij.terminal.frontend.toolwindow.TerminalTabsManagerListener
|
||||
import com.intellij.terminal.frontend.toolwindow.TerminalToolWindowTab
|
||||
@@ -31,36 +29,26 @@ import com.intellij.terminal.frontend.toolwindow.findTabByContent
|
||||
import com.intellij.terminal.frontend.view.TerminalView
|
||||
import com.intellij.terminal.frontend.view.TerminalViewSessionState
|
||||
import com.intellij.terminal.frontend.view.impl.TerminalViewBuilderOptions
|
||||
import com.intellij.terminal.frontend.view.impl.TerminalViewImpl
|
||||
import com.intellij.terminal.frontend.view.portForwarding.installPortForwarding
|
||||
import com.intellij.terminal.frontend.view.impl.createTerminalView
|
||||
import com.intellij.ui.content.ContentFactory
|
||||
import com.intellij.ui.content.ContentManager
|
||||
import com.intellij.util.AwaitCancellationAndInvoke
|
||||
import com.intellij.util.awaitCancellationAndInvoke
|
||||
import com.intellij.util.ui.initOnShow
|
||||
import com.jediterm.core.util.TermSize
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.future.await
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import org.jetbrains.plugins.terminal.JBTerminalSystemSettingsProvider
|
||||
import org.jetbrains.plugins.terminal.ShellStartupOptions
|
||||
import org.jetbrains.plugins.terminal.TerminalOptionsProvider
|
||||
import org.jetbrains.plugins.terminal.TerminalTabCloseListener
|
||||
import org.jetbrains.plugins.terminal.TerminalToolWindowFactory
|
||||
import org.jetbrains.plugins.terminal.TerminalToolWindowInitializer
|
||||
import org.jetbrains.plugins.terminal.TerminalToolWindowPanel
|
||||
import org.jetbrains.plugins.terminal.block.reworked.session.TerminalSessionTab
|
||||
import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId
|
||||
import org.jetbrains.plugins.terminal.block.ui.TerminalUiUtils
|
||||
import org.jetbrains.plugins.terminal.fus.ReworkedTerminalUsageCollector
|
||||
import org.jetbrains.plugins.terminal.fus.TerminalStartupFusInfo
|
||||
import org.jetbrains.plugins.terminal.fus.TerminalTabOpeningWay
|
||||
@@ -269,7 +257,7 @@ internal class TerminalToolWindowTabsManagerImpl(
|
||||
sourceNavigationProjectPath = builder.sourceNavigationProjectPath,
|
||||
startupFusInfo = builder.startupFusInfo,
|
||||
)
|
||||
val terminal = doCreateTerminalViewAndStartSession(
|
||||
val terminal = createTerminalView(
|
||||
project = project,
|
||||
options = viewOptions,
|
||||
existingBackendTabId = builder.backendTabId,
|
||||
@@ -287,113 +275,6 @@ internal class TerminalToolWindowTabsManagerImpl(
|
||||
return terminal
|
||||
}
|
||||
|
||||
private fun doCreateTerminalViewAndStartSession(
|
||||
project: Project,
|
||||
options: TerminalViewBuilderOptions,
|
||||
existingBackendTabId: Int?,
|
||||
coroutineScope: CoroutineScope,
|
||||
): TerminalView {
|
||||
val terminalView = TerminalViewImpl(
|
||||
project = project,
|
||||
settings = JBTerminalSystemSettingsProvider(),
|
||||
startupFusInfo = options.startupFusInfo,
|
||||
coroutineScope = coroutineScope,
|
||||
sourceNavigationProjectPath = options.sourceNavigationProjectPath,
|
||||
)
|
||||
createBackendTabAndStartSession(project, terminalView, options, existingBackendTabId)
|
||||
return terminalView
|
||||
}
|
||||
|
||||
@OptIn(AwaitCancellationAndInvoke::class)
|
||||
private fun createBackendTabAndStartSession(
|
||||
project: Project,
|
||||
terminal: TerminalViewImpl,
|
||||
options: TerminalViewBuilderOptions,
|
||||
existingBackendTabId: Int?,
|
||||
) = terminal.coroutineScope.launch {
|
||||
val backendTabId = existingBackendTabId ?: TerminalTabsManager.getInstance(project).createNewTerminalTab().id
|
||||
|
||||
terminal.coroutineScope.awaitCancellationAndInvoke(Dispatchers.EDT) {
|
||||
TerminalTabsManager.getInstance(project).closeTerminalTab(backendTabId)
|
||||
}
|
||||
|
||||
// Ideally, the backend tab should be under the tab scope, but now it has the lifecycle of the terminal scope
|
||||
updateBackendTabNameOnTitleChange(
|
||||
terminal,
|
||||
backendTabId,
|
||||
project,
|
||||
scope = terminal.coroutineScope.childScope("Backend tab name updating")
|
||||
)
|
||||
|
||||
scheduleSessionStart(project, terminal, options, backendTabId)
|
||||
}
|
||||
|
||||
private suspend fun scheduleSessionStart(
|
||||
project: Project,
|
||||
terminal: TerminalViewImpl,
|
||||
options: TerminalViewBuilderOptions,
|
||||
backendTabId: Int,
|
||||
) {
|
||||
if (options.deferSessionStartUntilUiShown) {
|
||||
withContext(Dispatchers.UI + ModalityState.any().asContextElement()) {
|
||||
// Non-cancellable because we expect it to be called only once even if the component was hidden immediately.
|
||||
terminal.component.initOnShow("Terminal Session start", context = NonCancellable) {
|
||||
doScheduleSessionStart(project, terminal, options.processOptions, backendTabId, calculateSizeFromComponent = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
doScheduleSessionStart(project, terminal, options.processOptions, backendTabId, calculateSizeFromComponent = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doScheduleSessionStart(
|
||||
project: Project,
|
||||
terminal: TerminalViewImpl,
|
||||
processOptions: TerminalRequestedProcessOptions,
|
||||
backendTabId: Int,
|
||||
calculateSizeFromComponent: Boolean,
|
||||
) = terminal.coroutineScope.launch(CoroutineName("Terminal Session start")) {
|
||||
val options = prepareStartupOptions(terminal, processOptions, calculateSizeFromComponent)
|
||||
val sessionTab = TerminalTabsManager.getInstance(project).startTerminalSessionForTab(backendTabId, options)
|
||||
connectSessionToTerminal(project, terminal, sessionTab.sessionId!!)
|
||||
}
|
||||
|
||||
private suspend fun prepareStartupOptions(
|
||||
terminal: TerminalView,
|
||||
processOptions: TerminalRequestedProcessOptions,
|
||||
calculateSizeFromComponent: Boolean,
|
||||
): ShellStartupOptions {
|
||||
val baseOptions = ShellStartupOptions.Builder()
|
||||
.shellCommand(processOptions.shellCommand)
|
||||
.workingDirectory(processOptions.workingDirectory)
|
||||
.envVariables(processOptions.envVariables)
|
||||
.processType(processOptions.processType)
|
||||
|
||||
return if (calculateSizeFromComponent) {
|
||||
withContext(Dispatchers.UI + ModalityState.any().asContextElement()) {
|
||||
TerminalUiUtils.getComponentSizeInitializedFuture(terminal.component).await()
|
||||
val termSize = terminal.gridSize?.let { TermSize(it.columns, it.rows) }
|
||||
baseOptions.initialTermSize(termSize).build()
|
||||
}
|
||||
}
|
||||
else {
|
||||
baseOptions.initialTermSize(TermSize(80, 20)).build()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun connectSessionToTerminal(
|
||||
project: Project,
|
||||
terminal: TerminalViewImpl,
|
||||
sessionId: TerminalSessionId,
|
||||
) = withContext(Dispatchers.UI + ModalityState.any().asContextElement()) {
|
||||
val session = TerminalSessionsManager.getInstance(project).getSession(sessionId)
|
||||
?: error("Failed to find TerminalSession with ID: $sessionId")
|
||||
terminal.connectToSession(session)
|
||||
|
||||
installPortForwarding(terminal, terminal.coroutineScope.childScope("PortForwarding"))
|
||||
}
|
||||
|
||||
private fun getToolWindow(): ToolWindow {
|
||||
val toolWindow = ToolWindowManager.getInstance(project).getToolWindow(TerminalToolWindowFactory.TOOL_WINDOW_ID)
|
||||
?: error("No terminal tool window found")
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
package com.intellij.terminal.frontend.view.impl
|
||||
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.UI
|
||||
import com.intellij.openapi.application.asContextElement
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.terminal.frontend.session.TerminalSessionsManager
|
||||
import com.intellij.terminal.frontend.session.TerminalTabsManager
|
||||
import com.intellij.terminal.frontend.toolwindow.impl.TerminalRequestedProcessOptions
|
||||
import com.intellij.terminal.frontend.toolwindow.impl.updateBackendTabNameOnTitleChange
|
||||
import com.intellij.terminal.frontend.view.TerminalView
|
||||
import com.intellij.terminal.frontend.view.portForwarding.installPortForwarding
|
||||
import com.intellij.util.AwaitCancellationAndInvoke
|
||||
import com.intellij.util.awaitCancellationAndInvoke
|
||||
import com.intellij.util.ui.initOnShow
|
||||
import com.jediterm.core.util.TermSize
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.future.await
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.plugins.terminal.JBTerminalSystemSettingsProvider
|
||||
import org.jetbrains.plugins.terminal.ShellStartupOptions
|
||||
import org.jetbrains.plugins.terminal.block.reworked.session.rpc.TerminalSessionId
|
||||
import org.jetbrains.plugins.terminal.block.ui.TerminalUiUtils
|
||||
|
||||
/**
|
||||
* Creates the [TerminalView], starts the terminal process according to [options],
|
||||
* and attaches the terminal session to the [TerminalView].
|
||||
*
|
||||
* Lifecycle of both [TerminalView] and started terminal process are bound to the [coroutineScope].
|
||||
*/
|
||||
internal fun createTerminalView(
|
||||
project: Project,
|
||||
options: TerminalViewBuilderOptions,
|
||||
existingBackendTabId: Int?,
|
||||
coroutineScope: CoroutineScope,
|
||||
): TerminalView {
|
||||
val terminalView = TerminalViewImpl(
|
||||
project = project,
|
||||
settings = JBTerminalSystemSettingsProvider(),
|
||||
startupFusInfo = options.startupFusInfo,
|
||||
coroutineScope = coroutineScope,
|
||||
sourceNavigationProjectPath = options.sourceNavigationProjectPath,
|
||||
)
|
||||
createBackendTabAndStartSession(project, terminalView, options, existingBackendTabId)
|
||||
return terminalView
|
||||
}
|
||||
|
||||
@OptIn(AwaitCancellationAndInvoke::class)
|
||||
private fun createBackendTabAndStartSession(
|
||||
project: Project,
|
||||
terminal: TerminalViewImpl,
|
||||
options: TerminalViewBuilderOptions,
|
||||
existingBackendTabId: Int?,
|
||||
) = terminal.coroutineScope.launch {
|
||||
val backendTabId = existingBackendTabId ?: TerminalTabsManager.getInstance(project).createNewTerminalTab().id
|
||||
|
||||
terminal.coroutineScope.awaitCancellationAndInvoke(Dispatchers.EDT) {
|
||||
TerminalTabsManager.getInstance(project).closeTerminalTab(backendTabId)
|
||||
}
|
||||
|
||||
// Ideally, the backend tab should be under the tab scope, but now it has the lifecycle of the terminal scope
|
||||
updateBackendTabNameOnTitleChange(
|
||||
terminal,
|
||||
backendTabId,
|
||||
project,
|
||||
scope = terminal.coroutineScope.childScope("Backend tab name updating")
|
||||
)
|
||||
|
||||
scheduleSessionStart(project, terminal, options, backendTabId)
|
||||
}
|
||||
|
||||
private suspend fun scheduleSessionStart(
|
||||
project: Project,
|
||||
terminal: TerminalViewImpl,
|
||||
options: TerminalViewBuilderOptions,
|
||||
backendTabId: Int,
|
||||
) {
|
||||
if (options.deferSessionStartUntilUiShown) {
|
||||
withContext(Dispatchers.UI + ModalityState.any().asContextElement()) {
|
||||
// Non-cancellable because we expect it to be called only once even if the component was hidden immediately.
|
||||
terminal.component.initOnShow("Terminal Session start", context = NonCancellable) {
|
||||
doScheduleSessionStart(project, terminal, options.processOptions, backendTabId, calculateSizeFromComponent = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
doScheduleSessionStart(project, terminal, options.processOptions, backendTabId, calculateSizeFromComponent = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doScheduleSessionStart(
|
||||
project: Project,
|
||||
terminal: TerminalViewImpl,
|
||||
processOptions: TerminalRequestedProcessOptions,
|
||||
backendTabId: Int,
|
||||
calculateSizeFromComponent: Boolean,
|
||||
) = terminal.coroutineScope.launch(CoroutineName("Terminal Session start")) {
|
||||
val options = prepareStartupOptions(terminal, processOptions, calculateSizeFromComponent)
|
||||
val sessionTab = TerminalTabsManager.getInstance(project).startTerminalSessionForTab(backendTabId, options)
|
||||
connectSessionToTerminal(project, terminal, sessionTab.sessionId!!)
|
||||
}
|
||||
|
||||
private suspend fun prepareStartupOptions(
|
||||
terminal: TerminalView,
|
||||
processOptions: TerminalRequestedProcessOptions,
|
||||
calculateSizeFromComponent: Boolean,
|
||||
): ShellStartupOptions {
|
||||
val baseOptions = ShellStartupOptions.Builder()
|
||||
.shellCommand(processOptions.shellCommand)
|
||||
.workingDirectory(processOptions.workingDirectory)
|
||||
.envVariables(processOptions.envVariables)
|
||||
.processType(processOptions.processType)
|
||||
|
||||
return if (calculateSizeFromComponent) {
|
||||
withContext(Dispatchers.UI + ModalityState.any().asContextElement()) {
|
||||
TerminalUiUtils.getComponentSizeInitializedFuture(terminal.component).await()
|
||||
val termSize = terminal.gridSize?.let { TermSize(it.columns, it.rows) }
|
||||
baseOptions.initialTermSize(termSize).build()
|
||||
}
|
||||
}
|
||||
else {
|
||||
baseOptions.initialTermSize(TermSize(80, 20)).build()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun connectSessionToTerminal(
|
||||
project: Project,
|
||||
terminal: TerminalViewImpl,
|
||||
sessionId: TerminalSessionId,
|
||||
) = withContext(Dispatchers.UI + ModalityState.any().asContextElement()) {
|
||||
val session = TerminalSessionsManager.getInstance(project).getSession(sessionId)
|
||||
?: error("Failed to find TerminalSession with ID: $sessionId")
|
||||
terminal.connectToSession(session)
|
||||
|
||||
installPortForwarding(terminal, terminal.coroutineScope.childScope("PortForwarding"))
|
||||
}
|
||||
Reference in New Issue
Block a user