diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index d0d0107677b..ab33d38451b 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -43,5 +43,20 @@ }, "publisherDisplayName": "Microsoft" } + }, + { + "name": "ms-vscode.cpptools", + "version": "0.20.1", + "repo": "https://github.com/Microsoft/vscode-cpptools", + "metadata": { + "id": "36d19e17-7569-4841-a001-947eb18602b2", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } } -] +] \ No newline at end of file diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 223def00fc1..035b0904b16 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -113,6 +113,15 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } } + public $terminalGetDimensions(terminalId: number): Promise { + const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) { + return Promise.resolve({ cols: terminalInstance.cols, rows: terminalInstance.rows }); + } + + return Promise.reject(new Error('Dimensions cannot be retrieved')); + } + public $terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void { const terminalInstance = this.terminalService.getInstanceFromId(terminalId); if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 3c55d1f6c67..31de7edb493 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -369,6 +369,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable { // Renderer $terminalRendererSetName(terminalId: number, name: string): void; $terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void; + $terminalGetDimensions(terminalId: number): Promise; $terminalRendererWrite(terminalId: number, text: string): void; $terminalRendererRegisterOnInputListener(terminalId: number): void; } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 0b221fc7a18..a96ab0933fc 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -28,7 +28,7 @@ import { import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; -import { ExtHostTerminalService, ExtHostTerminalRenderer } from 'vs/workbench/api/node/extHostTerminalService'; +import { ExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/node/extHostTerminalService'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -335,10 +335,10 @@ interface HandlerData { class ExtensionCallbackExecutionData implements IDisposable { private _cancellationSource?: CancellationTokenSource; - private _onDidOpenRendererTerminal?: IDisposable; - private _terminalId?: number; private readonly _onTaskExecutionComplete: Emitter = new Emitter(); private readonly _disposables: IDisposable[] = []; + private terminal?: vscode.Terminal; + private terminalId?: number; constructor( private readonly callbackData: vscode.ExtensionCallbackExecution, @@ -353,13 +353,25 @@ class ExtensionCallbackExecutionData implements IDisposable { return this._onTaskExecutionComplete.event; } - private onDidCloseTerminalRenderer(terminalRenderer: vscode.TerminalRenderer): void { - if (terminalRenderer instanceof ExtHostTerminalRenderer && terminalRenderer._id === this._terminalId) { + private onDidCloseTerminal(terminal: vscode.Terminal): void { + if (this.terminal === terminal) { this._cancellationSource.cancel(); } } - private onDidOpenRenderTerminal(terminalRenderer: vscode.TerminalRenderer): Thenable { + private onDidOpenTerminal(terminal: vscode.Terminal): void { + if (!(terminal instanceof ExtHostTerminal)) { + throw new Error('How could this not be a extension host terminal?'); + } + + if (this.terminalId && terminal._id === this.terminalId) { + this.startCallback(this.terminalId); + } + } + + public async startCallback(terminalId: number): Promise { + this.terminalId = terminalId; + // If we have already started the extension task callback, then // do not start it again. // It is completely valid for multiple terminals to be opened @@ -368,57 +380,32 @@ class ExtensionCallbackExecutionData implements IDisposable { return undefined; } - if (terminalRenderer instanceof ExtHostTerminalRenderer && terminalRenderer._id === this._terminalId) { - // Stop listening (if we are) for more terminals - // to be created. - if (this._onDidOpenRendererTerminal) { - this._onDidOpenRendererTerminal.dispose(); - this._onDidOpenRendererTerminal = undefined; - } + const callbackTerminals: vscode.Terminal[] = this.terminalService.terminals.filter((terminal) => terminal._id === terminalId); - if (!(terminalRenderer instanceof ExtHostTerminalRenderer)) { - throw new Error('Expected a terminal renderer'); - } - - this._cancellationSource = new CancellationTokenSource(); - this._disposables.push(this._cancellationSource); - - this._disposables.push(this.terminalService.onDidCloseTerminalRenderer(this.onDidCloseTerminalRenderer.bind(this))); - - // Regardless of how the task completes, we are done with this extension callback task execution. - return this.callbackData.callback(terminalRenderer, this._cancellationSource.token).then( - (success) => { - this._onTaskExecutionComplete.fire(this); - }, (rejected) => { - this._onTaskExecutionComplete.fire(this); - }); + if (!callbackTerminals || callbackTerminals.length === 0) { + this._disposables.push(this.terminalService.onDidOpenTerminal(this.onDidOpenTerminal.bind(this))); + return; } - return undefined; - } - - public startCallback(terminalId: number): void { - this._terminalId = terminalId; - - // In order to start the task, we need to wait for the extension host - // to know about the new terminal. - // The order in which the events make it to the extension host (currently) - // is "task created" followed by "terminal opened". - // So, we need to wait for that event. - // However, this loop below ensures that if the order of those events - // ever changes, this code continues to function. - - // Check to see if the extension host already knows about this terminal. - for (let terminal of this.terminalService.terminalRenderers) { - if (terminal._id === terminalId) { - this.onDidOpenRenderTerminal(terminal); - return; - } + if (callbackTerminals.length !== 1) { + throw new Error(`Expected to only have one terminal at this point`); } - // If we get here, then the terminal is unknown to the extension host. Let's wait - // for it to be created and the start our extension callback. - this._disposables.push(this.terminalService.onDidOpenTerminalRenderer(this.onDidOpenRenderTerminal.bind(this))); + this.terminal = callbackTerminals[0]; + const terminalRenderer: vscode.TerminalRenderer = await this.terminalService.createTerminalRendererForTerminal(this.terminal); + + this._cancellationSource = new CancellationTokenSource(); + this._disposables.push(this._cancellationSource); + + this._disposables.push(this.terminalService.onDidCloseTerminal(this.onDidCloseTerminal.bind(this))); + + // Regardless of how the task completes, we are done with this extension callback task execution. + this.callbackData.callback(terminalRenderer, this._cancellationSource.token).then( + (success) => { + this._onTaskExecutionComplete.fire(this); + }, (rejected) => { + this._onTaskExecutionComplete.fire(this); + }); } } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 9faeb66533d..e129b6a62bd 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -269,12 +269,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { private readonly _onDidCloseTerminal: Emitter = new Emitter(); public get onDidCloseTerminal(): Event { return this._onDidCloseTerminal && this._onDidCloseTerminal.event; } - private readonly _onDidCloseTerminalRenderer: Emitter = new Emitter(); - public get onDidCloseTerminalRenderer(): Event { return this._onDidCloseTerminalRenderer && this._onDidCloseTerminalRenderer.event; } private readonly _onDidOpenTerminal: Emitter = new Emitter(); public get onDidOpenTerminal(): Event { return this._onDidOpenTerminal && this._onDidOpenTerminal.event; } - private readonly _onDidOpenTerminalRenderer: Emitter = new Emitter(); - public get onDidOpenTerminalRenderer(): Event { return this._onDidOpenTerminalRenderer && this._onDidOpenTerminalRenderer.event; } private readonly _onDidChangeActiveTerminal: Emitter = new Emitter(); public get onDidChangeActiveTerminal(): Event { return this._onDidChangeActiveTerminal && this._onDidChangeActiveTerminal.event; } @@ -312,6 +308,24 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { return renderer; } + public async createTerminalRendererForTerminal(terminal: vscode.Terminal): Promise { + if (!(terminal instanceof ExtHostTerminal)) { + throw new Error('Only expected instance extension host terminal type'); + } + // Check to see if the extension host already knows about this terminal. + for (const terminalRenderer of this.terminalRenderers) { + if (terminalRenderer._id === terminal._id) { + return terminalRenderer; + } + } + + const dimensions = await this._proxy.$terminalGetDimensions(terminal._id); + const renderer = new ExtHostTerminalRenderer(this._proxy, terminal.name, terminal, terminal._id, dimensions.cols, dimensions.rows); + this._terminalRenderers.push(renderer); + + return renderer; + } + public $acceptActiveTerminalChanged(id: number | null): void { const original = this._activeTerminal; if (id === null) { @@ -365,24 +379,15 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { const terminal = this._terminals.splice(index, 1)[0]; this._onDidCloseTerminal.fire(terminal); } - - const renderer = this._getTerminalRendererById(id); - if (renderer) { - this._onDidCloseTerminalRenderer.fire(renderer); - } } public $acceptTerminalOpened(id: number, name: string, isRendererOnly: boolean, cols: number, rows: number): void { - const index = this._getTerminalObjectIndexById(this._terminals, id); + let index = this._getTerminalObjectIndexById(this._terminals, id); if (index !== null) { this._onDidOpenTerminal.fire(this.terminals[index]); } let renderer = this._getTerminalRendererById(id); - if (renderer) { - // The terminal has already been created (via createTerminal*), only fire the event - this._onDidOpenTerminalRenderer.fire(renderer); - } // If this is a terminal created by one of the public createTerminal* APIs // then @acceptTerminalOpened was called from the main thread task @@ -398,14 +403,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // objects to represent them. if (!index) { const terminal = new ExtHostTerminal(this._proxy, name, id, renderer ? RENDERER_NO_PROCESS_ID : undefined); - this._terminals.push(terminal); + index = this._terminals.push(terminal); this._onDidOpenTerminal.fire(terminal); - - if (!renderer && isRendererOnly) { - renderer = new ExtHostTerminalRenderer(this._proxy, name, terminal, id, cols, rows); - this.terminalRenderers.push(renderer); - this._onDidOpenTerminalRenderer.fire(renderer); - } } }