Merge pull request #209261 from microsoft/tyriar/181900_2

Move terminal WSL recommendation into a workbench contrib
This commit is contained in:
Daniel Imms 2024-04-01 10:07:13 -07:00 committed by GitHub
commit 7733a6a12b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 101 additions and 84 deletions

View file

@ -49,6 +49,7 @@ import { TerminalProfileService } from 'vs/workbench/contrib/terminal/browser/te
import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess';
import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService';
import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView';
import { TerminalWslRecommendationContribution } from 'vs/workbench/contrib/terminal/browser/terminalWslRecommendationContribution';
import { ITerminalProfileService, TERMINAL_VIEW_ID, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { registerTerminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration';
@ -83,6 +84,7 @@ CommandsRegistry.registerCommand({ id: quickAccessNavigatePreviousInTerminalPick
// This contribution blocks startup as it's critical to enable the web embedder window.createTerminal API
registerWorkbenchContribution2(TerminalMainContribution.ID, TerminalMainContribution, WorkbenchPhase.BlockStartup);
registerWorkbenchContribution2(RemoteTerminalBackendContribution.ID, RemoteTerminalBackendContribution, WorkbenchPhase.AfterRestored);
registerWorkbenchContribution2(TerminalWslRecommendationContribution.ID, TerminalWslRecommendationContribution, WorkbenchPhase.Eventually);
// Register configurations
registerTerminalPlatformConfiguration();

View file

@ -92,7 +92,6 @@ export interface ITerminalConfigHelper {
configFontIsMonospace(): boolean;
getFont(w: Window): ITerminalFont;
showRecommendations(shellLaunchConfig: IShellLaunchConfig): void;
}
export const enum Direction {

View file

@ -3,22 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { Disposable } from 'vs/base/common/lifecycle';
import { isLinux } from 'vs/base/common/platform';
import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, ITerminalFont } from 'vs/workbench/contrib/terminal/common/terminal';
import Severity from 'vs/base/common/severity';
import { INotificationService, NeverShowAgainScope } from 'vs/platform/notification/common/notification';
import { ITerminalConfigHelper, ITerminalConfigurationService, LinuxDistro } from 'vs/workbench/contrib/terminal/browser/terminal';
import { basename } from 'vs/base/common/path';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IProductService } from 'vs/platform/product/common/productService';
import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { Disposable } from 'vs/base/common/lifecycle';
import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, ITerminalFont, MINIMUM_LETTER_SPACING } from 'vs/workbench/contrib/terminal/common/terminal';
const enum FontConstants {
MinimumFontSize = 6,
@ -38,10 +29,6 @@ export class TerminalConfigHelper extends Disposable implements ITerminalConfigH
constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService,
@INotificationService private readonly _notificationService: INotificationService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IProductService private readonly _productService: IProductService,
@ITerminalConfigurationService private readonly _terminalConfigurationService: ITerminalConfigurationService,
) {
super();
@ -203,46 +190,4 @@ export class TerminalConfigHelper extends Disposable implements ITerminalConfigH
}
return r;
}
private _recommendationsShown = false;
async showRecommendations(shellLaunchConfig: IShellLaunchConfig): Promise<void> {
if (this._recommendationsShown) {
return;
}
this._recommendationsShown = true;
if (isWindows && shellLaunchConfig.executable && basename(shellLaunchConfig.executable).toLowerCase() === 'wsl.exe') {
const exeBasedExtensionTips = this._productService.exeBasedExtensionTips;
if (!exeBasedExtensionTips || !exeBasedExtensionTips.wsl) {
return;
}
const extId = Object.keys(exeBasedExtensionTips.wsl.recommendations).find(extId => exeBasedExtensionTips.wsl.recommendations[extId].important);
if (extId && ! await this._isExtensionInstalled(extId)) {
this._notificationService.prompt(
Severity.Info,
nls.localize(
'useWslExtension.title', "The '{0}' extension is recommended for opening a terminal in WSL.", exeBasedExtensionTips.wsl.friendlyName),
[
{
label: nls.localize('install', 'Install'),
run: () => {
this._instantiationService.createInstance(InstallRecommendedExtensionAction, extId).run();
}
}
],
{
sticky: true,
neverShowAgain: { id: 'terminalConfigHelper/launchRecommendationsIgnore', scope: NeverShowAgainScope.APPLICATION },
onCancel: () => { }
}
);
}
}
}
private async _isExtensionInstalled(id: string): Promise<boolean> {
const extensions = await this._extensionManagementService.getInstalled();
return extensions.some(e => e.identifier.id === id);
}
}

