diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 4ed13d3ca3e..dd34c902423 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -116,6 +116,9 @@ import { massageMessageBoxOptions } from 'vs/platform/dialogs/common/dialogs'; import { IUtilityProcessWorkerMainService, UtilityProcessWorkerMainService } from 'vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService'; import { ipcUtilityProcessWorkerChannelName } from 'vs/platform/utilityProcess/common/utilityProcessWorkerService'; import { firstOrDefault } from 'vs/base/common/arrays'; +import { ILocalPtyService, LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { ElectronPtyHostStarter } from 'vs/platform/terminal/electron-main/electronPtyHostStarter'; +import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService'; /** * The main VS Code application. There will only ever be one instance, @@ -912,6 +915,26 @@ export class CodeApplication extends Disposable { services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); services.set(IApplicationStorageMainService, new SyncDescriptor(ApplicationStorageMainService)); + // Terminal + const ptyHostStarter = new ElectronPtyHostStarter({ + graceTime: LocalReconnectConstants.GraceTime, + shortGraceTime: LocalReconnectConstants.ShortGraceTime, + scrollback: this.configurationService.getValue(TerminalSettingId.PersistentSessionScrollback) ?? 100 + }, false, this.environmentMainService); + const ptyHostService = new PtyHostService( + ptyHostStarter, + this.configurationService, + this.logService, + // accessor.get(ILoggerMainService) + ); + ptyHostService.initialize(); + services.set(ILocalPtyService, ptyHostService); + // services.set(ILocalPtyService, new SyncDescriptor(PtyHostService, [ + // ptyHostStarter, + // this.configurationService, + // this.logService + // ])); + // External terminal if (isWindows) { services.set(IExternalTerminalMainService, new SyncDescriptor(WindowsExternalTerminalService)); @@ -1057,6 +1080,10 @@ export class CodeApplication extends Disposable { const profileStorageListener = this._register(new ProfileStorageChangesListenerChannel(accessor.get(IStorageMainService), accessor.get(IUserDataProfilesMainService), this.logService)); sharedProcessClient.then(client => client.registerChannel('profileStorageListener', profileStorageListener)); + // Terminal + const ptyHostChannel = ProxyChannel.fromService(accessor.get(ILocalPtyService)); + mainProcessElectronServer.registerChannel(TerminalIpcChannels.LocalPty, ptyHostChannel); + // External Terminal const externalTerminalChannel = ProxyChannel.fromService(accessor.get(IExternalTerminalMainService)); mainProcessElectronServer.registerChannel('externalTerminal', externalTerminalChannel); diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 7f5800a7e8b..798594573ca 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -7,7 +7,6 @@ // TODO@bpasero remove these once utility process is the only way import { Server as BrowserWindowMessagePortServer } from 'vs/base/parts/ipc/electron-browser/ipc.mp'; import { SharedProcessWorkerService } from 'vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService'; -import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; import { hostname, release } from 'os'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -62,8 +61,6 @@ import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogA import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { supportsTelemetry, ITelemetryAppender, NullAppender, NullTelemetryService, getPiiPathsFromEnvironment, isInternalTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { CustomEndpointTelemetryService } from 'vs/platform/telemetry/node/customEndpointTelemetryService'; -import { LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService'; import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; import { IUserDataSyncBackupStoreService, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, IUserDataSyncUtilService, registerConfiguration as registerUserDataSyncConfiguration, IUserDataSyncResourceProviderService } from 'vs/platform/userDataSync/common/userDataSync'; @@ -119,7 +116,6 @@ import { UserDataAutoSyncService } from 'vs/platform/userDataSync/node/userDataA import { ExtensionTipsService } from 'vs/platform/extensionManagement/node/extensionTipsService'; import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/common/mainProcessService'; import { RemoteStorageService } from 'vs/platform/storage/common/storageService'; -import { NodePtyHostStarter } from 'vs/platform/terminal/node/nodePtyHostStarter'; class SharedProcessMain extends Disposable { @@ -379,22 +375,6 @@ class SharedProcessMain extends Disposable { services.set(IUserDataProfileStorageService, new SyncDescriptor(NativeUserDataProfileStorageService, undefined, true)); services.set(IUserDataSyncResourceProviderService, new SyncDescriptor(UserDataSyncResourceProviderService, undefined, true)); - // Terminal - const ptyHostStarter = new NodePtyHostStarter({ - graceTime: LocalReconnectConstants.GraceTime, - shortGraceTime: LocalReconnectConstants.ShortGraceTime, - scrollback: configurationService.getValue(TerminalSettingId.PersistentSessionScrollback) ?? 100 - }, false, environmentService); - const ptyHostService = new PtyHostService( - ptyHostStarter, - configurationService, - logService, - loggerService - ); - ptyHostService.initialize(); - - services.set(ILocalPtyService, this._register(ptyHostService)); - // Signing services.set(ISignService, new SyncDescriptor(SignService, undefined, false /* proxied to other processes */)); @@ -455,11 +435,6 @@ class SharedProcessMain extends Disposable { const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync); this.server.registerChannel('userDataAutoSync', userDataAutoSyncChannel); - // Terminal - const localPtyService = accessor.get(ILocalPtyService); - const localPtyChannel = ProxyChannel.fromService(localPtyService); - this.server.registerChannel(TerminalIpcChannels.LocalPty, localPtyChannel); - // Tunnel const sharedProcessTunnelChannel = ProxyChannel.fromService(accessor.get(ISharedProcessTunnelService)); this.server.registerChannel(ipcSharedProcessTunnelChannelName, sharedProcessTunnelChannel); diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 16c4d7dee9f..05566403ca0 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -902,3 +902,12 @@ export interface ITerminalCommandSelector { exitStatus: boolean; commandExitResult: 'success' | 'error'; } + +export const ILocalPtyService = createDecorator('localPtyService'); + +/** + * A service responsible for communicating with the pty host process on Electron. + * + * **This service should only be used within the terminal component.** + */ +export interface ILocalPtyService extends IPtyService { } diff --git a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts new file mode 100644 index 00000000000..12ab42d1357 --- /dev/null +++ b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { FileAccess } from 'vs/base/common/network'; +import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; +import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { parsePtyHostDebugPort } from 'vs/platform/environment/node/environmentService'; +import { IReconnectConstants } from 'vs/platform/terminal/common/terminal'; +import { IPtyHostConnection, IPtyHostStarter } from 'vs/platform/terminal/node/ptyHost'; + +export class ElectronPtyHostStarter implements IPtyHostStarter { + constructor( + private readonly _reconnectConstants: IReconnectConstants, + private readonly _isRemote: boolean, + @IEnvironmentService private readonly _environmentService: INativeEnvironmentService + ) { + } + + start(lastPtyId: number): IPtyHostConnection { + // TODO: Convert to use utility process + const opts: IIPCOptions = { + serverName: 'Pty Host', + args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath], + env: { + VSCODE_LAST_PTY_ID: lastPtyId, + VSCODE_PTY_REMOTE: this._isRemote, + VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', + VSCODE_PIPE_LOGGING: 'true', + VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client, + VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.graceTime, + VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.shortGraceTime, + VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback + } + }; + + const ptyHostDebug = parsePtyHostDebugPort(this._environmentService.args, this._environmentService.isBuilt); + if (ptyHostDebug) { + if (ptyHostDebug.break && ptyHostDebug.port) { + opts.debugBrk = ptyHostDebug.port; + } else if (!ptyHostDebug.break && ptyHostDebug.port) { + opts.debug = ptyHostDebug.port; + } + } + + const client = new Client(FileAccess.asFileUri('bootstrap-fork').fsPath, opts); + + return { + client, + dispose: client.dispose, + onDidProcessExit: client.onDidProcessExit + }; + } +} diff --git a/src/vs/platform/terminal/electron-sandbox/terminal.ts b/src/vs/platform/terminal/electron-sandbox/terminal.ts deleted file mode 100644 index 5a64d36080c..00000000000 --- a/src/vs/platform/terminal/electron-sandbox/terminal.ts +++ /dev/null @@ -1,16 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IPtyService } from 'vs/platform/terminal/common/terminal'; - -export const ILocalPtyService = createDecorator('localPtyService'); - -/** - * A service responsible for communicating with the pty host process on Electron. - * - * **This service should only be used within the terminal component.** - */ -export interface ILocalPtyService extends IPtyService { } diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 1971e20dd53..6f0e9d82c1c 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -25,7 +25,7 @@ let server: ChildProcessServer | UtilityProcessServer; if (isUtilityProcess(process)) { server = new UtilityProcessServer(); } else { - server = new ChildProcessServer('ptyHost'); + server = new ChildProcessServer(TerminalIpcChannels.PtyHost); } const lastPtyId = parseInt(process.env.VSCODE_LAST_PTY_ID || '0'); diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 6334f6fd46e..1d66fcac023 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -8,8 +8,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IProcessEnvironment, OperatingSystem, isWindows } from 'vs/base/common/platform'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILogService, ILoggerService } from 'vs/platform/log/common/log'; -import { RemoteLoggerChannelClient } from 'vs/platform/log/common/logIpc'; +import { ILogService } from 'vs/platform/log/common/log'; import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv'; import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabilities/capabilities'; import { RequestStore } from 'vs/platform/terminal/common/requestStore'; @@ -78,7 +77,7 @@ export class PtyHostService extends Disposable implements IPtyService { private readonly _ptyHostStarter: IPtyHostStarter, @IConfigurationService private readonly _configurationService: IConfigurationService, @ILogService private readonly _logService: ILogService, - @ILoggerService private readonly _loggerService: ILoggerService, + // @ILoggerService private readonly _loggerService: ILoggerService, ) { super(); @@ -152,7 +151,7 @@ export class PtyHostService extends Disposable implements IPtyService { })); // Setup logging - this._register(new RemoteLoggerChannelClient(this._loggerService, client.getChannel(TerminalIpcChannels.Logger))); + // this._register(new RemoteLoggerChannelClient(this._loggerService, client.getChannel(TerminalIpcChannels.Logger))); // Create proxy and forward events const proxy = ProxyChannel.toService(client.getChannel(TerminalIpcChannels.PtyHost)); @@ -321,6 +320,7 @@ export class PtyHostService extends Disposable implements IPtyService { } private _handleHeartbeat() { + this._logService.info('heartbeat'); this._clearHeartbeatTimeouts(); this._heartbeatFirstTimeout = setTimeout(() => this._handleHeartbeatFirstTimeout(), HeartbeatConstants.BeatInterval * HeartbeatConstants.FirstWaitMultiplier); if (!this._isResponsive) { diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index f296eef71d4..724fdc44923 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -5,7 +5,6 @@ import { exec } from 'child_process'; import { promises as fs } from 'fs'; -import type * as pty from 'node-pty'; import { timeout } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -20,6 +19,7 @@ import { FlowControlConstants, IShellLaunchConfig, ITerminalChildProcess, ITermi import { ChildProcessMonitor } from 'vs/platform/terminal/node/childProcessMonitor'; import { findExecutable, getShellIntegrationInjection, getWindowsBuildNumber, IShellIntegrationConfigInjection } from 'vs/platform/terminal/node/terminalEnvironment'; import { WindowsShellHelper } from 'vs/platform/terminal/node/windowsShellHelper'; +import { IPty, IPtyForkOptions, IWindowsPtyForkOptions, spawn } from 'node-pty'; const enum ShutdownConstants { /** @@ -104,7 +104,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess private _exitCode: number | undefined; private _exitMessage: string | undefined; private _closeTimeout: any; - private _ptyProcess: pty.IPty | undefined; + private _ptyProcess: IPty | undefined; private _currentTitle: string = ''; private _processStartupComplete: Promise | undefined; private _isDisposed: boolean = false; @@ -115,7 +115,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess private _writeTimeout: NodeJS.Timeout | undefined; private _delayedResizer: DelayedResizer | undefined; private readonly _initialCwd: string; - private readonly _ptyOptions: pty.IPtyForkOptions | pty.IWindowsPtyForkOptions; + private readonly _ptyOptions: IPtyForkOptions | IWindowsPtyForkOptions; private _isPtyPaused: boolean = false; private _unacknowledgedCharCount: number = 0; @@ -289,13 +289,13 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess private async setupPtyProcess( shellLaunchConfig: IShellLaunchConfig, - options: pty.IPtyForkOptions, + options: IPtyForkOptions, shellIntegrationInjection: IShellIntegrationConfigInjection | undefined ): Promise { const args = shellIntegrationInjection?.newArgs || shellLaunchConfig.args || []; await this._throttleKillSpawn(); this._logService.trace('IPty#spawn', shellLaunchConfig.executable, args, options); - const ptyProcess = (await import('node-pty')).spawn(shellLaunchConfig.executable!, args, options); + const ptyProcess = spawn(shellLaunchConfig.executable!, args, options); this._ptyProcess = ptyProcess; this._childProcessMonitor = this._register(new ChildProcessMonitor(ptyProcess.pid, this._logService)); this._childProcessMonitor.onDidChangeHasChildProcesses(value => this._onDidChangeProperty.fire({ type: ProcessPropertyType.HasChildProcesses, value })); @@ -337,7 +337,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess super.dispose(); } - private _setupTitlePolling(ptyProcess: pty.IPty) { + private _setupTitlePolling(ptyProcess: IPty) { // Send initial timeout async to give event listeners a chance to init setTimeout(() => this._sendProcessTitle(ptyProcess)); // Setup polling for non-Windows, for Windows `process` doesn't change @@ -401,7 +401,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess this._onProcessReady.fire({ pid, cwd: this._initialCwd, requiresWindowsMode: isWindows && getWindowsBuildNumber() < 21376 }); } - private _sendProcessTitle(ptyProcess: pty.IPty): void { + private _sendProcessTitle(ptyProcess: IPty): void { if (this._isDisposed) { return; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 85c116e3115..ac61e808bc6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -2156,7 +2156,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { && !this._shellLaunchConfig.isExtensionOwnedTerminal && !this._shellLaunchConfig.attachPersistentProcess ) { - this.relaunch(); + // this.relaunch(); return; } diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index d3a4b2402e6..9ef633999c2 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -14,9 +14,8 @@ import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IProcessPropertyMap, IShellLaunchConfig, ITerminalChildProcess, ITerminalEnvironment, ITerminalProcessOptions, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ProcessPropertyType, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; +import { ILocalPtyService, IProcessPropertyMap, IShellLaunchConfig, ITerminalChildProcess, ITerminalEnvironment, ITerminalProcessOptions, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ProcessPropertyType, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; -import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; 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 6cdaec893a4..13b8d6ab3fc 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/terminal.contribution.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; +import { registerMainProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; import { Registry } from 'vs/platform/registry/common/platform'; -import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; -import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; +import { ILocalPtyService, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalNativeContribution } from 'vs/workbench/contrib/terminal/electron-sandbox/terminalNativeContribution'; @@ -16,7 +15,8 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { LocalTerminalBackendContribution } from 'vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend'; // Register services -registerSharedProcessRemoteService(ILocalPtyService, TerminalIpcChannels.LocalPty); +registerMainProcessRemoteService(ILocalPtyService, TerminalIpcChannels.LocalPty); +// registerSharedProcessRemoteService(ILocalPtyService, TerminalIpcChannels.LocalPty); registerSingleton(ITerminalProfileResolverService, ElectronTerminalProfileResolverService, InstantiationType.Delayed); // Register workbench contributions