@@ -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,91 @@ 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
+ 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
+ })
171
264
}
172
265
}
0 commit comments