Skip to content

Commit e2fcf63

Browse files
authored
Merge pull request microsoft#186072 from microsoft/tyriar/185393_5
Speed up terminal reconnection
2 parents e682c14 + 4b54454 commit e2fcf63

File tree

12 files changed

+118
-84
lines changed

12 files changed

+118
-84
lines changed

extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { deepStrictEqual, doesNotThrow, equal, strictEqual, throws } from 'assert';
6+
import { deepStrictEqual, doesNotThrow, equal, ok, strictEqual, throws } from 'assert';
77
import { ConfigurationTarget, Disposable, env, EnvironmentVariableCollection, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EnvironmentVariableScope, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode';
88
import { assertNoRpc, poll } from '../utils';
99

@@ -368,11 +368,12 @@ import { assertNoRpc, poll } from '../utils';
368368
try {
369369
if (closeEvents.length === 1) {
370370
deepStrictEqual(openEvents, ['test1']);
371-
deepStrictEqual(dataEvents, [{ name: 'test1', data: 'write1' }]);
371+
ok(dataEvents.some(e => e.name === 'test1' && e.data === 'write1'));
372372
deepStrictEqual(closeEvents, ['test1']);
373373
} else if (closeEvents.length === 2) {
374374
deepStrictEqual(openEvents, ['test1', 'test2']);
375-
deepStrictEqual(dataEvents, [{ name: 'test1', data: 'write1' }, { name: 'test2', data: 'write2' }]);
375+
ok(dataEvents.some(e => e.name === 'test1' && e.data === 'write1'));
376+
ok(dataEvents.some(e => e.name === 'test2' && e.data === 'write2'));
376377
deepStrictEqual(closeEvents, ['test1', 'test2']);
377378
}
378379
resolveOnceClosed!();

src/vs/platform/terminal/common/terminal.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ export interface ITerminalChildProcess {
680680

681681
onProcessData: Event<IProcessDataEvent | string>;
682682
onProcessReady: Event<IProcessReadyEvent>;
683+
onProcessReplayComplete?: Event<void>;
683684
onDidChangeProperty: Event<IProcessProperty<any>>;
684685
onProcessExit: Event<number | undefined>;
685686
onRestoreCommands?: Event<ISerializedCommandDetectionCapability>;

src/vs/platform/terminal/node/ptyService.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -747,11 +747,6 @@ class PersistentTerminalProcess extends Disposable {
747747
}
748748

749749
async attach(): Promise<void> {
750-
// Something wrong happened if the disconnect runner is not canceled, this likely means
751-
// multiple windows attempted to attach.
752-
if (!await this._isOrphaned()) {
753-
throw new Error(`Cannot attach to persistent process "${this._persistentProcessId}", it is already adopted`);
754-
}
755750
if (!this._disconnectRunner1.isScheduled() && !this._disconnectRunner2.isScheduled()) {
756751
this._logService.warn(`Persistent process "${this._persistentProcessId}": Process had no disconnect runners but was an orphan`);
757752
}

src/vs/workbench/contrib/terminal/browser/remotePty.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { Barrier } from 'vs/base/common/async';
77
import { Emitter } from 'vs/base/common/event';
88
import { Disposable } from 'vs/base/common/lifecycle';
9+
import { mark } from 'vs/base/common/performance';
910
import { URI } from 'vs/base/common/uri';
1011
import { IPtyHostProcessReplayEvent, ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
1112
import { IProcessDataEvent, ITerminalChildProcess, ITerminalLaunchError, IProcessProperty, IProcessPropertyMap, ProcessPropertyType, IProcessReadyEvent, ITerminalLogService } from 'vs/platform/terminal/common/terminal';
@@ -32,6 +33,8 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
3233

3334
private readonly _onProcessData = this._register(new Emitter<string | IProcessDataEvent>());
3435
readonly onProcessData = this._onProcessData.event;
36+
private readonly _onProcessReplayComplete = this._register(new Emitter<void>());
37+
readonly onProcessReplayComplete = this._onProcessReplayComplete.event;
3538
private readonly _onProcessReady = this._register(new Emitter<IProcessReadyEvent>());
3639
readonly onProcessReady = this._onProcessReady.event;
3740
private readonly _onDidChangeProperty = this._register(new Emitter<IProcessProperty<any>>());
@@ -176,6 +179,7 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
176179
}
177180

178181
async handleReplay(e: IPtyHostProcessReplayEvent) {
182+
mark(`code/terminal/willHandleReplay/${this.id}`);
179183
try {
180184
this._inReplay = true;
181185
for (const innerEvent of e.events) {
@@ -197,6 +201,9 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
197201

198202
// remove size override
199203
this._onDidChangeProperty.fire({ type: ProcessPropertyType.OverrideDimensions, value: undefined });
204+
205+
mark(`code/terminal/didHandleReplay/${this.id}`);
206+
this._onProcessReplayComplete.fire();
200207
}
201208

202209
handleOrphanQuestion() {

src/vs/workbench/contrib/terminal/browser/terminal.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -155,26 +155,29 @@ export interface ITerminalService extends ITerminalInstanceHost {
155155
readonly instances: readonly ITerminalInstance[];
156156
/** Gets detached terminal instances created via {@link createDetachedXterm}. */
157157
readonly detachedXterms: Iterable<IXtermTerminal>;
158-
configHelper: ITerminalConfigHelper;
159-
isProcessSupportRegistered: boolean;
158+
readonly configHelper: ITerminalConfigHelper;
159+
readonly defaultLocation: TerminalLocation;
160+
161+
readonly isProcessSupportRegistered: boolean;
160162
readonly connectionState: TerminalConnectionState;
161163
readonly whenConnected: Promise<void>;
162-
readonly defaultLocation: TerminalLocation;
164+
/** The number of restored terminal groups on startup. */
165+
readonly restoredGroupCount: number;
163166

164-
onDidChangeActiveGroup: Event<ITerminalGroup | undefined>;
165-
onDidDisposeGroup: Event<ITerminalGroup>;
166-
onDidCreateInstance: Event<ITerminalInstance>;
167-
onDidReceiveProcessId: Event<ITerminalInstance>;
168-
onDidChangeInstanceDimensions: Event<ITerminalInstance>;
169-
onDidMaximumDimensionsChange: Event<ITerminalInstance>;
170-
onDidRequestStartExtensionTerminal: Event<IStartExtensionTerminalRequest>;
171-
onDidChangeInstanceTitle: Event<ITerminalInstance | undefined>;
172-
onDidChangeInstanceIcon: Event<{ instance: ITerminalInstance; userInitiated: boolean }>;
173-
onDidChangeInstanceColor: Event<{ instance: ITerminalInstance; userInitiated: boolean }>;
174-
onDidChangeInstancePrimaryStatus: Event<ITerminalInstance>;
175-
onDidInputInstanceData: Event<ITerminalInstance>;
176-
onDidRegisterProcessSupport: Event<void>;
177-
onDidChangeConnectionState: Event<void>;
167+
readonly onDidChangeActiveGroup: Event<ITerminalGroup | undefined>;
168+
readonly onDidDisposeGroup: Event<ITerminalGroup>;
169+
readonly onDidCreateInstance: Event<ITerminalInstance>;
170+
readonly onDidReceiveProcessId: Event<ITerminalInstance>;
171+
readonly onDidChangeInstanceDimensions: Event<ITerminalInstance>;
172+
readonly onDidMaximumDimensionsChange: Event<ITerminalInstance>;
173+
readonly onDidRequestStartExtensionTerminal: Event<IStartExtensionTerminalRequest>;
174+
readonly onDidChangeInstanceTitle: Event<ITerminalInstance | undefined>;
175+
readonly onDidChangeInstanceIcon: Event<{ instance: ITerminalInstance; userInitiated: boolean }>;
176+
readonly onDidChangeInstanceColor: Event<{ instance: ITerminalInstance; userInitiated: boolean }>;
177+
readonly onDidChangeInstancePrimaryStatus: Event<ITerminalInstance>;
178+
readonly onDidInputInstanceData: Event<ITerminalInstance>;
179+
readonly onDidRegisterProcessSupport: Event<void>;
180+
readonly onDidChangeConnectionState: Event<void>;
178181

179182
/**
180183
* Creates a terminal.
@@ -228,9 +231,9 @@ export interface ITerminalService extends ITerminalInstanceHost {
228231
safeDisposeTerminal(instance: ITerminalInstance): Promise<void>;
229232

230233
getDefaultInstanceHost(): ITerminalInstanceHost;
231-
getInstanceHost(target: ITerminalLocationOptions | undefined): ITerminalInstanceHost;
234+
getInstanceHost(target: ITerminalLocationOptions | undefined): Promise<ITerminalInstanceHost>;
232235

233-
resolveLocation(location?: ITerminalLocationOptions): TerminalLocation | undefined;
236+
resolveLocation(location?: ITerminalLocationOptions): Promise<TerminalLocation | undefined>;
234237
setNativeDelegate(nativeCalls: ITerminalServiceNativeDelegate): void;
235238

236239
getEditingTerminal(): ITerminalInstance | undefined;
@@ -286,7 +289,7 @@ export interface ISerializedTerminalEditorInput extends ITerminalEditorInputObje
286289
export interface IDeserializedTerminalEditorInput extends ITerminalEditorInputObject {
287290
}
288291

289-
export type ITerminalLocationOptions = TerminalLocation | TerminalEditorLocation | { parentTerminal: ITerminalInstance } | { splitActiveTerminal: boolean };
292+
export type ITerminalLocationOptions = TerminalLocation | TerminalEditorLocation | { parentTerminal: Promise<ITerminalInstance> | ITerminalInstance } | { splitActiveTerminal: boolean };
290293

291294
export interface ICreateTerminalOptions {
292295
/**
@@ -533,6 +536,7 @@ export interface ITerminalInstance {
533536
onDisposed: Event<ITerminalInstance>;
534537

535538
onProcessIdReady: Event<ITerminalInstance>;
539+
onProcessReplayComplete: Event<void>;
536540
onRequestExtHostProcess: Event<ITerminalInstance>;
537541
onDimensionsChanged: Event<void>;
538542
onMaximumDimensionsChanged: Event<void>;

src/vs/workbench/contrib/terminal/browser/terminalActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ export function registerTerminalActions() {
10381038
const commandService = accessor.get(ICommandService);
10391039
const workspaceContextService = accessor.get(IWorkspaceContextService);
10401040
const options = convertOptionsOrProfileToOptions(optionsOrProfile);
1041-
const activeInstance = c.service.getInstanceHost(options?.location).activeInstance;
1041+
const activeInstance = (await c.service.getInstanceHost(options?.location)).activeInstance;
10421042
if (!activeInstance) {
10431043
return;
10441044
}

src/vs/workbench/contrib/terminal/browser/terminalInstance.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
288288
readonly onDisposed = this._onDisposed.event;
289289
private readonly _onProcessIdReady = this._register(new Emitter<ITerminalInstance>());
290290
readonly onProcessIdReady = this._onProcessIdReady.event;
291+
private readonly _onProcessReplayComplete = this._register(new Emitter<void>());
292+
readonly onProcessReplayComplete = this._onProcessReplayComplete.event;
291293
private readonly _onTitleChanged = this._register(new Emitter<ITerminalInstance>());
292294
readonly onTitleChanged = this._onTitleChanged.event;
293295
private readonly _onIconChanged = this._register(new Emitter<{ instance: ITerminalInstance; userInitiated: boolean }>());
@@ -1395,6 +1397,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
13951397
this._initialDataEvents?.push(ev.data);
13961398
this._onData.fire(ev.data);
13971399
});
1400+
processManager.onProcessReplayComplete(() => this._onProcessReplayComplete.fire());
13981401
processManager.onEnvironmentVariableInfoChanged(e => this._onEnvironmentVariableInfoChanged(e));
13991402
processManager.onPtyDisconnect(() => {
14001403
if (this.xterm) {

src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
108108
readonly onBeforeProcessData = this._onBeforeProcessData.event;
109109
private readonly _onProcessData = this._register(new Emitter<IProcessDataEvent>());
110110
readonly onProcessData = this._onProcessData.event;
111+
private readonly _onProcessReplayComplete = this._register(new Emitter<void>());
112+
readonly onProcessReplayComplete = this._onProcessReplayComplete.event;
111113
private readonly _onDidChangeProperty = this._register(new Emitter<IProcessProperty<any>>());
112114
readonly onDidChangeProperty = this._onDidChangeProperty.event;
113115
private readonly _onEnvironmentVariableInfoChange = this._register(new Emitter<IEnvironmentVariableInfo>());
@@ -375,10 +377,11 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
375377
this._onDidChangeProperty.fire({ type, value });
376378
})
377379
];
380+
if (newProcess.onProcessReplayComplete) {
381+
this._processListeners.push(newProcess.onProcessReplayComplete(() => this._onProcessReplayComplete.fire()));
382+
}
378383
if (newProcess.onRestoreCommands) {
379-
this._processListeners.push(newProcess.onRestoreCommands(e => {
380-
this._onRestoreCommands.fire(e);
381-
}));
384+
this._processListeners.push(newProcess.onRestoreCommands(e => this._onRestoreCommands.fire(e)));
382385
}
383386
setTimeout(() => {
384387
if (this.processState === ProcessState.Launching) {

0 commit comments

Comments
 (0)