@@ -7,12 +7,14 @@ package io.gitpod.jetbrains.remote
7
7
import com.intellij.openapi.client.ClientProjectSession
8
8
import com.intellij.openapi.diagnostic.thisLogger
9
9
import com.intellij.util.application
10
+ import com.jediterm.terminal.ui.TerminalWidget
11
+ import com.jediterm.terminal.ui.TerminalWidgetListener
10
12
import com.jetbrains.rdserver.terminal.BackendTerminalManager
11
- import io.gitpod.jetbrains.remote.GitpodManager
12
13
import io.gitpod.supervisor.api.Status
13
14
import io.gitpod.supervisor.api.StatusServiceGrpc
14
15
import io.gitpod.supervisor.api.TerminalOuterClass
15
16
import io.gitpod.supervisor.api.TerminalServiceGrpc
17
+ import io.grpc.StatusRuntimeException
16
18
import io.grpc.stub.ClientCallStreamObserver
17
19
import io.grpc.stub.ClientResponseObserver
18
20
import org.jetbrains.plugins.terminal.ShellTerminalWidget
@@ -30,6 +32,7 @@ class GitpodTerminalService(session: ClientProjectSession) {
30
32
private val terminalView = TerminalView .getInstance(session.project)
31
33
private val backendTerminalManager = BackendTerminalManager .getInstance(session.project)
32
34
private val terminalServiceFutureStub = TerminalServiceGrpc .newFutureStub(GitpodManager .supervisorChannel)
35
+ private val terminalServiceStub = TerminalServiceGrpc .newStub(GitpodManager .supervisorChannel)
33
36
private val statusServiceStub = StatusServiceGrpc .newStub(GitpodManager .supervisorChannel)
34
37
35
38
init { start() }
@@ -49,7 +52,7 @@ class GitpodTerminalService(session: ClientProjectSession) {
49
52
}
50
53
}
51
54
52
- private fun createSharedTerminalAndExecuteCommand (title : String , command : String ) {
55
+ private fun createSharedTerminalAndExecuteCommand (title : String , command : String ): ShellTerminalWidget ? {
53
56
val registeredTerminals = terminalView.widgets.toMutableList()
54
57
55
58
backendTerminalManager.createNewSharedTerminal(UUID .randomUUID().toString(), title)
@@ -59,8 +62,14 @@ class GitpodTerminalService(session: ClientProjectSession) {
59
62
60
63
widget.terminalTitle.change { applicationTitle = title }
61
64
62
- (widget as ShellTerminalWidget ).executeCommand(command)
65
+ val shellTerminalWidget = widget as ShellTerminalWidget
66
+
67
+ shellTerminalWidget.executeCommand(command)
68
+
69
+ return shellTerminalWidget
63
70
}
71
+
72
+ return null
64
73
}
65
74
66
75
private fun createTerminalsAttachedToTasks (
@@ -120,8 +129,9 @@ class GitpodTerminalService(session: ClientProjectSession) {
120
129
}
121
130
122
131
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
125
135
)
126
136
}
127
137
@@ -150,8 +160,9 @@ class GitpodTerminalService(session: ClientProjectSession) {
150
160
}
151
161
152
162
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
155
166
)
156
167
}
157
168
@@ -164,9 +175,94 @@ class GitpodTerminalService(session: ClientProjectSession) {
164
175
}
165
176
166
177
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
+ })
171
267
}
172
268
}
0 commit comments