diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 85cdee88e32e60aa4d77d7ac7c7c7cf9b3ec1c7b..ba53e31e444d92875c183308add37616349f1ce3 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -51,6 +51,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape if (activeInstance) { this._proxy.$acceptActiveTerminalChanged(activeInstance.id); } + + this.terminalService.extHostReady(extHostContext.remoteAuthority); } public dispose(): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index edd7e5d24784a5684e17765ce944956516d7a63d..f280a830240d7c96d35f83634832ffce7123c27b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -22,6 +22,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IFileService } from 'vs/platform/files/common/files'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export abstract class TerminalService extends CommonTerminalService implements ITerminalService { protected _configHelper: IBrowserTerminalConfigHelper; @@ -38,8 +39,9 @@ export abstract class TerminalService extends CommonTerminalService implements I @IWorkbenchEnvironmentService private _environmentService: IWorkbenchEnvironmentService, @IExtensionService extensionService: IExtensionService, @IFileService fileService: IFileService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService ) { - super(contextKeyService, panelService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService); + super(contextKeyService, panelService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService, remoteAgentService); } protected abstract _getDefaultShell(p: platform.Platform): string; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index b4d9e485a1439fb34b1d36055aab1ec18500bb98..c1f58a7fc8f8669b679ab6b8d4906c6ed93f8d38 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -267,6 +267,7 @@ export interface ITerminalService { */ preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise; + extHostReady(remoteAuthority: string): void; requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; } diff --git a/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts index ed89adc9c7dbc96d77a5d1e10ba0dfda5716c2a5..4fb3bd1e70f96aff5f05d5a78c4ac13872b8f57a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts @@ -7,7 +7,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerminalProcessExtHostProxy { private _disposables: IDisposable[] = []; @@ -44,15 +43,9 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm activeWorkspaceRootUri: URI, cols: number, rows: number, - @ITerminalService private readonly _terminalService: ITerminalService, - @IExtensionService private readonly _extensionService: IExtensionService + @ITerminalService private readonly _terminalService: ITerminalService ) { - this._extensionService.whenInstalledExtensionsRegistered().then(() => { - // TODO: MainThreadTerminalService is not ready at this point, fix this - setTimeout(() => { - this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows); - }, 0); - }); + this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows); } public dispose(): void { diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index a7cafd24d034a66a974d3d361c7cd1196b10cc55..1b97370975582ca7ac5e48e90ad533ec29ccf22c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -19,6 +19,8 @@ import { IFileService } from 'vs/platform/files/common/files'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { isWindows } from 'vs/base/common/platform'; import { basename } from 'vs/base/common/path'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { timeout } from 'vs/base/common/async'; export abstract class TerminalService implements ITerminalService { public _serviceBrand: any; @@ -32,7 +34,7 @@ export abstract class TerminalService implements ITerminalService { return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); } private _findState: FindReplaceState; - + private _extHostsReady: { [authority: string]: boolean } = {}; private _activeTabIndex: number; public get activeTabIndex(): number { return this._activeTabIndex; } @@ -65,12 +67,13 @@ export abstract class TerminalService implements ITerminalService { constructor( @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IPanelService protected readonly _panelService: IPanelService, - @ILifecycleService lifecycleService: ILifecycleService, + @ILifecycleService readonly lifecycleService: ILifecycleService, @IStorageService protected readonly _storageService: IStorageService, @INotificationService protected readonly _notificationService: INotificationService, @IDialogService private readonly _dialogService: IDialogService, @IExtensionService private readonly _extensionService: IExtensionService, - @IFileService protected readonly _fileService: IFileService + @IFileService protected readonly _fileService: IFileService, + @IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService ) { this._activeTabIndex = 0; this._isShuttingDown = false; @@ -116,15 +119,22 @@ export abstract class TerminalService implements ITerminalService { } public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void { - // Ensure extension host is ready before requesting a process - this._extensionService.whenInstalledExtensionsRegistered().then(() => { - // TODO: MainThreadTerminalService is not ready at this point, fix this - setTimeout(() => { - this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows }); - }, 500); + this._extensionService.whenInstalledExtensionsRegistered().then(async () => { + // Wait for the remoteAuthority to be ready (and listening for events) before proceeding + const conn = this._remoteAgentService.getConnection(); + const remoteAuthority = conn ? conn.remoteAuthority : 'null'; + let retries = 0; + while (!this._extHostsReady[remoteAuthority] && ++retries < 50) { + await timeout(100); + } + this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows }); }); } + public extHostReady(remoteAuthority: string): void { + this._extHostsReady[remoteAuthority] = true; + } + private _onBeforeShutdown(): boolean | Promise { if (this.terminalInstances.length === 0) { // No terminal instances, don't veto diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index c24f3ffdb8c6205dd3949097118a2426a13e4b87..d7e3026986edd90d094c2c24c5fd3da5424f206f 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -28,6 +28,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { execFile } from 'child_process'; import { URI } from 'vs/base/common/uri'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export class TerminalService extends BrowserTerminalService implements ITerminalService { public get configHelper(): ITerminalConfigHelper { return this._configHelper; } @@ -45,9 +46,10 @@ export class TerminalService extends BrowserTerminalService implements ITerminal @IDialogService dialogService: IDialogService, @IExtensionService extensionService: IExtensionService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @IFileService fileService: IFileService + @IFileService fileService: IFileService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService ) { - super(contextKeyService, panelService, layoutService, lifecycleService, storageService, notificationService, dialogService, instantiationService, environmentService, extensionService, fileService); + super(contextKeyService, panelService, layoutService, lifecycleService, storageService, notificationService, dialogService, instantiationService, environmentService, extensionService, fileService, remoteAgentService); this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, linuxDistro); ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => {