Skip to content

Commit

Permalink
Move terminal instance service methods to local terminal service
Browse files Browse the repository at this point in the history
Part of #116467
  • Loading branch information
Tyriar committed Mar 4, 2021
1 parent df863f9 commit 3a1f4c4
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 19 deletions.
22 changes: 22 additions & 0 deletions src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
Expand Down Expand Up @@ -65,6 +66,27 @@ export enum TerminalIpcChannels {
Heartbeat = 'heartbeat'
}

export interface IOffProcessTerminalService {
readonly _serviceBrand: undefined;

/** Fired when the ptyHost process goes down, losing all connections to the service's ptys. */
onPtyHostExit: Event<void>;
/**
* Fired when the ptyHost process becomes non-responsive, this should disable stdin for all
* terminals using this pty host connection and mark them as disconnected.
*/
onPtyHostUnresponsive: Event<void>;

createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean, shouldPersist: boolean): Promise<ITerminalChildProcess>;
attachToProcess(id: number): Promise<ITerminalChildProcess | undefined>;
setTerminalLayoutInfo(args?: ISetTerminalLayoutInfoArgs): void;
setTerminalLayoutInfo(layout: ITerminalsLayoutInfoById): void;
getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined>;
}

export const ILocalTerminalService = createDecorator<ILocalTerminalService>('localTerminalService');
export interface ILocalTerminalService extends IOffProcessTerminalService { }

export interface IPtyService {
readonly _serviceBrand: undefined;

Expand Down
1 change: 0 additions & 1 deletion src/vs/platform/terminal/electron-sandbox/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { IPtyService } from 'vs/platform/terminal/common/terminal';

export const ILocalPtyService = createDecorator<ILocalPtyService>('localPtyService');

export interface ILocalPtyService extends IPtyService { }
6 changes: 0 additions & 6 deletions src/vs/workbench/contrib/terminal/browser/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { URI } from 'vs/base/common/uri';
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ITerminalTabLayoutInfoById } from 'vs/platform/terminal/common/terminal';
import { ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess';
import { IAvailableShellsRequest, ICommandTracker, IDefaultShellAndArgsRequest, INavigationMode, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, IWindowsShellHelper, LinuxDistro, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import type { Terminal as XTermTerminal } from 'xterm';
import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search';
Expand Down Expand Up @@ -55,13 +54,8 @@ export interface ITerminalInstanceService {
getXtermUnicode11Constructor(): Promise<typeof XTermUnicode11Addon>;
getXtermWebglConstructor(): Promise<typeof XTermWebglAddon>;
createWindowsShellHelper(shellProcessId: number, xterm: XTermTerminal): IWindowsShellHelper;
createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean, shouldPersist: boolean): Promise<ITerminalChildProcess>;
attachToProcess(id: number): Promise<ITerminalChildProcess | undefined>;
getDefaultShellAndArgs(useAutomationShell: boolean, platformOverride?: Platform): Promise<{ shell: string, args: string[] | string | undefined }>;
getMainProcessParentEnv(): Promise<IProcessEnvironment>;
setTerminalLayoutInfo(args?: ISetTerminalLayoutInfoArgs): void;
setTerminalLayoutInfo(layout: ITerminalsLayoutInfoById): void;
getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined>;
}

export interface IBrowserTerminalConfigHelper extends ITerminalConfigHelper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { Emitter, Event } from 'vs/base/common/event';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { Schemas } from 'vs/base/common/network';
Expand All @@ -27,7 +27,7 @@ import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } fr
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { URI } from 'vs/base/common/uri';
import { IEnvironmentVariableInfo, IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants } from 'vs/platform/terminal/common/terminal';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, ILocalTerminalService } from 'vs/platform/terminal/common/terminal';

/** The amount of time to consider terminal errors to be related to the launch */
const LAUNCHING_DURATION = 500;
Expand Down Expand Up @@ -101,6 +101,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
return this._hasWrittenData;
}

private readonly _localTerminalService?: ILocalTerminalService;

