Skip to content

Commit 989371b

Browse files
committed
Exit Gitpod Task when terminal widget is closed, close terminal widget when Gitpod Task exits, and update terminal widget when Gitpod Task title changes
1 parent 074d984 commit 989371b

File tree

2 files changed

+105
-12
lines changed

2 files changed

+105
-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: 104 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,91 @@ 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+
while (true) {
193+
val completableFuture = CompletableFuture<Void>()
194+
195+
val listenTerminalRequest = TerminalOuterClass.ListenTerminalRequest.newBuilder()
196+
.setAlias(supervisorTerminal.alias)
197+
.build()
198+
199+
val listenTerminalResponseObserver =
200+
object : ClientResponseObserver<TerminalOuterClass.ListenTerminalRequest, TerminalOuterClass.ListenTerminalResponse> {
201+
override fun beforeStart(request: ClientCallStreamObserver<TerminalOuterClass.ListenTerminalRequest>) {
202+
@Suppress("ObjectLiteralToLambda")
203+
shellTerminalWidget.addListener(object : TerminalWidgetListener {
204+
override fun allSessionsClosed(widget: TerminalWidget) {
205+
request.cancel("gitpod: Terminal closed on the client.", null)
206+
}
207+
})
208+
}
209+
210+
override fun onNext(response: TerminalOuterClass.ListenTerminalResponse) {
211+
when {
212+
response.hasTitle() -> application.invokeLater {
213+
shellTerminalWidget.terminalTitle.change {
214+
applicationTitle = response.title
215+
}
216+
}
217+
218+
response.hasExitCode() -> application.invokeLater {
219+
shellTerminalWidget.close()
220+
}
221+
}
222+
}
223+
224+
override fun onCompleted() = Unit
225+
226+
override fun onError(throwable: Throwable) {
227+
completableFuture.completeExceptionally(throwable)
228+
}
229+
}
230+
231+
terminalServiceStub.listen(listenTerminalRequest, listenTerminalResponseObserver)
232+
233+
try {
234+
completableFuture.get()
235+
} catch (throwable: Throwable) {
236+
if (throwable is StatusRuntimeException || throwable is InterruptedException) {
237+
break
238+
}
239+
240+
thisLogger()
241+
.error("gitpod: Got an error while listening to " +
242+
"'${supervisorTerminal.title}' terminal. Trying again in one second.", throwable)
243+
}
244+
245+
TimeUnit.SECONDS.sleep(1)
246+
}
247+
}
248+
249+
250+
private fun exitTaskWhenTerminalWidgetGetsClosed(
251+
supervisorTerminal: TerminalOuterClass.Terminal,
252+
shellTerminalWidget: ShellTerminalWidget
253+
) {
254+
@Suppress("ObjectLiteralToLambda")
255+
shellTerminalWidget.addListener(object : TerminalWidgetListener {
256+
override fun allSessionsClosed(widget: TerminalWidget) {
257+
terminalServiceFutureStub.shutdown(
258+
TerminalOuterClass.ShutdownTerminalRequest.newBuilder()
259+
.setAlias(supervisorTerminal.alias)
260+
.build()
261+
)
262+
}
263+
})
171264
}
172265
}

0 commit comments

Comments
 (0)