Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow extensions to access all terminals #47678

Merged
merged 7 commits into from
Apr 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -769,4 +769,23 @@ declare module 'vscode' {
}

//#endregion

//#region Terminal

export namespace window {
/**
* The currently active terminals or an empty array.
*
* @readonly
*/
export let terminals: Terminal[];

/**
* An [event](#Event) which fires when a terminal has been created, either through the
* [createTerminal](#window.createTerminal) API or commands.
*/
export const onDidOpenTerminal: Event<Terminal>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think about a TerminalOpenEvent so that you a chance of adding more stuff in the future..

Copy link
Member

@jrieken jrieken Apr 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then, should open come with close?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missed that 🤦‍♂️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different file 😉

}

//#endregion
}
16 changes: 16 additions & 0 deletions src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,20 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
this._toDispose = [];
this._toDispose.push(terminalService.onInstanceCreated((terminalInstance) => {
// Delay this message so the TerminalInstance constructor has a chance to finish and
// return the ID normally to the extension host. The ID that is passed here will be used
// to register non-extension API terminals in the extension host.
setTimeout(() => this._onTerminalOpened(terminalInstance), 100);
}));
this._toDispose.push(terminalService.onInstanceDisposed((terminalInstance) => this._onTerminalDisposed(terminalInstance)));
this._toDispose.push(terminalService.onInstanceProcessIdReady((terminalInstance) => this._onTerminalProcessIdReady(terminalInstance)));

// Set initial ext host state
this.terminalService.terminalInstances.forEach(t => {
this._onTerminalOpened(t);
t.processReady.then(() => this._onTerminalProcessIdReady(t));
});
}

public dispose(): void {
Expand Down Expand Up @@ -78,6 +90,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$acceptTerminalClosed(terminalInstance.id);
}

private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title);
}

