This commit is contained in:
Connor Peet
2020-10-20 23:09:19 +00:00
committed by GitHub
17 changed files with 331 additions and 143 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.51.0",
"distro": "2efd579dedb12e7e9ed9ebd112120fa8b3b0922e",
"distro": "5a2a07b357e780fb7130743e24cf814126485ef9",
"author": {
"name": "Microsoft Corporation"
},
@@ -63,7 +63,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._toDispose.add(_terminalService.onInstanceRequestSpawnExtHostProcess(request => this._onRequestSpawnExtHostProcess(request)));
this._toDispose.add(_terminalService.onInstanceRequestStartExtensionTerminal(e => this._onRequestStartExtensionTerminal(e)));
this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null)));
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => this._onTitleChanged(instance.id, instance.title)));
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.id, instance.title)));
this._toDispose.add(_terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed)));
this._toDispose.add(_terminalService.onRequestAvailableShells(e => this._onRequestAvailableShells(e)));
@@ -9,7 +9,7 @@ import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShap
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalChildProcess, EXT_HOST_CREATION_DELAY, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal';
import { timeout } from 'vs/base/common/async';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
@@ -245,8 +245,8 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = new Emitter<string>();
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensions | undefined>();
public get onProcessOverrideDimensions(): Event<ITerminalDimensions | undefined> { return this._onProcessOverrideDimensions.event; }
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensionsOverride | undefined>();
public get onProcessOverrideDimensions(): Event<ITerminalDimensionsOverride | undefined> { return this._onProcessOverrideDimensions.event; }
constructor(private readonly _pty: vscode.Pseudoterminal) { }
@@ -46,6 +46,9 @@ export class TreeViewPane extends ViewPane {
if (options.title !== this.treeView.title) {
this.updateTitle(this.treeView.title);
}
if (options.titleDescription !== this.treeView.description) {
this.updateTitleDescription(this.treeView.description);
}
this.updateTreeVisibility();
}
@@ -8,24 +8,21 @@ import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { revive } from 'vs/base/common/marshalling';
import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { IRemoteTerminalService, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteTerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteTerminalProcessExecCommandEvent, IShellLaunchConfigDto, RemoteTerminalChannelClient, REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel';
import { IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { IProcessDataEvent, IRemoteTerminalAttachTarget, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export class RemoteTerminalService extends Disposable implements IRemoteTerminalService {
public _serviceBrand: undefined;
private readonly _remoteTerminalChannel: RemoteTerminalChannelClient | null;
private _hasConnectedToRemote = false;
constructor(
@ITerminalService _terminalService: ITerminalService,
@ITerminalInstanceService readonly terminalInstanceService: ITerminalInstanceService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@ILogService private readonly _logService: ILogService,
@@ -46,34 +43,42 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
throw new Error(`Cannot create remote terminal when there is no remote!`);
}
let isPreconnectionTerminal = false;
if (!this._hasConnectedToRemote) {
isPreconnectionTerminal = true;
this._remoteAgentService.getEnvironment().then(() => {
this._hasConnectedToRemote = true;
});
}
return new RemoteTerminalProcess(terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper, this._remoteTerminalChannel, this._remoteAgentService, this._logService, this._commandService);
}
return new RemoteTerminalProcess(terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper, isPreconnectionTerminal, this._remoteTerminalChannel, this._remoteAgentService, this._logService, this._commandService);
public async listTerminals(): Promise<IRemoteTerminalAttachTarget[]> {
const terms = this._remoteTerminalChannel ? await this._remoteTerminalChannel.listTerminals() : [];
return terms.map(termDto => {
return <IRemoteTerminalAttachTarget>{
id: termDto.id,
pid: termDto.pid,
title: termDto.title,
cwd: termDto.cwd
};
});
}
}
export class RemoteTerminalProcess extends Disposable implements ITerminalChildProcess {
public readonly _onProcessData = this._register(new Emitter<string>());
public readonly onProcessData: Event<string> = this._onProcessData.event;
public readonly _onProcessData = this._register(new Emitter<IProcessDataEvent>());
public readonly onProcessData: Event<IProcessDataEvent> = this._onProcessData.event;
private readonly _onProcessExit = this._register(new Emitter<number | undefined>());
public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
public readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>());
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = this._register(new Emitter<string>());
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensionsOverride | undefined>());
public readonly onProcessOverrideDimensions: Event<ITerminalDimensionsOverride | undefined> = this._onProcessOverrideDimensions.event;
private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>());
public get onProcessResolvedShellLaunchConfig(): Event<IShellLaunchConfig> { return this._onProcessResolvedShellLaunchConfig.event; }
private _startBarrier: Barrier;
private _remoteTerminalId: number;
private _inReplay = false;
constructor(
private readonly _terminalId: number,
private readonly _shellLaunchConfig: IShellLaunchConfig,
@@ -81,7 +86,6 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP
private readonly _cols: number,
private readonly _rows: number,
private readonly _configHelper: ITerminalConfigHelper,
private readonly _isPreconnectionTerminal: boolean,
private readonly _remoteTerminalChannel: RemoteTerminalChannelClient,
private readonly _remoteAgentService: IRemoteAgentService,
private readonly _logService: ILogService,
@@ -94,11 +98,6 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP
public async start(): Promise<ITerminalLaunchError | undefined> {
// Add a loading title only if this terminal is instantiated before a connection is up and running
if (this._isPreconnectionTerminal) {
setTimeout(() => this._onProcessTitleChanged.fire(nls.localize('terminal.integrated.starting', "Starting...")), 0);
}
// Fetch the environment to check shell permissions
const env = await this._remoteAgentService.getEnvironment();
if (!env) {
@@ -106,49 +105,45 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP
throw new Error('Could not fetch remote environment');
}
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(env.os);
if (!this._shellLaunchConfig.remoteAttach) {
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(env.os);
const shellLaunchConfigDto: IShellLaunchConfigDto = {
name: this._shellLaunchConfig.name,
executable: this._shellLaunchConfig.executable,
args: this._shellLaunchConfig.args,
cwd: this._shellLaunchConfig.cwd,
env: this._shellLaunchConfig.env
};
const shellLaunchConfigDto: IShellLaunchConfigDto = {
name: this._shellLaunchConfig.name,
executable: this._shellLaunchConfig.executable,
args: this._shellLaunchConfig.args,
cwd: this._shellLaunchConfig.cwd,
env: this._shellLaunchConfig.env
};
this._logService.trace('Spawning remote agent process', { terminalId: this._terminalId, shellLaunchConfigDto });
this._logService.trace('Spawning remote agent process', { terminalId: this._terminalId, shellLaunchConfigDto });
const result = await this._remoteTerminalChannel.createTerminalProcess(
shellLaunchConfigDto,
this._activeWorkspaceRootUri,
this._cols,
this._rows,
isWorkspaceShellAllowed,
);
const result = await this._remoteTerminalChannel.createTerminalProcess(
shellLaunchConfigDto,
this._activeWorkspaceRootUri,
this._cols,
this._rows,
isWorkspaceShellAllowed,
);
this._remoteTerminalId = result.terminalId;
this._register(this._remoteTerminalChannel.onTerminalProcessEvent(this._remoteTerminalId)(event => {
switch (event.type) {
case 'ready':
return this._onProcessReady.fire({ pid: event.pid, cwd: event.cwd });
case 'titleChanged':
return this._onProcessTitleChanged.fire(event.title);
case 'data':
return this._onProcessData.fire(event.data);
case 'exit':
return this._onProcessExit.fire(event.exitCode);
case 'execCommand':
return this._execCommand(event);
this._remoteTerminalId = result.terminalId;
this.setupTerminalEventListener();
this._onProcessResolvedShellLaunchConfig.fire(reviveIShellLaunchConfig(result.resolvedShellLaunchConfig));
const startResult = await this._remoteTerminalChannel.startTerminalProcess(this._remoteTerminalId);
if (typeof startResult !== 'undefined') {
// An error occurred
return startResult;
}
}));
} else {
this._remoteTerminalId = this._shellLaunchConfig.remoteAttach.id;
this._onProcessReady.fire({ pid: this._shellLaunchConfig.remoteAttach.pid, cwd: this._shellLaunchConfig.remoteAttach.cwd });
this.setupTerminalEventListener();
this._onProcessResolvedShellLaunchConfig.fire(reviveIShellLaunchConfig(result.resolvedShellLaunchConfig));
const startResult = await this._remoteTerminalChannel.startTerminalProcess(this._remoteTerminalId);
if (typeof startResult !== 'undefined') {
// An error occurred
return startResult;
setTimeout(() => {
this._onProcessTitleChanged.fire(this._shellLaunchConfig.remoteAttach!.title);
}, 0);
}
this._startBarrier.open();
@@ -162,13 +157,62 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP
}
public input(data: string): void {
if (this._inReplay) {
return;
}
this._startBarrier.wait().then(_ => {
this._remoteTerminalChannel.sendInputToTerminalProcess(this._remoteTerminalId, data);
});
}
private setupTerminalEventListener(): void {
this._register(this._remoteTerminalChannel.onTerminalProcessEvent(this._remoteTerminalId)(event => {
switch (event.type) {
case 'ready':
return this._onProcessReady.fire({ pid: event.pid, cwd: event.cwd });
case 'titleChanged':
return this._onProcessTitleChanged.fire(event.title);
case 'data':
return this._onProcessData.fire({ data: event.data, sync: false });
case 'replay': {
try {
this._inReplay = true;
for (const e of event.events) {
if (e.cols !== 0 || e.rows !== 0) {
// never override with 0x0 as that is a marker for an unknown initial size
this._onProcessOverrideDimensions.fire({ cols: e.cols, rows: e.rows, forceExactSize: true });
}
this._onProcessData.fire({ data: e.data, sync: true });
}
} finally {
this._inReplay = false;
}
// remove size override
this._onProcessOverrideDimensions.fire(undefined);
return;
}
case 'exit':
return this._onProcessExit.fire(event.exitCode);
case 'execCommand':
return this._execCommand(event);
case 'orphan?': {
this._remoteTerminalChannel.orphanQuestionReply(this._remoteTerminalId);
return;
}
}
}));
}
public resize(cols: number, rows: number): void {
if (this._inReplay) {
return;
}
this._startBarrier.wait().then(_ => {
this._remoteTerminalChannel.resizeTerminalProcess(this._remoteTerminalId, cols, rows);
});
}
@@ -7,7 +7,7 @@ import type { Terminal as XTermTerminal } from 'xterm';
import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search';
import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11';
import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl';
import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError, ITerminalNativeWindowsDelegate, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal';
import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError, ITerminalNativeWindowsDelegate, LinuxDistro, IRemoteTerminalAttachTarget } from 'vs/workbench/contrib/terminal/common/terminal';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IProcessEnvironment, Platform } from 'vs/base/common/platform';
import { Event } from 'vs/base/common/event';
@@ -79,6 +79,7 @@ export interface ITerminalService {
terminalTabs: ITerminalTab[];
isProcessSupportRegistered: boolean;
initializeTerminals(): Promise<void>;
onActiveTabChanged: Event<void>;
onTabDisposed: Event<ITerminalTab>;
onInstanceCreated: Event<ITerminalInstance>;
@@ -89,7 +90,7 @@ export interface ITerminalService {
onInstanceRequestSpawnExtHostProcess: Event<ISpawnExtHostProcessRequest>;
onInstanceRequestStartExtensionTerminal: Event<IStartExtensionTerminalRequest>;
onInstancesChanged: Event<void>;
onInstanceTitleChanged: Event<ITerminalInstance>;
onInstanceTitleChanged: Event<ITerminalInstance | undefined>;
onActiveInstanceChanged: Event<ITerminalInstance | undefined>;
onRequestAvailableShells: Event<IAvailableShellsRequest>;
onDidRegisterProcessSupport: Event<void>;
@@ -180,6 +181,7 @@ export interface IRemoteTerminalService {
dispose(): void;
listTerminals(): Promise<IRemoteTerminalAttachTarget[]>;
createRemoteTerminalProcess(terminalId: number, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, configHelper: ITerminalConfigHelper,): Promise<ITerminalChildProcess>;
}
@@ -79,23 +79,9 @@ export class ToggleTerminalAction extends ToggleViewAction {
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IContextKeyService contextKeyService: IContextKeyService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITerminalService private readonly terminalService: ITerminalService
) {
super(id, label, TERMINAL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService);
}
async run() {
if (this.terminalService.isProcessSupportRegistered && this.terminalService.terminalInstances.length === 0) {
// If there is not yet an instance attempt to create it here so that we can suggest a
// new shell on Windows (and not do so when the panel is restored on reload).
const newTerminalInstance = this.terminalService.createTerminal(undefined);
const toDispose = newTerminalInstance.onProcessIdReady(() => {
newTerminalInstance.focus();
toDispose.dispose();
});
}
return super.run();
}
}
export class KillTerminalAction extends Action {
@@ -25,7 +25,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB
import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager';
import { IShellLaunchConfig, ITerminalDimensions, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal';
import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager';
@@ -94,7 +94,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
private _terminalA11yTreeFocusContextKey: IContextKey<boolean>;
private _cols: number = 0;
private _rows: number = 0;
private _dimensionsOverride: ITerminalDimensions | undefined;
private _dimensionsOverride: ITerminalDimensionsOverride | undefined;
private _windowsShellHelper: IWindowsShellHelper | undefined;
private _xtermReadyPromise: Promise<XTermTerminal>;
private _titleReadyPromise: Promise<string>;
@@ -117,12 +117,18 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
public get id(): number { return this._id; }
public get cols(): number {
if (this._dimensionsOverride && this._dimensionsOverride.cols) {
if (this._dimensionsOverride.forceExactSize) {
return this._dimensionsOverride.cols;
}
return Math.min(Math.max(this._dimensionsOverride.cols, 2), this._cols);
}
return this._cols;
}
public get rows(): number {
if (this._dimensionsOverride && this._dimensionsOverride.rows) {
if (this._dimensionsOverride.forceExactSize) {
return this._dimensionsOverride.rows;
}
return Math.min(Math.max(this._dimensionsOverride.rows, 2), this._rows);
}
return this._rows;
@@ -415,7 +421,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._xterm.onKey(e => this._onKey(e.key, e.domEvent));
this._xterm.onSelectionChange(async () => this._onSelectionChange());
this._processManager.onProcessData(data => this._onProcessData(data));
this._processManager.onProcessData(e => this._onProcessData(e));
this._xterm.onData(data => this._processManager.write(data));
this.processReady.then(async () => {
if (this._linkManager) {
@@ -882,11 +888,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper);
this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this));
this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode));
this._processManager.onProcessData(data => {
this._initialDataEvents?.push(data);
this._onData.fire(data);
this._processManager.onProcessData(ev => {
this._initialDataEvents?.push(ev.data);
this._onData.fire(ev.data);
});
this._processManager.onProcessOverrideDimensions(e => this.setDimensions(e));
this._processManager.onProcessOverrideDimensions(e => this.setDimensions(e, true));
this._processManager.onProcessResolvedShellLaunchConfig(e => this._setResolvedShellLaunchConfig(e));
this._processManager.onEnvironmentVariableInfoChanged(e => this._onEnvironmentVariableInfoChanged(e));
@@ -959,9 +965,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}
private _onProcessData(data: string): void {
const messageId = ++this._latestXtermWriteData;
this._xterm?.write(data, () => this._latestXtermParseData = messageId);
private _onProcessData(ev: IProcessDataEvent): void {
if (ev.sync) {
this._xtermCore?.writeSync(ev.data);
} else {
const messageId = ++this._latestXtermWriteData;
this._xterm?.write(ev.data, () => this._latestXtermParseData = messageId);
}
}
/**
@@ -1349,6 +1359,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
@debounce(50)
private async _resize(): Promise<void> {
this._resizeNow(false);
}
private async _resizeNow(immediate: boolean): Promise<void> {
let cols = this.cols;
let rows = this.rows;
@@ -1398,8 +1412,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}
await this._processManager.ptyProcessReady;
this._processManager.setDimensions(cols, rows);
if (immediate) {
// do not await, call setDimensions synchronously
this._processManager.setDimensions(cols, rows);
} else {
await this._processManager.ptyProcessReady;
this._processManager.setDimensions(cols, rows);
}
}
public setShellType(shellType: TerminalShellType) {
@@ -1442,9 +1461,18 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
return this._titleReadyPromise;
}
public setDimensions(dimensions: ITerminalDimensions | undefined): void {
public setDimensions(dimensions: ITerminalDimensionsOverride | undefined, immediate: boolean = false): void {
if (this._dimensionsOverride && this._dimensionsOverride.forceExactSize && !dimensions && this._rows === 0 && this._cols === 0) {
// this terminal never had a real size => keep the last dimensions override exact size
this._cols = this._dimensionsOverride.cols;
this._rows = this._dimensionsOverride.rows;
}
this._dimensionsOverride = dimensions;
this._resize();
if (immediate) {
this._resizeNow(true);
} else {
this._resize();
}
}
private _setResolvedShellLaunchConfig(shellLaunchConfig: IShellLaunchConfig): void {
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
@@ -23,8 +23,8 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = this._register(new Emitter<string>());
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensions | undefined>());
public get onProcessOverrideDimensions(): Event<ITerminalDimensions | undefined> { return this._onProcessOverrideDimensions.event; }
private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensionsOverride | undefined>());
public get onProcessOverrideDimensions(): Event<ITerminalDimensionsOverride | undefined> { return this._onProcessOverrideDimensions.event; }
private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>());
public get onProcessResolvedShellLaunchConfig(): Event<IShellLaunchConfig> { return this._onProcessResolvedShellLaunchConfig.event; }
@@ -6,7 +6,7 @@
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { env as processEnv } from 'vs/base/common/process';
import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment, ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal';
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';
@@ -69,14 +69,14 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
public get onProcessReady(): Event<void> { return this._onProcessReady.event; }
private readonly _onBeforeProcessData = this._register(new Emitter<IBeforeProcessDataEvent>());
public get onBeforeProcessData(): Event<IBeforeProcessDataEvent> { return this._onBeforeProcessData.event; }
private readonly _onProcessData = this._register(new Emitter<string>());
public get onProcessData(): Event<string> { return this._onProcessData.event; }
private readonly _onProcessData = this._register(new Emitter<IProcessDataEvent>());
public get onProcessData(): Event<IProcessDataEvent> { return this._onProcessData.event; }
private readonly _onProcessTitle = this._register(new Emitter<string>());
public get onProcessTitle(): Event<string> { return this._onProcessTitle.event; }
private readonly _onProcessExit = this._register(new Emitter<number | undefined>());
public get onProcessExit(): Event<number | undefined> { return this._onProcessExit.event; }
private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensions | undefined>());
public get onProcessOverrideDimensions(): Event<ITerminalDimensions | undefined> { return this._onProcessOverrideDimensions.event; }
private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensionsOverride | undefined>());
public get onProcessOverrideDimensions(): Event<ITerminalDimensionsOverride | undefined> { return this._onProcessOverrideDimensions.event; }
private readonly _onProcessOverrideShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>());
public get onProcessResolvedShellLaunchConfig(): Event<IShellLaunchConfig> { return this._onProcessOverrideShellLaunchConfig.event; }
private readonly _onEnvironmentVariableInfoChange = this._register(new Emitter<IEnvironmentVariableInfo>());
@@ -158,8 +158,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot();
const enableRemoteAgentTerminals = this._workspaceConfigurationService.getValue('terminal.integrated.serverSpawn');
if (enableRemoteAgentTerminals) {
const enableRemoteAgentTerminals = this._workspaceConfigurationService.getValue<boolean | undefined>('terminal.integrated.serverSpawn');
if (enableRemoteAgentTerminals !== false) {
this._process = await this._remoteTerminalService.createRemoteTerminalProcess(this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper);
} else {
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper);
@@ -171,11 +171,13 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
this.processState = ProcessState.LAUNCHING;
this._process.onProcessData(data => {
this._process.onProcessData(ev => {
const data = (typeof ev === 'string' ? ev : ev.data);
const sync = (typeof ev === 'string' ? false : ev.sync);
const beforeProcessDataEvent: IBeforeProcessDataEvent = { data };
this._onBeforeProcessData.fire(beforeProcessDataEvent);
if (beforeProcessDataEvent.data && beforeProcessDataEvent.data.length > 0) {
this._onProcessData.fire(beforeProcessDataEvent.data);
this._onProcessData.fire({ data: beforeProcessDataEvent.data, sync });
}
});
@@ -14,7 +14,7 @@ import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal';
import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider, IRemoteTerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput';
@@ -85,8 +85,8 @@ export class TerminalService implements ITerminalService {
public get onInstanceMaximumDimensionsChanged(): Event<ITerminalInstance> { return this._onInstanceMaximumDimensionsChanged.event; }
private readonly _onInstancesChanged = new Emitter<void>();
public get onInstancesChanged(): Event<void> { return this._onInstancesChanged.event; }
private readonly _onInstanceTitleChanged = new Emitter<ITerminalInstance>();
public get onInstanceTitleChanged(): Event<ITerminalInstance> { return this._onInstanceTitleChanged.event; }
private readonly _onInstanceTitleChanged = new Emitter<ITerminalInstance | undefined>();
public get onInstanceTitleChanged(): Event<ITerminalInstance | undefined> { return this._onInstanceTitleChanged.event; }
private readonly _onActiveInstanceChanged = new Emitter<ITerminalInstance | undefined>();
public get onActiveInstanceChanged(): Event<ITerminalInstance | undefined> { return this._onActiveInstanceChanged.event; }
private readonly _onTabDisposed = new Emitter<ITerminalTab>();
@@ -108,7 +108,9 @@ export class TerminalService implements ITerminalService {
@IConfigurationService private _configurationService: IConfigurationService,
@IViewsService private _viewsService: IViewsService,
@IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
@IConfigurationService private readonly _workspaceConfigurationService: IConfigurationService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService
) {
this._activeTabIndex = 0;
this._isShuttingDown = false;
@@ -221,7 +223,7 @@ export class TerminalService implements ITerminalService {
}
public getTabLabels(): string[] {
return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`);
return this._terminalTabs.map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`);
}
public getFindState(): FindReplaceState {
@@ -339,6 +341,31 @@ export class TerminalService implements ITerminalService {
}
}
public async initializeTerminals(): Promise<void> {
const enableRemoteAgentTerminals = this._workspaceConfigurationService.getValue<boolean | undefined>('terminal.integrated.serverSpawn');
if (!!this._environmentService.remoteAuthority && enableRemoteAgentTerminals !== false) {
const emptyTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, undefined);
this._terminalTabs.push(emptyTab);
this._onInstanceTitleChanged.fire(undefined);
const remoteTerms = await this._remoteTerminalService.listTerminals();
if (remoteTerms.length > 0) {
// Reattach to all remote terms
this.createTerminal({ remoteAttach: remoteTerms[0] }, emptyTab);
for (let term of remoteTerms.slice(1)) {
this.createTerminal({ remoteAttach: term });
}
} else {
// Remote, no terminals to attach to
this.createTerminal(undefined, emptyTab);
}
} else {
if (this.terminalInstances.length === 0) {
// Local, just create a terminal
this.createTerminal();
}
}
}
private _getInstanceFromGlobalInstanceIndex(index: number): { tab: ITerminalTab, tabIndex: number, instance: ITerminalInstance, localInstanceIndex: number } | null {
let currentTabIndex = 0;
while (index >= 0 && currentTabIndex < this._terminalTabs.length) {
@@ -610,7 +637,7 @@ export class TerminalService implements ITerminalService {
return instance;
}
public createTerminal(shell: IShellLaunchConfig = {}): ITerminalInstance {
public createTerminal(shell: IShellLaunchConfig = {}, terminalTab?: TerminalTab): ITerminalInstance {
if (!this.isProcessSupportRegistered) {
throw new Error('Could not create terminal when process support is not registered');
}
@@ -620,8 +647,14 @@ export class TerminalService implements ITerminalService {
this._initInstanceListeners(instance);
return instance;
}
const terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell);
this._terminalTabs.push(terminalTab);
if (terminalTab) {
terminalTab.addInstance(shell);
} else {
terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell);
this._terminalTabs.push(terminalTab);
}
const instance = terminalTab.terminalInstances[0];
terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed));
terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged));
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IShellLaunchConfig, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview';
@@ -228,7 +229,7 @@ export class TerminalTab extends Disposable implements ITerminalTab {
constructor(
private _container: HTMLElement | undefined,
shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance,
shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance | undefined,
@ITerminalService private readonly _terminalService: ITerminalService,
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
@IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService,
@@ -236,6 +237,18 @@ export class TerminalTab extends Disposable implements ITerminalTab {
) {
super();
if (shellLaunchConfigOrInstance) {
this.addInstance(shellLaunchConfigOrInstance);
}
this._activeInstanceIndex = 0;
if (this._container) {
this.attachToElement(this._container);
}
}
public addInstance(shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance): void {
let instance: ITerminalInstance;
if ('id' in shellLaunchConfigOrInstance) {
instance = shellLaunchConfigOrInstance;
@@ -244,11 +257,12 @@ export class TerminalTab extends Disposable implements ITerminalTab {
}
this._terminalInstances.push(instance);
this._initInstanceListeners(instance);
this._activeInstanceIndex = 0;
if (this._container) {
this.attachToElement(this._container);
if (this._splitPaneContainer) {
this._splitPaneContainer!.split(instance);
}
this._onInstancesChanged.fire();
}
public dispose(): void {
@@ -358,6 +372,10 @@ export class TerminalTab extends Disposable implements ITerminalTab {
}
public get title(): string {
if (!this.terminalInstances.length) {
return nls.localize('terminal.integrated.starting', "Starting...");
}
let title = this.terminalInstances[0].title;
for (let i = 1; i < this.terminalInstances.length; i++) {
if (this.terminalInstances[i].title) {
@@ -41,6 +41,7 @@ export class TerminalViewPane extends ViewPane {
private _terminalContainer: HTMLElement | undefined;
private _findWidget: TerminalFindWidget | undefined;
private _splitTerminalAction: IAction | undefined;
private _terminalsInitialized = false;
private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 };
constructor(
@@ -109,14 +110,21 @@ export class TerminalViewPane extends ViewPane {
this._register(this.onDidChangeBodyVisibility(visible => {
if (visible) {
const hadTerminals = this._terminalService.terminalInstances.length > 0;
if (!hadTerminals) {
this._terminalService.createTerminal();
const hadTerminals = !!this._terminalService.terminalTabs.length;
if (this._terminalsInitialized) {
if (!hadTerminals) {
this._terminalService.createTerminal();
}
} else {
this._terminalsInitialized = true;
this._terminalService.initializeTerminals();
}
this._updateTheme();
if (hadTerminals) {
this._terminalService.getActiveTab()?.setVisible(visible);
} else {
// TODO@Tyriar - this call seems unnecessary
this.layoutBody(this._bodyDimensions.height, this._bodyDimensions.width);
}
} else {
@@ -26,6 +26,8 @@ export interface XTermCore {
};
_onIntersectionChange: any;
};
writeSync(data: string | Uint8Array): void;
}
export interface IEventEmitter<T> {
@@ -116,6 +116,21 @@ export interface ISendCommandResultToTerminalProcessArguments {
payload: any;
}
export interface IOrphanQuestionReplyArgs {
id: number;
}
export interface IRemoteTerminalDescriptionDto {
id: number;
pid: number;
title: string;
cwd: string;
}
export interface ITriggerTerminalDataReplayArguments {
id: number;
}
export interface IRemoteTerminalProcessReadyEvent {
type: 'ready';
pid: number;
@@ -126,11 +141,16 @@ export interface IRemoteTerminalProcessTitleChangedEvent {
title: string;
}
export interface IRemoteTerminalProcessDataEvent {
type: 'data'
type: 'data';
data: string;
}
export interface ReplayEntry { cols: number; rows: number; data: string; }
export interface IRemoteTerminalProcessReplayEvent {
type: 'replay';
events: ReplayEntry[];
}
export interface IRemoteTerminalProcessExitEvent {
type: 'exit'
type: 'exit';
exitCode: number | undefined;
}
export interface IRemoteTerminalProcessExecCommandEvent {
@@ -139,12 +159,17 @@ export interface IRemoteTerminalProcessExecCommandEvent {
commandId: string;
commandArgs: any[];
}
export interface IRemoteTerminalProcessOrphanQuestionEvent {
type: 'orphan?';
}
export type IRemoteTerminalProcessEvent = (
IRemoteTerminalProcessReadyEvent
| IRemoteTerminalProcessTitleChangedEvent
| IRemoteTerminalProcessDataEvent
| IRemoteTerminalProcessReplayEvent
| IRemoteTerminalProcessExitEvent
| IRemoteTerminalProcessExecCommandEvent
| IRemoteTerminalProcessOrphanQuestionEvent
);
export interface IOnTerminalProcessEventArguments {
@@ -166,7 +191,7 @@ export class RemoteTerminalChannelClient {
) {
}
private _readSingleTemrinalConfiguration<T>(key: string): ISingleTerminalConfiguration<T> {
private _readSingleTerminalConfiguration<T>(key: string): ISingleTerminalConfiguration<T> {
const result = this._configurationService.inspect<T>(key);
return {
userValue: result.userValue,
@@ -178,18 +203,18 @@ export class RemoteTerminalChannelClient {
public async createTerminalProcess(shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ICreateTerminalProcessResult> {
const terminalConfig = this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION);
const configuration: ICompleteTerminalConfiguration = {
'terminal.integrated.automationShell.windows': this._readSingleTemrinalConfiguration('terminal.integrated.automationShell.windows'),
'terminal.integrated.automationShell.osx': this._readSingleTemrinalConfiguration('terminal.integrated.automationShell.osx'),
'terminal.integrated.automationShell.linux': this._readSingleTemrinalConfiguration('terminal.integrated.automationShell.linux'),
'terminal.integrated.shell.windows': this._readSingleTemrinalConfiguration('terminal.integrated.shell.windows'),
'terminal.integrated.shell.osx': this._readSingleTemrinalConfiguration('terminal.integrated.shell.osx'),
'terminal.integrated.shell.linux': this._readSingleTemrinalConfiguration('terminal.integrated.shell.linux'),
'terminal.integrated.shellArgs.windows': this._readSingleTemrinalConfiguration('terminal.integrated.shellArgs.windows'),
'terminal.integrated.shellArgs.osx': this._readSingleTemrinalConfiguration('terminal.integrated.shellArgs.osx'),
'terminal.integrated.shellArgs.linux': this._readSingleTemrinalConfiguration('terminal.integrated.shellArgs.linux'),
'terminal.integrated.env.windows': this._readSingleTemrinalConfiguration('terminal.integrated.env.windows'),
'terminal.integrated.env.osx': this._readSingleTemrinalConfiguration('terminal.integrated.env.osx'),
'terminal.integrated.env.linux': this._readSingleTemrinalConfiguration('terminal.integrated.env.linux'),
'terminal.integrated.automationShell.windows': this._readSingleTerminalConfiguration('terminal.integrated.automationShell.windows'),
'terminal.integrated.automationShell.osx': this._readSingleTerminalConfiguration('terminal.integrated.automationShell.osx'),
'terminal.integrated.automationShell.linux': this._readSingleTerminalConfiguration('terminal.integrated.automationShell.linux'),
'terminal.integrated.shell.windows': this._readSingleTerminalConfiguration('terminal.integrated.shell.windows'),
'terminal.integrated.shell.osx': this._readSingleTerminalConfiguration('terminal.integrated.shell.osx'),
'terminal.integrated.shell.linux': this._readSingleTerminalConfiguration('terminal.integrated.shell.linux'),
'terminal.integrated.shellArgs.windows': this._readSingleTerminalConfiguration('terminal.integrated.shellArgs.windows'),
'terminal.integrated.shellArgs.osx': this._readSingleTerminalConfiguration('terminal.integrated.shellArgs.osx'),
'terminal.integrated.shellArgs.linux': this._readSingleTerminalConfiguration('terminal.integrated.shellArgs.linux'),
'terminal.integrated.env.windows': this._readSingleTerminalConfiguration('terminal.integrated.env.windows'),
'terminal.integrated.env.osx': this._readSingleTerminalConfiguration('terminal.integrated.env.osx'),
'terminal.integrated.env.linux': this._readSingleTerminalConfiguration('terminal.integrated.env.linux'),
'terminal.integrated.inheritEnv': terminalConfig.inheritEnv,
'terminal.integrated.cwd': terminalConfig.cwd,
'terminal.integrated.detectLocale': terminalConfig.detectLocale,
@@ -306,4 +331,15 @@ export class RemoteTerminalChannelClient {
};
return this._channel.call<void>('$sendCommandResultToTerminalProcess', args);
}
public orphanQuestionReply(id: number): Promise<void> {
const args: IOrphanQuestionReplyArgs = {
id
};
return this._channel.call<void>('$orphanQuestionReply', args);
}
public listTerminals(): Promise<IRemoteTerminalDescriptionDto[]> {
return this._channel.call<IRemoteTerminalDescriptionDto[]>('$listTerminals');
}
}
@@ -165,6 +165,13 @@ export interface ITerminalEnvironment {
[key: string]: string | null;
}
export interface IRemoteTerminalAttachTarget {
id: number;
pid: number;
title: string;
cwd: string;
}
export interface IShellLaunchConfig {
/**
* The name of the terminal, if this is not set the name of the process will be used.
@@ -217,6 +224,11 @@ export interface IShellLaunchConfig {
*/
isExtensionTerminal?: boolean;
/**
* This is a terminal that attaches to an already running remote terminal.
*/
remoteAttach?: { id: number; pid: number; title: string; cwd: string; };
/**
* Whether the terminal process environment should be exactly as provided in
* `TerminalOptions.env`. When this is false (default), the environment will be based on the
@@ -268,6 +280,13 @@ export interface ITerminalDimensions {
readonly rows: number;
}
export interface ITerminalDimensionsOverride extends ITerminalDimensions {
/**
* indicate that xterm must receive these exact dimensions, even if they overflow the ui!
*/
forceExactSize?: boolean;
}
export interface ICommandTracker {
scrollToPreviousCommand(): void;
scrollToNextCommand(): void;
@@ -291,6 +310,11 @@ export interface IBeforeProcessDataEvent {
data: string;
}
export interface IProcessDataEvent {
data: string;
sync: boolean;
}
export interface ITerminalProcessManager extends IDisposable {
readonly processState: ProcessState;
readonly ptyProcessReady: Promise<void>;
@@ -302,10 +326,10 @@ export interface ITerminalProcessManager extends IDisposable {
readonly onProcessReady: Event<void>;
readonly onBeforeProcessData: Event<IBeforeProcessDataEvent>;
readonly onProcessData: Event<string>;
readonly onProcessData: Event<IProcessDataEvent>;
readonly onProcessTitle: Event<string>;
readonly onProcessExit: Event<number | undefined>;
readonly onProcessOverrideDimensions: Event<ITerminalDimensions | undefined>;
readonly onProcessOverrideDimensions: Event<ITerminalDimensionsOverride | undefined>;
readonly onProcessResolvedShellLaunchConfig: Event<IShellLaunchConfig>;
readonly onEnvironmentVariableInfoChanged: Event<IEnvironmentVariableInfo>;
@@ -416,11 +440,11 @@ export interface ITerminalLaunchError {
* child_process.ChildProcess node.js interface.
*/
export interface ITerminalChildProcess {
onProcessData: Event<string>;
onProcessData: Event<IProcessDataEvent | string>;
onProcessExit: Event<number | undefined>;
onProcessReady: Event<{ pid: number, cwd: string }>;
onProcessTitleChanged: Event<string>;
onProcessOverrideDimensions?: Event<ITerminalDimensions | undefined>;
onProcessOverrideDimensions?: Event<ITerminalDimensionsOverride | undefined>;
onProcessResolvedShellLaunchConfig?: Event<IShellLaunchConfig>;
/**
@@ -5,6 +5,7 @@
import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal';
interface TerminalDataBuffer extends IDisposable {
data: string[];
@@ -23,19 +24,20 @@ export class TerminalDataBufferer implements IDisposable {
}
}
startBuffering(id: number, event: Event<string>, throttleBy: number = 5): IDisposable {
startBuffering(id: number, event: Event<string | IProcessDataEvent>, throttleBy: number = 5): IDisposable {
let disposable: IDisposable;
disposable = event((e: string) => {
disposable = event((e: string | IProcessDataEvent) => {
const data = (typeof e === 'string' ? e : e.data);
let buffer = this._terminalBufferMap.get(id);
if (buffer) {
buffer.data.push(e);
buffer.data.push(data);
return;
}
const timeoutId = setTimeout(() => this._flushBuffer(id), throttleBy);
buffer = {
data: [e],
data: [data],
timeoutId: timeoutId,
dispose: () => {
clearTimeout(timeoutId);