diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 27edca6528b..3b9ce8d4529 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -669,7 +669,7 @@ import { assertNoRpc } from '../utils'; }); suite('environmentVariableCollection', () => { - test('should have collection variables apply to terminals immediately after setting', async (done) => { + test('should have collection variables apply to terminals immediately after setting', (done) => { // Text to match on before passing the test const expectedText = [ '~a2~', @@ -697,7 +697,7 @@ import { assertNoRpc } from '../utils'; collection.replace('A', '~a2~'); collection.append('B', '~b2~'); collection.prepend('C', '~c2~'); - const terminal = await window.createTerminal({ + const terminal = window.createTerminal({ env: { A: 'a1', B: 'b1', @@ -714,7 +714,7 @@ import { assertNoRpc } from '../utils'; terminal.sendText('echo $C'); }); - test('should have collection variables apply to environment variables that don\'t exist', async (done) => { + test('should have collection variables apply to environment variables that don\'t exist', (done) => { // Text to match on before passing the test const expectedText = [ '~a2~', @@ -742,7 +742,7 @@ import { assertNoRpc } from '../utils'; collection.replace('A', '~a2~'); collection.append('B', '~b2~'); collection.prepend('C', '~c2~'); - const terminal = await window.createTerminal({ + const terminal = window.createTerminal({ env: { A: null, B: null, @@ -759,7 +759,7 @@ import { assertNoRpc } from '../utils'; terminal.sendText('echo $C'); }); - test('should respect clearing entries', async (done) => { + test('should respect clearing entries', (done) => { // Text to match on before passing the test const expectedText = [ '~a1~', @@ -786,7 +786,7 @@ import { assertNoRpc } from '../utils'; collection.replace('A', '~a2~'); collection.replace('B', '~a2~'); collection.clear(); - const terminal = await window.createTerminal({ + const terminal = window.createTerminal({ env: { A: '~a1~', B: '~b1~' @@ -800,7 +800,7 @@ import { assertNoRpc } from '../utils'; terminal.sendText('echo $B'); }); - test('should respect deleting entries', async (done) => { + test('should respect deleting entries', (done) => { // Text to match on before passing the test const expectedText = [ '~a1~', @@ -827,7 +827,7 @@ import { assertNoRpc } from '../utils'; collection.replace('A', '~a2~'); collection.replace('B', '~b2~'); collection.delete('A'); - const terminal = await window.createTerminal({ + const terminal = window.createTerminal({ env: { A: '~a1~', B: '~b2~' diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 491e43d2f9b..d624feda6ec 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -30,7 +30,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape * to a numeric terminal id (an id generated on the renderer side) * This comes in play only when dealing with terminals created on the extension host side */ - private _extHostTerminalIds = new Map(); + private _extHostTerminals = new Map>(); private readonly _toDispose = new DisposableStore(); private readonly _terminalProcessProxies = new Map(); private readonly _profileProviders = new Map(); @@ -109,19 +109,11 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._proxy.$acceptDefaultProfile(...await Promise.all([defaultProfile, defaultAutomationProfile])); } - private _getTerminalId(id: TerminalIdentifier): number | undefined { - if (typeof id === 'number') { - return id; + private async _getTerminalInstance(id: TerminalIdentifier): Promise { + if (typeof id === 'string') { + return this._extHostTerminals.get(id); } - return this._extHostTerminalIds.get(id); - } - - private _getTerminalInstance(id: TerminalIdentifier): ITerminalInstance | undefined { - const rendererId = this._getTerminalId(id); - if (typeof rendererId === 'number') { - return this._terminalService.getInstanceFromId(rendererId); - } - return undefined; + return this._terminalService.getInstanceFromId(id); } public async $createTerminal(extHostTerminalId: string, launchConfig: TerminalLaunchConfig): Promise { @@ -141,29 +133,31 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape customPtyImplementation: launchConfig.isExtensionCustomPtyTerminal ? (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this._terminalService) : undefined, - extHostTerminalId: extHostTerminalId, + extHostTerminalId, isFeatureTerminal: launchConfig.isFeatureTerminal, isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal, useShellEnvironment: launchConfig.useShellEnvironment }; - let terminal: ITerminalInstance | undefined; - if (launchConfig.isSplitTerminal) { - const activeInstance = this._terminalService.getInstanceHost(launchConfig.target).activeInstance; - if (activeInstance) { - terminal = withNullAsUndefined(await this._terminalService.splitInstance(activeInstance, shellLaunchConfig)); + this._extHostTerminals.set(extHostTerminalId, new Promise(async r => { + let terminal: ITerminalInstance | undefined; + if (launchConfig.isSplitTerminal) { + const activeInstance = this._terminalService.getInstanceHost(launchConfig.target).activeInstance; + if (activeInstance) { + terminal = withNullAsUndefined(await this._terminalService.splitInstance(activeInstance, shellLaunchConfig)); + } } - } - if (!terminal) { - terminal = await this._terminalService.createTerminal({ - config: shellLaunchConfig, - target: launchConfig.target - }); - } - this._extHostTerminalIds.set(extHostTerminalId, terminal.instanceId); + if (!terminal) { + terminal = await this._terminalService.createTerminal({ + config: shellLaunchConfig, + target: launchConfig.target + }); + } + r(terminal); + })); } - public $show(id: TerminalIdentifier, preserveFocus: boolean): void { - const terminalInstance = this._getTerminalInstance(id); + public async $show(id: TerminalIdentifier, preserveFocus: boolean): Promise { + const terminalInstance = await this._getTerminalInstance(id); if (terminalInstance) { this._terminalService.setActiveInstance(terminalInstance); if (terminalInstance.target === TerminalLocation.Editor) { @@ -174,20 +168,21 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } } - public $hide(id: TerminalIdentifier): void { - const rendererId = this._getTerminalId(id); - const instance = this._terminalService.activeInstance; - if (instance && instance.instanceId === rendererId && instance.target !== TerminalLocation.Editor) { + public async $hide(id: TerminalIdentifier): Promise { + const instanceToHide = await this._getTerminalInstance(id); + const activeInstance = this._terminalService.activeInstance; + if (activeInstance && activeInstance.instanceId === instanceToHide?.instanceId && activeInstance.target !== TerminalLocation.Editor) { this._terminalGroupService.hidePanel(); } } - public $dispose(id: TerminalIdentifier): void { - this._getTerminalInstance(id)?.dispose(); + public async $dispose(id: TerminalIdentifier): Promise { + (await this._getTerminalInstance(id))?.dispose(); } - public $sendText(id: TerminalIdentifier, text: string, addNewLine: boolean): void { - this._getTerminalInstance(id)?.sendText(text, addNewLine); + public async $sendText(id: TerminalIdentifier, text: string, addNewLine: boolean): Promise { + const instance = await this._getTerminalInstance(id); + await instance?.sendText(text, addNewLine); } public $startSendingDataEvents(): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 9a72ab1bc01..28228575f0d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -618,7 +618,7 @@ export interface ITerminalInstance { * required to run a command in the terminal. The character(s) added are \n or \r\n * depending on the platform. This defaults to `true`. */ - sendText(text: string, addNewLine: boolean): void; + sendText(text: string, addNewLine: boolean): Promise; /** Scroll the terminal buffer down 1 line. */ scrollDownLine(): void; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 9aa893116e2..f500e32f9ee 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -229,8 +229,9 @@ export class TerminalService implements ITerminalService { lifecycleService.onWillShutdown(e => this._onWillShutdown(e)); this._configurationService.onDidChangeConfiguration(async e => { - if (e.affectsConfiguration(TerminalSettingPrefix.DefaultProfile + this._getPlatformKey()) || - e.affectsConfiguration(TerminalSettingPrefix.Profiles + this._getPlatformKey()) || + const platformKey = await this._getPlatformKey(); + if (e.affectsConfiguration(TerminalSettingPrefix.DefaultProfile + platformKey) || + e.affectsConfiguration(TerminalSettingPrefix.Profiles + platformKey) || e.affectsConfiguration(TerminalSettingId.UseWslProfiles)) { this._refreshAvailableProfiles(); } @@ -957,7 +958,6 @@ export class TerminalService implements ITerminalService { return; // Should never happen } else if ('id' in value.profile) { // extension contributed profile - console.log(value.profile.title); await this._configurationService.updateValue(`terminal.integrated.defaultProfile.${platformKey}`, value.profile.title, ConfigurationTarget.USER); this._registerContributedProfile(value.profile.extensionIdentifier, value.profile.id, value.profile.title, { @@ -1067,7 +1067,7 @@ export class TerminalService implements ITerminalService { return { label, description: profile.path, profile, buttons }; } - private _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile | IExtensionTerminalProfile, cwd?: string | URI): IShellLaunchConfig { + private _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { const profile = shellLaunchConfigOrProfile; if (!profile.path) { @@ -1084,8 +1084,8 @@ export class TerminalService implements ITerminalService { }; } - // Shell launch config was provided - if (shellLaunchConfigOrProfile && 'cwd' in shellLaunchConfigOrProfile) { + // A shell launch config was provided + if (shellLaunchConfigOrProfile) { if (cwd) { shellLaunchConfigOrProfile.cwd = cwd; } @@ -1109,11 +1109,27 @@ export class TerminalService implements ITerminalService { } async createTerminal(options?: ICreateTerminalOptions): Promise { - const shellLaunchConfig = this._convertProfileToShellLaunchConfig(options?.config || options); + const config = options?.config; + const shellLaunchConfig = config && 'extensionIdentifier' in config + ? {} + : this._convertProfileToShellLaunchConfig(config || {}); - const contributedDefaultProfile = await this._getContributedDefaultProfile(shellLaunchConfig); - if (contributedDefaultProfile) { - await this.createContributedTerminalProfile(contributedDefaultProfile.extensionIdentifier, contributedDefaultProfile.id, { isSplitTerminal: false, icon: contributedDefaultProfile.icon }); + // Get the contributed profile if it was provided + let contributedProfile = config && 'extensionIdentifier' in config ? config : undefined; + + // Get the default profile as a contributed profile if it exists + if (!contributedProfile && !options) { + contributedProfile = await this._getContributedDefaultProfile(shellLaunchConfig); + } + + // Launch the contributed profile + if (contributedProfile) { + // TODO: createContributedTerminalProfile should be private if we want all terminal creation to go through createTerminal + await this.createContributedTerminalProfile(contributedProfile.extensionIdentifier, contributedProfile.id, { + isSplitTerminal: false, + icon: contributedProfile.icon + }); + // TODO: The extension terminal may be created in the editor area return this._terminalGroupService.instances[this._terminalGroupService.instances.length - 1]; }