Skip to content

Commit 29a4da1

Browse files
authored
remove terminal data when terminal is disposed of for reconnected terminals (microsoft#155233)
1 parent c1956b8 commit 29a4da1

File tree

4 files changed

+64
-44
lines changed

4 files changed

+64
-44
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export interface IPtyHostAttachTarget {
168168
fixedDimensions: IFixedTerminalDimensions | undefined;
169169
environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined;
170170
reconnectionOwner?: string;
171-
task?: { label: string; id: string; lastTask?: string; group?: string };
171+
task?: { label: string; id: string; lastTask: string; group?: string };
172172
}
173173

174174
export enum TitleEventSource {
@@ -469,7 +469,7 @@ export interface IShellLaunchConfig {
469469
/**
470470
* This is a terminal that attaches to an already running terminal.
471471
*/
472-
attachPersistentProcess?: { id: number; findRevivedId?: boolean; pid: number; title: string; titleSource: TitleEventSource; cwd: string; icon?: TerminalIcon; color?: string; hasChildProcesses?: boolean; fixedDimensions?: IFixedTerminalDimensions; environmentVariableCollections?: ISerializableEnvironmentVariableCollections; reconnectionOwner?: string; task?: { label: string; id: string; lastTask?: string; group?: string } };
472+
attachPersistentProcess?: { id: number; findRevivedId?: boolean; pid: number; title: string; titleSource: TitleEventSource; cwd: string; icon?: TerminalIcon; color?: string; hasChildProcesses?: boolean; fixedDimensions?: IFixedTerminalDimensions; environmentVariableCollections?: ISerializableEnvironmentVariableCollections; reconnectionOwner?: string; task?: { label: string; id: string; lastTask: string; group?: string } };
473473

474474
/**
475475
* Whether the terminal process environment should be exactly as provided in
@@ -544,7 +544,7 @@ export interface IShellLaunchConfig {
544544
/**
545545
* The task associated with this terminal
546546
*/
547-
task?: { lastTask?: string; group?: string; label: string; id: string };
547+
task?: { lastTask: string; group?: string; label: string; id: string };
548548
}
549549

550550
export interface ICreateContributedTerminalProfileOptions {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export interface IProcessDetails {
6161
fixedDimensions: IFixedTerminalDimensions | undefined;
6262
environmentVariableCollections: ISerializableEnvironmentVariableCollections | undefined;
6363
reconnectionOwner?: string;
64-
task?: { label: string; id: string; lastTask?: string; group?: string };
64+
task?: { label: string; id: string; lastTask: string; group?: string };
6565
}
6666

6767
export type ITerminalTabLayoutInfoDto = IRawTerminalTabLayoutInfo<IProcessDetails>;

src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
200200

201201
private static _nextHandle: number = 0;
202202

203+
private _tasksReconnected: boolean = false;
203204
private _schemaVersion: JsonSchemaVersion | undefined;
204205
private _executionEngine: ExecutionEngine | undefined;
205206
private _workspaceFolders: IWorkspaceFolder[] | undefined;
@@ -337,11 +338,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
337338
processContext.set(process && !isVirtual);
338339
}
339340
this._onDidRegisterSupportedExecutions.fire();
341+
if (this._jsonTasksSupported && !this._tasksReconnected) {
342+
this._reconnectTasks();
343+
}
340344
}
341345

342-
private async _restartTasks(): Promise<void> {
346+
private async _reconnectTasks(): Promise<void> {
343347
const recentlyUsedTasks = await this.readRecentTasks();
344-
if (!recentlyUsedTasks) {
348+
if (!recentlyUsedTasks.length) {
345349
return;
346350
}
347351
for (const task of recentlyUsedTasks) {
@@ -354,6 +358,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
354358
this.run(task, undefined, TaskRunSource.Reconnect);
355359
}
356360
}
361+
this._tasksReconnected = true;
357362
}
358363

359364
public get onDidStateChange(): Event<ITaskEvent> {
@@ -618,7 +623,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
618623
return infosCount > 0;
619624
}
620625

621-
public async registerTaskSystem(key: string, info: ITaskSystemInfo): Promise<void> {
626+
public registerTaskSystem(key: string, info: ITaskSystemInfo): void {
622627
// Ideally the Web caller of registerRegisterTaskSystem would use the correct key.
623628
// However, the caller doesn't know about the workspace folders at the time of the call, even though we know about them here.
624629
if (info.platform === Platform.Platform.Web) {
@@ -638,7 +643,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
638643

639644
if (this.hasTaskSystemInfo) {
640645
this._onDidChangeTaskSystemInfo.fire();
641-
await this._restartTasks();
642646
}
643647
}
644648

src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ interface IActiveTerminalData {
6363
state?: TaskEventKind;
6464
}
6565

66+
const ReconnectionType = 'Task';
67+
6668
class InstanceManager {
6769
private _currentInstances: number = 0;
6870
private _counter: number = 0;
@@ -255,6 +257,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
255257
this._logService.trace(`Could not reconnect to terminal ${terminal.instanceId} with process details ${terminal.shellLaunchConfig.attachPersistentProcess}`);
256258
}
257259
}
260+
this._hasReconnected = true;
258261
}
259262

260263
public get onDidStateChange(): Event<ITaskEvent> {
@@ -270,10 +273,13 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
270273
}
271274

272275
public reconnect(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult | undefined {
273-
const terminals = this._terminalService.getReconnectedTerminals('Task');
276+
const terminals = this._terminalService.getReconnectedTerminals(ReconnectionType);
277+
if (!terminals || terminals?.length === 0) {
278+
return;
279+
}
274280
if (!this._hasReconnected && terminals && terminals.length > 0) {
281+
this._reviveTerminals();
275282
this._reconnectToTerminals(terminals);
276-
this._hasReconnected = true;
277283
}
278284
if (this._tasksToReconnect.includes(task._id)) {
279285
this._lastTask = new VerifiedTask(task, resolver, trigger);
@@ -449,12 +455,14 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
449455
}
450456
}
451457

452-
private _removeFromActiveTasks(task: Task): void {
453-
if (!this._activeTasks[task.getMapKey()]) {
458+
private _removeFromActiveTasks(task: Task | string): void {
459+
const key = typeof task === 'string' ? task : task.getMapKey();
460+
if (!this._activeTasks[key]) {
454461
return;
455462
}
456-
delete this._activeTasks[task.getMapKey()];
457-
this._removeInstances(task);
463+
const taskToRemove = this._activeTasks[key];
464+
delete this._activeTasks[key];
465+
this._removeInstances(taskToRemove.task);
458466
}
459467

460468
private _fireTaskEvent(event: ITaskEvent) {
@@ -1059,7 +1067,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
10591067
const isShellCommand = task.command.runtime === RuntimeType.Shell;
10601068
const needsFolderQualification = this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
10611069
const terminalName = this._createTerminalName(task);
1062-
const type = 'Task';
1070+
const type = ReconnectionType;
10631071
const originalCommand = task.command.name;
10641072
if (isShellCommand) {
10651073
let os: Platform.OperatingSystem;
@@ -1289,17 +1297,40 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
12891297
}
12901298

12911299
private _reviveTerminals(): void {
1292-
if (Object.entries(this._terminals).length === 0) {
1293-
for (const terminal of this._terminalService.instances) {
1294-
if (terminal.shellLaunchConfig.attachPersistentProcess?.task?.lastTask) {
1295-
this._terminals[terminal.instanceId] = { lastTask: terminal.shellLaunchConfig.attachPersistentProcess.task.lastTask, group: terminal.shellLaunchConfig.attachPersistentProcess.task.group, terminal };
1296-
}
1300+
if (Object.entries(this._terminals).length > 0) {
1301+
return;
1302+
}
1303+
const terminals = this._terminalService.getReconnectedTerminals(ReconnectionType)?.filter(t => !t.isDisposed);
1304+
if (!terminals?.length) {
1305+
return;
1306+
}
1307+
for (const terminal of terminals) {
1308+
const task = terminal.shellLaunchConfig.attachPersistentProcess?.task;
1309+
if (!task) {
1310+
continue;
12971311
}
1312+
const terminalData = { lastTask: task.lastTask, group: task.group, terminal };
1313+
this._terminals[terminal.instanceId] = terminalData;
1314+
terminal.onDisposed(() => this._deleteTaskAndTerminal(terminal, terminalData));
1315+
}
1316+
}
1317+
1318+
private _deleteTaskAndTerminal(terminal: ITerminalInstance, terminalData: ITerminalData): void {
1319+
delete this._terminals[terminal.instanceId];
1320+
delete this._sameTaskTerminals[terminalData.lastTask];
1321+
this._idleTaskTerminals.delete(terminalData.lastTask);
1322+
// Delete the task now as a work around for cases when the onExit isn't fired.
1323+
// This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected.
1324+
// For correct terminal re-use, the task needs to be deleted immediately.
1325+
// Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate.
1326+
const mapKey = terminalData.lastTask;
1327+
this._removeFromActiveTasks(mapKey);
1328+
if (this._busyTasks[mapKey]) {
1329+
delete this._busyTasks[mapKey];
12981330
}
12991331
}
13001332

13011333
private async _createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder | undefined): Promise<[ITerminalInstance | undefined, TaskError | undefined]> {
1302-
this._reviveTerminals();
13031334
const platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform;
13041335
const options = await this._resolveOptions(resolver, task.command.options);
13051336
const presentationOptions = task.command.presentation;
@@ -1398,29 +1429,14 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
13981429
}
13991430

14001431
this._terminalCreationQueue = this._terminalCreationQueue.then(() => this._doCreateTerminal(group, launchConfigs!));
1401-
const result: ITerminalInstance = (await this._terminalCreationQueue)!;
1402-
result.shellLaunchConfig.task = { lastTask: taskKey, group, label: task._label, id: task._id };
1403-
result.shellLaunchConfig.reconnectionOwner = 'Task';
1404-
const terminalKey = result.instanceId.toString();
1405-
result.onDisposed(() => {
1406-
const terminalData = this._terminals[terminalKey];
1407-
if (terminalData) {
1408-
delete this._terminals[terminalKey];
1409-
delete this._sameTaskTerminals[terminalData.lastTask];
1410-
this._idleTaskTerminals.delete(terminalData.lastTask);
1411-
// Delete the task now as a work around for cases when the onExit isn't fired.
1412-
// This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected.
1413-
// For correct terminal re-use, the task needs to be deleted immediately.
1414-
// Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate.
1415-
const mapKey = task.getMapKey();
1416-
this._removeFromActiveTasks(task);
1417-
if (this._busyTasks[mapKey]) {
1418-
delete this._busyTasks[mapKey];
1419-
}
1420-
}
1421-
});
1422-
this._terminals[terminalKey] = { terminal: result, lastTask: taskKey, group };
1423-
return [result, undefined];
1432+
const terminal: ITerminalInstance = (await this._terminalCreationQueue)!;
1433+
terminal.shellLaunchConfig.task = { lastTask: taskKey, group, label: task._label, id: task._id };
1434+
terminal.shellLaunchConfig.reconnectionOwner = ReconnectionType;
1435+
const terminalKey = terminal.instanceId.toString();
1436+
const terminalData = { terminal: terminal, lastTask: taskKey, group };
1437+
terminal.onDisposed(() => this._deleteTaskAndTerminal(terminal, terminalData));
1438+
this._terminals[terminalKey] = terminalData;
1439+
return [terminal, undefined];
14241440
}
14251441

14261442
private _buildShellCommandLine(platform: Platform.Platform, shellExecutable: string, shellOptions: IShellConfiguration | undefined, command: CommandString, originalCommand: CommandString | undefined, args: CommandString[]): string {

0 commit comments

Comments
 (0)