diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index e3e5042e131..d77dce7ecc6 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -793,5 +793,8 @@ export interface IShellIntegration { export enum TerminalExitReason { Unknown = 0, - Shutdown = 1 + Shutdown = 1, + Process = 2, + User = 3, + Extension = 4, } diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index f6421747044..b96d10e4131 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { StopWatch } from 'vs/base/common/stopwatch'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { IProcessProperty, IShellLaunchConfig, IShellLaunchConfigDto, ProcessPropertyType, TerminalLocation, TitleEventSource } from 'vs/platform/terminal/common/terminal'; +import { IProcessProperty, IShellLaunchConfig, IShellLaunchConfigDto, ProcessPropertyType, TerminalExitReason, TerminalLocation, TitleEventSource } from 'vs/platform/terminal/common/terminal'; import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering'; import { ITerminalEditorService, ITerminalExternalLinkProvider, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, ITerminalLink, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy'; @@ -183,7 +183,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } public async $dispose(id: ExtHostTerminalIdentifier): Promise { - (await this._getTerminalInstance(id))?.dispose(); + (await this._getTerminalInstance(id))?.dispose(TerminalExitReason.Extension); } public async $sendText(id: ExtHostTerminalIdentifier, text: string, addNewLine: boolean): Promise { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 93c514d238f..d2240a26fbd 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1273,7 +1273,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TaskPanelKind: extHostTypes.TaskPanelKind, TaskRevealKind: extHostTypes.TaskRevealKind, TaskScope: extHostTypes.TaskScope, - TerminalExitReason: extHostTypes.TerminalExitReason, TerminalLink: extHostTypes.TerminalLink, TerminalLocation: extHostTypes.TerminalLocation, TerminalProfile: extHostTypes.TerminalProfile, @@ -1342,7 +1341,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TabInputNotebook: extHostTypes.NotebookEditorTabInput, TabInputNotebookDiff: extHostTypes.NotebookDiffEditorTabInput, TabInputWebview: extHostTypes.WebviewEditorTabInput, - TabInputTerminal: extHostTypes.TerminalEditorTabInput + TabInputTerminal: extHostTypes.TerminalEditorTabInput, + TerminalExitReason: extHostTypes.TerminalExitReason }; }; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 745e7761ceb..aac7633b6f0 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1856,7 +1856,10 @@ export enum SourceControlInputBoxValidationType { export enum TerminalExitReason { Unknown = 0, - Shutdown = 1 + Shutdown = 1, + Process = 2, + User = 3, + Extension = 4 } export class TerminalLink implements vscode.TerminalLink { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index ea2e9200ecc..baefdbf7a67 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -653,14 +653,17 @@ export interface ITerminalInstance { /** * Dispose the terminal instance, removing it from the panel/service and freeing up resources. * - * @param isShutdown Whether the kill was triggered by lifecycle shutdown + * @param reason The reason why the terminal is being disposed */ - dispose(isShutdown?: boolean): void; + dispose(reason?: TerminalExitReason): void; /** - * Inform the process that the terminal is now detached. + * Informs the process that the terminal is now detached and + * then disposes the terminal. + * + * @param reason The reason why the terminal is being disposed */ - detachFromProcess(): Promise; + detachProcessAndDispose(reason: TerminalExitReason): Promise; /** * Check if anything is selected in terminal. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index ed78d4a79e8..7bc8ca063a0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -28,7 +28,7 @@ import { IListService } from 'vs/platform/list/browser/listService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { ITerminalProfile, TerminalLocation, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; +import { ITerminalProfile, TerminalExitReason, TerminalLocation, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; @@ -1062,7 +1062,7 @@ export function registerTerminalActions() { } async run(accessor: ServicesAccessor) { const terminalService = accessor.get(ITerminalService); - await terminalService.activeInstance?.detachFromProcess(); + await terminalService.activeInstance?.detachProcessAndDispose(TerminalExitReason.User); } }); registerAction2(class extends Action2 { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts index b80d8fae41c..839209a2cca 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditorInput.ts @@ -13,7 +13,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { ITerminalInstance, ITerminalInstanceService, terminalEditorId } from 'vs/workbench/contrib/terminal/browser/terminal'; import { getColorClass, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IShellLaunchConfig, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, TerminalExitReason, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ConfirmOnKill } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -167,7 +167,9 @@ export class TerminalEditorInput extends EditorInput { this._register(toDisposable(() => { if (!this._isDetached && !this._isShuttingDown) { - instance.dispose(); + // Will be ignored if triggered by onExit or onDisposed terminal events + // as disposed was already called + instance.dispose(TerminalExitReason.User); } })); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 1d6005c587e..695a359ef0e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1307,7 +1307,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return confirmation.confirmed; } - override dispose(isShutdown?: boolean): void { + override dispose(reason?: TerminalExitReason): void { if (this._isDisposed) { return; } @@ -1346,7 +1346,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._pressAnyKeyToCloseListener = undefined; } - this._exitReason = isShutdown ? TerminalExitReason.Shutdown : TerminalExitReason.Unknown; + this._exitReason = reason || TerminalExitReason.Unknown; this._processManager.dispose(); // Process manager dispose/shutdown doesn't fire process exit, trigger with undefined if it // hasn't happened yet @@ -1357,11 +1357,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { super.dispose(); } - async detachFromProcess(): Promise { + async detachProcessAndDispose(reason: TerminalExitReason): Promise { // Detach the process and dispose the instance, without the instance dispose the terminal // won't go away await this._processManager.detachFromProcess(); - this.dispose(); + this.dispose(reason); } focus(force?: boolean): void { @@ -1697,7 +1697,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } }); } else { - this.dispose(); + this.dispose(TerminalExitReason.Process); if (exitMessage) { const failedDuringLaunch = this._processManager.processState === ProcessState.KilledDuringLaunch; if (failedDuringLaunch || this._configHelper.config.showExitAlert) { @@ -1767,7 +1767,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (this._pressAnyKeyToCloseListener) { this._pressAnyKeyToCloseListener.dispose(); this._pressAnyKeyToCloseListener = undefined; - this.dispose(); + this.dispose(TerminalExitReason.Process); event.preventDefault(); } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 23d34d8018d..0a17ca0fc8f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -20,7 +20,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ICreateContributedTerminalProfileOptions, IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalLocation, TerminalLocationString, TitleEventSource } from 'vs/platform/terminal/common/terminal'; +import { ICreateContributedTerminalProfileOptions, IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalExitReason, TerminalLocation, TerminalLocationString, TitleEventSource } from 'vs/platform/terminal/common/terminal'; import { iconForeground } from 'vs/platform/theme/common/colorRegistry'; import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; @@ -282,7 +282,7 @@ export class TerminalService implements ITerminalService { } else { this._terminalGroupService.getGroupForInstance(instanceToDetach)?.removeInstance(instanceToDetach); } - await instanceToDetach.detachFromProcess(); + await instanceToDetach.detachProcessAndDispose(TerminalExitReason.User); await this._primaryBackend?.acceptDetachInstanceReply(e.requestId, persistentProcessId); } else { // will get rejected without a persistentProcessId to attach to @@ -371,7 +371,7 @@ export class TerminalService implements ITerminalService { } return new Promise(r => { instance.onExit(() => r()); - instance.dispose(); + instance.dispose(TerminalExitReason.User); }); } @@ -615,14 +615,14 @@ export class TerminalService implements ITerminalService { const shouldPersistTerminals = this._configHelper.config.enablePersistentSessions && e.reason === ShutdownReason.RELOAD; if (shouldPersistTerminals) { for (const instance of this.instances) { - instance.detachFromProcess(); + instance.detachProcessAndDispose(TerminalExitReason.Shutdown); } return; } // Force dispose of all terminal instances for (const instance of this.instances) { - instance.dispose(true); + instance.dispose(TerminalExitReason.Shutdown); } // Clear terminal layout info only when not persisting diff --git a/src/vscode-dts/vscode.proposed.terminalExitReason.d.ts b/src/vscode-dts/vscode.proposed.terminalExitReason.d.ts index bf6c83e0822..586f1448414 100644 --- a/src/vscode-dts/vscode.proposed.terminalExitReason.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalExitReason.d.ts @@ -12,17 +12,29 @@ declare module 'vscode' { */ export enum TerminalExitReason { /** - * Unknow reason, possible reasons could be: - * - Triggered by user action - * - Triggered by running process - * - Triggered by an extension + * Unknow reason. */ Unknown = 0, /** * The window closed/reloaded. */ - Shutdown = 1 + Shutdown = 1, + + /** + * The shell process exited. + */ + Process = 2, + + /** + * The user closed the terminal. + */ + User = 3, + + /** + * An extension disposed the terminal. + */ + Extension = 4, } export interface TerminalExitStatus {