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

Support Terminal.exitStatus API

Fixes #62103
上级 7ab195a8
......@@ -97,6 +97,27 @@ suite('window namespace tests', () => {
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) => {
// const reg1 = window.onDidChangeActiveTerminal((active: Terminal | undefined) => {
// equal(active, terminal);
......@@ -362,9 +383,97 @@ suite('window namespace tests', () => {
const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidOverrideDimensions: overrideDimensionsEmitter.event,
open: () => {
overrideDimensionsEmitter.fire({ columns: 10, rows: 5 });
},
open: () => 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: () => { }
};
const terminal = window.createTerminal({ name: 'foo', pty });
......
......@@ -674,6 +674,17 @@ declare module 'vscode' {
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 {
/**
* An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change.
......@@ -695,6 +706,21 @@ declare module 'vscode' {
* created.
*/
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
......
......@@ -163,7 +163,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalClosed(terminalInstance.id);
this._proxy.$acceptTerminalClosed(terminalInstance.id, terminalInstance.exitCode);
}
private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
......@@ -257,6 +257,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd));
}
// TODO: This should be number | undefined
public $sendProcessExit(terminalId: number, exitCode: number): void {
this._getTerminalProcess(terminalId).then(e => e.emitExit(exitCode));
this._terminalProcesses.delete(terminalId);
......
......@@ -1196,7 +1196,7 @@ export interface ITerminalDimensionsDto {
}
export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalClosed(id: number, exitCode: number | undefined): void;
$acceptTerminalOpened(id: number, name: string): void;
$acceptActiveTerminalChanged(id: number | null): void;
$acceptTerminalProcessId(id: number, processId: number): void;
......
......@@ -97,6 +97,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
private _cols: number | undefined;
private _pidPromiseComplete: ((value: number | undefined) => any) | undefined;
private _rows: number | undefined;
private _exitStatus: vscode.TerminalExitStatus | undefined;
public isOpen: boolean = false;
......@@ -138,6 +139,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
this._name = name;
}
public get exitStatus(): vscode.TerminalExitStatus | undefined {
return this._exitStatus;
}
public get dimensions(): vscode.TerminalDimensions | undefined {
if (this._cols === undefined || this._rows === undefined) {
return undefined;
......@@ -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 {
if (cols === this._cols && rows === this._rows) {
// Nothing changed
......@@ -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);
const index = this._getTerminalObjectIndexById(this.terminals, id);
if (index !== null) {
const terminal = this._terminals.splice(index, 1)[0];
terminal.setExitCode(exitCode);
this._onDidCloseTerminal.fire(terminal);
}
}
......
......@@ -236,6 +236,8 @@ export interface ITerminalInstance {
*/
onExit: Event<number | undefined>;
readonly exitCode: number | undefined;
processReady: Promise<void>;
/**
......
......@@ -181,6 +181,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
private _hadFocusOnExit: boolean;
private _isVisible: boolean;
private _isDisposed: boolean;
private _exitCode: number | undefined;
private _skipTerminalCommands: string[];
private _shellType: TerminalShellType;
private _title: string = '';
......@@ -227,6 +228,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
// TODO: How does this work with detached processes?
// TODO: Should this be an event as it can fire twice?
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 hadFocusOnExit(): boolean { return this._hadFocusOnExit; }
public get isTitleSetByProcess(): boolean { return !!this._messageTitleDisposable; }
......@@ -1011,6 +1013,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._logService.debug(`Terminal process exit (id: ${this.id}) with code ${exitCode}`);
this._exitCode = exitCode;
this._isExiting = true;
let exitCodeMessage: string | undefined;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册