Skip to content

Commit

Permalink
Disconnect the terminal widget when Gitpod Task exits, and update ter…
Browse files Browse the repository at this point in the history
…minal widget tab when Gitpod Task title changes
  • Loading branch information
felladrin committed Aug 30, 2022
1 parent 33c613c commit ec9fce2
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 12 deletions.
2 changes: 1 addition & 1 deletion components/ide/jetbrains/backend-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ plugins {
// Kotlin support - check the latest version at https://plugins.gradle.org/plugin/org.jetbrains.kotlin.jvm
id("org.jetbrains.kotlin.jvm") version "1.7.0"
// gradle-intellij-plugin - read more: https://github.com/JetBrains/gradle-intellij-plugin
id("org.jetbrains.intellij") version "1.8.0"
id("org.jetbrains.intellij") version "1.8.1"
// detekt linter - read more: https://detekt.github.io/detekt/gradle.html
id("io.gitlab.arturbosch.detekt") version "1.17.1"
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ package io.gitpod.jetbrains.remote
import com.intellij.openapi.client.ClientProjectSession
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.util.application
import com.jediterm.terminal.ui.TerminalWidget
import com.jediterm.terminal.ui.TerminalWidgetListener
import com.jetbrains.rdserver.terminal.BackendTerminalManager
import io.gitpod.jetbrains.remote.GitpodManager
import io.gitpod.supervisor.api.Status
import io.gitpod.supervisor.api.StatusServiceGrpc
import io.gitpod.supervisor.api.TerminalOuterClass
import io.gitpod.supervisor.api.TerminalServiceGrpc
import io.grpc.StatusRuntimeException
import io.grpc.stub.ClientCallStreamObserver
import io.grpc.stub.ClientResponseObserver
import org.jetbrains.plugins.terminal.ShellTerminalWidget
import org.jetbrains.plugins.terminal.TerminalView
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit

@Suppress("UnstableApiUsage")
Expand All @@ -30,6 +33,7 @@ class GitpodTerminalService(session: ClientProjectSession) {
private val terminalView = TerminalView.getInstance(session.project)
private val backendTerminalManager = BackendTerminalManager.getInstance(session.project)
private val terminalServiceFutureStub = TerminalServiceGrpc.newFutureStub(GitpodManager.supervisorChannel)
private val terminalServiceStub = TerminalServiceGrpc.newStub(GitpodManager.supervisorChannel)
private val statusServiceStub = StatusServiceGrpc.newStub(GitpodManager.supervisorChannel)

init { start() }
Expand All @@ -49,7 +53,7 @@ class GitpodTerminalService(session: ClientProjectSession) {
}
}

private fun createSharedTerminalAndExecuteCommand(title: String, command: String) {
private fun createSharedTerminalAndExecuteCommand(title: String, command: String): ShellTerminalWidget? {
val registeredTerminals = terminalView.widgets.toMutableList()

backendTerminalManager.createNewSharedTerminal(UUID.randomUUID().toString(), title)
Expand All @@ -59,8 +63,14 @@ class GitpodTerminalService(session: ClientProjectSession) {

widget.terminalTitle.change { applicationTitle = title }

(widget as ShellTerminalWidget).executeCommand(command)
val shellTerminalWidget = widget as ShellTerminalWidget

shellTerminalWidget.executeCommand(command)

return shellTerminalWidget
}

return null
}

private fun createTerminalsAttachedToTasks(
Expand Down Expand Up @@ -120,8 +130,9 @@ class GitpodTerminalService(session: ClientProjectSession) {
}

thisLogger().error(
"gitpod: Got an error while trying to get tasks list from Supervisor. Trying again in on second.",
throwable
"gitpod: Got an error while trying to get tasks list from Supervisor. " +
"Trying again in one second.",
throwable
)
}

Expand Down Expand Up @@ -150,8 +161,9 @@ class GitpodTerminalService(session: ClientProjectSession) {
}

thisLogger().error(
"gitpod: Got an error while trying to get terminals list from Supervisor. Trying again in on second.",
throwable
"gitpod: Got an error while trying to get terminals list from Supervisor. " +
"Trying again in one second.",
throwable
)
}

Expand All @@ -164,9 +176,101 @@ class GitpodTerminalService(session: ClientProjectSession) {
}

private fun createAttachedSharedTerminal(supervisorTerminal: TerminalOuterClass.Terminal) {
createSharedTerminalAndExecuteCommand(
supervisorTerminal.title,
"gp tasks attach ${supervisorTerminal.alias}"
)
val shellTerminalWidget = createSharedTerminalAndExecuteCommand(
supervisorTerminal.title,
"gp tasks attach ${supervisorTerminal.alias}"
) ?: return

exitTaskWhenTerminalWidgetGetsClosed(supervisorTerminal, shellTerminalWidget)

listenForTaskTerminationAndTitleChanges(supervisorTerminal, shellTerminalWidget)
}

private fun listenForTaskTerminationAndTitleChanges(
supervisorTerminal: TerminalOuterClass.Terminal,
shellTerminalWidget: ShellTerminalWidget
) = application.executeOnPooledThread {
var hasOpenSessions = true

while (hasOpenSessions) {
val completableFuture = CompletableFuture<Void>()

val listenTerminalRequest = TerminalOuterClass.ListenTerminalRequest.newBuilder()
.setAlias(supervisorTerminal.alias)
.build()

val listenTerminalResponseObserver =
object : ClientResponseObserver<TerminalOuterClass.ListenTerminalRequest, TerminalOuterClass.ListenTerminalResponse> {
override fun beforeStart(request: ClientCallStreamObserver<TerminalOuterClass.ListenTerminalRequest>) {
@Suppress("ObjectLiteralToLambda")
shellTerminalWidget.addListener(object : TerminalWidgetListener {
override fun allSessionsClosed(widget: TerminalWidget) {
hasOpenSessions = false
request.cancel("gitpod: Terminal closed on the client.", null)
}
})
}

override fun onNext(response: TerminalOuterClass.ListenTerminalResponse) {
when {
response.hasTitle() -> application.invokeLater {
shellTerminalWidget.terminalTitle.change {
applicationTitle = response.title
}
}

response.hasExitCode() -> application.invokeLater {
shellTerminalWidget.close()
}
}
}

override fun onCompleted() = Unit

override fun onError(throwable: Throwable) {
completableFuture.completeExceptionally(throwable)
}
}

terminalServiceStub.listen(listenTerminalRequest, listenTerminalResponseObserver)

try {
completableFuture.get()
} catch (throwable: Throwable) {
if (
throwable is StatusRuntimeException ||
throwable is ExecutionException ||
throwable is InterruptedException
) {
shellTerminalWidget.close()
thisLogger()
.info("gitpod: Stopped listening to " +
"'${supervisorTerminal.title}' terminal due to an expected exception.", throwable)
break
}

thisLogger()
.error("gitpod: Got an error while listening to " +
"'${supervisorTerminal.title}' terminal. Trying again in one second.", throwable)
}

TimeUnit.SECONDS.sleep(1)
}
}

private fun exitTaskWhenTerminalWidgetGetsClosed(
supervisorTerminal: TerminalOuterClass.Terminal,
shellTerminalWidget: ShellTerminalWidget
) {
@Suppress("ObjectLiteralToLambda")
shellTerminalWidget.addListener(object : TerminalWidgetListener {
override fun allSessionsClosed(widget: TerminalWidget) {
terminalServiceFutureStub.shutdown(
TerminalOuterClass.ShutdownTerminalRequest.newBuilder()
.setAlias(supervisorTerminal.alias)
.build()
)
}
})
}
}

0 comments on commit ec9fce2

Please sign in to comment.