View file

@ -1324,7 +1324,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
const processManager = this._scopedInstantiationService.createInstance(
TerminalProcessManager,
this._instanceId,
this._configHelper,
this.shellLaunchConfig?.cwd,
deserializedCollections,
this.shellLaunchConfig.attachPersistentProcess?.shellIntegrationNonce

View file

@ -22,7 +22,7 @@ import { FlowControlConstants, IProcessDataEvent, IProcessProperty, IProcessProp
import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } from 'vs/workbench/contrib/terminal/browser/environmentVariableInfo';
import { ITerminalConfigHelper, ITerminalConfigurationService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { ITerminalConfigurationService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IEnvironmentVariableInfo, IEnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { MergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableCollection';
import { serializeEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariableShared';
@ -130,7 +130,6 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
constructor(
private readonly _instanceId: number,
private readonly _configHelper: ITerminalConfigHelper,
cwd: string | URI | undefined,
environmentVariableCollections: ReadonlyMap<string, IEnvironmentVariableCollection> | undefined,
shellIntegrationNonce: string | undefined,
@ -428,7 +427,6 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
const workspaceFolder = terminalEnvironment.getWorkspaceForTerminal(shellLaunchConfig.cwd, this._workspaceContextService, this._historyService);
const platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux');
const envFromConfigValue = this._configurationService.getValue<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
this._configHelper.showRecommendations(shellLaunchConfig);
let baseEnv: IProcessEnvironment;
if (shellLaunchConfig.useShellEnvironment) {

View file

@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, type IDisposable } from 'vs/base/common/lifecycle';
import { basename } from 'vs/base/common/path';
import { isWindows } from 'vs/base/common/platform';
import { localize } from 'vs/nls';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService, NeverShowAgainScope, Severity } from 'vs/platform/notification/common/notification';
import { IProductService } from 'vs/platform/product/common/productService';
import type { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
export class TerminalWslRecommendationContribution extends Disposable implements IWorkbenchContribution {
static ID = 'terminalWslRecommendation';
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@IProductService productService: IProductService,
@INotificationService notificationService: INotificationService,
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
@ITerminalService terminalService: ITerminalService,
) {
super();
if (!isWindows) {
return;
}
const exeBasedExtensionTips = productService.exeBasedExtensionTips;
if (!exeBasedExtensionTips || !exeBasedExtensionTips.wsl) {
return;
}
let listener: IDisposable | undefined = terminalService.onDidCreateInstance(async instance => {
async function isExtensionInstalled(id: string): Promise<boolean> {
const extensions = await extensionManagementService.getInstalled();
return extensions.some(e => e.identifier.id === id);
}
if (!instance.shellLaunchConfig.executable || basename(instance.shellLaunchConfig.executable).toLowerCase() !== 'wsl.exe') {
return;
}
listener?.dispose();
listener = undefined;
const extId = Object.keys(exeBasedExtensionTips.wsl.recommendations).find(extId => exeBasedExtensionTips.wsl.recommendations[extId].important);
if (!extId || await isExtensionInstalled(extId)) {
return;
}
notificationService.prompt(
Severity.Info,
localize('useWslExtension.title', "The '{0}' extension is recommended for opening a terminal in WSL.", exeBasedExtensionTips.wsl.friendlyName),
[
{
label: localize('install', 'Install'),
run: () => {
instantiationService.createInstance(InstallRecommendedExtensionAction, extId).run();
}
}
],
{
sticky: true,
neverShowAgain: { id: 'terminalConfigHelper/launchRecommendationsIgnore', scope: NeverShowAgainScope.APPLICATION },
onCancel: () => { }
}
);
});
}
}

View file

@ -38,7 +38,7 @@ suite('Workbench - TerminalConfigHelper', function () {
editor: { fontFamily: 'foo' },
terminal: { integrated: { fontFamily: 'bar' } }
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontFamily, 'bar, monospace', 'terminal.integrated.fontFamily should be selected over editor.fontFamily');
});
@ -48,7 +48,7 @@ suite('Workbench - TerminalConfigHelper', function () {
editor: { fontFamily: 'foo' },
terminal: { integrated: { fontFamily: null } }
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.linuxDistro = LinuxDistro.Fedora;
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set');
@ -59,7 +59,7 @@ suite('Workbench - TerminalConfigHelper', function () {
editor: { fontFamily: 'foo' },
terminal: { integrated: { fontFamily: null } }
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.linuxDistro = LinuxDistro.Ubuntu;
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set');
@ -70,7 +70,7 @@ suite('Workbench - TerminalConfigHelper', function () {
editor: { fontFamily: 'foo' },
terminal: { integrated: { fontFamily: null } }
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontFamily, 'foo, monospace', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set');
});
@ -88,7 +88,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize');
});
@ -105,12 +105,12 @@ suite('Workbench - TerminalConfigHelper', function () {
}
}
});
let configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
let configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.linuxDistro = LinuxDistro.Ubuntu;
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it');
configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it');
});
@ -127,7 +127,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontSize, 100, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it');
});
@ -144,12 +144,12 @@ suite('Workbench - TerminalConfigHelper', function () {
}
}
});
let configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
let configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.linuxDistro = LinuxDistro.Ubuntu;
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set');
configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set');
});
@ -167,7 +167,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight');
});
@ -185,7 +185,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.getFont(getActiveWindow()).lineHeight, 1, 'editor.lineHeight should be 1 when terminal.integrated.lineHeight not set');
});
@ -199,7 +199,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.configFontIsMonospace(), true, 'monospace is monospaced');
});
@ -212,7 +212,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced');
});
@ -225,7 +225,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.configFontIsMonospace(), false, 'serif is not monospaced');
});
@ -242,7 +242,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.configFontIsMonospace(), true, 'monospace is monospaced');
});
@ -259,7 +259,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced');
});
@ -276,7 +276,7 @@ suite('Workbench - TerminalConfigHelper', function () {
}
});
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, store.add(new TerminalConfigurationService(configurationService))));
const configHelper = store.add(new TestTerminalConfigHelper(configurationService, store.add(new TerminalConfigurationService(configurationService))));
configHelper.panelContainer = fixture;
assert.strictEqual(configHelper.configFontIsMonospace(), false, 'serif is not monospaced');
});

View file

@ -5,7 +5,6 @@
import { strictEqual } from 'assert';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ITestInstantiationService, TestTerminalProfileResolverService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
@ -109,8 +108,7 @@ suite('Workbench - TerminalProcessManager', () => {
instantiationService.stub(ITerminalProfileResolverService, TestTerminalProfileResolverService);
instantiationService.stub(ITerminalInstanceService, new TestTerminalInstanceService());
const configHelper = store.add(instantiationService.createInstance(TerminalConfigHelper));
manager = store.add(instantiationService.createInstance(TerminalProcessManager, 1, configHelper, undefined, undefined, undefined));
manager = store.add(instantiationService.createInstance(TerminalProcessManager, 1, undefined, undefined, undefined));
});
teardown(() => store.dispose());