private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
}
Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/api/node/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ export function createApiFactory(
get visibleTextEditors() {
return extHostEditors.getVisibleTextEditors();
},
get terminals() {
return extHostTerminalService.terminals;
},
showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor> {
let documentPromise: TPromise<vscode.TextDocument>;
if (URI.isUri(documentOrUri)) {
Expand Down Expand Up @@ -351,6 +354,9 @@ export function createApiFactory(
onDidCloseTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables);
},
onDidOpenTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidOpenTerminal(listener, thisArg, disposables);
},
get state() {
return extHostWindow.state;
},
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@ export interface ExtHostQuickOpenShape {

export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalOpened(id: number, name: string): void;
$acceptTerminalProcessId(id: number, processId: number): void;
}

Expand Down
51 changes: 39 additions & 12 deletions src/vs/workbench/api/node/extHostTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,28 @@ export class ExtHostTerminal implements vscode.Terminal {

constructor(
proxy: MainThreadTerminalServiceShape,
name?: string,
shellPath?: string,
shellArgs?: string[],
cwd?: string,
env?: { [key: string]: string },
waitOnExit?: boolean
name: string = '',
id?: number
) {
this._proxy = proxy;
this._name = name;
if (id) {
this._id = id;
}
this._queuedRequests = [];
this._proxy = proxy;
this._pidPromise = new Promise<number>(c => {
this._pidPromiseComplete = c;
});
}

this._proxy.$createTerminal(name, shellPath, shellArgs, cwd, env, waitOnExit).then((id) => {
public create(
shellPath?: string,
shellArgs?: string[],
cwd?: string,
env?: { [key: string]: string },
waitOnExit?: boolean
): void {
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit).then((id) => {
this._id = id;
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
Expand All @@ -44,12 +51,10 @@ export class ExtHostTerminal implements vscode.Terminal {
}

public get name(): string {
this._checkDisposed();
return this._name;
}

public get processId(): Thenable<number> {
this._checkDisposed();
return this._pidPromise;
}

Expand Down Expand Up @@ -99,23 +104,29 @@ export class ExtHostTerminal implements vscode.Terminal {
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {

private readonly _onDidCloseTerminal: Emitter<vscode.Terminal>;
private readonly _onDidOpenTerminal: Emitter<vscode.Terminal>;
private _proxy: MainThreadTerminalServiceShape;
private _terminals: ExtHostTerminal[];

public get terminals(): ExtHostTerminal[] { return this._terminals; }

constructor(mainContext: IMainContext) {
this._onDidCloseTerminal = new Emitter<vscode.Terminal>();
this._onDidOpenTerminal = new Emitter<vscode.Terminal>();
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
this._terminals = [];
}

public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, name, shellPath, shellArgs);
let terminal = new ExtHostTerminal(this._proxy, name);
terminal.create(shellPath, shellArgs);
this._terminals.push(terminal);
return terminal;
}

public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, options.name, options.shellPath, options.shellArgs, options.cwd, options.env /*, options.waitOnExit*/);
let terminal = new ExtHostTerminal(this._proxy, options.name);
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env /*, options.waitOnExit*/);
this._terminals.push(terminal);
return terminal;
}
Expand All @@ -124,6 +135,10 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return this._onDidCloseTerminal && this._onDidCloseTerminal.event;
}

public get onDidOpenTerminal(): Event<vscode.Terminal> {
return this._onDidOpenTerminal && this._onDidOpenTerminal.event;
}

public $acceptTerminalClosed(id: number): void {
let index = this._getTerminalIndexById(id);
if (index === null) {
Expand All @@ -134,6 +149,18 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._onDidCloseTerminal.fire(terminal);
}

public $acceptTerminalOpened(id: number, name: string): void {
let index = this._getTerminalIndexById(id);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
this._onDidOpenTerminal.fire(this.terminals[index]);
return;
}
let terminal = new ExtHostTerminal(this._proxy, name, id);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
}

public $acceptTerminalProcessId(id: number, processId: number): void {
let terminal = this._getTerminalById(id);
if (terminal) {
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/parts/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export interface ITerminalService {
configHelper: ITerminalConfigHelper;
onActiveTabChanged: Event<void>;
onTabDisposed: Event<ITerminalTab>;
onInstanceCreated: Event<ITerminalInstance>;
onInstanceDisposed: Event<ITerminalInstance>;
onInstanceProcessIdReady: Event<ITerminalInstance>;
onInstancesChanged: Event<void>;
Expand Down Expand Up @@ -234,6 +235,8 @@ export interface ITerminalInstance {

onProcessIdReady: Event<ITerminalInstance>;

processReady: TPromise<void>;

/**
* The title of the terminal. This is either title or the process currently running or an
* explicit name given to the terminal instance through the extension API.
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/parts/terminal/common/terminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export abstract class TerminalService implements ITerminalService {
protected _terminalContainer: HTMLElement;
protected _onInstancesChanged: Emitter<void>;
protected _onTabDisposed: Emitter<ITerminalTab>;
protected _onInstanceCreated: Emitter<ITerminalInstance>;
protected _onInstanceDisposed: Emitter<ITerminalInstance>;
protected _onInstanceProcessIdReady: Emitter<ITerminalInstance>;
protected _onInstanceTitleChanged: Emitter<string>;
Expand All @@ -36,6 +37,7 @@ export abstract class TerminalService implements ITerminalService {
public get activeTabIndex(): number { return this._activeTabIndex; }
public get onActiveTabChanged(): Event<void> { return this._onActiveTabChanged.event; }
public get onTabDisposed(): Event<ITerminalTab> { return this._onTabDisposed.event; }
public get onInstanceCreated(): Event<ITerminalInstance> { return this._onInstanceCreated.event; }
public get onInstanceDisposed(): Event<ITerminalInstance> { return this._onInstanceDisposed.event; }
public get onInstanceProcessIdReady(): Event<ITerminalInstance> { return this._onInstanceProcessIdReady.event; }
public get onInstanceTitleChanged(): Event<string> { return this._onInstanceTitleChanged.event; }
Expand All @@ -57,6 +59,7 @@ export abstract class TerminalService implements ITerminalService {

this._onActiveTabChanged = new Emitter<void>();
this._onTabDisposed = new Emitter<ITerminalTab>();
this._onInstanceCreated = new Emitter<ITerminalInstance>();
this._onInstanceDisposed = new Emitter<ITerminalInstance>();
this._onInstanceProcessIdReady = new Emitter<ITerminalInstance>();
this._onInstanceTitleChanged = new Emitter<string>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ export class TerminalInstance implements ITerminalInstance {

public disableLayout: boolean;
public get id(): number { return this._id; }
// TODO: Ideally processId would be merged into processReady
public get processId(): number { return this._processId; }
public get processReady(): TPromise<void> { return this._processReady; }
public get onDisposed(): Event<ITerminalInstance> { return this._onDisposed.event; }
public get onFocused(): Event<ITerminalInstance> { return this._onFocused.event; }
public get onProcessIdReady(): Event<ITerminalInstance> { return this._onProcessIdReady.event; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina
// It's the first instance so it should be made active automatically
this.setActiveInstanceByIndex(0);
}
this._onInstanceCreated.fire(instance);
this._onInstancesChanged.fire();
this._suggestShellChange(wasNewTerminalAction);
return instance;
Expand Down