Skip to content

Commit 757856f

Browse files
committed
Disconnect the terminal widget when Gitpod Task exits, and update terminal widget tab when Gitpod Task title changes
1 parent 074d984 commit 757856f

File tree

2 files changed

+108
-12
lines changed

2 files changed

+108
-12
lines changed

components/ide/jetbrains/backend-plugin/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ plugins {
1313
// Kotlin support - check the latest version at https://plugins.gradle.org/plugin/org.jetbrains.kotlin.jvm
1414
id("org.jetbrains.kotlin.jvm") version "1.7.0"
1515
// gradle-intellij-plugin - read more: https://github.com/JetBrains/gradle-intellij-plugin
16-
id("org.jetbrains.intellij") version "1.8.0"
16+
id("org.jetbrains.intellij") version "1.8.1"
1717
// detekt linter - read more: https://detekt.github.io/detekt/gradle.html
1818
id("io.gitlab.arturbosch.detekt") version "1.17.1"
1919
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle

components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodTerminalService.kt

Lines changed: 107 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ package io.gitpod.jetbrains.remote
77
import com.intellij.openapi.client.ClientProjectSession
88
import com.intellij.openapi.diagnostic.thisLogger
99
import com.intellij.util.application
10+
import com.jediterm.terminal.ui.TerminalWidget
11+
import com.jediterm.terminal.ui.TerminalWidgetListener
1012
import com.jetbrains.rdserver.terminal.BackendTerminalManager
11-
import io.gitpod.jetbrains.remote.GitpodManager
1213
import io.gitpod.supervisor.api.Status
1314
import io.gitpod.supervisor.api.StatusServiceGrpc
1415
import io.gitpod.supervisor.api.TerminalOuterClass
1516
import io.gitpod.supervisor.api.TerminalServiceGrpc
17+
import io.grpc.StatusRuntimeException
1618
import io.grpc.stub.ClientCallStreamObserver
1719
import io.grpc.stub.ClientResponseObserver
1820
import org.jetbrains.plugins.terminal.ShellTerminalWidget
@@ -30,6 +32,7 @@ class GitpodTerminalService(session: ClientProjectSession) {
3032
private val terminalView = TerminalView.getInstance(session.project)
3133
private val backendTerminalManager = BackendTerminalManager.getInstance(session.project)
3234
private val terminalServiceFutureStub = TerminalServiceGrpc.newFutureStub(GitpodManager.supervisorChannel)
35+
private val terminalServiceStub = TerminalServiceGrpc.newStub(GitpodManager.supervisorChannel)
3336
private val statusServiceStub = StatusServiceGrpc.newStub(GitpodManager.supervisorChannel)
3437

3538
init { start() }
@@ -49,7 +52,7 @@ class GitpodTerminalService(session: ClientProjectSession) {
4952
}
5053
}
5154

52-
private fun createSharedTerminalAndExecuteCommand(title: String, command: String) {
55+
private fun createSharedTerminalAndExecuteCommand(title: String, command: String): ShellTerminalWidget? {
5356
val registeredTerminals = terminalView.widgets.toMutableList()
5457

5558
backendTerminalManager.createNewSharedTerminal(UUID.randomUUID().toString(), title)
@@ -59,8 +62,14 @@ class GitpodTerminalService(session: ClientProjectSession) {
5962

6063
widget.terminalTitle.change { applicationTitle = title }
6164

62-
(widget as ShellTerminalWidget).executeCommand(command)
65+
val shellTerminalWidget = widget as ShellTerminalWidget
66+
67+
shellTerminalWidget.executeCommand(command)
68+
69+
return shellTerminalWidget
6370
}
71+
72+
return null
6473
}
6574

6675
private fun createTerminalsAttachedToTasks(
@@ -120,8 +129,9 @@ class GitpodTerminalService(session: ClientProjectSession) {
120129
}
121130

122131
thisLogger().error(
123-
"gitpod: Got an error while trying to get tasks list from Supervisor. Trying again in on second.",
124-
throwable
132+
"gitpod: Got an error while trying to get tasks list from Supervisor. " +
133+
"Trying again in one second.",
134+
throwable
125135
)
126136
}
127137

@@ -150,8 +160,9 @@ class GitpodTerminalService(session: ClientProjectSession) {
150160
}
151161

152162
thisLogger().error(
153-
"gitpod: Got an error while trying to get terminals list from Supervisor. Trying again in on second.",
154-
throwable
163+
"gitpod: Got an error while trying to get terminals list from Supervisor. " +
164+
"Trying again in one second.",
165+
throwable
155166
)
156167
}
157168

@@ -164,9 +175,94 @@ class GitpodTerminalService(session: ClientProjectSession) {
164175
}
165176

166177
private fun createAttachedSharedTerminal(supervisorTerminal: TerminalOuterClass.Terminal) {
167-
createSharedTerminalAndExecuteCommand(
168-
supervisorTerminal.title,
169-
"gp tasks attach ${supervisorTerminal.alias}"
170-
)
178+
val shellTerminalWidget = createSharedTerminalAndExecuteCommand(
179+
supervisorTerminal.title,
180+
"gp tasks attach ${supervisorTerminal.alias}"
181+
) ?: return
182+
183+
exitTaskWhenTerminalWidgetGetsClosed(supervisorTerminal, shellTerminalWidget)
184+
185+
listenForTaskTerminationAndTitleChanges(supervisorTerminal, shellTerminalWidget)
186+
}
187+
188+
private fun listenForTaskTerminationAndTitleChanges(
189+
supervisorTerminal: TerminalOuterClass.Terminal,
190+
shellTerminalWidget: ShellTerminalWidget
191+
) = application.executeOnPooledThread {
192+
var hasOpenSessions = true
193+
194+
while (hasOpenSessions) {
195+
val completableFuture = CompletableFuture<Void>()
196+
197+
val listenTerminalRequest = TerminalOuterClass.ListenTerminalRequest.newBuilder()
198+
.setAlias(supervisorTerminal.alias)
199+
.build()
200+
201+
val listenTerminalResponseObserver =
202+
object : ClientResponseObserver<TerminalOuterClass.ListenTerminalRequest, TerminalOuterClass.ListenTerminalResponse> {
203+
override fun beforeStart(request: ClientCallStreamObserver<TerminalOuterClass.ListenTerminalRequest>) {
204+
@Suppress("ObjectLiteralToLambda")
205+
shellTerminalWidget.addListener(object : TerminalWidgetListener {
206+
override fun allSessionsClosed(widget: TerminalWidget) {
207+
hasOpenSessions = false
208+
request.cancel("gitpod: Terminal closed on the client.", null)
209+
}
210+
})
211+
}
212+
213+
override fun onNext(response: TerminalOuterClass.ListenTerminalResponse) {
214+
when {
215+
response.hasTitle() -> application.invokeLater {
216+
shellTerminalWidget.terminalTitle.change {
217+
applicationTitle = response.title
218+
}
219+
}
220+
221+
response.hasExitCode() -> application.invokeLater {
222+
shellTerminalWidget.close()
223+
}
224+
}
225+
}
226+
227+
override fun onCompleted() = Unit
228+
229+
override fun onError(throwable: Throwable) {
230+
completableFuture.completeExceptionally(throwable)
231+
}
232+
}
233+
234+
terminalServiceStub.listen(listenTerminalRequest, listenTerminalResponseObserver)
235+
236+
try {
237+
completableFuture.get()
238+
} catch (throwable: Throwable) {
239+
if (throwable is StatusRuntimeException || throwable is InterruptedException) {
240+
break
241+
}
242+
243+
thisLogger()
244+
.error("gitpod: Got an error while listening to " +
245+
"'${supervisorTerminal.title}' terminal. Trying again in one second.", throwable)
246+
}
247+
248+
TimeUnit.SECONDS.sleep(1)
249+
}
250+
}
251+
252+
253+
private fun exitTaskWhenTerminalWidgetGetsClosed(
254+
supervisorTerminal: TerminalOuterClass.Terminal,
255+
shellTerminalWidget: ShellTerminalWidget
256+
) {
257+
@Suppress("ObjectLiteralToLambda")
258+
shellTerminalWidget.addListener(object : TerminalWidgetListener {
259+
override fun allSessionsClosed(widget: TerminalWidget) {
260+
terminalServiceFutureStub.shutdown(
261+
TerminalOuterClass.ShutdownTerminalRequest.newBuilder()
262+
.setAlias(supervisorTerminal.alias)
263+
.build()
264+
)
265+
}
266+
})
171267
}
172268
}

0 commit comments

Comments
 (0)