4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
6
import { timeout } from 'vs/base/common/async' ;
7
+ import { debounce } from 'vs/base/common/decorators' ;
7
8
import { Emitter , Event } from 'vs/base/common/event' ;
8
9
import { IDisposable } from 'vs/base/common/lifecycle' ;
9
10
import { basename } from 'vs/base/common/path' ;
@@ -17,14 +18,13 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
17
18
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
18
19
import { IPickOptions , IQuickInputService , IQuickPickItem } from 'vs/platform/quickinput/common/quickInput' ;
19
20
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
20
- import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace' ;
21
21
import { IViewDescriptorService , IViewsService , ViewContainerLocation } from 'vs/workbench/common/views' ;
22
22
import { TerminalConnectionState , IRemoteTerminalService , ITerminalExternalLinkProvider , ITerminalInstance , ITerminalService , ITerminalTab , TerminalShellType , WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal' ;
23
23
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper' ;
24
24
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance' ;
25
25
import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab' ;
26
26
import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView' ;
27
- import { IAvailableShellsRequest , IRemoteTerminalAttachTarget , IShellDefinition , IShellLaunchConfig , ISpawnExtHostProcessRequest , IStartExtensionTerminalRequest , ITerminalConfigHelper , ITerminalLaunchError , ITerminalNativeWindowsDelegate , ITerminalProcessExtHostProxy , KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE , KEYBINDING_CONTEXT_TERMINAL_FOCUS , KEYBINDING_CONTEXT_TERMINAL_IS_OPEN , KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED , KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE , LinuxDistro , TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal' ;
27
+ import { IAvailableShellsRequest , IRemoteTerminalAttachTarget , IShellDefinition , IShellLaunchConfig , ISpawnExtHostProcessRequest , IStartExtensionTerminalRequest , ITerminalConfigHelper , ITerminalLaunchError , ITerminalNativeWindowsDelegate , ITerminalProcessExtHostProxy , ITerminalsLayoutInfoById , KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE , KEYBINDING_CONTEXT_TERMINAL_FOCUS , KEYBINDING_CONTEXT_TERMINAL_IS_OPEN , KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED , KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE , LinuxDistro , TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal' ;
28
28
import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment' ;
29
29
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService' ;
30
30
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions' ;
@@ -117,8 +117,7 @@ export class TerminalService implements ITerminalService {
117
117
@IViewDescriptorService private readonly _viewDescriptorService : IViewDescriptorService ,
118
118
@IWorkbenchEnvironmentService private readonly _environmentService : IWorkbenchEnvironmentService ,
119
119
@IRemoteTerminalService private readonly _remoteTerminalService : IRemoteTerminalService ,
120
- @ITelemetryService private readonly _telemetryService : ITelemetryService ,
121
- @IWorkspaceContextService private readonly _workspaceContextService : IWorkspaceContextService
120
+ @ITelemetryService private readonly _telemetryService : ITelemetryService
122
121
) {
123
122
this . _activeTabIndex = 0 ;
124
123
this . _isShuttingDown = false ;
@@ -135,7 +134,6 @@ export class TerminalService implements ITerminalService {
135
134
this . _onActiveInstanceChanged . fire ( instance ? instance : undefined ) ;
136
135
} ) ;
137
136
this . onInstanceLinksReady ( instance => this . _setInstanceLinkProviders ( instance ) ) ;
138
-
139
137
this . _handleInstanceContextKeys ( ) ;
140
138
this . _processSupportContextKey = KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED . bindTo ( this . _contextKeyService ) ;
141
139
this . _processSupportContextKey . set ( ! isWeb || this . _remoteAgentService . getConnection ( ) !== null ) ;
@@ -147,36 +145,74 @@ export class TerminalService implements ITerminalService {
147
145
this . _connectionState = TerminalConnectionState . Connecting ;
148
146
} else {
149
147
this . _connectionState = TerminalConnectionState . Connected ;
148
+ this . attachRemoteListeners ( ) ;
150
149
}
151
150
}
152
151
153
152
private async _reconnectToRemoteTerminals ( ) : Promise < void > {
154
- const remoteTerms = await this . _remoteTerminalService . listTerminals ( true ) ;
155
- const workspace = this . _workspaceContextService . getWorkspace ( ) ;
156
- const unattachedWorkspaceRemoteTerms = remoteTerms
157
- . filter ( term => term . workspaceId === workspace . id )
158
- . filter ( term => ! this . isAttachedToTerminal ( term ) ) ;
159
-
153
+ // Reattach to all remote terminals
154
+ const layoutInfo = await this . _remoteTerminalService . getTerminalLayoutInfo ( ) ;
155
+ let reconnectCounter = 0 ;
156
+ let activeTab : ITerminalTab | undefined ;
157
+ if ( layoutInfo ) {
158
+ layoutInfo . tabs . forEach ( ( tabLayout ) => {
159
+ const terminalLayouts = tabLayout . terminals . filter ( t => t . terminal && t . terminal . isOrphan ) ;
160
+ if ( terminalLayouts . length ) {
161
+ reconnectCounter += terminalLayouts . length ;
162
+ let terminalInstance : ITerminalInstance | undefined ;
163
+ let tab : ITerminalTab | undefined ;
164
+ terminalLayouts . forEach ( ( terminalLayout ) => {
165
+ if ( ! terminalInstance ) {
166
+ // create tab and terminal
167
+ terminalInstance = this . createTerminal ( { remoteAttach : terminalLayout . terminal ! } ) ;
168
+ tab = this . _getTabForInstance ( terminalInstance ) ;
169
+ if ( tabLayout . isActive ) {
170
+ activeTab = tab ;
171
+ }
172
+ } else {
173
+ // add split terminals to this tab
174
+ this . splitInstance ( terminalInstance , { remoteAttach : terminalLayout . terminal ! } ) ;
175
+ }
176
+ } ) ;
177
+ const activeInstance = this . terminalInstances . find ( t => t . shellLaunchConfig . remoteAttach ?. pid === tabLayout . activeTerminalProcessId ) ;
178
+ if ( activeInstance ) {
179
+ this . setActiveInstance ( activeInstance ) ;
180
+ }
181
+ tab ?. resizePanes ( tabLayout . terminals . map ( terminal => terminal . relativeSize ) ) ;
182
+ }
183
+ } ) ;
184
+ if ( layoutInfo . tabs . length ) {
185
+ this . setActiveTabByIndex ( activeTab ? this . terminalTabs . indexOf ( activeTab ) : 0 ) ;
186
+ }
187
+ }
160
188
/* __GDPR__
161
189
"terminalReconnection" : {
162
190
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
163
191
}
164
192
*/
165
193
const data = {
166
- count : unattachedWorkspaceRemoteTerms . length
194
+ count : reconnectCounter
167
195
} ;
168
196
this . _telemetryService . publicLog ( 'terminalReconnection' , data ) ;
169
- if ( unattachedWorkspaceRemoteTerms . length > 0 ) {
170
- // Reattach to all remote terminals
171
- for ( let term of unattachedWorkspaceRemoteTerms ) {
172
- this . createTerminal ( { remoteAttach : term } ) ;
173
- }
174
- }
175
-
176
197
this . _connectionState = TerminalConnectionState . Connected ;
198
+ // now that terminals have been restored,
199
+ // attach listeners to update remote when terminals are changed
200
+ this . attachRemoteListeners ( ) ;
177
201
this . _onDidChangeConnectionState . fire ( ) ;
178
202
}
179
203
204
+ private attachRemoteListeners ( ) : void {
205
+ this . onActiveTabChanged ( ( ) => {
206
+ this . _updateRemoteState ( ) ;
207
+ } ) ;
208
+ this . onActiveInstanceChanged ( ( ) => {
209
+ this . _updateRemoteState ( ) ;
210
+ } ) ;
211
+ this . onInstancesChanged ( ( ) => {
212
+ this . _updateRemoteState ( ) ;
213
+ } ) ;
214
+ }
215
+
180
216
public setNativeWindowsDelegate ( delegate : ITerminalNativeWindowsDelegate ) : void {
181
217
this . _nativeWindowsDelegate = delegate ;
182
218
}
@@ -253,7 +289,7 @@ export class TerminalService implements ITerminalService {
253
289
}
254
290
255
291
private async _onBeforeShutdownAsync ( ) : Promise < boolean > {
256
- // veto if configured to show confirmation and the user choosed not to exit
292
+ // veto if configured to show confirmation and the user chose not to exit
257
293
const veto = await this . _showTerminalCloseConfirmation ( ) ;
258
294
if ( ! veto ) {
259
295
this . _isShuttingDown = true ;
@@ -274,6 +310,16 @@ export class TerminalService implements ITerminalService {
274
310
return this . _findState ;
275
311
}
276
312
313
+ @debounce ( 500 )
314
+ private _updateRemoteState ( ) : void {
315
+ if ( ! ! this . _environmentService . remoteAuthority ) {
316
+ const state : ITerminalsLayoutInfoById = {
317
+ tabs : this . terminalTabs . map ( t => t . getLayoutInfo ( t === this . getActiveTab ( ) ) )
318
+ } ;
319
+ this . _remoteTerminalService . setTerminalLayoutInfo ( state ) ;
320
+ }
321
+ }
322
+
277
323
private _removeTab ( tab : ITerminalTab ) : void {
278
324
// Get the index of the tab and remove it from the list
279
325
const index = this . _terminalTabs . indexOf ( tab ) ;
@@ -467,6 +513,7 @@ export class TerminalService implements ITerminalService {
467
513
}
468
514
469
515
const instance = tab . split ( shellLaunchConfig ) ;
516
+
470
517
this . _initInstanceListeners ( instance ) ;
471
518
this . _onInstancesChanged . fire ( ) ;
472
519
@@ -479,7 +526,10 @@ export class TerminalService implements ITerminalService {
479
526
instance . addDisposable ( instance . onTitleChanged ( this . _onInstanceTitleChanged . fire , this . _onInstanceTitleChanged ) ) ;
480
527
instance . addDisposable ( instance . onProcessIdReady ( this . _onInstanceProcessIdReady . fire , this . _onInstanceProcessIdReady ) ) ;
481
528
instance . addDisposable ( instance . onLinksReady ( this . _onInstanceLinksReady . fire , this . _onInstanceLinksReady ) ) ;
482
- instance . addDisposable ( instance . onDimensionsChanged ( ( ) => this . _onInstanceDimensionsChanged . fire ( instance ) ) ) ;
529
+ instance . addDisposable ( instance . onDimensionsChanged ( ( ) => {
530
+ this . _onInstanceDimensionsChanged . fire ( instance ) ;
531
+ this . _updateRemoteState ( ) ;
532
+ } ) ) ;
483
533
instance . addDisposable ( instance . onMaximumDimensionsChanged ( ( ) => this . _onInstanceMaximumDimensionsChanged . fire ( instance ) ) ) ;
484
534
instance . addDisposable ( instance . onFocus ( this . _onActiveInstanceChanged . fire , this . _onActiveInstanceChanged ) ) ;
485
535
}
@@ -746,7 +796,7 @@ export class TerminalService implements ITerminalService {
746
796
}
747
797
}
748
798
749
- public setContainers ( panelContainer : HTMLElement , terminalContainer : HTMLElement ) : void {
799
+ public async setContainers ( panelContainer : HTMLElement , terminalContainer : HTMLElement ) : Promise < void > {
750
800
this . _configHelper . panelContainer = panelContainer ;
751
801
this . _terminalContainer = terminalContainer ;
752
802
this . _terminalTabs . forEach ( tab => tab . attachToElement ( terminalContainer ) ) ;
0 commit comments