未验证 提交 e41c71b0 编写于 作者: D Daniel Imms 提交者: GitHub

Merge branch 'master' into tyriar/63052

...@@ -121,6 +121,27 @@ suite('window namespace tests', () => { ...@@ -121,6 +121,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);
...@@ -386,9 +407,97 @@ suite('window namespace tests', () => { ...@@ -386,9 +407,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.
...@@ -702,6 +713,21 @@ declare module 'vscode' { ...@@ -702,6 +713,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 {
...@@ -264,7 +264,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape ...@@ -264,7 +264,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd)); this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd));
} }
public $sendProcessExit(terminalId: number, exitCode: number): void { public $sendProcessExit(terminalId: number, exitCode: number | undefined): 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);
} }
......
...@@ -420,7 +420,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable { ...@@ -420,7 +420,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$sendProcessTitle(terminalId: number, title: string): void; $sendProcessTitle(terminalId: number, title: string): void;
$sendProcessData(terminalId: number, data: string): void; $sendProcessData(terminalId: number, data: string): void;
$sendProcessReady(terminalId: number, pid: number, cwd: string): void; $sendProcessReady(terminalId: number, pid: number, cwd: string): void;
$sendProcessExit(terminalId: number, exitCode: number): void; $sendProcessExit(terminalId: number, exitCode: number | undefined): void;
$sendProcessInitialCwd(terminalId: number, cwd: string): void; $sendProcessInitialCwd(terminalId: number, cwd: string): void;
$sendProcessCwd(terminalId: number, initialCwd: string): void; $sendProcessCwd(terminalId: number, initialCwd: string): void;
$sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void; $sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void;
...@@ -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, shellLaunchConfig: IShellLaunchConfigDto): void; $acceptTerminalOpened(id: number, name: string, shellLaunchConfig: IShellLaunchConfigDto): 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;
...@@ -140,6 +141,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi ...@@ -140,6 +141,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;
...@@ -150,6 +155,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi ...@@ -150,6 +155,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
...@@ -216,8 +225,8 @@ class ApiRequest { ...@@ -216,8 +225,8 @@ class ApiRequest {
export class ExtHostPseudoterminal implements ITerminalChildProcess { export class ExtHostPseudoterminal implements ITerminalChildProcess {
private readonly _onProcessData = new Emitter<string>(); private readonly _onProcessData = new Emitter<string>();
public readonly onProcessData: Event<string> = this._onProcessData.event; public readonly onProcessData: Event<string> = this._onProcessData.event;
private readonly _onProcessExit = new Emitter<number>(); private readonly _onProcessExit = new Emitter<number | undefined>();
public readonly onProcessExit: Event<number> = this._onProcessExit.event; public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
private readonly _onProcessReady = new Emitter<{ pid: number, cwd: string }>(); private readonly _onProcessReady = new Emitter<{ pid: number, cwd: string }>();
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = new Emitter<string>(); private readonly _onProcessTitleChanged = new Emitter<string>();
...@@ -259,7 +268,9 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess { ...@@ -259,7 +268,9 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
// Attach the listeners // Attach the listeners
this._pty.onDidWrite(e => this._onProcessData.fire(e)); this._pty.onDidWrite(e => this._onProcessData.fire(e));
if (this._pty.onDidClose) { if (this._pty.onDidClose) {
this._pty.onDidClose(e => this._onProcessExit.fire(e || 0)); this._pty.onDidClose((e: number | undefined = undefined) => {
this._onProcessExit.fire(e);
});
} }
if (this._pty.onDidOverrideDimensions) { if (this._pty.onDidOverrideDimensions) {
this._pty.onDidOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e ? { cols: e.columns, rows: e.rows } : e)); this._pty.onDidOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e ? { cols: e.columns, rows: e.rows } : e));
...@@ -387,11 +398,12 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ ...@@ -387,11 +398,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);
} }
} }
...@@ -524,7 +536,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ ...@@ -524,7 +536,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
return id; return id;
} }
private _onProcessExit(id: number, exitCode: number): void { private _onProcessExit(id: number, exitCode: number | undefined): void {
this._bufferer.stopBuffering(id); this._bufferer.stopBuffering(id);
// Remove process reference // Remove process reference
......
...@@ -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;
......
...@@ -17,8 +17,8 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal ...@@ -17,8 +17,8 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal
private readonly _onProcessData = this._register(new Emitter<string>()); private readonly _onProcessData = this._register(new Emitter<string>());
public readonly onProcessData: Event<string> = this._onProcessData.event; public readonly onProcessData: Event<string> = this._onProcessData.event;
private readonly _onProcessExit = this._register(new Emitter<number>()); private readonly _onProcessExit = this._register(new Emitter<number | undefined>());
public readonly onProcessExit: Event<number> = this._onProcessExit.event; public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
private readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>()); private readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>());
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = this._register(new Emitter<string>()); private readonly _onProcessTitleChanged = this._register(new Emitter<string>());
...@@ -87,7 +87,7 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal ...@@ -87,7 +87,7 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal
this._onProcessReady.fire({ pid, cwd }); this._onProcessReady.fire({ pid, cwd });
} }
public emitExit(exitCode: number): void { public emitExit(exitCode: number | undefined): void {
this._onProcessExit.fire(exitCode); this._onProcessExit.fire(exitCode);
this.dispose(); this.dispose();
} }
......
...@@ -67,8 +67,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce ...@@ -67,8 +67,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
public get onProcessData(): Event<string> { return this._onProcessData.event; } public get onProcessData(): Event<string> { return this._onProcessData.event; }
private readonly _onProcessTitle = this._register(new Emitter<string>()); private readonly _onProcessTitle = this._register(new Emitter<string>());
public get onProcessTitle(): Event<string> { return this._onProcessTitle.event; } public get onProcessTitle(): Event<string> { return this._onProcessTitle.event; }
private readonly _onProcessExit = this._register(new Emitter<number>()); private readonly _onProcessExit = this._register(new Emitter<number | undefined>());
public get onProcessExit(): Event<number> { return this._onProcessExit.event; } public get onProcessExit(): Event<number | undefined> { return this._onProcessExit.event; }
private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensions | undefined>()); private readonly _onProcessOverrideDimensions = this._register(new Emitter<ITerminalDimensions | undefined>());
public get onProcessOverrideDimensions(): Event<ITerminalDimensions | undefined> { return this._onProcessOverrideDimensions.event; } public get onProcessOverrideDimensions(): Event<ITerminalDimensions | undefined> { return this._onProcessOverrideDimensions.event; }
private readonly _onProcessOverrideShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>()); private readonly _onProcessOverrideShellLaunchConfig = this._register(new Emitter<IShellLaunchConfig>());
...@@ -285,7 +285,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce ...@@ -285,7 +285,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
return Promise.resolve(this._latency); return Promise.resolve(this._latency);
} }
private _onExit(exitCode: number): void { private _onExit(exitCode: number | undefined): void {
this._process = null; this._process = null;
// If the process is marked as launching then mark the process as killed // If the process is marked as launching then mark the process as killed
......
...@@ -285,7 +285,7 @@ export interface ITerminalProcessManager extends IDisposable { ...@@ -285,7 +285,7 @@ export interface ITerminalProcessManager extends IDisposable {
readonly onBeforeProcessData: Event<IBeforeProcessDataEvent>; readonly onBeforeProcessData: Event<IBeforeProcessDataEvent>;
readonly onProcessData: Event<string>; readonly onProcessData: Event<string>;
readonly onProcessTitle: Event<string>; readonly onProcessTitle: Event<string>;
readonly onProcessExit: Event<number>; readonly onProcessExit: Event<number | undefined>;
readonly onProcessOverrideDimensions: Event<ITerminalDimensions | undefined>; readonly onProcessOverrideDimensions: Event<ITerminalDimensions | undefined>;
readonly onProcessResolvedShellLaunchConfig: Event<IShellLaunchConfig>; readonly onProcessResolvedShellLaunchConfig: Event<IShellLaunchConfig>;
...@@ -324,7 +324,7 @@ export interface ITerminalProcessExtHostProxy extends IDisposable { ...@@ -324,7 +324,7 @@ export interface ITerminalProcessExtHostProxy extends IDisposable {
emitData(data: string): void; emitData(data: string): void;
emitTitle(title: string): void; emitTitle(title: string): void;
emitReady(pid: number, cwd: string): void; emitReady(pid: number, cwd: string): void;
emitExit(exitCode: number): void; emitExit(exitCode: number | undefined): void;
emitOverrideDimensions(dimensions: ITerminalDimensions | undefined): void; emitOverrideDimensions(dimensions: ITerminalDimensions | undefined): void;
emitResolvedShellLaunchConfig(shellLaunchConfig: IShellLaunchConfig): void; emitResolvedShellLaunchConfig(shellLaunchConfig: IShellLaunchConfig): void;
emitInitialCwd(initialCwd: string): void; emitInitialCwd(initialCwd: string): void;
...@@ -388,7 +388,7 @@ export interface IWindowsShellHelper extends IDisposable { ...@@ -388,7 +388,7 @@ export interface IWindowsShellHelper extends IDisposable {
*/ */
export interface ITerminalChildProcess { export interface ITerminalChildProcess {
onProcessData: Event<string>; onProcessData: Event<string>;
onProcessExit: Event<number>; onProcessExit: Event<number | undefined>;
onProcessReady: Event<{ pid: number, cwd: string }>; onProcessReady: Event<{ pid: number, cwd: string }>;
onProcessTitleChanged: Event<string>; onProcessTitleChanged: Event<string>;
onProcessOverrideDimensions?: Event<ITerminalDimensions | undefined>; onProcessOverrideDimensions?: Event<ITerminalDimensions | undefined>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册