提交 2a0d3d01 编写于 作者: D Daniel Imms

Support Terminal.exitStatus API

Fixes #62103
上级 7ab195a8
...@@ -97,6 +97,27 @@ suite('window namespace tests', () => { ...@@ -97,6 +97,27 @@ suite('window namespace tests', () => {
const terminal = window.createTerminal('b'); const terminal = window.createTerminal('b');
}); });
test('exitStatus.code should be set to undefined after a terminal is disposed', (done) => {
disposables.push(window.onDidOpenTerminal(term => {
try {
equal(term, terminal);
} catch (e) {
done(e);
}
disposables.push(window.onDidCloseTerminal(t => {
try {
deepEqual(t.exitStatus, { code: undefined });
} catch (e) {
done(e);
return;
}
done();
}));
terminal.dispose();
}));
const terminal = window.createTerminal();
});
// test('onDidChangeActiveTerminal should fire when new terminals are created', (done) => { // test('onDidChangeActiveTerminal should fire when new terminals are created', (done) => {
// const reg1 = window.onDidChangeActiveTerminal((active: Terminal | undefined) => { // const reg1 = window.onDidChangeActiveTerminal((active: Terminal | undefined) => {
// equal(active, terminal); // equal(active, terminal);
...@@ -362,9 +383,97 @@ suite('window namespace tests', () => { ...@@ -362,9 +383,97 @@ suite('window namespace tests', () => {
const pty: Pseudoterminal = { const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event, onDidWrite: writeEmitter.event,
onDidOverrideDimensions: overrideDimensionsEmitter.event, onDidOverrideDimensions: overrideDimensionsEmitter.event,
open: () => { open: () => overrideDimensionsEmitter.fire({ columns: 10, rows: 5 }),
overrideDimensionsEmitter.fire({ columns: 10, rows: 5 }); close: () => { }
}, };
const terminal = window.createTerminal({ name: 'foo', pty });
});
test('exitStatus.code should be set to the exit code (undefined)', (done) => {
disposables.push(window.onDidOpenTerminal(term => {
try {
equal(terminal, term);
equal(terminal.exitStatus, undefined);
} catch (e) {
done(e);
}
disposables.push(window.onDidCloseTerminal(t => {
try {
equal(terminal, t);
deepEqual(terminal.exitStatus, { code: undefined });
} catch (e) {
done(e);
return;
}
done();
}));
}));
const writeEmitter = new EventEmitter<string>();
const closeEmitter = new EventEmitter<number | undefined>();
const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidClose: closeEmitter.event,
open: () => closeEmitter.fire(),
close: () => { }
};
const terminal = window.createTerminal({ name: 'foo', pty });
});
test('exitStatus.code should be set to the exit code (zero)', (done) => {
disposables.push(window.onDidOpenTerminal(term => {
try {
equal(terminal, term);
equal(terminal.exitStatus, undefined);
} catch (e) {
done(e);
}
disposables.push(window.onDidCloseTerminal(t => {
try {
equal(terminal, t);
deepEqual(terminal.exitStatus, { code: 0 });
} catch (e) {
done(e);
return;
}
done();
}));
}));
const writeEmitter = new EventEmitter<string>();
const closeEmitter = new EventEmitter<number | undefined>();
const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidClose: closeEmitter.event,
open: () => closeEmitter.fire(0),
close: () => { }
};
const terminal = window.createTerminal({ name: 'foo', pty });
});
test('exitStatus.code should be set to the exit code (non-zero)', (done) => {
disposables.push(window.onDidOpenTerminal(term => {
try {
equal(terminal, term);
equal(terminal.exitStatus, undefined);
} catch (e) {
done(e);
}
disposables.push(window.onDidCloseTerminal(t => {
try {
equal(terminal, t);
deepEqual(terminal.exitStatus, { code: 22 });
} catch (e) {
done(e);
return;
}
done();
}));
}));
const writeEmitter = new EventEmitter<string>();
const closeEmitter = new EventEmitter<number | undefined>();
const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidClose: closeEmitter.event,
open: () => closeEmitter.fire(22),
close: () => { } close: () => { }
}; };
const terminal = window.createTerminal({ name: 'foo', pty }); const terminal = window.createTerminal({ name: 'foo', pty });
......
...@@ -674,6 +674,17 @@ declare module 'vscode' { ...@@ -674,6 +674,17 @@ declare module 'vscode' {
readonly data: string; readonly data: string;
} }
export interface TerminalExitStatus {
/**
* The exit code that a terminal exited with, it can have the following values:
* - Zero: the terminal process or custom execution succeeded.
* - Non-zero: the terminal process or custom execution failed.
* - `undefined`: the user forcefully closed the terminal or a custom execution exited
* without providing an exit code.
*/
readonly code: number | undefined;
}
namespace window { namespace window {
/** /**
* An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change. * An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change.
...@@ -695,6 +706,21 @@ declare module 'vscode' { ...@@ -695,6 +706,21 @@ declare module 'vscode' {
* created. * created.
*/ */
readonly dimensions: TerminalDimensions | undefined; readonly dimensions: TerminalDimensions | undefined;
/**
* The exit status of the terminal, this will be undefined while the terminal is active.
*
* **Example:** Show a notification with the exit code when the terminal exits with a
* non-zero exit code.
* ```typescript
* window.onDidCloseTerminal(t => {
* if (t.exitStatus && t.exitStatus.code) {
* vscode.window.showInformationMessage(`Exit code: ${t.exitStatus.code}`);
* }
* });
* ```
*/
readonly exitStatus: TerminalExitStatus | undefined;
} }
//#endregion //#endregion
......
...@@ -163,7 +163,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape ...@@ -163,7 +163,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
} }
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void { private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalClosed(terminalInstance.id); this._proxy.$acceptTerminalClosed(terminalInstance.id, terminalInstance.exitCode);
} }
private _onTerminalOpened(terminalInstance: ITerminalInstance): void { private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
...@@ -257,6 +257,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape ...@@ -257,6 +257,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd)); this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd));
} }
// TODO: This should be number | undefined
public $sendProcessExit(terminalId: number, exitCode: number): void { public $sendProcessExit(terminalId: number, exitCode: number): void {
this._getTerminalProcess(terminalId).then(e => e.emitExit(exitCode)); this._getTerminalProcess(terminalId).then(e => e.emitExit(exitCode));
this._terminalProcesses.delete(terminalId); this._terminalProcesses.delete(terminalId);
......
...@@ -1196,7 +1196,7 @@ export interface ITerminalDimensionsDto { ...@@ -1196,7 +1196,7 @@ export interface ITerminalDimensionsDto {
} }
export interface ExtHostTerminalServiceShape { export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void; $acceptTerminalClosed(id: number, exitCode: number | undefined): void;
$acceptTerminalOpened(id: number, name: string): void; $acceptTerminalOpened(id: number, name: string): void;
$acceptActiveTerminalChanged(id: number | null): void; $acceptActiveTerminalChanged(id: number | null): void;
$acceptTerminalProcessId(id: number, processId: number): void; $acceptTerminalProcessId(id: number, processId: number): void;
......
...@@ -97,6 +97,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi ...@@ -97,6 +97,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
private _cols: number | undefined; private _cols: number | undefined;
private _pidPromiseComplete: ((value: number | undefined) => any) | undefined; private _pidPromiseComplete: ((value: number | undefined) => any) | undefined;
private _rows: number | undefined; private _rows: number | undefined;
private _exitStatus: vscode.TerminalExitStatus | undefined;
public isOpen: boolean = false; public isOpen: boolean = false;
...@@ -138,6 +139,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi ...@@ -138,6 +139,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
this._name = name; this._name = name;
} }
public get exitStatus(): vscode.TerminalExitStatus | undefined {
return this._exitStatus;
}
public get dimensions(): vscode.TerminalDimensions | undefined { public get dimensions(): vscode.TerminalDimensions | undefined {
if (this._cols === undefined || this._rows === undefined) { if (this._cols === undefined || this._rows === undefined) {
return undefined; return undefined;
...@@ -148,6 +153,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi ...@@ -148,6 +153,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
}; };
} }
public setExitCode(code: number | undefined) {
this._exitStatus = Object.freeze({ code });
}
public setDimensions(cols: number, rows: number): boolean { public setDimensions(cols: number, rows: number): boolean {
if (cols === this._cols && rows === this._rows) { if (cols === this._cols && rows === this._rows) {
// Nothing changed // Nothing changed
...@@ -381,11 +390,12 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ ...@@ -381,11 +390,12 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
} }
} }
public async $acceptTerminalClosed(id: number): Promise<void> { public async $acceptTerminalClosed(id: number, exitCode: number | undefined): Promise<void> {
await this._getTerminalByIdEventually(id); await this._getTerminalByIdEventually(id);
const index = this._getTerminalObjectIndexById(this.terminals, id); const index = this._getTerminalObjectIndexById(this.terminals, id);
if (index !== null) { if (index !== null) {
const terminal = this._terminals.splice(index, 1)[0]; const terminal = this._terminals.splice(index, 1)[0];
terminal.setExitCode(exitCode);
this._onDidCloseTerminal.fire(terminal); this._onDidCloseTerminal.fire(terminal);
} }
} }
......
...@@ -236,6 +236,8 @@ export interface ITerminalInstance { ...@@ -236,6 +236,8 @@ export interface ITerminalInstance {
*/ */
onExit: Event<number | undefined>; onExit: Event<number | undefined>;
readonly exitCode: number | undefined;
processReady: Promise<void>; processReady: Promise<void>;
/** /**
......
...@@ -181,6 +181,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { ...@@ -181,6 +181,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
private _hadFocusOnExit: boolean; private _hadFocusOnExit: boolean;
private _isVisible: boolean; private _isVisible: boolean;
private _isDisposed: boolean; private _isDisposed: boolean;
private _exitCode: number | undefined;
private _skipTerminalCommands: string[]; private _skipTerminalCommands: string[];
private _shellType: TerminalShellType; private _shellType: TerminalShellType;
private _title: string = ''; private _title: string = '';
...@@ -227,6 +228,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { ...@@ -227,6 +228,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
// TODO: How does this work with detached processes? // TODO: How does this work with detached processes?
// TODO: Should this be an event as it can fire twice? // TODO: Should this be an event as it can fire twice?
public get processReady(): Promise<void> { return this._processManager.ptyProcessReady; } public get processReady(): Promise<void> { return this._processManager.ptyProcessReady; }
public get exitCode(): number | undefined { return this._exitCode; }
public get title(): string { return this._title; } public get title(): string { return this._title; }
public get hadFocusOnExit(): boolean { return this._hadFocusOnExit; } public get hadFocusOnExit(): boolean { return this._hadFocusOnExit; }
public get isTitleSetByProcess(): boolean { return !!this._messageTitleDisposable; } public get isTitleSetByProcess(): boolean { return !!this._messageTitleDisposable; }
...@@ -1011,6 +1013,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { ...@@ -1011,6 +1013,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._logService.debug(`Terminal process exit (id: ${this.id}) with code ${exitCode}`); this._logService.debug(`Terminal process exit (id: ${this.id}) with code ${exitCode}`);
this._exitCode = exitCode;
this._isExiting = true; this._isExiting = true;
let exitCodeMessage: string | undefined; let exitCodeMessage: string | undefined;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册