constructor(
private readonly _instanceId: number,
private readonly _configHelper: ITerminalConfigHelper,
Expand All @@ -117,8 +119,11 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
@IPathService private readonly _pathService: IPathService,
@IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService,
@IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService,
@optional(ILocalTerminalService) localTerminalService: ILocalTerminalService,
) {
super();
this._localTerminalService = localTerminalService;

this.ptyProcessReady = new Promise<void>(c => {
this.onProcessReady(() => {
this._logService.debug(`Terminal process ready (shellProcessId: ${this.shellProcessId})`);
Expand Down Expand Up @@ -190,18 +195,22 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
const shouldPersist = !shellLaunchConfig.isFeatureTerminal && this._configHelper.config.enablePersistentSessions;
this._process = await this._remoteTerminalService.createRemoteTerminalProcess(this._instanceId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, shouldPersist, this._configHelper);
} else {
if (!this._localTerminalService) {
this._logService.trace(`Tried to launch a local terminal which is not supported in this window`);
return undefined;
}
// Flow control is not needed for ptys hosted in the same process (ie. the electron
// renderer).
if (shellLaunchConfig.attachPersistentProcess) {
const result = await this._terminalInstanceService.attachToProcess(shellLaunchConfig.attachPersistentProcess.id);
const result = await this._localTerminalService.attachToProcess(shellLaunchConfig.attachPersistentProcess.id);
if (result) {
this._process = result;
} else {
this._logService.trace(`Attach to process failed for terminal ${shellLaunchConfig.attachPersistentProcess}`);
return undefined;
}
} else {
this._process = await this._launchLocalProcess(shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled);
this._process = await this._launchLocalProcess(this._localTerminalService, shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled);
}
}
}
Expand Down Expand Up @@ -284,6 +293,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}

private async _launchLocalProcess(
localTerminalService: ILocalTerminalService,
shellLaunchConfig: IShellLaunchConfig,
cols: number,
rows: number,
Expand Down Expand Up @@ -339,7 +349,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
this._ptyResponsiveListener?.dispose();
this._ptyResponsiveListener = undefined;
}));
return await this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty, shouldPersist);

return await localTerminalService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty, shouldPersist);
}

