From 67dda13a3aec239d4ab3e42e65fa11ccefae6274 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 10 Feb 2021 14:03:34 -0800 Subject: [PATCH] Move pty to shared process --- .../sharedProcess/sharedProcessMain.ts | 13 +++++ src/vs/code/electron-main/app.ts | 6 -- src/vs/platform/terminal/common/terminal.ts | 18 +++--- .../localPtyService.ts} | 55 ++++++++---------- .../terminal/electron-browser/terminalIpc.ts | 32 ++++++++++ .../terminal/electron-sandbox/terminal.ts | 2 +- .../terminalProcessMainProxy.ts | 12 ++-- .../electron-sandbox/localPtyService.ts | 18 ------ .../electron-sandbox/localPtyServiceProxy.ts | 58 +++++++++++++++++++ .../electron-sandbox/terminal.contribution.ts | 4 +- 10 files changed, 148 insertions(+), 70 deletions(-) rename src/vs/platform/terminal/{electron-main/localPtyMainService.ts => electron-browser/localPtyService.ts} (50%) create mode 100644 src/vs/platform/terminal/electron-browser/terminalIpc.ts delete mode 100644 src/vs/workbench/contrib/terminal/electron-sandbox/localPtyService.ts create mode 100644 src/vs/workbench/contrib/terminal/electron-sandbox/localPtyServiceProxy.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 18f4dd64f99..c726c230bd2 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -81,6 +81,10 @@ import { DeprecatedExtensionsCleaner } from 'vs/code/electron-browser/sharedProc import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { join } from 'vs/base/common/path'; +import { LocalPtyChannel } from 'vs/platform/terminal/electron-browser/terminalIpc'; +import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; +import { LocalPtyService } from 'vs/platform/terminal/electron-browser/localPtyService'; +import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; class SharedProcessMain extends Disposable { @@ -259,6 +263,10 @@ class SharedProcessMain extends Disposable { services.set(IUserDataSyncResourceEnablementService, new SyncDescriptor(UserDataSyncResourceEnablementService)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); + // Terminal + // TODO: Move out of electron-main + services.set(ILocalPtyService, new SyncDescriptor(LocalPtyService)); + return new InstantiationService(services); } @@ -304,6 +312,11 @@ class SharedProcessMain extends Disposable { const userDataAutoSync = this._register(accessor.get(IInstantiationService).createInstance(UserDataAutoSyncService)); const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync); this.server.registerChannel('userDataAutoSync', userDataAutoSyncChannel); + + // Terminal + const localPtyMainService = accessor.get(ILocalPtyService); + const localPtyMainChannel = new LocalPtyChannel(localPtyMainService); + this.server.registerChannel(TerminalIpcChannels.LocalPty, localPtyMainChannel); } private registerErrorHandler(logService: ILogService): void { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index c3b41999165..ff58b3f5664 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -89,7 +89,6 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService'; import { once } from 'vs/base/common/functional'; -import { ILocalPtyMainService, LocalPtyMainService } from 'vs/platform/terminal/electron-main/localPtyMainService'; export class CodeApplication extends Disposable { private windowsMainService: IWindowsMainService | undefined; @@ -528,7 +527,6 @@ export class CodeApplication extends Disposable { services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService)); services.set(IDiagnosticsService, ProxyChannel.toService(getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics'))))); - services.set(ILocalPtyMainService, new SyncDescriptor(LocalPtyMainService)); services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId])); services.set(IKeyboardLayoutMainService, new SyncDescriptor(KeyboardLayoutMainService)); @@ -616,10 +614,6 @@ export class CodeApplication extends Disposable { const updateChannel = new UpdateChannel(updateService); electronIpcServer.registerChannel('update', updateChannel); - const localPtyMainService = accessor.get(ILocalPtyMainService); - const localPtyChannel = createChannelReceiver(localPtyMainService); - electronIpcServer.registerChannel('localPty', localPtyChannel); - const issueMainService = accessor.get(IIssueMainService); const issueChannel = ProxyChannel.fromService(issueMainService); electronIpcServer.registerChannel('issue', issueChannel); diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 5f5b8ea0fdb..d516d1a9cc7 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -7,15 +7,19 @@ import { Event } from 'vs/base/common/event'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -export interface ICommonLocalPtyService { +export enum TerminalIpcChannels { + LocalPty = 'localPty' +} + +export interface ILocalPtyService { readonly _serviceBrand: undefined; - readonly onProcessData: Event<{ id: number, event: IProcessDataEvent | string }>; - readonly onProcessExit: Event<{ id: number, event: number | undefined }>; - readonly onProcessReady: Event<{ id: number, event: { pid: number, cwd: string } }>; - readonly onProcessTitleChanged: Event<{ id: number, event: string }>; - readonly onProcessOverrideDimensions: Event<{ id: number, event: ITerminalDimensionsOverride | undefined }>; - readonly onProcessResolvedShellLaunchConfig: Event<{ id: number, event: IShellLaunchConfig }>; + // readonly onProcessData: Event<{ id: number, event: IProcessDataEvent | string }>; + // readonly onProcessExit: Event<{ id: number, event: number | undefined }>; + // readonly onProcessReady: Event<{ id: number, event: { pid: number, cwd: string } }>; + // readonly onProcessTitleChanged: Event<{ id: number, event: string }>; + // readonly onProcessOverrideDimensions: Event<{ id: number, event: ITerminalDimensionsOverride | undefined }>; + // readonly onProcessResolvedShellLaunchConfig: Event<{ id: number, event: IShellLaunchConfig }>; createProcess( shellLaunchConfig: IShellLaunchConfig, diff --git a/src/vs/platform/terminal/electron-main/localPtyMainService.ts b/src/vs/platform/terminal/electron-browser/localPtyService.ts similarity index 50% rename from src/vs/platform/terminal/electron-main/localPtyMainService.ts rename to src/vs/platform/terminal/electron-browser/localPtyService.ts index e8bc16679d1..0eb68976d9e 100644 --- a/src/vs/platform/terminal/electron-main/localPtyMainService.ts +++ b/src/vs/platform/terminal/electron-browser/localPtyService.ts @@ -3,37 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IProcessEnvironment } from 'vs/base/common/platform'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { ICommonLocalPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; +import { ILocalPtyService, IShellLaunchConfig, ITerminalChildProcess, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; import { TerminalProcess } from 'vs/platform/terminal/node/terminalProcess'; -export const ILocalPtyMainService = createDecorator('localPtyMainService'); - -export interface ILocalPtyMainService extends ICommonLocalPtyService { } - let currentLocalPtyId = 0; -export class LocalPtyMainService extends Disposable implements ICommonLocalPtyService { +export class LocalPtyService extends Disposable implements ILocalPtyService { declare readonly _serviceBrand: undefined; private readonly _localPtys: Map = new Map(); - private readonly _onProcessData = this._register(new Emitter<{ id: number, event: IProcessDataEvent | string }>()); - readonly onProcessData = this._onProcessData.event; - private readonly _onProcessExit = this._register(new Emitter<{ id: number, event: number | undefined }>()); - readonly onProcessExit = this._onProcessExit.event; - private readonly _onProcessReady = this._register(new Emitter<{ id: number, event: { pid: number, cwd: string } }>()); - readonly onProcessReady = this._onProcessReady.event; - private readonly _onProcessTitleChanged = this._register(new Emitter<{ id: number, event: string }>()); - readonly onProcessTitleChanged = this._onProcessTitleChanged.event; - private readonly _onProcessOverrideDimensions = this._register(new Emitter<{ id: number, event: ITerminalDimensionsOverride | undefined }>()); - readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event; - private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<{ id: number, event: IShellLaunchConfig }>()); - readonly onProcessResolvedShellLaunchConfig = this._onProcessResolvedShellLaunchConfig.event; + // private readonly _onProcessData = this._register(new Emitter<{ id: number, event: IProcessDataEvent | string }>()); + // readonly onProcessData = this._onProcessData.event; + // private readonly _onProcessExit = this._register(new Emitter<{ id: number, event: number | undefined }>()); + // readonly onProcessExit = this._onProcessExit.event; + // private readonly _onProcessReady = this._register(new Emitter<{ id: number, event: { pid: number, cwd: string } }>()); + // readonly onProcessReady = this._onProcessReady.event; + // private readonly _onProcessTitleChanged = this._register(new Emitter<{ id: number, event: string }>()); + // readonly onProcessTitleChanged = this._onProcessTitleChanged.event; + // private readonly _onProcessOverrideDimensions = this._register(new Emitter<{ id: number, event: ITerminalDimensionsOverride | undefined }>()); + // readonly onProcessOverrideDimensions = this._onProcessOverrideDimensions.event; + // private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<{ id: number, event: IShellLaunchConfig }>()); + // readonly onProcessResolvedShellLaunchConfig = this._onProcessResolvedShellLaunchConfig.event; constructor( @ILogService private readonly _logService: ILogService @@ -43,17 +37,18 @@ export class LocalPtyMainService extends Disposable implements ICommonLocalPtySe async createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean): Promise { const id = ++currentLocalPtyId; + console.log('LocalPtyMainService#createProcess ' + id, shellLaunchConfig, cwd, cols); const process = new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, windowsEnableConpty, this._logService); - process.onProcessData(event => this._onProcessData.fire({ id, event })); - process.onProcessExit(event => this._onProcessExit.fire({ id, event })); - process.onProcessReady(event => this._onProcessReady.fire({ id, event })); - process.onProcessTitleChanged(event => this._onProcessTitleChanged.fire({ id, event })); - if (process.onProcessOverrideDimensions) { - process.onProcessOverrideDimensions(event => this._onProcessOverrideDimensions.fire({ id, event })); - } - if (process.onProcessResolvedShellLaunchConfig) { - process.onProcessResolvedShellLaunchConfig(event => this._onProcessResolvedShellLaunchConfig.fire({ id, event })); - } + // process.onProcessData(event => this._onProcessData.fire({ id, event })); + // process.onProcessExit(event => this._onProcessExit.fire({ id, event })); + // process.onProcessReady(event => this._onProcessReady.fire({ id, event })); + // process.onProcessTitleChanged(event => this._onProcessTitleChanged.fire({ id, event })); + // if (process.onProcessOverrideDimensions) { + // process.onProcessOverrideDimensions(event => this._onProcessOverrideDimensions.fire({ id, event })); + // } + // if (process.onProcessResolvedShellLaunchConfig) { + // process.onProcessResolvedShellLaunchConfig(event => this._onProcessResolvedShellLaunchConfig.fire({ id, event })); + // } this._localPtys.set(id, process); return id; } diff --git a/src/vs/platform/terminal/electron-browser/terminalIpc.ts b/src/vs/platform/terminal/electron-browser/terminalIpc.ts new file mode 100644 index 00000000000..04e39f3db74 --- /dev/null +++ b/src/vs/platform/terminal/electron-browser/terminalIpc.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ILocalPtyService } from 'vs/platform/terminal/common/terminal'; + +export class LocalPtyChannel extends Disposable implements IServerChannel { + constructor( + private readonly _localPtyMainService: ILocalPtyService + ) { + super(); + } + + listen(_: unknown, event: string): Event { + throw new Error(`Event not found: ${event}`); + } + + async call(_: unknown, command: string, arg?: any): Promise { + switch (command) { + case '$createProcess': return this._localPtyMainService.createProcess(...arg as [any, any, any, any, any, any, any]); + case '$start': return this._localPtyMainService.start(...(arg as [any])); + // TODO: Fill in other calls + + default: + throw new Error(`Call not found: ${command}`); + } + } +} diff --git a/src/vs/platform/terminal/electron-sandbox/terminal.ts b/src/vs/platform/terminal/electron-sandbox/terminal.ts index bf381cda52b..a987c576787 100644 --- a/src/vs/platform/terminal/electron-sandbox/terminal.ts +++ b/src/vs/platform/terminal/electron-sandbox/terminal.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ICommonLocalPtyService } from 'vs/platform/terminal/common/terminal'; +import { ILocalPtyService as ICommonLocalPtyService } from 'vs/platform/terminal/common/terminal'; export const ILocalPtyService = createDecorator('localPtyService'); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessMainProxy.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessMainProxy.ts index b992448ca97..eef0fd04e28 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessMainProxy.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessMainProxy.ts @@ -31,12 +31,12 @@ export class TerminalProcessMainProxy extends Disposable implements ITerminalChi @ILocalPtyService private readonly _localPtyService: ILocalPtyService ) { super(); - this._localPtyService.onProcessData(e => e.id === this._localPtyId && this._onProcessData.fire(e.event)); - this._localPtyService.onProcessExit(e => e.id === this._localPtyId && this._onProcessExit.fire(e.event)); - this._localPtyService.onProcessReady(e => e.id === this._localPtyId && this._onProcessReady.fire(e.event)); - this._localPtyService.onProcessTitleChanged(e => e.id === this._localPtyId && this._onProcessTitleChanged.fire(e.event)); - this._localPtyService.onProcessOverrideDimensions(e => e.id === this._localPtyId && this._onProcessOverrideDimensions.fire(e.event)); - this._localPtyService.onProcessResolvedShellLaunchConfig(e => e.id === this._localPtyId && this._onProcessResolvedShellLaunchConfig.fire(e.event)); + // this._localPtyService.onProcessData(e => e.id === this._localPtyId && this._onProcessData.fire(e.event)); + // this._localPtyService.onProcessExit(e => e.id === this._localPtyId && this._onProcessExit.fire(e.event)); + // this._localPtyService.onProcessReady(e => e.id === this._localPtyId && this._onProcessReady.fire(e.event)); + // this._localPtyService.onProcessTitleChanged(e => e.id === this._localPtyId && this._onProcessTitleChanged.fire(e.event)); + // this._localPtyService.onProcessOverrideDimensions(e => e.id === this._localPtyId && this._onProcessOverrideDimensions.fire(e.event)); + // this._localPtyService.onProcessResolvedShellLaunchConfig(e => e.id === this._localPtyId && this._onProcessResolvedShellLaunchConfig.fire(e.event)); } start(): Promise { diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localPtyService.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localPtyService.ts deleted file mode 100644 index 963f947f35d..00000000000 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localPtyService.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; -import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; -import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; - -// @ts-ignore: interface is implemented via proxy -export class LocalPtyService implements ILocalPtyService { - - declare readonly _serviceBrand: undefined; - - constructor(@IMainProcessService mainProcessService: IMainProcessService) { - return createChannelSender(mainProcessService.getChannel('localPty')); - } -} diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localPtyServiceProxy.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localPtyServiceProxy.ts new file mode 100644 index 00000000000..0b396994242 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localPtyServiceProxy.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; +import { IShellLaunchConfig, ITerminalLaunchError, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/sharedProcessService'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IProcessEnvironment } from 'vs/base/common/platform'; + +export class LocalPtyServiceProxy implements ILocalPtyService { + + declare readonly _serviceBrand: undefined; + + private readonly _channel: IChannel; + + constructor( + @ISharedProcessService sharedProcessService: ISharedProcessService + ) { + this._channel = sharedProcessService.getChannel(TerminalIpcChannels.LocalPty); + } + + // onProcessData: Event<{ id: number; event: string | IProcessDataEvent; }>; + // onProcessExit: Event<{ id: number; event: number | undefined; }>; + // onProcessReady: Event<{ id: number; event: { pid: number; cwd: string; }; }>; + // onProcessTitleChanged: Event<{ id: number; event: string; }>; + // onProcessOverrideDimensions: Event<{ id: number; event: ITerminalDimensionsOverride | undefined; }>; + // onProcessResolvedShellLaunchConfig: Event<{ id: number; event: IShellLaunchConfig; }>; + + createProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, executableEnv: IProcessEnvironment, windowsEnableConpty: boolean): Promise { + return this._channel.call('$createProcess', Array.prototype.slice.call(arguments)); + } + start(id: number): Promise { + return this._channel.call('$start', Array.prototype.slice.call(arguments)); + } + shutdown(id: number, immediate: boolean): Promise { + return this._channel.call('$shutdown', Array.prototype.slice.call(arguments)); + } + input(id: number, data: string): Promise { + return this._channel.call('$input', Array.prototype.slice.call(arguments)); + } + resize(id: number, cols: number, rows: number): Promise { + return this._channel.call('$resize', Array.prototype.slice.call(arguments)); + } + acknowledgeDataEvent(id: number, charCount: number): Promise { + return this._channel.call('$acknowledgeDataEvents', Array.prototype.slice.call(arguments)); + } + getInitialCwd(id: number): Promise { + return this._channel.call('$getInitialCwd', Array.prototype.slice.call(arguments)); + } + getCwd(id: number): Promise { + return this._channel.call('$getCwd', Array.prototype.slice.call(arguments)); + } + getLatency(id: number): Promise { + return this._channel.call('$getLatency', Array.prototype.slice.call(arguments)); + } +} diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts index d2201d3b676..66c8d6b6630 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts @@ -5,6 +5,6 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; -import { LocalPtyService } from 'vs/workbench/contrib/terminal/electron-sandbox/localPtyService'; +import { LocalPtyServiceProxy } from 'vs/workbench/contrib/terminal/electron-sandbox/localPtyServiceProxy'; -registerSingleton(ILocalPtyService, LocalPtyService, true); +registerSingleton(ILocalPtyService, LocalPtyServiceProxy, true);