44
55package io.gitpod.jetbrains.remote
66
7+ import com.intellij.openapi.Disposable
78import com.intellij.openapi.diagnostic.thisLogger
89import com.intellij.openapi.project.Project
10+ import com.intellij.openapi.rd.defineNestedLifetime
11+ import com.intellij.remoteDev.util.onTerminationOrNow
912import com.intellij.util.application
1013import com.jediterm.terminal.ui.TerminalWidget
1114import com.jediterm.terminal.ui.TerminalWidgetListener
15+ import com.jetbrains.rd.framework.util.launch
1216import com.jetbrains.rdserver.terminal.BackendTerminalManager
1317import io.gitpod.supervisor.api.Status
1418import io.gitpod.supervisor.api.StatusServiceGrpc
@@ -17,18 +21,18 @@ import io.gitpod.supervisor.api.TerminalServiceGrpc
1721import io.grpc.StatusRuntimeException
1822import io.grpc.stub.ClientCallStreamObserver
1923import io.grpc.stub.ClientResponseObserver
24+ import kotlinx.coroutines.delay
25+ import kotlinx.coroutines.future.await
26+ import kotlinx.coroutines.guava.await
2027import org.jetbrains.plugins.terminal.ShellTerminalWidget
2128import org.jetbrains.plugins.terminal.TerminalView
2229import java.util.*
2330import java.util.concurrent.CompletableFuture
2431import java.util.concurrent.ExecutionException
25- import java.util.concurrent.TimeUnit
26-
27- class GitpodTerminalService (project : Project ) {
28- private companion object {
29- var hasStarted = false
30- }
3132
33+ @Suppress(" UnstableApiUsage" )
34+ class GitpodTerminalService (project : Project ): Disposable {
35+ private val lifetime = defineNestedLifetime()
3236 private val terminalView = TerminalView .getInstance(project)
3337 private val backendTerminalManager = BackendTerminalManager .getInstance(project)
3438 private val terminalServiceFutureStub = TerminalServiceGrpc .newFutureStub(GitpodManager .supervisorChannel)
@@ -39,12 +43,12 @@ class GitpodTerminalService(project: Project) {
3943 start()
4044 }
4145
42- private fun start () {
43- if (application.isHeadlessEnvironment || hasStarted) return
46+ override fun dispose () = Unit
4447
45- hasStarted = true
48+ private fun start () {
49+ if (application.isHeadlessEnvironment) return
4650
47- application.executeOnPooledThread {
51+ lifetime.launch {
4852 val terminals = getSupervisorTerminalsList()
4953 val tasks = getSupervisorTasksList()
5054
@@ -95,7 +99,7 @@ class GitpodTerminalService(project: Project) {
9599 }
96100 }
97101
98- private tailrec fun getSupervisorTasksList (): List <Status .TaskStatus > {
102+ private tailrec suspend fun getSupervisorTasksList (): List <Status .TaskStatus > {
99103 var tasksList: List <Status .TaskStatus >? = null
100104
101105 try {
@@ -124,7 +128,7 @@ class GitpodTerminalService(project: Project) {
124128
125129 statusServiceStub.tasksStatus(taskStatusRequest, taskStatusResponseObserver)
126130
127- tasksList = completableFuture.get ()
131+ tasksList = completableFuture.await ()
128132 } catch (throwable: Throwable ) {
129133 if (throwable is InterruptedException ) {
130134 throw throwable
@@ -140,20 +144,20 @@ class GitpodTerminalService(project: Project) {
140144 return if (tasksList != null ) {
141145 tasksList
142146 } else {
143- TimeUnit . SECONDS .sleep( 1 )
147+ delay( 1000 )
144148 getSupervisorTasksList()
145149 }
146150 }
147151
148- private tailrec fun getSupervisorTerminalsList (): List <TerminalOuterClass .Terminal > {
152+ private tailrec suspend fun getSupervisorTerminalsList (): List <TerminalOuterClass .Terminal > {
149153 var terminalsList: List <TerminalOuterClass .Terminal >? = null
150154
151155 try {
152156 val listTerminalsRequest = TerminalOuterClass .ListTerminalsRequest .newBuilder().build()
153157
154158 val listTerminalsResponseFuture = terminalServiceFutureStub.list(listTerminalsRequest)
155159
156- val listTerminalsResponse = listTerminalsResponseFuture.get ()
160+ val listTerminalsResponse = listTerminalsResponseFuture.await ()
157161
158162 terminalsList = listTerminalsResponse.terminalsList
159163 } catch (throwable: Throwable ) {
@@ -171,7 +175,7 @@ class GitpodTerminalService(project: Project) {
171175 return if (terminalsList != null ) {
172176 terminalsList
173177 } else {
174- TimeUnit . SECONDS .sleep( 1 )
178+ delay( 1000 )
175179 getSupervisorTerminalsList()
176180 }
177181 }
@@ -182,6 +186,8 @@ class GitpodTerminalService(project: Project) {
182186 " gp tasks attach ${supervisorTerminal.alias} "
183187 ) ? : return
184188
189+ closeTerminalWidgetWhenClientGetsClosed(shellTerminalWidget)
190+
185191 exitTaskWhenTerminalWidgetGetsClosed(supervisorTerminal, shellTerminalWidget)
186192
187193 listenForTaskTerminationAndTitleChanges(supervisorTerminal, shellTerminalWidget)
@@ -190,7 +196,7 @@ class GitpodTerminalService(project: Project) {
190196 private fun listenForTaskTerminationAndTitleChanges (
191197 supervisorTerminal : TerminalOuterClass .Terminal ,
192198 shellTerminalWidget : ShellTerminalWidget
193- ) = application.executeOnPooledThread {
199+ ) = lifetime.launch {
194200 var hasOpenSessions = true
195201
196202 while (hasOpenSessions) {
@@ -241,7 +247,7 @@ class GitpodTerminalService(project: Project) {
241247 terminalServiceStub.listen(listenTerminalRequest, listenTerminalResponseObserver)
242248
243249 try {
244- completableFuture.get ()
250+ completableFuture.await ()
245251 } catch (throwable: Throwable ) {
246252 if (
247253 throwable is StatusRuntimeException ||
@@ -259,7 +265,7 @@ class GitpodTerminalService(project: Project) {
259265 " '${supervisorTerminal.title} ' terminal. Trying again in one second." , throwable)
260266 }
261267
262- TimeUnit . SECONDS .sleep( 1 )
268+ delay( 1000 )
263269 }
264270 }
265271
@@ -270,12 +276,23 @@ class GitpodTerminalService(project: Project) {
270276 @Suppress(" ObjectLiteralToLambda" )
271277 shellTerminalWidget.addListener(object : TerminalWidgetListener {
272278 override fun allSessionsClosed (widget : TerminalWidget ) {
273- terminalServiceFutureStub.shutdown(
274- TerminalOuterClass .ShutdownTerminalRequest .newBuilder()
275- .setAlias(supervisorTerminal.alias)
276- .build()
277- )
279+ lifetime.launch {
280+ delay(5000 )
281+ terminalServiceFutureStub.shutdown(
282+ TerminalOuterClass .ShutdownTerminalRequest .newBuilder()
283+ .setAlias(supervisorTerminal.alias)
284+ .build()
285+ )
286+ }
278287 }
279288 })
280289 }
290+
291+ private fun closeTerminalWidgetWhenClientGetsClosed (
292+ shellTerminalWidget : ShellTerminalWidget
293+ ) {
294+ lifetime.onTerminationOrNow {
295+ shellTerminalWidget.close()
296+ }
297+ }
281298}
0 commit comments