public setDimensions(cols: number, rows: number): void {
Expand Down
21 changes: 14 additions & 7 deletions src/vs/workbench/contrib/terminal/browser/terminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import * as nls from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/platform/terminal/common/terminal';
import { ILocalTerminalService, IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/platform/terminal/common/terminal';
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalInstanceService, ITerminalService, ITerminalTab, TerminalConnectionState, TerminalShellType, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalTab, TerminalConnectionState, TerminalShellType, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab';
Expand Down Expand Up @@ -102,6 +102,8 @@ export class TerminalService implements ITerminalService {
public get onDidChangeConnectionState(): Event<void> { return this._onDidChangeConnectionState.event; }
public get connectionState(): TerminalConnectionState { return this._connectionState; }

private readonly _localTerminalService?: ILocalTerminalService;

constructor(
@IContextKeyService private _contextKeyService: IContextKeyService,
@IWorkbenchLayoutService private _layoutService: IWorkbenchLayoutService,
Expand All @@ -116,8 +118,10 @@ export class TerminalService implements ITerminalService {
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService
@optional(ILocalTerminalService) localTerminalService: ILocalTerminalService
) {
this._localTerminalService = localTerminalService;

this._activeTabIndex = 0;
this._isShuttingDown = false;
this._findState = new FindReplaceState();
Expand Down Expand Up @@ -172,8 +176,11 @@ export class TerminalService implements ITerminalService {
}

private async _reconnectToLocalTerminals(): Promise<void> {
if (!this._localTerminalService) {
return;
}
// Reattach to all local terminals
const layoutInfo = await this._terminalInstanceService.getTerminalLayoutInfo();
const layoutInfo = await this._localTerminalService.getTerminalLayoutInfo();
if (layoutInfo && layoutInfo.tabs.length > 0) {
this._recreateTerminalTabs(layoutInfo);
// now that terminals have been restored,
Expand Down Expand Up @@ -317,7 +324,7 @@ export class TerminalService implements ITerminalService {
// windows
this.terminalInstances.forEach(instance => instance.dispose(!isWindows));

this._terminalInstanceService.setTerminalLayoutInfo(undefined);
this._localTerminalService!.setTerminalLayoutInfo(undefined);
}

public getTabLabels(): string[] {
Expand Down Expand Up @@ -345,7 +352,7 @@ export class TerminalService implements ITerminalService {
const state: ITerminalsLayoutInfoById = {
tabs: this.terminalTabs.map(t => t.getLayoutInfo(t === this.getActiveTab()))
};
this._terminalInstanceService.setTerminalLayoutInfo(state);
this._localTerminalService!.setTerminalLayoutInfo(state);
}

private _removeTab(tab: ITerminalTab): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* 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 { localize } from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
import { ILocalTerminalService, IShellLaunchConfig, ITerminalChildProcess, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/platform/terminal/common/terminal';
import { IGetTerminalLayoutInfoArgs, 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 { LocalPty } from 'vs/workbench/contrib/terminal/electron-sandbox/localPty';

export class LocalTerminalService extends Disposable implements ILocalTerminalService {
public _serviceBrand: undefined;

private readonly _ptys: Map<number, LocalPty> = new Map();

private readonly _onPtyHostExit = this._register(new Emitter<void>());
readonly onPtyHostExit = this._onPtyHostExit.event;
private readonly _onPtyHostUnresponsive = this._register(new Emitter<void>());
readonly onPtyHostUnresponsive = this._onPtyHostUnresponsive.event;

constructor(
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@ILogService private readonly _logService: ILogService,
@ILocalPtyService private readonly _localPtyService: ILocalPtyService,
@ILabelService private readonly _labelService: ILabelService,
@INotificationService notificationService: INotificationService
) {
super();

// Attach process listeners
this._localPtyService.onProcessData(e => this._ptys.get(e.id)?.handleData(e.event));
this._localPtyService.onProcessExit(e => {
const pty = this._ptys.get(e.id);
if (pty) {
pty.handleExit(e.event);
this._ptys.delete(e.id);
}
});
this._localPtyService.onProcessReady(e => this._ptys.get(e.id)?.handleReady(e.event));
this._localPtyService.onProcessTitleChanged(e => this._ptys.get(e.id)?.handleTitleChanged(e.event));
this._localPtyService.onProcessOverrideDimensions(e => this._ptys.get(e.id)?.handleOverrideDimensions(e.event));
this._localPtyService.onProcessResolvedShellLaunchConfig(e => this._ptys.get(e.id)?.handleResolvedShellLaunchConfig(e.event));
this._localPtyService.onProcessReplay(e => this._ptys.get(e.id)?.handleReplay(e.event));

// Attach pty host listeners
if (this._localPtyService.onPtyHostExit) {
this._localPtyService.onPtyHostExit(e => {
this._onPtyHostExit.fire();
notificationService.error(`The terminal's pty host process exited, the connection to all terminal processes was lost`);
});
}
if (this._localPtyService.onPtyHostStart) {
this._localPtyService.onPtyHostStart(() => {
this._logService.info(`ptyHost restarted`);
});
}
if (this._localPtyService.onPtyHostUnresponsive) {
this._localPtyService.onPtyHostUnresponsive(() => {
const choices: IPromptChoice[] = [{
label: localize('restartPtyHost', "Restart pty host"),
run: () => this._localPtyService.restartPtyHost!()
}];
notificationService.prompt(Severity.Error, localize('nonResponsivePtyHost', "The connection to the terminal's pty host process is unresponsive, the terminals may stop working."), choices);
this._onPtyHostUnresponsive.fire();
});
}
}

public async createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean, shouldPersist: boolean): Promise<ITerminalChildProcess> {
const id = await this._localPtyService.createProcess(shellLaunchConfig, cwd, cols, rows, env, process.env as IProcessEnvironment, windowsEnableConpty, shouldPersist, this._getWorkspaceId(), this._getWorkspaceName());
const pty = this._instantiationService.createInstance(LocalPty, id, shouldPersist);
this._ptys.set(id, pty);
return pty;
}

public async attachToProcess(id: number): Promise<ITerminalChildProcess | undefined> {
try {
await this._localPtyService.attachToProcess(id);
const pty = this._instantiationService.createInstance(LocalPty, id, true);
this._ptys.set(id, pty);
return pty;
} catch (e) {
this._logService.trace(`Couldn't attach to process ${e.message}`);
}
return undefined;
}

public setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): void {
const args: ISetTerminalLayoutInfoArgs = {
workspaceId: this._getWorkspaceId(),
tabs: layoutInfo ? layoutInfo.tabs : []
};
this._localPtyService.setTerminalLayoutInfo(args);
}

public async getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined> {
const layoutArgs: IGetTerminalLayoutInfoArgs = {
workspaceId: this._getWorkspaceId()
};
let result = await this._localPtyService.getTerminalLayoutInfo(layoutArgs);
return result;
}

private _getWorkspaceId(): string {
return this._workspaceContextService.getWorkspace().id;
}

private _getWorkspaceName(): string {
return this._labelService.getWorkspaceLabel(this._workspaceContextService.getWorkspace());
}
}

0 comments on commit 3a1f4c4

Please sign in to comment.