Path: blob/main/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
5260 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import './media/extensionActions.css';6import { localize, localize2 } from '../../../../nls.js';7import { IAction, Action, Separator, SubmenuAction, IActionChangeEvent } from '../../../../base/common/actions.js';8import { Delayer, Promises, Throttler } from '../../../../base/common/async.js';9import * as DOM from '../../../../base/browser/dom.js';10import { Emitter, Event } from '../../../../base/common/event.js';11import * as json from '../../../../base/common/json.js';12import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';13import { disposeIfDisposable } from '../../../../base/common/lifecycle.js';14import { IExtension, ExtensionState, IExtensionsWorkbenchService, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, UPDATE_ACTIONS_GROUP, ExtensionEditorTab, ExtensionRuntimeActionType, IExtensionArg, AutoUpdateConfigurationKey } from '../common/extensions.js';15import { ExtensionsConfigurationInitialContent } from '../common/extensionsFileTemplate.js';16import { IGalleryExtension, IExtensionGalleryService, ILocalExtension, InstallOptions, InstallOperation, ExtensionManagementErrorCode, IAllowedExtensionsService, shouldRequireRepositorySignatureFor } from '../../../../platform/extensionManagement/common/extensionManagement.js';17import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js';18import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js';19import { areSameExtensions, getExtensionId } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';20import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension, getWorkspaceSupportTypeMessage, TargetPlatform, isApplicationScopedExtension } from '../../../../platform/extensions/common/extensions.js';21import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';22import { IFileService, IFileContent } from '../../../../platform/files/common/files.js';23import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js';24import { IHostService } from '../../../services/host/browser/host.js';25import { IExtensionService, toExtension, toExtensionDescription } from '../../../services/extensions/common/extensions.js';26import { URI } from '../../../../base/common/uri.js';27import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';28import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';29import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from '../../../../platform/theme/common/themeService.js';30import { ThemeIcon } from '../../../../base/common/themables.js';31import { buttonBackground, buttonForeground, buttonHoverBackground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, registerColor, editorWarningForeground, editorInfoForeground, editorErrorForeground, buttonSeparator, buttonBorder, contrastBorder } from '../../../../platform/theme/common/colorRegistry.js';32import { IJSONEditingService } from '../../../services/configuration/common/jsonEditing.js';33import { ITextEditorSelection } from '../../../../platform/editor/common/editor.js';34import { ITextModelService } from '../../../../editor/common/services/resolverService.js';35import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';36import { MenuId, IMenuService, MenuItemAction, SubmenuItemAction } from '../../../../platform/actions/common/actions.js';37import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from '../../../browser/actions/workspaceCommands.js';38import { INotificationService, IPromptChoice, Severity } from '../../../../platform/notification/common/notification.js';39import { IOpenerService } from '../../../../platform/opener/common/opener.js';40import { IEditorService } from '../../../services/editor/common/editorService.js';41import { IQuickPickItem, IQuickInputService, QuickPickItem } from '../../../../platform/quickinput/common/quickInput.js';42import { CancellationToken } from '../../../../base/common/cancellation.js';43import { alert } from '../../../../base/browser/ui/aria/aria.js';44import { IWorkbenchThemeService, IWorkbenchTheme, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme } from '../../../services/themes/common/workbenchThemeService.js';45import { ILabelService } from '../../../../platform/label/common/label.js';46import { ITextFileService } from '../../../services/textfile/common/textfiles.js';47import { IProductService } from '../../../../platform/product/common/productService.js';48import { IDialogService, IPromptButton } from '../../../../platform/dialogs/common/dialogs.js';49import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js';50import { IActionViewItemOptions, ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js';51import { EXTENSIONS_CONFIG, IExtensionsConfigContent } from '../../../services/extensionRecommendations/common/workspaceExtensionsConfig.js';52import { getErrorMessage, isCancellationError } from '../../../../base/common/errors.js';53import { IUserDataSyncEnablementService } from '../../../../platform/userDataSync/common/userDataSync.js';54import { IContextMenuProvider } from '../../../../base/browser/contextmenu.js';55import { ILogService } from '../../../../platform/log/common/log.js';56import { errorIcon, infoIcon, manageExtensionIcon, syncEnabledIcon, syncIgnoredIcon, trustIcon, warningIcon } from './extensionsIcons.js';57import { isIOS, isWeb, language } from '../../../../base/common/platform.js';58import { IExtensionManifestPropertiesService } from '../../../services/extensions/common/extensionManifestPropertiesService.js';59import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js';60import { isVirtualWorkspace } from '../../../../platform/workspace/common/virtualWorkspace.js';61import { createCommandUri, escapeMarkdownSyntaxTokens, IMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js';62import { fromNow } from '../../../../base/common/date.js';63import { IPreferencesService } from '../../../services/preferences/common/preferences.js';64import { getLocale } from '../../../../platform/languagePacks/common/languagePacks.js';65import { ILocaleService } from '../../../services/localization/common/locale.js';66import { isString } from '../../../../base/common/types.js';67import { showWindowLogActionId } from '../../../services/log/common/logConstants.js';68import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';69import { Extensions, IExtensionFeaturesManagementService, IExtensionFeaturesRegistry } from '../../../services/extensionManagement/common/extensionFeatures.js';70import { Registry } from '../../../../platform/registry/common/platform.js';71import { IUpdateService } from '../../../../platform/update/common/update.js';72import { ActionWithDropdownActionViewItem, IActionWithDropdownActionViewItemOptions } from '../../../../base/browser/ui/dropdown/dropdownActionViewItem.js';73import { IAuthenticationUsageService } from '../../../services/authentication/browser/authenticationUsageService.js';74import { IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js';75import { IWorkbenchIssueService } from '../../issue/common/issue.js';76import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js';7778export class PromptExtensionInstallFailureAction extends Action {7980constructor(81private readonly extension: IExtension,82private readonly options: InstallOptions | undefined,83private readonly version: string,84private readonly installOperation: InstallOperation,85private readonly error: Error,86@IProductService private readonly productService: IProductService,87@IOpenerService private readonly openerService: IOpenerService,88@INotificationService private readonly notificationService: INotificationService,89@IDialogService private readonly dialogService: IDialogService,90@ICommandService private readonly commandService: ICommandService,91@ILogService private readonly logService: ILogService,92@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,93@IInstantiationService private readonly instantiationService: IInstantiationService,94@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,95@IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService,96@IWorkbenchIssueService private readonly workbenchIssueService: IWorkbenchIssueService,97) {98super('extension.promptExtensionInstallFailure');99}100101override async run(): Promise<void> {102if (isCancellationError(this.error)) {103return;104}105106this.logService.error(this.error);107108if (this.error.name === ExtensionManagementErrorCode.Unsupported) {109const productName = isWeb ? localize('VS Code for Web', "{0} for the Web", this.productService.nameLong) : this.productService.nameLong;110const message = localize('cannot be installed', "The '{0}' extension is not available in {1}. Click 'More Information' to learn more.", this.extension.displayName || this.extension.identifier.id, productName);111const { confirmed } = await this.dialogService.confirm({112type: Severity.Info,113message,114primaryButton: localize({ key: 'more information', comment: ['&& denotes a mnemonic'] }, "&&More Information"),115cancelButton: localize('close', "Close")116});117if (confirmed) {118this.openerService.open(isWeb ? URI.parse('https://aka.ms/vscode-web-extensions-guide') : URI.parse('https://aka.ms/vscode-remote'));119}120return;121}122123if (ExtensionManagementErrorCode.ReleaseVersionNotFound === (<ExtensionManagementErrorCode>this.error.name)) {124await this.dialogService.prompt({125type: 'error',126message: getErrorMessage(this.error),127buttons: [{128label: localize('install prerelease', "Install Pre-Release"),129run: () => {130const installAction = this.instantiationService.createInstance(InstallAction, { installPreReleaseVersion: true });131installAction.extension = this.extension;132return installAction.run();133}134}],135cancelButton: localize('cancel', "Cancel")136});137return;138}139140if ([ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatibleApi, ExtensionManagementErrorCode.IncompatibleTargetPlatform, ExtensionManagementErrorCode.Malicious, ExtensionManagementErrorCode.Deprecated].includes(<ExtensionManagementErrorCode>this.error.name)) {141await this.dialogService.info(getErrorMessage(this.error));142return;143}144145if (ExtensionManagementErrorCode.PackageNotSigned === (<ExtensionManagementErrorCode>this.error.name)) {146await this.dialogService.prompt({147type: 'error',148message: localize('not signed', "'{0}' is an extension from an unknown source. Are you sure you want to install?", this.extension.displayName),149detail: getErrorMessage(this.error),150buttons: [{151label: localize('install anyway', "Install Anyway"),152run: () => {153const installAction = this.instantiationService.createInstance(InstallAction, { ...this.options, donotVerifySignature: true, });154installAction.extension = this.extension;155return installAction.run();156}157}],158cancelButton: true159});160return;161}162163if (ExtensionManagementErrorCode.SignatureVerificationFailed === (<ExtensionManagementErrorCode>this.error.name)) {164await this.dialogService.prompt({165type: 'error',166message: localize('verification failed', "Cannot install '{0}' extension because {1} cannot verify the extension signature", this.extension.displayName, this.productService.nameLong),167detail: getErrorMessage(this.error),168buttons: [{169label: localize('learn more', "Learn More"),170run: () => this.openerService.open('https://code.visualstudio.com/docs/editor/extension-marketplace#_the-extension-signature-cannot-be-verified-by-vs-code')171}, {172label: localize('install donot verify', "Install Anyway (Don't Verify Signature)"),173run: () => {174const installAction = this.instantiationService.createInstance(InstallAction, { ...this.options, donotVerifySignature: true, });175installAction.extension = this.extension;176return installAction.run();177}178}],179cancelButton: true180});181return;182}183184if (ExtensionManagementErrorCode.SignatureVerificationInternal === (<ExtensionManagementErrorCode>this.error.name)) {185await this.dialogService.prompt({186type: 'error',187message: localize('verification failed', "Cannot install '{0}' extension because {1} cannot verify the extension signature", this.extension.displayName, this.productService.nameLong),188detail: getErrorMessage(this.error),189buttons: [{190label: localize('learn more', "Learn More"),191run: () => this.openerService.open('https://code.visualstudio.com/docs/editor/extension-marketplace#_the-extension-signature-cannot-be-verified-by-vs-code')192}, {193label: localize('report issue', "Report Issue"),194run: () => this.workbenchIssueService.openReporter({195issueTitle: localize('report issue title', "Extension Signature Verification Failed: {0}", this.extension.displayName),196issueBody: localize('report issue body', "Please include following log `F1 > Open View... > Shared` below.\n\n")197})198}, {199label: localize('install donot verify', "Install Anyway (Don't Verify Signature)"),200run: () => {201const installAction = this.instantiationService.createInstance(InstallAction, { ...this.options, donotVerifySignature: true, });202installAction.extension = this.extension;203return installAction.run();204}205}],206cancelButton: true207});208return;209}210211const operationMessage = this.installOperation === InstallOperation.Update ? localize('update operation', "Error while updating '{0}' extension.", this.extension.displayName || this.extension.identifier.id)212: localize('install operation', "Error while installing '{0}' extension.", this.extension.displayName || this.extension.identifier.id);213let additionalMessage;214const promptChoices: IPromptChoice[] = [];215216const downloadUrl = await this.getDownloadUrl();217if (downloadUrl) {218additionalMessage = localize('check logs', "Please check the [log]({0}) for more details.", createCommandUri(showWindowLogActionId).toString());219promptChoices.push({220label: localize('download', "Try Downloading Manually..."),221run: () => this.openerService.open(downloadUrl).then(() => {222this.notificationService.prompt(223Severity.Info,224localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', this.extension.identifier.id),225[{226label: localize('installVSIX', "Install from VSIX..."),227run: () => this.commandService.executeCommand(SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID)228}]229);230})231});232}233234const message = `${operationMessage}${additionalMessage ? ` ${additionalMessage}` : ''}`;235this.notificationService.prompt(Severity.Error, message, promptChoices);236}237238private async getDownloadUrl(): Promise<URI | undefined> {239if (isIOS) {240return undefined;241}242if (!this.extension.gallery) {243return undefined;244}245if (!this.extensionManagementServerService.localExtensionManagementServer && !this.extensionManagementServerService.remoteExtensionManagementServer) {246return undefined;247}248let targetPlatform = this.extension.gallery.properties.targetPlatform;249if (targetPlatform !== TargetPlatform.UNIVERSAL && targetPlatform !== TargetPlatform.UNDEFINED && this.extensionManagementServerService.remoteExtensionManagementServer) {250try {251const manifest = await this.galleryService.getManifest(this.extension.gallery, CancellationToken.None);252if (manifest && this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(manifest)) {253targetPlatform = await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getTargetPlatform();254}255} catch (error) {256this.logService.error(error);257return undefined;258}259}260if (targetPlatform === TargetPlatform.UNKNOWN) {261return undefined;262}263264const [extension] = await this.galleryService.getExtensions([{265...this.extension.identifier,266version: this.version267}], {268targetPlatform269}, CancellationToken.None);270271if (!extension) {272return undefined;273}274return URI.parse(extension.assets.download.uri);275}276277}278279export interface IExtensionActionChangeEvent extends IActionChangeEvent {280readonly hidden?: boolean;281readonly menuActions?: IAction[];282}283284export abstract class ExtensionAction extends Action implements IExtensionContainer {285286protected override _onDidChange = this._register(new Emitter<IExtensionActionChangeEvent>());287override get onDidChange() { return this._onDidChange.event; }288289static readonly EXTENSION_ACTION_CLASS = 'extension-action';290static readonly TEXT_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} text`;291static readonly LABEL_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} label`;292static readonly ICON_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} icon`;293294private _extension: IExtension | null = null;295get extension(): IExtension | null { return this._extension; }296set extension(extension: IExtension | null) { this._extension = extension; this.update(); }297298private _hidden: boolean = false;299get hidden(): boolean { return this._hidden; }300set hidden(hidden: boolean) {301if (this._hidden !== hidden) {302this._hidden = hidden;303this._onDidChange.fire({ hidden });304}305}306307protected override _setEnabled(value: boolean): void {308super._setEnabled(value);309if (this.hideOnDisabled) {310this.hidden = !value;311}312}313314protected hideOnDisabled: boolean = true;315316abstract update(): void;317}318319export class ButtonWithDropDownExtensionAction extends ExtensionAction {320321private primaryAction: IAction | undefined;322323readonly menuActionClassNames: string[] = [];324private _menuActions: IAction[] = [];325get menuActions(): IAction[] { return [...this._menuActions]; }326327override get extension(): IExtension | null {328return super.extension;329}330331override set extension(extension: IExtension | null) {332this.extensionActions.forEach(a => a.extension = extension);333super.extension = extension;334}335336protected readonly extensionActions: ExtensionAction[];337338constructor(339id: string,340clazz: string,341private readonly actionsGroups: ExtensionAction[][],342) {343clazz = `${clazz} action-dropdown`;344super(id, undefined, clazz);345this.menuActionClassNames = clazz.split(' ');346this.hideOnDisabled = false;347this.extensionActions = actionsGroups.flat();348this.update();349this._register(Event.any(...this.extensionActions.map(a => a.onDidChange))(() => this.update(true)));350this.extensionActions.forEach(a => this._register(a));351}352353update(donotUpdateActions?: boolean): void {354if (!donotUpdateActions) {355this.extensionActions.forEach(a => a.update());356}357358const actionsGroups = this.actionsGroups.map(actionsGroup => actionsGroup.filter(a => !a.hidden));359360let actions: IAction[] = [];361for (const visibleActions of actionsGroups) {362if (visibleActions.length) {363actions = [...actions, ...visibleActions, new Separator()];364}365}366actions = actions.length ? actions.slice(0, actions.length - 1) : actions;367368this.primaryAction = actions[0];369this._menuActions = actions.length > 1 ? actions : [];370this._onDidChange.fire({ menuActions: this._menuActions });371372if (this.primaryAction) {373this.hidden = false;374this.enabled = this.primaryAction.enabled;375this.label = this.getLabel(this.primaryAction as ExtensionAction);376this.tooltip = this.primaryAction.tooltip;377} else {378this.hidden = true;379this.enabled = false;380}381}382383override async run(): Promise<void> {384if (this.enabled) {385await this.primaryAction?.run();386}387}388389protected getLabel(action: ExtensionAction): string {390return action.label;391}392}393394export class ButtonWithDropdownExtensionActionViewItem extends ActionWithDropdownActionViewItem {395396constructor(397action: ButtonWithDropDownExtensionAction,398options: IActionViewItemOptions & IActionWithDropdownActionViewItemOptions,399contextMenuProvider: IContextMenuProvider400) {401super(null, action, options, contextMenuProvider);402this._register(action.onDidChange(e => {403if (e.hidden !== undefined || e.menuActions !== undefined) {404this.updateClass();405}406}));407}408409override render(container: HTMLElement): void {410super.render(container);411this.updateClass();412}413414protected override updateClass(): void {415super.updateClass();416if (this.element && this.dropdownMenuActionViewItem?.element) {417this.element.classList.toggle('hide', (<ButtonWithDropDownExtensionAction>this._action).hidden);418const isMenuEmpty = (<ButtonWithDropDownExtensionAction>this._action).menuActions.length === 0;419this.element.classList.toggle('empty', isMenuEmpty);420this.dropdownMenuActionViewItem.element.classList.toggle('hide', isMenuEmpty);421}422}423424}425426export class InstallAction extends ExtensionAction {427428static readonly CLASS = `${this.LABEL_ACTION_CLASS} prominent install`;429private static readonly HIDE = `${this.CLASS} hide`;430431protected _manifest: IExtensionManifest | null = null;432set manifest(manifest: IExtensionManifest | null) {433this._manifest = manifest;434this.updateLabel();435}436437private readonly updateThrottler = this._register(new Throttler());438public readonly options: InstallOptions;439440constructor(441options: InstallOptions,442@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,443@IInstantiationService private readonly instantiationService: IInstantiationService,444@IExtensionService private readonly runtimeExtensionService: IExtensionService,445@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,446@ILabelService private readonly labelService: ILabelService,447@IDialogService private readonly dialogService: IDialogService,448@IPreferencesService private readonly preferencesService: IPreferencesService,449@ITelemetryService private readonly telemetryService: ITelemetryService,450@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,451@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,452@IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService,453) {454super('extensions.install', localize('install', "Install"), InstallAction.CLASS, false);455this.hideOnDisabled = false;456this.options = { isMachineScoped: false, ...options };457this.update();458this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => this.update()));459this._register(this.labelService.onDidChangeFormatters(() => this.updateLabel(), this));460}461462update(): void {463this.updateThrottler.queue(() => this.computeAndUpdateEnablement());464}465466protected async computeAndUpdateEnablement(): Promise<void> {467this.enabled = false;468this.class = InstallAction.HIDE;469this.hidden = true;470if (!this.extension) {471return;472}473if (this.extension.isBuiltin) {474return;475}476if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {477return;478}479if (this.extension.state !== ExtensionState.Uninstalled) {480return;481}482if (this.options.installPreReleaseVersion && (!this.extension.hasPreReleaseVersion || this.allowedExtensionsService.isAllowed({ id: this.extension.identifier.id, publisherDisplayName: this.extension.publisherDisplayName, prerelease: true }) !== true)) {483return;484}485if (!this.options.installPreReleaseVersion && !this.extension.hasReleaseVersion) {486return;487}488this.hidden = false;489this.class = InstallAction.CLASS;490if (await this.extensionsWorkbenchService.canInstall(this.extension) === true) {491this.enabled = true;492this.updateLabel();493}494}495496override async run(): Promise<any> {497if (!this.extension) {498return;499}500501if (this.extension.gallery && !this.extension.gallery.isSigned && shouldRequireRepositorySignatureFor(this.extension.private, await this.extensionGalleryManifestService.getExtensionGalleryManifest())) {502const { result } = await this.dialogService.prompt({503type: Severity.Warning,504message: localize('not signed', "'{0}' is an extension from an unknown source. Are you sure you want to install?", this.extension.displayName),505detail: localize('not signed detail', "Extension is not signed."),506buttons: [507{508label: localize('install anyway', "Install Anyway"),509run: () => {510this.options.donotVerifySignature = true;511return true;512}513}514],515cancelButton: {516run: () => false517}518});519if (!result) {520return;521}522}523524if (this.extension.deprecationInfo) {525let detail: string | MarkdownString = localize('deprecated message', "This extension is deprecated as it is no longer being maintained.");526enum DeprecationChoice {527InstallAnyway = 0,528ShowAlternateExtension = 1,529ConfigureSettings = 2,530Cancel = 3531}532const buttons: IPromptButton<DeprecationChoice>[] = [533{534label: localize('install anyway', "Install Anyway"),535run: () => DeprecationChoice.InstallAnyway536}537];538539if (this.extension.deprecationInfo.extension) {540detail = localize('deprecated with alternate extension message', "This extension is deprecated. Use the {0} extension instead.", this.extension.deprecationInfo.extension.displayName);541542const alternateExtension = this.extension.deprecationInfo.extension;543buttons.push({544label: localize({ key: 'Show alternate extension', comment: ['&& denotes a mnemonic'] }, "&&Open {0}", this.extension.deprecationInfo.extension.displayName),545run: async () => {546const [extension] = await this.extensionsWorkbenchService.getExtensions([{ id: alternateExtension.id, preRelease: alternateExtension.preRelease }], CancellationToken.None);547await this.extensionsWorkbenchService.open(extension);548549return DeprecationChoice.ShowAlternateExtension;550}551});552} else if (this.extension.deprecationInfo.settings) {553detail = localize('deprecated with alternate settings message', "This extension is deprecated as this functionality is now built-in to VS Code.");554555const settings = this.extension.deprecationInfo.settings;556buttons.push({557label: localize({ key: 'configure in settings', comment: ['&& denotes a mnemonic'] }, "&&Configure Settings"),558run: async () => {559await this.preferencesService.openSettings({ query: settings.map(setting => `@id:${setting}`).join(' ') });560561return DeprecationChoice.ConfigureSettings;562}563});564} else if (this.extension.deprecationInfo.additionalInfo) {565detail = new MarkdownString(`${detail} ${this.extension.deprecationInfo.additionalInfo}`);566}567568const { result } = await this.dialogService.prompt({569type: Severity.Warning,570message: localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName),571detail: isString(detail) ? detail : undefined,572custom: isString(detail) ? undefined : {573markdownDetails: [{574markdown: detail575}]576},577buttons,578cancelButton: {579run: () => DeprecationChoice.Cancel580}581});582if (result !== DeprecationChoice.InstallAnyway) {583return;584}585}586587this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: this.options.installPreReleaseVersion });588589alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));590591/* __GDPR__592"extensions:action:install" : {593"owner": "sandy081",594"actionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },595"${include}": [596"${GalleryExtensionTelemetryData}"597]598}599*/600this.telemetryService.publicLog('extensions:action:install', { ...this.extension.telemetryData, actionId: this.id });601602const extension = await this.install(this.extension);603604if (extension?.local) {605alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName));606const runningExtension = await this.getRunningExtension(extension.local);607if (runningExtension && !(runningExtension.activationEvents && runningExtension.activationEvents.some(activationEent => activationEent.startsWith('onLanguage')))) {608const action = await this.getThemeAction(extension);609if (action) {610action.extension = extension;611try {612return action.run({ showCurrentTheme: true, ignoreFocusLost: true });613} finally {614action.dispose();615}616}617}618}619620}621622private async getThemeAction(extension: IExtension): Promise<ExtensionAction | undefined> {623const colorThemes = await this.workbenchThemeService.getColorThemes();624if (colorThemes.some(theme => isThemeFromExtension(theme, extension))) {625return this.instantiationService.createInstance(SetColorThemeAction);626}627const fileIconThemes = await this.workbenchThemeService.getFileIconThemes();628if (fileIconThemes.some(theme => isThemeFromExtension(theme, extension))) {629return this.instantiationService.createInstance(SetFileIconThemeAction);630}631const productIconThemes = await this.workbenchThemeService.getProductIconThemes();632if (productIconThemes.some(theme => isThemeFromExtension(theme, extension))) {633return this.instantiationService.createInstance(SetProductIconThemeAction);634}635return undefined;636}637638private async install(extension: IExtension): Promise<IExtension | undefined> {639try {640return await this.extensionsWorkbenchService.install(extension, this.options);641} catch (error) {642await this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, this.options, extension.latestVersion, InstallOperation.Install, error).run();643return undefined;644}645}646647private async getRunningExtension(extension: ILocalExtension): Promise<IExtensionDescription | null> {648const runningExtension = await this.runtimeExtensionService.getExtension(extension.identifier.id);649if (runningExtension) {650return runningExtension;651}652if (this.runtimeExtensionService.canAddExtension(toExtensionDescription(extension))) {653return new Promise<IExtensionDescription | null>((c, e) => {654const disposable = this.runtimeExtensionService.onDidChangeExtensions(async () => {655const runningExtension = await this.runtimeExtensionService.getExtension(extension.identifier.id);656if (runningExtension) {657disposable.dispose();658c(runningExtension);659}660});661});662}663return null;664}665666protected updateLabel(): void {667this.label = this.getLabel();668}669670getLabel(primary?: boolean): string {671if (this.extension?.isWorkspaceScoped && this.extension.resourceExtension && this.contextService.isInsideWorkspace(this.extension.resourceExtension.location)) {672return localize('install workspace version', "Install Workspace Extension");673}674/* install pre-release version */675if (this.options.installPreReleaseVersion && this.extension?.hasPreReleaseVersion) {676return primary ? localize('install pre-release', "Install Pre-Release") : localize('install pre-release version', "Install Pre-Release Version");677}678/* install released version that has a pre release version */679if (this.extension?.hasPreReleaseVersion) {680return primary ? localize('install', "Install") : localize('install release version', "Install Release Version");681}682return localize('install', "Install");683}684685}686687export class InstallDropdownAction extends ButtonWithDropDownExtensionAction {688689set manifest(manifest: IExtensionManifest | null) {690this.extensionActions.forEach(a => (<InstallAction>a).manifest = manifest);691this.update();692}693694constructor(695@IInstantiationService instantiationService: IInstantiationService,696@IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService,697) {698super(`extensions.installActions`, InstallAction.CLASS, [699[700instantiationService.createInstance(InstallAction, { installPreReleaseVersion: extensionManagementService.preferPreReleases }),701instantiationService.createInstance(InstallAction, { installPreReleaseVersion: !extensionManagementService.preferPreReleases }),702]703]);704}705706protected override getLabel(action: InstallAction): string {707return action.getLabel(true);708}709710}711712export class InstallingLabelAction extends ExtensionAction {713714private static readonly LABEL = localize('installing', "Installing");715private static readonly CLASS = `${ExtensionAction.LABEL_ACTION_CLASS} install installing`;716717constructor() {718super('extension.installing', InstallingLabelAction.LABEL, InstallingLabelAction.CLASS, false);719}720721update(): void {722this.class = `${InstallingLabelAction.CLASS}${this.extension && this.extension.state === ExtensionState.Installing ? '' : ' hide'}`;723}724}725726export abstract class InstallInOtherServerAction extends ExtensionAction {727728protected static readonly INSTALL_LABEL = localize('install', "Install");729protected static readonly INSTALLING_LABEL = localize('installing', "Installing");730731private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} prominent install-other-server`;732private static readonly InstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} install-other-server installing`;733734updateWhenCounterExtensionChanges: boolean = true;735736constructor(737id: string,738private readonly server: IExtensionManagementServer | null,739private readonly canInstallAnyWhere: boolean,740@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,741@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,742@IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService,743) {744super(id, InstallInOtherServerAction.INSTALL_LABEL, InstallInOtherServerAction.Class, false);745this.update();746}747748update(): void {749this.enabled = false;750this.class = InstallInOtherServerAction.Class;751752if (this.canInstall()) {753const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server === this.server)[0];754if (extensionInOtherServer) {755// Getting installed in other server756if (extensionInOtherServer.state === ExtensionState.Installing && !extensionInOtherServer.local) {757this.enabled = true;758this.label = InstallInOtherServerAction.INSTALLING_LABEL;759this.class = InstallInOtherServerAction.InstallingClass;760}761} else {762// Not installed in other server763this.enabled = true;764this.label = this.getInstallLabel();765}766}767}768769protected canInstall(): boolean {770// Disable if extension is not installed or not an user extension771if (772!this.extension773|| !this.server774|| !this.extension.local775|| this.extension.state !== ExtensionState.Installed776|| this.extension.type !== ExtensionType.User777|| this.extension.enablementState === EnablementState.DisabledByEnvironment || this.extension.enablementState === EnablementState.DisabledByTrustRequirement || this.extension.enablementState === EnablementState.DisabledByVirtualWorkspace778) {779return false;780}781782if (isLanguagePackExtension(this.extension.local.manifest)) {783return true;784}785786// Prefers to run on UI787if (this.server === this.extensionManagementServerService.localExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnUI(this.extension.local.manifest)) {788return true;789}790791// Prefers to run on Workspace792if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(this.extension.local.manifest)) {793return true;794}795796// Prefers to run on Web797if (this.server === this.extensionManagementServerService.webExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnWeb(this.extension.local.manifest)) {798return true;799}800801if (this.canInstallAnyWhere) {802// Can run on UI803if (this.server === this.extensionManagementServerService.localExtensionManagementServer && this.extensionManifestPropertiesService.canExecuteOnUI(this.extension.local.manifest)) {804return true;805}806807// Can run on Workspace808if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManifestPropertiesService.canExecuteOnWorkspace(this.extension.local.manifest)) {809return true;810}811}812813return false;814}815816override async run(): Promise<void> {817if (!this.extension?.local) {818return;819}820if (!this.extension?.server) {821return;822}823if (!this.server) {824return;825}826this.extensionsWorkbenchService.open(this.extension);827alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));828return this.extensionsWorkbenchService.installInServer(this.extension, this.server);829}830831protected abstract getInstallLabel(): string;832}833834export class RemoteInstallAction extends InstallInOtherServerAction {835836constructor(837canInstallAnyWhere: boolean,838@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,839@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,840@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,841) {842super(`extensions.remoteinstall`, extensionManagementServerService.remoteExtensionManagementServer, canInstallAnyWhere, extensionsWorkbenchService, extensionManagementServerService, extensionManifestPropertiesService);843}844845protected getInstallLabel(): string {846return this.extensionManagementServerService.remoteExtensionManagementServer847? localize({ key: 'install in remote', comment: ['This is the name of the action to install an extension in remote server. Placeholder is for the name of remote server.'] }, "Install in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label)848: InstallInOtherServerAction.INSTALL_LABEL;849}850851}852853export class LocalInstallAction extends InstallInOtherServerAction {854855constructor(856@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,857@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,858@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,859) {860super(`extensions.localinstall`, extensionManagementServerService.localExtensionManagementServer, false, extensionsWorkbenchService, extensionManagementServerService, extensionManifestPropertiesService);861}862863protected getInstallLabel(): string {864return localize('install locally', "Install Locally");865}866867}868869export class WebInstallAction extends InstallInOtherServerAction {870871constructor(872@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,873@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,874@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,875) {876super(`extensions.webInstall`, extensionManagementServerService.webExtensionManagementServer, false, extensionsWorkbenchService, extensionManagementServerService, extensionManifestPropertiesService);877}878879protected getInstallLabel(): string {880return localize('install browser', "Install in Browser");881}882883}884885export class UninstallAction extends ExtensionAction {886887static readonly UninstallLabel = localize('uninstallAction', "Uninstall");888private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling");889890static readonly UninstallClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall`;891private static readonly UnInstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall uninstalling`;892893constructor(894@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,895@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,896@IDialogService private readonly dialogService: IDialogService897) {898super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false);899this.update();900}901902update(): void {903if (!this.extension) {904this.enabled = false;905return;906}907908const state = this.extension.state;909910if (state === ExtensionState.Uninstalling) {911this.label = UninstallAction.UninstallingLabel;912this.class = UninstallAction.UnInstallingClass;913this.enabled = false;914return;915}916917this.label = this.extension.local?.isApplicationScoped && this.userDataProfilesService.profiles.length > 1 ? localize('uninstallAll', "Uninstall (All Profiles)") : UninstallAction.UninstallLabel;918this.class = UninstallAction.UninstallClass;919this.tooltip = UninstallAction.UninstallLabel;920921if (state !== ExtensionState.Installed) {922this.enabled = false;923return;924}925926if (this.extension.isBuiltin) {927this.enabled = false;928return;929}930931this.enabled = true;932}933934override async run(): Promise<any> {935if (!this.extension) {936return;937}938alert(localize('uninstallExtensionStart', "Uninstalling extension {0} started.", this.extension.displayName));939940try {941await this.extensionsWorkbenchService.uninstall(this.extension);942alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName));943} catch (error) {944if (!isCancellationError(error)) {945this.dialogService.error(getErrorMessage(error));946}947}948}949}950951export class UpdateAction extends ExtensionAction {952953private static readonly EnabledClass = `${this.LABEL_ACTION_CLASS} update`;954private static readonly DisabledClass = `${this.EnabledClass} disabled`;955956private readonly updateThrottler = this._register(new Throttler());957958constructor(959private readonly verbose: boolean,960@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,961@IDialogService private readonly dialogService: IDialogService,962@IOpenerService private readonly openerService: IOpenerService,963@IInstantiationService private readonly instantiationService: IInstantiationService,964) {965super(`extensions.update`, localize('update', "Update"), UpdateAction.DisabledClass, false);966this.update();967}968969update(): void {970this.updateThrottler.queue(() => this.computeAndUpdateEnablement());971if (this.extension) {972this.label = this.verbose ? localize('update to', "Update to v{0}", this.extension.latestVersion) : localize('update', "Update");973}974}975976private async computeAndUpdateEnablement(): Promise<void> {977this.enabled = false;978this.class = UpdateAction.DisabledClass;979980if (!this.extension) {981return;982}983984if (this.extension.deprecationInfo) {985return;986}987988const canInstall = await this.extensionsWorkbenchService.canInstall(this.extension);989const isInstalled = this.extension.state === ExtensionState.Installed;990991this.enabled = canInstall === true && isInstalled && this.extension.outdated;992this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass;993}994995override async run(): Promise<any> {996if (!this.extension) {997return;998}9991000const consent = await this.extensionsWorkbenchService.shouldRequireConsentToUpdate(this.extension);1001if (consent) {1002const { result } = await this.dialogService.prompt<'update' | 'review' | 'cancel'>({1003type: 'warning',1004title: localize('updateExtensionConsentTitle', "Update {0} Extension", this.extension.displayName),1005message: localize('updateExtensionConsent', "{0}\n\nWould you like to update the extension?", consent),1006buttons: [{1007label: localize('update', "Update"),1008run: () => 'update'1009}, {1010label: localize('review', "Review"),1011run: () => 'review'1012}, {1013label: localize('cancel', "Cancel"),1014run: () => 'cancel'1015}]1016});1017if (result === 'cancel') {1018return;1019}1020if (result === 'review') {1021if (this.extension.hasChangelog()) {1022return this.extensionsWorkbenchService.open(this.extension, { tab: ExtensionEditorTab.Changelog });1023}1024if (this.extension.repository) {1025return this.openerService.open(this.extension.repository);1026}1027return this.extensionsWorkbenchService.open(this.extension);1028}1029}10301031const installOptions: InstallOptions = {};1032if (this.extension.local?.source === 'vsix' && this.extension.local.pinned) {1033installOptions.pinned = false;1034}1035if (this.extension.local?.preRelease) {1036installOptions.installPreReleaseVersion = true;1037}1038try {1039alert(localize('updateExtensionStart', "Updating extension {0} to version {1} started.", this.extension.displayName, this.extension.latestVersion));1040await this.extensionsWorkbenchService.install(this.extension, installOptions);1041alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", this.extension.displayName, this.extension.latestVersion));1042} catch (err) {1043this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension, installOptions, this.extension.latestVersion, InstallOperation.Update, err).run();1044}1045}1046}10471048export class ToggleAutoUpdateForExtensionAction extends ExtensionAction {10491050static readonly ID = 'workbench.extensions.action.toggleAutoUpdateForExtension';1051static readonly LABEL = localize2('enableAutoUpdateLabel', "Auto Update");10521053private static readonly EnabledClass = `${ExtensionAction.EXTENSION_ACTION_CLASS} auto-update`;1054private static readonly DisabledClass = `${this.EnabledClass} hide`;10551056constructor(1057@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1058@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1059@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,1060@IConfigurationService configurationService: IConfigurationService,1061) {1062super(ToggleAutoUpdateForExtensionAction.ID, ToggleAutoUpdateForExtensionAction.LABEL.value, ToggleAutoUpdateForExtensionAction.DisabledClass);1063this._register(configurationService.onDidChangeConfiguration(e => {1064if (e.affectsConfiguration(AutoUpdateConfigurationKey)) {1065this.update();1066}1067}));1068this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(e => this.update()));1069this.update();1070}10711072override update() {1073this.enabled = false;1074this.class = ToggleAutoUpdateForExtensionAction.DisabledClass;1075if (!this.extension) {1076return;1077}1078if (this.extension.isBuiltin) {1079return;1080}1081if (this.extension.deprecationInfo?.disallowInstall) {1082return;1083}10841085const extension = this.extension.local ?? this.extension.gallery;1086if (extension && this.allowedExtensionsService.isAllowed(extension) !== true) {1087return;1088}1089if (this.extensionsWorkbenchService.getAutoUpdateValue() === 'onlyEnabledExtensions' && !this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState)) {1090return;1091}1092this.enabled = true;1093this.class = ToggleAutoUpdateForExtensionAction.EnabledClass;1094this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);1095}10961097override async run(): Promise<any> {1098if (!this.extension) {1099return;1100}11011102const enableAutoUpdate = !this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);1103await this.extensionsWorkbenchService.updateAutoUpdateEnablementFor(this.extension, enableAutoUpdate);11041105if (enableAutoUpdate) {1106alert(localize('enableAutoUpdate', "Enabled auto updates for", this.extension.displayName));1107} else {1108alert(localize('disableAutoUpdate', "Disabled auto updates for", this.extension.displayName));1109}1110}1111}11121113export class ToggleAutoUpdatesForPublisherAction extends ExtensionAction {11141115static readonly ID = 'workbench.extensions.action.toggleAutoUpdatesForPublisher';1116static readonly LABEL = localize('toggleAutoUpdatesForPublisherLabel', "Auto Update All (From Publisher)");11171118constructor(1119@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService1120) {1121super(ToggleAutoUpdatesForPublisherAction.ID, ToggleAutoUpdatesForPublisherAction.LABEL);1122}11231124override update() { }11251126override async run(): Promise<any> {1127if (!this.extension) {1128return;1129}1130alert(localize('ignoreExtensionUpdatePublisher', "Ignoring updates published by {0}.", this.extension.publisherDisplayName));1131const enableAutoUpdate = !this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension.publisher);1132await this.extensionsWorkbenchService.updateAutoUpdateEnablementFor(this.extension.publisher, enableAutoUpdate);1133if (enableAutoUpdate) {1134alert(localize('enableAutoUpdate', "Enabled auto updates for", this.extension.displayName));1135} else {1136alert(localize('disableAutoUpdate', "Disabled auto updates for", this.extension.displayName));1137}1138}1139}11401141export class MigrateDeprecatedExtensionAction extends ExtensionAction {11421143private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} migrate`;1144private static readonly DisabledClass = `${this.EnabledClass} disabled`;11451146constructor(1147private readonly small: boolean,1148@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService1149) {1150super('extensionsAction.migrateDeprecatedExtension', localize('migrateExtension', "Migrate"), MigrateDeprecatedExtensionAction.DisabledClass, false);1151this.update();1152}11531154update(): void {1155this.enabled = false;1156this.class = MigrateDeprecatedExtensionAction.DisabledClass;1157if (!this.extension?.local) {1158return;1159}1160if (this.extension.state !== ExtensionState.Installed) {1161return;1162}1163if (!this.extension.deprecationInfo?.extension) {1164return;1165}1166const id = this.extension.deprecationInfo.extension.id;1167if (this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, { id }))) {1168return;1169}1170this.enabled = true;1171this.class = MigrateDeprecatedExtensionAction.EnabledClass;1172this.tooltip = localize('migrate to', "Migrate to {0}", this.extension.deprecationInfo.extension.displayName);1173this.label = this.small ? localize('migrate', "Migrate") : this.tooltip;1174}11751176override async run(): Promise<any> {1177if (!this.extension?.deprecationInfo?.extension) {1178return;1179}1180const local = this.extension.local;1181await this.extensionsWorkbenchService.uninstall(this.extension);1182const [extension] = await this.extensionsWorkbenchService.getExtensions([{ id: this.extension.deprecationInfo.extension.id, preRelease: this.extension.deprecationInfo?.extension?.preRelease }], CancellationToken.None);1183await this.extensionsWorkbenchService.install(extension, { isMachineScoped: local?.isMachineScoped });1184}1185}11861187export abstract class DropDownExtensionAction extends ExtensionAction {11881189constructor(1190id: string,1191label: string,1192cssClass: string,1193enabled: boolean,1194@IInstantiationService protected instantiationService: IInstantiationService1195) {1196super(id, label, cssClass, enabled);1197}11981199private _actionViewItem: DropDownExtensionActionViewItem | null = null;1200createActionViewItem(options: IActionViewItemOptions): DropDownExtensionActionViewItem {1201this._actionViewItem = this.instantiationService.createInstance(DropDownExtensionActionViewItem, this, options);1202return this._actionViewItem;1203}12041205public override run(actionGroups: IAction[][]): Promise<any> {1206this._actionViewItem?.showMenu(actionGroups);1207return Promise.resolve();1208}1209}12101211export class DropDownExtensionActionViewItem extends ActionViewItem {12121213constructor(1214action: IAction,1215options: IActionViewItemOptions,1216@IContextMenuService private readonly contextMenuService: IContextMenuService1217) {1218super(null, action, { ...options, icon: true, label: true });1219}12201221public showMenu(menuActionGroups: IAction[][]): void {1222if (this.element) {1223const actions = this.getActions(menuActionGroups);1224const elementPosition = DOM.getDomNodePagePosition(this.element);1225const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 };1226this.contextMenuService.showContextMenu({1227getAnchor: () => anchor,1228getActions: () => actions,1229actionRunner: this.actionRunner,1230onHide: () => disposeIfDisposable(actions)1231});1232}1233}12341235private getActions(menuActionGroups: IAction[][]): IAction[] {1236let actions: IAction[] = [];1237for (const menuActions of menuActionGroups) {1238actions = [...actions, ...menuActions, new Separator()];1239}1240return actions.length ? actions.slice(0, actions.length - 1) : actions;1241}1242}12431244async function getContextMenuActionsGroups(extension: IExtension | undefined | null, contextKeyService: IContextKeyService, instantiationService: IInstantiationService): Promise<[string, Array<MenuItemAction | SubmenuItemAction>][]> {1245return instantiationService.invokeFunction(async accessor => {1246const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);1247const extensionEnablementService = accessor.get(IWorkbenchExtensionEnablementService);1248const menuService = accessor.get(IMenuService);1249const extensionRecommendationsService = accessor.get(IExtensionRecommendationsService);1250const extensionIgnoredRecommendationsService = accessor.get(IExtensionIgnoredRecommendationsService);1251const workbenchThemeService = accessor.get(IWorkbenchThemeService);1252const authenticationUsageService = accessor.get(IAuthenticationUsageService);1253const allowedExtensionsService = accessor.get(IAllowedExtensionsService);1254const cksOverlay: [string, any][] = [];12551256if (extension) {1257cksOverlay.push(['extension', extension.identifier.id]);1258cksOverlay.push(['isBuiltinExtension', extension.isBuiltin]);1259cksOverlay.push(['isDefaultApplicationScopedExtension', extension.local && isApplicationScopedExtension(extension.local.manifest)]);1260cksOverlay.push(['isApplicationScopedExtension', extension.local && extension.local.isApplicationScoped]);1261cksOverlay.push(['isWorkspaceScopedExtension', extension.isWorkspaceScoped]);1262cksOverlay.push(['isGalleryExtension', !!extension.identifier.uuid]);1263if (extension.local) {1264cksOverlay.push(['extensionSource', extension.local.source]);1265}1266cksOverlay.push(['extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration]);1267cksOverlay.push(['extensionHasKeybindings', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.keybindings]);1268cksOverlay.push(['extensionHasCommands', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes?.commands]);1269cksOverlay.push(['isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]]);1270cksOverlay.push(['isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace]);1271cksOverlay.push(['isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())]);1272cksOverlay.push(['isExtensionPinned', extension.pinned]);1273cksOverlay.push(['isExtensionEnabled', extensionEnablementService.isEnabledEnablementState(extension.enablementState)]);1274switch (extension.state) {1275case ExtensionState.Installing:1276cksOverlay.push(['extensionStatus', 'installing']);1277break;1278case ExtensionState.Installed:1279cksOverlay.push(['extensionStatus', 'installed']);1280break;1281case ExtensionState.Uninstalling:1282cksOverlay.push(['extensionStatus', 'uninstalling']);1283break;1284case ExtensionState.Uninstalled:1285cksOverlay.push(['extensionStatus', 'uninstalled']);1286break;1287}1288cksOverlay.push(['installedExtensionIsPreReleaseVersion', !!extension.local?.isPreReleaseVersion]);1289cksOverlay.push(['installedExtensionIsOptedToPreRelease', !!extension.local?.preRelease]);1290cksOverlay.push(['galleryExtensionIsPreReleaseVersion', !!extension.gallery?.properties.isPreReleaseVersion]);1291cksOverlay.push(['galleryExtensionHasPreReleaseVersion', extension.gallery?.hasPreReleaseVersion]);1292cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]);1293cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]);1294cksOverlay.push(['extensionDisallowInstall', extension.isMalicious || extension.deprecationInfo?.disallowInstall]);1295cksOverlay.push(['isExtensionAllowed', allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName }) === true]);1296cksOverlay.push(['isPreReleaseExtensionAllowed', allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName, prerelease: true }) === true]);1297cksOverlay.push(['extensionIsUnsigned', extension.gallery && !extension.gallery.isSigned]);1298cksOverlay.push(['extensionIsPrivate', extension.gallery?.private]);12991300const [colorThemes, fileIconThemes, productIconThemes, extensionUsesAuth] = await Promise.all([workbenchThemeService.getColorThemes(), workbenchThemeService.getFileIconThemes(), workbenchThemeService.getProductIconThemes(), authenticationUsageService.extensionUsesAuth(extension.identifier.id.toLowerCase())]);1301cksOverlay.push(['extensionHasColorThemes', colorThemes.some(theme => isThemeFromExtension(theme, extension))]);1302cksOverlay.push(['extensionHasFileIconThemes', fileIconThemes.some(theme => isThemeFromExtension(theme, extension))]);1303cksOverlay.push(['extensionHasProductIconThemes', productIconThemes.some(theme => isThemeFromExtension(theme, extension))]);1304cksOverlay.push(['extensionHasAccountPreferences', extensionUsesAuth]);13051306cksOverlay.push(['canSetLanguage', extensionsWorkbenchService.canSetLanguage(extension)]);1307cksOverlay.push(['isActiveLanguagePackExtension', extension.gallery && language === getLocale(extension.gallery)]);1308}13091310const actionsGroups = menuService.getMenuActions(MenuId.ExtensionContext, contextKeyService.createOverlay(cksOverlay), { shouldForwardArgs: true });1311return actionsGroups;1312});1313}13141315function toActions(actionsGroups: [string, Array<MenuItemAction | SubmenuItemAction>][], instantiationService: IInstantiationService): IAction[][] {1316const result: IAction[][] = [];1317for (const [, actions] of actionsGroups) {1318result.push(actions.map(action => {1319if (action instanceof SubmenuAction) {1320return action;1321}1322return instantiationService.createInstance(MenuItemExtensionAction, action);1323}));1324}1325return result;1326}132713281329export async function getContextMenuActions(extension: IExtension | undefined | null, contextKeyService: IContextKeyService, instantiationService: IInstantiationService): Promise<IAction[][]> {1330const actionsGroups = await getContextMenuActionsGroups(extension, contextKeyService, instantiationService);1331return toActions(actionsGroups, instantiationService);1332}13331334export class ManageExtensionAction extends DropDownExtensionAction {13351336static readonly ID = 'extensions.manage';13371338private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage ` + ThemeIcon.asClassName(manageExtensionIcon);1339private static readonly HideManageExtensionClass = `${this.Class} hide`;13401341constructor(1342@IInstantiationService instantiationService: IInstantiationService,1343@IExtensionService private readonly extensionService: IExtensionService,1344@IContextKeyService private readonly contextKeyService: IContextKeyService,1345) {13461347super(ManageExtensionAction.ID, '', '', true, instantiationService);13481349this.tooltip = localize('manage', "Manage");13501351this.update();1352}13531354async getActionGroups(): Promise<IAction[][]> {1355const groups: IAction[][] = [];1356const contextMenuActionsGroups = await getContextMenuActionsGroups(this.extension, this.contextKeyService, this.instantiationService);1357const themeActions: IAction[] = [], installActions: IAction[] = [], updateActions: IAction[] = [], otherActionGroups: IAction[][] = [];1358for (const [group, actions] of contextMenuActionsGroups) {1359if (group === INSTALL_ACTIONS_GROUP) {1360installActions.push(...toActions([[group, actions]], this.instantiationService)[0]);1361} else if (group === UPDATE_ACTIONS_GROUP) {1362updateActions.push(...toActions([[group, actions]], this.instantiationService)[0]);1363} else if (group === THEME_ACTIONS_GROUP) {1364themeActions.push(...toActions([[group, actions]], this.instantiationService)[0]);1365} else {1366otherActionGroups.push(...toActions([[group, actions]], this.instantiationService));1367}1368}13691370if (themeActions.length) {1371groups.push(themeActions);1372}13731374groups.push([1375this.instantiationService.createInstance(EnableGloballyAction),1376this.instantiationService.createInstance(EnableForWorkspaceAction)1377]);1378groups.push([1379this.instantiationService.createInstance(DisableGloballyAction),1380this.instantiationService.createInstance(DisableForWorkspaceAction)1381]);1382if (updateActions.length) {1383groups.push(updateActions);1384}1385groups.push([1386...(installActions.length ? installActions : []),1387this.instantiationService.createInstance(InstallAnotherVersionAction, this.extension, false),1388this.instantiationService.createInstance(UninstallAction),1389]);13901391otherActionGroups.forEach(actions => groups.push(actions));13921393groups.forEach(group => group.forEach(extensionAction => {1394if (extensionAction instanceof ExtensionAction) {1395extensionAction.extension = this.extension;1396}1397}));13981399return groups;1400}14011402override async run(): Promise<any> {1403await this.extensionService.whenInstalledExtensionsRegistered();1404return super.run(await this.getActionGroups());1405}14061407update(): void {1408this.class = ManageExtensionAction.HideManageExtensionClass;1409this.enabled = false;1410if (this.extension) {1411const state = this.extension.state;1412this.enabled = state === ExtensionState.Installed;1413this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass;1414}1415}1416}14171418export class ExtensionEditorManageExtensionAction extends DropDownExtensionAction {14191420constructor(1421private readonly contextKeyService: IContextKeyService,1422instantiationService: IInstantiationService1423) {1424super('extensionEditor.manageExtension', '', `${ExtensionAction.ICON_ACTION_CLASS} manage ${ThemeIcon.asClassName(manageExtensionIcon)}`, true, instantiationService);1425this.tooltip = localize('manage', "Manage");1426}14271428update(): void { }14291430override async run(): Promise<any> {1431const actionGroups: IAction[][] = [];1432(await getContextMenuActions(this.extension, this.contextKeyService, this.instantiationService)).forEach(actions => actionGroups.push(actions));1433actionGroups.forEach(group => group.forEach(extensionAction => {1434if (extensionAction instanceof ExtensionAction) {1435extensionAction.extension = this.extension;1436}1437}));1438return super.run(actionGroups);1439}14401441}14421443export class MenuItemExtensionAction extends ExtensionAction {14441445constructor(1446private readonly action: IAction,1447@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1448) {1449super(action.id, action.label);1450}14511452override get enabled(): boolean {1453return this.action.enabled;1454}14551456override set enabled(value: boolean) {1457this.action.enabled = value;1458}14591460update() {1461if (!this.extension) {1462return;1463}1464if (this.action.id === TOGGLE_IGNORE_EXTENSION_ACTION_ID) {1465this.checked = !this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension);1466} else if (this.action.id === ToggleAutoUpdateForExtensionAction.ID) {1467this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);1468} else if (this.action.id === ToggleAutoUpdatesForPublisherAction.ID) {1469this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension.publisher);1470} else {1471this.checked = this.action.checked;1472}1473}14741475override async run(): Promise<void> {1476if (this.extension) {1477const id = this.extension.local ? getExtensionId(this.extension.local.manifest.publisher, this.extension.local.manifest.name)1478: this.extension.gallery ? getExtensionId(this.extension.gallery.publisher, this.extension.gallery.name)1479: this.extension.identifier.id;1480const extensionArg: IExtensionArg = {1481id: this.extension.identifier.id,1482version: this.extension.version,1483location: this.extension.local?.location,1484galleryLink: this.extension.url1485};1486await this.action.run(id, extensionArg);1487}1488}1489}14901491export class TogglePreReleaseExtensionAction extends ExtensionAction {14921493static readonly ID = 'workbench.extensions.action.togglePreRlease';1494static readonly LABEL = localize('togglePreRleaseLabel', "Pre-Release");14951496private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} prominent pre-release`;1497private static readonly DisabledClass = `${this.EnabledClass} hide`;14981499constructor(1500@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1501@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,1502) {1503super(TogglePreReleaseExtensionAction.ID, TogglePreReleaseExtensionAction.LABEL, TogglePreReleaseExtensionAction.DisabledClass);1504this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => this.update()));1505this.update();1506}15071508override update() {1509this.enabled = false;1510this.class = TogglePreReleaseExtensionAction.DisabledClass;1511if (!this.extension) {1512return;1513}1514if (this.extension.isBuiltin) {1515return;1516}1517if (this.extension.state !== ExtensionState.Installed) {1518return;1519}1520if (!this.extension.hasPreReleaseVersion) {1521return;1522}1523if (!this.extension.gallery) {1524return;1525}1526if (this.extension.preRelease) {1527if (!this.extension.isPreReleaseVersion) {1528return;1529}1530if (this.allowedExtensionsService.isAllowed({ id: this.extension.identifier.id, publisherDisplayName: this.extension.publisherDisplayName }) !== true) {1531return;1532}1533}1534if (!this.extension.preRelease) {1535if (!this.extension.gallery.hasPreReleaseVersion) {1536return;1537}1538if (this.allowedExtensionsService.isAllowed(this.extension.gallery) !== true) {1539return;1540}1541}1542this.enabled = true;1543this.class = TogglePreReleaseExtensionAction.EnabledClass;15441545if (this.extension.preRelease) {1546this.label = localize('togglePreRleaseDisableLabel', "Switch to Release Version");1547this.tooltip = localize('togglePreRleaseDisableTooltip', "This will switch and enable updates to release versions");1548} else {1549this.label = localize('switchToPreReleaseLabel', "Switch to Pre-Release Version");1550this.tooltip = localize('switchToPreReleaseTooltip', "This will switch to pre-release version and enable updates to latest version always");1551}1552}15531554override async run(): Promise<any> {1555if (!this.extension) {1556return;1557}1558this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: !this.extension.preRelease });1559await this.extensionsWorkbenchService.togglePreRelease(this.extension);1560}1561}15621563export class InstallAnotherVersionAction extends ExtensionAction {15641565static readonly ID = 'workbench.extensions.action.install.anotherVersion';1566static readonly LABEL = localize('install another version', "Install Specific Version...");15671568constructor(1569extension: IExtension | null,1570private readonly whenInstalled: boolean,1571@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1572@IWorkbenchExtensionManagementService private readonly extensionManagementService: IWorkbenchExtensionManagementService,1573@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,1574@IQuickInputService private readonly quickInputService: IQuickInputService,1575@IInstantiationService private readonly instantiationService: IInstantiationService,1576@IDialogService private readonly dialogService: IDialogService,1577@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,1578) {1579super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1580this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => this.update()));1581this.extension = extension;1582this.update();1583}15841585update(): void {1586this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.identifier.uuid && !this.extension.deprecationInfo1587&& this.allowedExtensionsService.isAllowed({ id: this.extension.identifier.id, publisherDisplayName: this.extension.publisherDisplayName }) === true;1588if (this.enabled && this.whenInstalled) {1589this.enabled = !!this.extension?.local && !!this.extension.server && this.extension.state === ExtensionState.Installed;1590}1591}15921593override async run(): Promise<any> {1594if (!this.enabled) {1595return;1596}1597if (!this.extension) {1598return;1599}1600const targetPlatform = this.extension.server ? await this.extension.server.extensionManagementService.getTargetPlatform() : await this.extensionManagementService.getTargetPlatform();1601const allVersions = await this.extensionGalleryService.getAllCompatibleVersions(this.extension.identifier, this.extension.local?.preRelease ?? this.extension.gallery?.properties.isPreReleaseVersion ?? false, targetPlatform);1602if (!allVersions.length) {1603await this.dialogService.info(localize('no versions', "This extension has no other versions."));1604return;1605}16061607const picks = allVersions.map((v, i) => {1608return {1609id: v.version,1610label: v.version,1611description: `${fromNow(new Date(Date.parse(v.date)), true)}${v.isPreReleaseVersion ? ` (${localize('pre-release', "pre-release")})` : ''}${v.version === this.extension?.local?.manifest.version ? ` (${localize('current', "current")})` : ''}`,1612ariaLabel: `${v.isPreReleaseVersion ? 'Pre-Release version' : 'Release version'} ${v.version}`,1613isPreReleaseVersion: v.isPreReleaseVersion1614};1615});1616const pick = await this.quickInputService.pick(picks,1617{1618placeHolder: localize('selectVersion', "Select Version to Install"),1619matchOnDetail: true1620});1621if (pick) {1622if (this.extension.local?.manifest.version === pick.id) {1623return;1624}1625const options = { installPreReleaseVersion: pick.isPreReleaseVersion, version: pick.id };1626try {1627await this.extensionsWorkbenchService.install(this.extension, options);1628} catch (error) {1629this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension, options, pick.id, InstallOperation.Install, error).run();1630}1631}1632return null;1633}16341635}16361637export class EnableForWorkspaceAction extends ExtensionAction {16381639static readonly ID = 'extensions.enableForWorkspace';1640static readonly LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)");16411642constructor(1643@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1644@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService1645) {1646super(EnableForWorkspaceAction.ID, EnableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1647this.tooltip = localize('enableForWorkspaceActionToolTip', "Enable this extension only in this workspace");1648this.update();1649}16501651update(): void {1652this.enabled = false;1653if (this.extension && this.extension.local && !this.extension.isWorkspaceScoped) {1654this.enabled = this.extension.state === ExtensionState.Installed1655&& !this.extensionEnablementService.isEnabled(this.extension.local)1656&& this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local);1657}1658}16591660override async run(): Promise<any> {1661if (!this.extension) {1662return;1663}1664return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.EnabledWorkspace);1665}1666}16671668export class EnableGloballyAction extends ExtensionAction {16691670static readonly ID = 'extensions.enableGlobally';1671static readonly LABEL = localize('enableGloballyAction', "Enable");16721673constructor(1674@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1675@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService1676) {1677super(EnableGloballyAction.ID, EnableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1678this.tooltip = localize('enableGloballyActionToolTip', "Enable this extension");1679this.update();1680}16811682update(): void {1683this.enabled = false;1684if (this.extension && this.extension.local && !this.extension.isWorkspaceScoped) {1685this.enabled = this.extension.state === ExtensionState.Installed1686&& this.extensionEnablementService.isDisabledGlobally(this.extension.local)1687&& this.extensionEnablementService.canChangeEnablement(this.extension.local);1688}1689}16901691override async run(): Promise<any> {1692if (!this.extension) {1693return;1694}1695return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.EnabledGlobally);1696}1697}16981699export class DisableForWorkspaceAction extends ExtensionAction {17001701static readonly ID = 'extensions.disableForWorkspace';1702static readonly LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)");17031704constructor(1705@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,1706@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1707@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1708@IExtensionService private readonly extensionService: IExtensionService,1709) {1710super(DisableForWorkspaceAction.ID, DisableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1711this.tooltip = localize('disableForWorkspaceActionToolTip', "Disable this extension only in this workspace");1712this.update();1713this._register(this.extensionService.onDidChangeExtensions(() => this.update()));1714}17151716update(): void {1717this.enabled = false;1718if (this.extension && this.extension.local && !this.extension.isWorkspaceScoped && this.extensionService.extensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {1719this.enabled = this.extension.state === ExtensionState.Installed1720&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)1721&& this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local);1722}1723}17241725override async run(): Promise<any> {1726if (!this.extension) {1727return;1728}1729return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.DisabledWorkspace);1730}1731}17321733export class DisableGloballyAction extends ExtensionAction {17341735static readonly ID = 'extensions.disableGlobally';1736static readonly LABEL = localize('disableGloballyAction', "Disable");17371738constructor(1739@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1740@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1741@IExtensionService private readonly extensionService: IExtensionService,1742) {1743super(DisableGloballyAction.ID, DisableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1744this.tooltip = localize('disableGloballyActionToolTip', "Disable this extension");1745this.update();1746this._register(this.extensionService.onDidChangeExtensions(() => this.update()));1747}17481749update(): void {1750this.enabled = false;1751if (this.extension && this.extension.local && !this.extension.isWorkspaceScoped && this.extensionService.extensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))) {1752this.enabled = this.extension.state === ExtensionState.Installed1753&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)1754&& this.extensionEnablementService.canChangeEnablement(this.extension.local);1755}1756}17571758override async run(): Promise<any> {1759if (!this.extension) {1760return;1761}1762return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.DisabledGlobally);1763}1764}17651766export class EnableDropDownAction extends ButtonWithDropDownExtensionAction {17671768constructor(1769@IInstantiationService instantiationService: IInstantiationService1770) {1771super('extensions.enable', ExtensionAction.LABEL_ACTION_CLASS, [1772[1773instantiationService.createInstance(EnableGloballyAction),1774instantiationService.createInstance(EnableForWorkspaceAction)1775]1776]);1777}1778}17791780export class DisableDropDownAction extends ButtonWithDropDownExtensionAction {17811782constructor(1783@IInstantiationService instantiationService: IInstantiationService1784) {1785super('extensions.disable', ExtensionAction.LABEL_ACTION_CLASS, [[1786instantiationService.createInstance(DisableGloballyAction),1787instantiationService.createInstance(DisableForWorkspaceAction)1788]]);1789}17901791}17921793export class ExtensionRuntimeStateAction extends ExtensionAction {17941795private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} prominent reload`;1796private static readonly DisabledClass = `${this.EnabledClass} disabled`;17971798updateWhenCounterExtensionChanges: boolean = true;17991800constructor(1801@IHostService private readonly hostService: IHostService,1802@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1803@IUpdateService private readonly updateService: IUpdateService,1804@IExtensionService private readonly extensionService: IExtensionService,1805@IProductService private readonly productService: IProductService,1806@ITelemetryService private readonly telemetryService: ITelemetryService,1807) {1808super('extensions.runtimeState', '', ExtensionRuntimeStateAction.DisabledClass, false);1809this._register(this.extensionService.onDidChangeExtensions(() => this.update()));1810this.update();1811}18121813update(): void {1814this.enabled = false;1815this.tooltip = '';1816this.class = ExtensionRuntimeStateAction.DisabledClass;18171818if (!this.extension) {1819return;1820}18211822const state = this.extension.state;1823if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {1824return;1825}18261827if (this.extension.local && this.extension.local.manifest && this.extension.local.manifest.contributes && this.extension.local.manifest.contributes.localizations && this.extension.local.manifest.contributes.localizations.length > 0) {1828return;1829}18301831const runtimeState = this.extension.runtimeState;1832if (!runtimeState) {1833return;1834}18351836this.enabled = true;1837this.class = ExtensionRuntimeStateAction.EnabledClass;1838this.tooltip = runtimeState.reason;1839this.label = runtimeState.action === ExtensionRuntimeActionType.ReloadWindow ? localize('reload window', 'Reload Window')1840: runtimeState.action === ExtensionRuntimeActionType.RestartExtensions ? localize('restart extensions', 'Restart Extensions')1841: runtimeState.action === ExtensionRuntimeActionType.QuitAndInstall ? localize('restart product', 'Restart to Update')1842: runtimeState.action === ExtensionRuntimeActionType.ApplyUpdate || runtimeState.action === ExtensionRuntimeActionType.DownloadUpdate ? localize('update product', 'Update {0}', this.productService.nameShort) : '';1843}18441845override async run(): Promise<any> {1846const runtimeState = this.extension?.runtimeState;1847if (!runtimeState?.action) {1848return;1849}18501851type ExtensionRuntimeStateActionClassification = {1852owner: 'sandy081';1853comment: 'Extension runtime state action event';1854action: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Executed action' };1855};1856type ExtensionRuntimeStateActionEvent = {1857action: string;1858};1859this.telemetryService.publicLog2<ExtensionRuntimeStateActionEvent, ExtensionRuntimeStateActionClassification>('extensions:runtimestate:action', {1860action: runtimeState.action1861});18621863if (runtimeState?.action === ExtensionRuntimeActionType.ReloadWindow) {1864return this.hostService.reload();1865}18661867else if (runtimeState?.action === ExtensionRuntimeActionType.RestartExtensions) {1868return this.extensionsWorkbenchService.updateRunningExtensions();1869}18701871else if (runtimeState?.action === ExtensionRuntimeActionType.DownloadUpdate) {1872return this.updateService.downloadUpdate(true);1873}18741875else if (runtimeState?.action === ExtensionRuntimeActionType.ApplyUpdate) {1876return this.updateService.applyUpdate();1877}18781879else if (runtimeState?.action === ExtensionRuntimeActionType.QuitAndInstall) {1880return this.updateService.quitAndInstall();1881}18821883}1884}18851886function isThemeFromExtension(theme: IWorkbenchTheme, extension: IExtension | undefined | null): boolean {1887return !!(extension && theme.extensionData && ExtensionIdentifier.equals(theme.extensionData.extensionId, extension.identifier.id));1888}18891890function getQuickPickEntries(themes: IWorkbenchTheme[], currentTheme: IWorkbenchTheme, extension: IExtension | null | undefined, showCurrentTheme: boolean): QuickPickItem[] {1891const picks: QuickPickItem[] = [];1892for (const theme of themes) {1893if (isThemeFromExtension(theme, extension) && !(showCurrentTheme && theme === currentTheme)) {1894picks.push({ label: theme.label, id: theme.id });1895}1896}1897if (showCurrentTheme) {1898picks.push({ type: 'separator', label: localize('current', "current") });1899picks.push({ label: currentTheme.label, id: currentTheme.id });1900}1901return picks;1902}19031904export class SetColorThemeAction extends ExtensionAction {19051906static readonly ID = 'workbench.extensions.action.setColorTheme';1907static readonly TITLE = localize2('workbench.extensions.action.setColorTheme', 'Set Color Theme');19081909private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;1910private static readonly DisabledClass = `${this.EnabledClass} disabled`;19111912constructor(1913@IExtensionService extensionService: IExtensionService,1914@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,1915@IQuickInputService private readonly quickInputService: IQuickInputService,1916@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1917) {1918super(SetColorThemeAction.ID, SetColorThemeAction.TITLE.value, SetColorThemeAction.DisabledClass, false);1919this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidColorThemeChange)(() => this.update(), this));1920this.update();1921}19221923update(): void {1924this.workbenchThemeService.getColorThemes().then(colorThemes => {1925this.enabled = this.computeEnablement(colorThemes);1926this.class = this.enabled ? SetColorThemeAction.EnabledClass : SetColorThemeAction.DisabledClass;1927});1928}19291930private computeEnablement(colorThemes: IWorkbenchColorTheme[]): boolean {1931return !!this.extension && this.extension.state === ExtensionState.Installed && this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState) && colorThemes.some(th => isThemeFromExtension(th, this.extension));1932}19331934override async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean; ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {1935const colorThemes = await this.workbenchThemeService.getColorThemes();19361937if (!this.computeEnablement(colorThemes)) {1938return;1939}1940const currentTheme = this.workbenchThemeService.getColorTheme();19411942const delayer = new Delayer<any>(100);1943const picks = getQuickPickEntries(colorThemes, currentTheme, this.extension, showCurrentTheme);1944const pickedTheme = await this.quickInputService.pick(1945picks,1946{1947placeHolder: localize('select color theme', "Select Color Theme"),1948onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setColorTheme(item.id, undefined)),1949ignoreFocusLost1950});1951return this.workbenchThemeService.setColorTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');1952}1953}19541955export class SetFileIconThemeAction extends ExtensionAction {19561957static readonly ID = 'workbench.extensions.action.setFileIconTheme';1958static readonly TITLE = localize2('workbench.extensions.action.setFileIconTheme', 'Set File Icon Theme');19591960private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;1961private static readonly DisabledClass = `${this.EnabledClass} disabled`;19621963constructor(1964@IExtensionService extensionService: IExtensionService,1965@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,1966@IQuickInputService private readonly quickInputService: IQuickInputService,1967@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1968) {1969super(SetFileIconThemeAction.ID, SetFileIconThemeAction.TITLE.value, SetFileIconThemeAction.DisabledClass, false);1970this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidFileIconThemeChange)(() => this.update(), this));1971this.update();1972}19731974update(): void {1975this.workbenchThemeService.getFileIconThemes().then(fileIconThemes => {1976this.enabled = this.computeEnablement(fileIconThemes);1977this.class = this.enabled ? SetFileIconThemeAction.EnabledClass : SetFileIconThemeAction.DisabledClass;1978});1979}19801981private computeEnablement(colorThemfileIconThemess: IWorkbenchFileIconTheme[]): boolean {1982return !!this.extension && this.extension.state === ExtensionState.Installed && this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState) && colorThemfileIconThemess.some(th => isThemeFromExtension(th, this.extension));1983}19841985override async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean; ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {1986const fileIconThemes = await this.workbenchThemeService.getFileIconThemes();1987if (!this.computeEnablement(fileIconThemes)) {1988return;1989}1990const currentTheme = this.workbenchThemeService.getFileIconTheme();19911992const delayer = new Delayer<any>(100);1993const picks = getQuickPickEntries(fileIconThemes, currentTheme, this.extension, showCurrentTheme);1994const pickedTheme = await this.quickInputService.pick(1995picks,1996{1997placeHolder: localize('select file icon theme', "Select File Icon Theme"),1998onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setFileIconTheme(item.id, undefined)),1999ignoreFocusLost2000});2001return this.workbenchThemeService.setFileIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');2002}2003}20042005export class SetProductIconThemeAction extends ExtensionAction {20062007static readonly ID = 'workbench.extensions.action.setProductIconTheme';2008static readonly TITLE = localize2('workbench.extensions.action.setProductIconTheme', 'Set Product Icon Theme');20092010private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;2011private static readonly DisabledClass = `${this.EnabledClass} disabled`;20122013constructor(2014@IExtensionService extensionService: IExtensionService,2015@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,2016@IQuickInputService private readonly quickInputService: IQuickInputService,2017@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,2018) {2019super(SetProductIconThemeAction.ID, SetProductIconThemeAction.TITLE.value, SetProductIconThemeAction.DisabledClass, false);2020this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidProductIconThemeChange)(() => this.update(), this));2021this.update();2022}20232024update(): void {2025this.workbenchThemeService.getProductIconThemes().then(productIconThemes => {2026this.enabled = this.computeEnablement(productIconThemes);2027this.class = this.enabled ? SetProductIconThemeAction.EnabledClass : SetProductIconThemeAction.DisabledClass;2028});2029}20302031private computeEnablement(productIconThemes: IWorkbenchProductIconTheme[]): boolean {2032return !!this.extension && this.extension.state === ExtensionState.Installed && this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState) && productIconThemes.some(th => isThemeFromExtension(th, this.extension));2033}20342035override async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean; ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {2036const productIconThemes = await this.workbenchThemeService.getProductIconThemes();2037if (!this.computeEnablement(productIconThemes)) {2038return;2039}20402041const currentTheme = this.workbenchThemeService.getProductIconTheme();20422043const delayer = new Delayer<any>(100);2044const picks = getQuickPickEntries(productIconThemes, currentTheme, this.extension, showCurrentTheme);2045const pickedTheme = await this.quickInputService.pick(2046picks,2047{2048placeHolder: localize('select product icon theme', "Select Product Icon Theme"),2049onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setProductIconTheme(item.id, undefined)),2050ignoreFocusLost2051});2052return this.workbenchThemeService.setProductIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');2053}2054}20552056export class SetLanguageAction extends ExtensionAction {20572058static readonly ID = 'workbench.extensions.action.setDisplayLanguage';2059static readonly TITLE = localize2('workbench.extensions.action.setDisplayLanguage', 'Set Display Language');20602061private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} language`;2062private static readonly DisabledClass = `${this.EnabledClass} disabled`;20632064constructor(2065@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2066) {2067super(SetLanguageAction.ID, SetLanguageAction.TITLE.value, SetLanguageAction.DisabledClass, false);2068this.update();2069}20702071update(): void {2072this.enabled = false;2073this.class = SetLanguageAction.DisabledClass;2074if (!this.extension) {2075return;2076}2077if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {2078return;2079}2080if (this.extension.gallery && language === getLocale(this.extension.gallery)) {2081return;2082}2083this.enabled = true;2084this.class = SetLanguageAction.EnabledClass;2085}20862087override async run(): Promise<any> {2088return this.extension && this.extensionsWorkbenchService.setLanguage(this.extension);2089}2090}20912092export class ClearLanguageAction extends ExtensionAction {20932094static readonly ID = 'workbench.extensions.action.clearLanguage';2095static readonly TITLE = localize2('workbench.extensions.action.clearLanguage', 'Clear Display Language');20962097private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} language`;2098private static readonly DisabledClass = `${this.EnabledClass} disabled`;20992100constructor(2101@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2102@ILocaleService private readonly localeService: ILocaleService,2103) {2104super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);2105this.update();2106}21072108update(): void {2109this.enabled = false;2110this.class = ClearLanguageAction.DisabledClass;2111if (!this.extension) {2112return;2113}2114if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {2115return;2116}2117if (this.extension.gallery && language !== getLocale(this.extension.gallery)) {2118return;2119}2120this.enabled = true;2121this.class = ClearLanguageAction.EnabledClass;2122}21232124override async run(): Promise<any> {2125return this.extension && this.localeService.clearLocalePreference();2126}2127}21282129export class ShowRecommendedExtensionAction extends Action {21302131static readonly ID = 'workbench.extensions.action.showRecommendedExtension';2132static readonly LABEL = localize('showRecommendedExtension', "Show Recommended Extension");21332134private extensionId: string;21352136constructor(2137extensionId: string,2138@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,2139) {2140super(ShowRecommendedExtensionAction.ID, ShowRecommendedExtensionAction.LABEL, undefined, false);2141this.extensionId = extensionId;2142}21432144override async run(): Promise<any> {2145await this.extensionWorkbenchService.openSearch(`@id:${this.extensionId}`);2146const [extension] = await this.extensionWorkbenchService.getExtensions([{ id: this.extensionId }], { source: 'install-recommendation' }, CancellationToken.None);2147if (extension) {2148return this.extensionWorkbenchService.open(extension);2149}2150return null;2151}2152}21532154export class InstallRecommendedExtensionAction extends Action {21552156static readonly ID = 'workbench.extensions.action.installRecommendedExtension';2157static readonly LABEL = localize('installRecommendedExtension', "Install Recommended Extension");21582159private extensionId: string;21602161constructor(2162extensionId: string,2163@IInstantiationService private readonly instantiationService: IInstantiationService,2164@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,2165) {2166super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, undefined, false);2167this.extensionId = extensionId;2168}21692170override async run(): Promise<any> {2171await this.extensionWorkbenchService.openSearch(`@id:${this.extensionId}`);2172const [extension] = await this.extensionWorkbenchService.getExtensions([{ id: this.extensionId }], { source: 'install-recommendation' }, CancellationToken.None);2173if (extension) {2174await this.extensionWorkbenchService.open(extension);2175try {2176await this.extensionWorkbenchService.install(extension);2177} catch (err) {2178this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, undefined, extension.latestVersion, InstallOperation.Install, err).run();2179}2180}2181}2182}21832184export class IgnoreExtensionRecommendationAction extends Action {21852186static readonly ID = 'extensions.ignore';21872188private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} ignore`;21892190constructor(2191private readonly extension: IExtension,2192@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,2193) {2194super(IgnoreExtensionRecommendationAction.ID, 'Ignore Recommendation');21952196this.class = IgnoreExtensionRecommendationAction.Class;2197this.tooltip = localize('ignoreExtensionRecommendation', "Do not recommend this extension again");2198this.enabled = true;2199}22002201public override run(): Promise<any> {2202this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, true);2203return Promise.resolve();2204}2205}22062207export class UndoIgnoreExtensionRecommendationAction extends Action {22082209static readonly ID = 'extensions.ignore';22102211private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} undo-ignore`;22122213constructor(2214private readonly extension: IExtension,2215@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,2216) {2217super(UndoIgnoreExtensionRecommendationAction.ID, 'Undo');22182219this.class = UndoIgnoreExtensionRecommendationAction.Class;2220this.tooltip = localize('undo', "Undo");2221this.enabled = true;2222}22232224public override run(): Promise<any> {2225this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, false);2226return Promise.resolve();2227}2228}22292230export abstract class AbstractConfigureRecommendedExtensionsAction extends Action {22312232constructor(2233id: string,2234label: string,2235@IWorkspaceContextService protected contextService: IWorkspaceContextService,2236@IFileService private readonly fileService: IFileService,2237@ITextFileService private readonly textFileService: ITextFileService,2238@IEditorService protected editorService: IEditorService,2239@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,2240@ITextModelService private readonly textModelResolverService: ITextModelService2241) {2242super(id, label);2243}22442245protected openExtensionsFile(extensionsFileResource: URI): Promise<any> {2246return this.getOrCreateExtensionsFile(extensionsFileResource)2247.then(({ created, content }) =>2248this.getSelectionPosition(content, extensionsFileResource, ['recommendations'])2249.then(selection => this.editorService.openEditor({2250resource: extensionsFileResource,2251options: {2252pinned: created,2253selection2254}2255})),2256error => Promise.reject(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error))));2257}22582259protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<any> {2260return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)2261.then(content => this.getSelectionPosition(content.value.toString(), content.resource, ['extensions', 'recommendations']))2262.then(selection => this.editorService.openEditor({2263resource: workspaceConfigurationFile,2264options: {2265selection,2266forceReload: true // because content has changed2267}2268}));2269}22702271private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<IFileContent> {2272return Promise.resolve(this.fileService.readFile(workspaceConfigurationFile))2273.then(content => {2274const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value.toString())['extensions'];2275if (!workspaceRecommendations || !workspaceRecommendations.recommendations) {2276return this.jsonEditingService.write(workspaceConfigurationFile, [{ path: ['extensions'], value: { recommendations: [] } }], true)2277.then(() => this.fileService.readFile(workspaceConfigurationFile));2278}2279return content;2280});2281}22822283private getSelectionPosition(content: string, resource: URI, path: json.JSONPath): Promise<ITextEditorSelection | undefined> {2284const tree = json.parseTree(content);2285const node = json.findNodeAtLocation(tree, path);2286if (node && node.parent && node.parent.children) {2287const recommendationsValueNode = node.parent.children[1];2288const lastExtensionNode = recommendationsValueNode.children && recommendationsValueNode.children.length ? recommendationsValueNode.children[recommendationsValueNode.children.length - 1] : null;2289const offset = lastExtensionNode ? lastExtensionNode.offset + lastExtensionNode.length : recommendationsValueNode.offset + 1;2290return Promise.resolve(this.textModelResolverService.createModelReference(resource))2291.then(reference => {2292const position = reference.object.textEditorModel.getPositionAt(offset);2293reference.dispose();2294return {2295startLineNumber: position.lineNumber,2296startColumn: position.column,2297endLineNumber: position.lineNumber,2298endColumn: position.column,2299};2300});2301}2302return Promise.resolve(undefined);2303}23042305private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean; extensionsFileResource: URI; content: string }> {2306return Promise.resolve(this.fileService.readFile(extensionsFileResource)).then(content => {2307return { created: false, extensionsFileResource, content: content.value.toString() };2308}, err => {2309return this.textFileService.write(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {2310return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };2311});2312});2313}2314}23152316export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {23172318static readonly ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions';2319static readonly LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)");23202321constructor(2322id: string,2323label: string,2324@IFileService fileService: IFileService,2325@ITextFileService textFileService: ITextFileService,2326@IWorkspaceContextService contextService: IWorkspaceContextService,2327@IEditorService editorService: IEditorService,2328@IJSONEditingService jsonEditingService: IJSONEditingService,2329@ITextModelService textModelResolverService: ITextModelService2330) {2331super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);2332this._register(this.contextService.onDidChangeWorkbenchState(() => this.update(), this));2333this.update();2334}23352336private update(): void {2337this.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;2338}23392340public override run(): Promise<void> {2341switch (this.contextService.getWorkbenchState()) {2342case WorkbenchState.FOLDER:2343return this.openExtensionsFile(this.contextService.getWorkspace().folders[0].toResource(EXTENSIONS_CONFIG));2344case WorkbenchState.WORKSPACE:2345return this.openWorkspaceConfigurationFile(this.contextService.getWorkspace().configuration!);2346}2347return Promise.resolve();2348}2349}23502351export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {23522353static readonly ID = 'workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions';2354static readonly LABEL = localize('configureWorkspaceFolderRecommendedExtensions', "Configure Recommended Extensions (Workspace Folder)");23552356constructor(2357id: string,2358label: string,2359@IFileService fileService: IFileService,2360@ITextFileService textFileService: ITextFileService,2361@IWorkspaceContextService contextService: IWorkspaceContextService,2362@IEditorService editorService: IEditorService,2363@IJSONEditingService jsonEditingService: IJSONEditingService,2364@ITextModelService textModelResolverService: ITextModelService,2365@ICommandService private readonly commandService: ICommandService2366) {2367super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);2368}23692370public override run(): Promise<any> {2371const folderCount = this.contextService.getWorkspace().folders.length;2372const pickFolderPromise = folderCount === 1 ? Promise.resolve(this.contextService.getWorkspace().folders[0]) : this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID);2373return Promise.resolve(pickFolderPromise)2374.then(workspaceFolder => {2375if (workspaceFolder) {2376return this.openExtensionsFile(workspaceFolder.toResource(EXTENSIONS_CONFIG));2377}2378return null;2379});2380}2381}23822383export class ExtensionStatusLabelAction extends Action implements IExtensionContainer {23842385private static readonly ENABLED_CLASS = `${ExtensionAction.TEXT_ACTION_CLASS} extension-status-label`;2386private static readonly DISABLED_CLASS = `${this.ENABLED_CLASS} hide`;23872388private initialStatus: ExtensionState | null = null;2389private status: ExtensionState | null = null;2390private version: string | null = null;2391private enablementState: EnablementState | null = null;23922393private _extension: IExtension | null = null;2394get extension(): IExtension | null { return this._extension; }2395set extension(extension: IExtension | null) {2396if (!(this._extension && extension && areSameExtensions(this._extension.identifier, extension.identifier))) {2397// Different extension. Reset2398this.initialStatus = null;2399this.status = null;2400this.enablementState = null;2401}2402this._extension = extension;2403this.update();2404}24052406constructor(2407@IExtensionService private readonly extensionService: IExtensionService,2408@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,2409@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService2410) {2411super('extensions.action.statusLabel', '', ExtensionStatusLabelAction.DISABLED_CLASS, false);2412}24132414update(): void {2415const label = this.computeLabel();2416this.label = label || '';2417this.class = label ? ExtensionStatusLabelAction.ENABLED_CLASS : ExtensionStatusLabelAction.DISABLED_CLASS;2418}24192420private computeLabel(): string | null {2421if (!this.extension) {2422return null;2423}24242425const currentStatus = this.status;2426const currentVersion = this.version;2427const currentEnablementState = this.enablementState;2428this.status = this.extension.state;2429this.version = this.extension.version;2430if (this.initialStatus === null) {2431this.initialStatus = this.status;2432}2433this.enablementState = this.extension.enablementState;24342435const canAddExtension = () => {2436const runningExtension = this.extensionService.extensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];2437if (this.extension!.local) {2438if (runningExtension && this.extension!.version === runningExtension.version) {2439return true;2440}2441return this.extensionService.canAddExtension(toExtensionDescription(this.extension!.local));2442}2443return false;2444};2445const canRemoveExtension = () => {2446if (this.extension!.local) {2447if (this.extensionService.extensions.every(e => !(areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.extension!.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(e))))) {2448return true;2449}2450return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension!.local));2451}2452return false;2453};24542455if (currentStatus !== null) {2456if (currentStatus === ExtensionState.Installing && this.status === ExtensionState.Installed) {2457if (this.initialStatus === ExtensionState.Uninstalled && canAddExtension()) {2458return localize('installed', "Installed");2459}2460if (this.initialStatus === ExtensionState.Installed && this.version !== currentVersion && canAddExtension()) {2461return localize('updated', "Updated");2462}2463return null;2464}2465if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) {2466this.initialStatus = this.status;2467return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null;2468}2469}24702471if (currentEnablementState !== null) {2472const currentlyEnabled = this.extensionEnablementService.isEnabledEnablementState(currentEnablementState);2473const enabled = this.extensionEnablementService.isEnabledEnablementState(this.enablementState);2474if (!currentlyEnabled && enabled) {2475return canAddExtension() ? localize('enabled', "Enabled") : null;2476}2477if (currentlyEnabled && !enabled) {2478return canRemoveExtension() ? localize('disabled', "Disabled") : null;2479}24802481}24822483return null;2484}24852486override run(): Promise<any> {2487return Promise.resolve();2488}24892490}24912492export class ToggleSyncExtensionAction extends DropDownExtensionAction {24932494private static readonly IGNORED_SYNC_CLASS = `${ExtensionAction.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncIgnoredIcon)}`;2495private static readonly SYNC_CLASS = `${this.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncEnabledIcon)}`;24962497constructor(2498@IConfigurationService private readonly configurationService: IConfigurationService,2499@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2500@IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService,2501@IInstantiationService instantiationService: IInstantiationService,2502) {2503super('extensions.sync', '', ToggleSyncExtensionAction.SYNC_CLASS, false, instantiationService);2504this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('settingsSync.ignoredExtensions'))(() => this.update()));2505this._register(userDataSyncEnablementService.onDidChangeEnablement(() => this.update()));2506this.update();2507}25082509update(): void {2510this.enabled = !!this.extension && this.userDataSyncEnablementService.isEnabled() && this.extension.state === ExtensionState.Installed;2511if (this.extension) {2512const isIgnored = this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension);2513this.class = isIgnored ? ToggleSyncExtensionAction.IGNORED_SYNC_CLASS : ToggleSyncExtensionAction.SYNC_CLASS;2514this.tooltip = isIgnored ? localize('ignored', "This extension is ignored during sync") : localize('synced', "This extension is synced");2515}2516}25172518override async run(): Promise<any> {2519return super.run([2520[2521new Action(2522'extensions.syncignore',2523this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension!) ? localize('sync', "Sync this extension") : localize('do not sync', "Do not sync this extension")2524, undefined, true, () => this.extensionsWorkbenchService.toggleExtensionIgnoredToSync(this.extension!))2525]2526]);2527}2528}25292530export type ExtensionStatus = { readonly message: IMarkdownString; readonly icon?: ThemeIcon };25312532export class ExtensionStatusAction extends ExtensionAction {25332534private static readonly CLASS = `${ExtensionAction.ICON_ACTION_CLASS} extension-status`;25352536updateWhenCounterExtensionChanges: boolean = true;25372538private _status: ExtensionStatus[] = [];2539get status(): ExtensionStatus[] { return this._status; }25402541private readonly _onDidChangeStatus = this._register(new Emitter<void>());2542readonly onDidChangeStatus = this._onDidChangeStatus.event;25432544private readonly updateThrottler = this._register(new Throttler());25452546constructor(2547@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,2548@ILabelService private readonly labelService: ILabelService,2549@ICommandService private readonly commandService: ICommandService,2550@IWorkspaceTrustEnablementService private readonly workspaceTrustEnablementService: IWorkspaceTrustEnablementService,2551@IWorkspaceTrustManagementService private readonly workspaceTrustService: IWorkspaceTrustManagementService,2552@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2553@IExtensionService private readonly extensionService: IExtensionService,2554@IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService,2555@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,2556@IProductService private readonly productService: IProductService,2557@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,2558@IWorkbenchExtensionEnablementService private readonly workbenchExtensionEnablementService: IWorkbenchExtensionEnablementService,2559@IExtensionFeaturesManagementService private readonly extensionFeaturesManagementService: IExtensionFeaturesManagementService,2560@IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService,2561) {2562super('extensions.status', '', `${ExtensionStatusAction.CLASS} hide`, false);2563this._register(this.labelService.onDidChangeFormatters(() => this.update(), this));2564this._register(this.extensionService.onDidChangeExtensions(() => this.update()));2565this._register(this.extensionFeaturesManagementService.onDidChangeAccessData(() => this.update()));2566this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => this.update()));2567this.update();2568}25692570update(): void {2571this.updateThrottler.queue(() => this.computeAndUpdateStatus());2572}25732574private async computeAndUpdateStatus(): Promise<void> {2575this.updateStatus(undefined, true);2576this.enabled = false;25772578if (!this.extension) {2579return;2580}25812582if (this.extension.isMalicious) {2583this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('malicious tooltip', "This extension was reported to be problematic.")) }, true);2584return;2585}25862587if (this.extension.state === ExtensionState.Uninstalled && this.extension.gallery && !this.extension.gallery.isSigned && shouldRequireRepositorySignatureFor(this.extension.private, await this.extensionGalleryManifestService.getExtensionGalleryManifest())) {2588this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('not signed tooltip', "This extension is not signed by the Extension Marketplace.")) }, true);2589return;2590}25912592if (this.extension.deprecationInfo) {2593if (this.extension.deprecationInfo.extension) {2594const link = `[${this.extension.deprecationInfo.extension.displayName}](${createCommandUri('extension.open', this.extension.deprecationInfo.extension.id)})`;2595this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate extension tooltip', "This extension is deprecated. Use the {0} extension instead.", link)) }, true);2596} else if (this.extension.deprecationInfo.settings) {2597const link = `[${localize('settings', "settings")}](${createCommandUri('workbench.action.openSettings', this.extension.deprecationInfo.settings.map(setting => `@id:${setting}`).join(' '))}})`;2598this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate settings tooltip', "This extension is deprecated as this functionality is now built-in to VS Code. Configure these {0} to use this functionality.", link)) }, true);2599} else {2600const message = new MarkdownString(localize('deprecated tooltip', "This extension is deprecated as it is no longer being maintained."));2601if (this.extension.deprecationInfo.additionalInfo) {2602message.appendMarkdown(` ${this.extension.deprecationInfo.additionalInfo}`);2603}2604this.updateStatus({ icon: warningIcon, message }, true);2605}2606return;2607}26082609if (this.extension.missingFromGallery) {2610this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('missing from gallery tooltip', "This extension is no longer available on the Extension Marketplace.")) }, true);2611return;2612}26132614if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {2615return;2616}26172618if (this.extension.outdated) {2619const message = await this.extensionsWorkbenchService.shouldRequireConsentToUpdate(this.extension);2620if (message) {2621const markdown = new MarkdownString();2622markdown.appendMarkdown(`${message} `);2623markdown.appendMarkdown(2624localize('auto update message', "Please [review the extension]({0}) and update it manually.",2625this.extension.hasChangelog()2626? createCommandUri('extension.open', this.extension.identifier.id, ExtensionEditorTab.Changelog).toString()2627: this.extension.repository2628? this.extension.repository2629: createCommandUri('extension.open', this.extension.identifier.id).toString()2630));2631this.updateStatus({ icon: warningIcon, message: markdown }, true);2632}2633}26342635if (this.extension.gallery && this.extension.state === ExtensionState.Uninstalled) {2636const result = await this.extensionsWorkbenchService.canInstall(this.extension);2637if (result !== true) {2638this.updateStatus({ icon: warningIcon, message: result }, true);2639return;2640}2641}26422643if (!this.extension.local ||2644!this.extension.server ||2645this.extension.state !== ExtensionState.Installed2646) {2647return;2648}26492650// Extension is disabled by allowed list2651if (this.extension.enablementState === EnablementState.DisabledByAllowlist) {2652const result = this.allowedExtensionsService.isAllowed(this.extension.local);2653if (result !== true) {2654this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('disabled - not allowed', "This extension is disabled because {0}", result.value)) }, true);2655return;2656}2657}26582659// Extension is disabled by environment2660if (this.extension.enablementState === EnablementState.DisabledByEnvironment) {2661this.updateStatus({ message: new MarkdownString(localize('disabled by environment', "This extension is disabled by the environment.")) }, true);2662return;2663}26642665// Extension is enabled by environment2666if (this.extension.enablementState === EnablementState.EnabledByEnvironment) {2667this.updateStatus({ message: new MarkdownString(localize('enabled by environment', "This extension is enabled because it is required in the current environment.")) }, true);2668return;2669}26702671// Extension is disabled by virtual workspace2672if (this.extension.enablementState === EnablementState.DisabledByVirtualWorkspace) {2673const details = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.virtualWorkspaces);2674this.updateStatus({ icon: infoIcon, message: new MarkdownString(details ? escapeMarkdownSyntaxTokens(details) : localize('disabled because of virtual workspace', "This extension has been disabled because it does not support virtual workspaces.")) }, true);2675return;2676}26772678// Limited support in Virtual Workspace2679if (isVirtualWorkspace(this.contextService.getWorkspace())) {2680const virtualSupportType = this.extensionManifestPropertiesService.getExtensionVirtualWorkspaceSupportType(this.extension.local.manifest);2681const details = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.virtualWorkspaces);2682if (virtualSupportType === 'limited' || details) {2683this.updateStatus({ icon: warningIcon, message: new MarkdownString(details ? escapeMarkdownSyntaxTokens(details) : localize('extension limited because of virtual workspace', "This extension has limited features because the current workspace is virtual.")) }, true);2684return;2685}2686}26872688// Unification2689if (this.extension.enablementState === EnablementState.DisabledByUnification) {2690this.updateStatus({ icon: infoIcon, message: new MarkdownString(localize('extension disabled because of unification', "All GitHub Copilot functionality is now being served from the GitHub Copilot Chat extension. To temporarily opt out of this extension unification, toggle the {0} setting.", '`chat.extensionUnification.enabled`')) }, true);2691return;2692}26932694if (!this.workspaceTrustService.isWorkspaceTrusted() &&2695// Extension is disabled by untrusted workspace2696(this.extension.enablementState === EnablementState.DisabledByTrustRequirement ||2697// All disabled dependencies of the extension are disabled by untrusted workspace2698(this.extension.enablementState === EnablementState.DisabledByExtensionDependency && this.workbenchExtensionEnablementService.getDependenciesEnablementStates(this.extension.local).every(([, enablementState]) => this.workbenchExtensionEnablementService.isEnabledEnablementState(enablementState) || enablementState === EnablementState.DisabledByTrustRequirement)))) {2699this.enabled = true;2700const untrustedDetails = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.untrustedWorkspaces);2701this.updateStatus({ icon: trustIcon, message: new MarkdownString(untrustedDetails ? escapeMarkdownSyntaxTokens(untrustedDetails) : localize('extension disabled because of trust requirement', "This extension has been disabled because the current workspace is not trusted.")) }, true);2702return;2703}27042705// Limited support in Untrusted Workspace2706if (this.workspaceTrustEnablementService.isWorkspaceTrustEnabled() && !this.workspaceTrustService.isWorkspaceTrusted()) {2707const untrustedSupportType = this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(this.extension.local.manifest);2708const untrustedDetails = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.untrustedWorkspaces);2709if (untrustedSupportType === 'limited' || untrustedDetails) {2710this.enabled = true;2711this.updateStatus({ icon: trustIcon, message: new MarkdownString(untrustedDetails ? escapeMarkdownSyntaxTokens(untrustedDetails) : localize('extension limited because of trust requirement', "This extension has limited features because the current workspace is not trusted.")) }, true);2712return;2713}2714}27152716// Extension is disabled by extension kind2717if (this.extension.enablementState === EnablementState.DisabledByExtensionKind) {2718if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {2719let message;2720// Extension on Local Server2721if (this.extensionManagementServerService.localExtensionManagementServer === this.extension.server) {2722if (this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(this.extension.local.manifest)) {2723if (this.extensionManagementServerService.remoteExtensionManagementServer) {2724message = new MarkdownString(`${localize('Install in remote server to enable', "This extension is disabled in this workspace because it is defined to run in the Remote Extension Host. Please install the extension in '{0}' to enable.", this.extensionManagementServerService.remoteExtensionManagementServer.label)} [${localize('learn more', "Learn More")}](https://code.visualstudio.com/api/advanced-topics/remote-extensions#architecture-and-extension-kinds)`);2725}2726}2727}2728// Extension on Remote Server2729else if (this.extensionManagementServerService.remoteExtensionManagementServer === this.extension.server) {2730if (this.extensionManifestPropertiesService.prefersExecuteOnUI(this.extension.local.manifest)) {2731if (this.extensionManagementServerService.localExtensionManagementServer) {2732message = new MarkdownString(`${localize('Install in local server to enable', "This extension is disabled in this workspace because it is defined to run in the Local Extension Host. Please install the extension locally to enable.", this.extensionManagementServerService.remoteExtensionManagementServer.label)} [${localize('learn more', "Learn More")}](https://code.visualstudio.com/api/advanced-topics/remote-extensions#architecture-and-extension-kinds)`);2733} else if (isWeb) {2734message = new MarkdownString(`${localize('Defined to run in desktop', "This extension is disabled because it is defined to run only in {0} for the Desktop.", this.productService.nameLong)} [${localize('learn more', "Learn More")}](https://code.visualstudio.com/api/advanced-topics/remote-extensions#architecture-and-extension-kinds)`);2735}2736}2737}2738// Extension on Web Server2739else if (this.extensionManagementServerService.webExtensionManagementServer === this.extension.server) {2740message = new MarkdownString(`${localize('Cannot be enabled', "This extension is disabled because it is not supported in {0} for the Web.", this.productService.nameLong)} [${localize('learn more', "Learn More")}](https://code.visualstudio.com/api/advanced-topics/remote-extensions#architecture-and-extension-kinds)`);2741}2742if (message) {2743this.updateStatus({ icon: warningIcon, message }, true);2744}2745return;2746}2747}27482749const extensionId = new ExtensionIdentifier(this.extension.identifier.id);2750const features = Registry.as<IExtensionFeaturesRegistry>(Extensions.ExtensionFeaturesRegistry).getExtensionFeatures();2751for (const feature of features) {2752const status = this.extensionFeaturesManagementService.getAccessData(extensionId, feature.id)?.current?.status;2753const manageAccessLink = `[${localize('manage access', 'Manage Access')}](${createCommandUri('extension.open', this.extension.identifier.id, ExtensionEditorTab.Features, false, feature.id)})`;2754if (status?.severity === Severity.Error) {2755this.updateStatus({ icon: errorIcon, message: new MarkdownString().appendText(status.message).appendMarkdown(` ${manageAccessLink}`) }, true);2756return;2757}2758if (status?.severity === Severity.Warning) {2759this.updateStatus({ icon: warningIcon, message: new MarkdownString().appendText(status.message).appendMarkdown(` ${manageAccessLink}`) }, true);2760return;2761}2762}27632764// Remote Workspace2765if (this.extensionManagementServerService.remoteExtensionManagementServer) {2766if (isLanguagePackExtension(this.extension.local.manifest)) {2767if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {2768const message = this.extension.server === this.extensionManagementServerService.localExtensionManagementServer2769? new MarkdownString(localize('Install language pack also in remote server', "Install the language pack extension on '{0}' to enable it there also.", this.extensionManagementServerService.remoteExtensionManagementServer.label))2770: new MarkdownString(localize('Install language pack also locally', "Install the language pack extension locally to enable it there also."));2771this.updateStatus({ icon: infoIcon, message }, true);2772}2773return;2774}27752776const runningExtension = this.extensionService.extensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];2777const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)) : null;2778if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {2779if (this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(this.extension.local.manifest)) {2780this.updateStatus({ icon: infoIcon, message: new MarkdownString(`${localize('enabled remotely', "This extension is enabled in the Remote Extension Host because it prefers to run there.")} [${localize('learn more', "Learn More")}](https://code.visualstudio.com/api/advanced-topics/remote-extensions#architecture-and-extension-kinds)`) }, true);2781}2782return;2783}27842785if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {2786if (this.extensionManifestPropertiesService.prefersExecuteOnUI(this.extension.local.manifest)) {2787this.updateStatus({ icon: infoIcon, message: new MarkdownString(`${localize('enabled locally', "This extension is enabled in the Local Extension Host because it prefers to run there.")} [${localize('learn more', "Learn More")}](https://code.visualstudio.com/api/advanced-topics/remote-extensions#architecture-and-extension-kinds)`) }, true);2788}2789return;2790}27912792if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.webExtensionManagementServer) {2793if (this.extensionManifestPropertiesService.canExecuteOnWeb(this.extension.local.manifest)) {2794this.updateStatus({ icon: infoIcon, message: new MarkdownString(`${localize('enabled in web worker', "This extension is enabled in the Web Worker Extension Host because it prefers to run there.")} [${localize('learn more', "Learn More")}](https://code.visualstudio.com/api/advanced-topics/remote-extensions#architecture-and-extension-kinds)`) }, true);2795}2796return;2797}2798}27992800// Extension is disabled by its dependency2801if (this.extension.enablementState === EnablementState.DisabledByExtensionDependency) {2802this.updateStatus({2803icon: warningIcon,2804message: new MarkdownString(localize('extension disabled because of dependency', "This extension depends on an extension that is disabled."))2805.appendMarkdown(` [${localize('dependencies', "Show Dependencies")}](${createCommandUri('extension.open', this.extension.identifier.id, ExtensionEditorTab.Dependencies)})`)2806}, true);2807return;2808}28092810if (!this.extension.local.isValid) {2811const errors = this.extension.local.validations.filter(([severity]) => severity === Severity.Error).map(([, message]) => message);2812this.updateStatus({ icon: warningIcon, message: new MarkdownString(errors.join(' ').trim()) }, true);2813return;2814}28152816const isEnabled = this.workbenchExtensionEnablementService.isEnabled(this.extension.local);2817const isRunning = this.extensionService.extensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier));28182819if (!this.extension.isWorkspaceScoped && isEnabled && isRunning) {2820if (this.extension.enablementState === EnablementState.EnabledWorkspace) {2821this.updateStatus({ message: new MarkdownString(localize('workspace enabled', "This extension is enabled for this workspace by the user.")) }, true);2822return;2823}2824if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {2825if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {2826this.updateStatus({ message: new MarkdownString(localize('extension enabled on remote', "Extension is enabled on '{0}'", this.extension.server.label)) }, true);2827return;2828}2829}2830if (this.extension.enablementState === EnablementState.EnabledGlobally) {2831return;2832}2833}28342835if (!isEnabled && !isRunning) {2836if (this.extension.enablementState === EnablementState.DisabledGlobally) {2837this.updateStatus({ message: new MarkdownString(localize('globally disabled', "This extension is disabled globally by the user.")) }, true);2838return;2839}2840if (this.extension.enablementState === EnablementState.DisabledWorkspace) {2841this.updateStatus({ message: new MarkdownString(localize('workspace disabled', "This extension is disabled for this workspace by the user.")) }, true);2842return;2843}2844}2845}28462847private updateStatus(status: ExtensionStatus | undefined, updateClass: boolean): void {2848if (status) {2849if (this._status.some(s => s.message.value === status.message.value && s.icon?.id === status.icon?.id)) {2850return;2851}2852} else {2853if (this._status.length === 0) {2854return;2855}2856this._status = [];2857}28582859if (status) {2860this._status.push(status);2861this._status.sort((a, b) =>2862b.icon === trustIcon ? -1 :2863a.icon === trustIcon ? 1 :2864b.icon === errorIcon ? -1 :2865a.icon === errorIcon ? 1 :2866b.icon === warningIcon ? -1 :2867a.icon === warningIcon ? 1 :2868b.icon === infoIcon ? -1 :2869a.icon === infoIcon ? 1 :287002871);2872}28732874if (updateClass) {2875if (status?.icon === errorIcon) {2876this.class = `${ExtensionStatusAction.CLASS} extension-status-error ${ThemeIcon.asClassName(errorIcon)}`;2877}2878else if (status?.icon === warningIcon) {2879this.class = `${ExtensionStatusAction.CLASS} extension-status-warning ${ThemeIcon.asClassName(warningIcon)}`;2880}2881else if (status?.icon === infoIcon) {2882this.class = `${ExtensionStatusAction.CLASS} extension-status-info ${ThemeIcon.asClassName(infoIcon)}`;2883}2884else if (status?.icon === trustIcon) {2885this.class = `${ExtensionStatusAction.CLASS} ${ThemeIcon.asClassName(trustIcon)}`;2886}2887else {2888this.class = `${ExtensionStatusAction.CLASS} hide`;2889}2890}2891this._onDidChangeStatus.fire();2892}28932894override async run(): Promise<any> {2895if (this._status[0]?.icon === trustIcon) {2896return this.commandService.executeCommand('workbench.trust.manage');2897}2898}2899}29002901export class InstallSpecificVersionOfExtensionAction extends Action {29022903static readonly ID = 'workbench.extensions.action.install.specificVersion';2904static readonly LABEL = localize('install previous version', "Install Specific Version of Extension...");29052906constructor(2907id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL,2908@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2909@IQuickInputService private readonly quickInputService: IQuickInputService,2910@IInstantiationService private readonly instantiationService: IInstantiationService,2911@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,2912) {2913super(id, label);2914}29152916override get enabled(): boolean {2917return this.extensionsWorkbenchService.local.some(l => this.isEnabled(l));2918}29192920override async run(): Promise<any> {2921const extensionPick = await this.quickInputService.pick(this.getExtensionEntries(), { placeHolder: localize('selectExtension', "Select Extension"), matchOnDetail: true });2922if (extensionPick && extensionPick.extension) {2923const action = this.instantiationService.createInstance(InstallAnotherVersionAction, extensionPick.extension, true);2924await action.run();2925await this.extensionsWorkbenchService.openSearch(extensionPick.extension.identifier.id);2926}2927}29282929private isEnabled(extension: IExtension): boolean {2930const action = this.instantiationService.createInstance(InstallAnotherVersionAction, extension, true);2931return action.enabled && !!extension.local && this.extensionEnablementService.isEnabled(extension.local);2932}29332934private async getExtensionEntries(): Promise<IExtensionPickItem[]> {2935const installed = await this.extensionsWorkbenchService.queryLocal();2936const entries: IExtensionPickItem[] = [];2937for (const extension of installed) {2938if (this.isEnabled(extension)) {2939entries.push({2940id: extension.identifier.id,2941label: extension.displayName || extension.identifier.id,2942description: extension.identifier.id,2943extension,2944});2945}2946}2947return entries.sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName));2948}2949}29502951interface IExtensionPickItem extends IQuickPickItem {2952extension: IExtension;2953}29542955export abstract class AbstractInstallExtensionsInServerAction extends Action {29562957private extensions: IExtension[] | undefined = undefined;29582959constructor(2960id: string,2961@IExtensionsWorkbenchService protected readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2962@IQuickInputService private readonly quickInputService: IQuickInputService,2963@INotificationService private readonly notificationService: INotificationService,2964@IProgressService private readonly progressService: IProgressService,2965) {2966super(id);2967this.update();2968this.extensionsWorkbenchService.queryLocal().then(() => this.updateExtensions());2969this._register(this.extensionsWorkbenchService.onChange(() => {2970if (this.extensions) {2971this.updateExtensions();2972}2973}));2974}29752976private updateExtensions(): void {2977this.extensions = this.extensionsWorkbenchService.local;2978this.update();2979}29802981private update(): void {2982this.enabled = !!this.extensions && this.getExtensionsToInstall(this.extensions).length > 0;2983this.tooltip = this.label;2984}29852986override async run(): Promise<void> {2987return this.selectAndInstallExtensions();2988}29892990private async queryExtensionsToInstall(): Promise<IExtension[]> {2991const local = await this.extensionsWorkbenchService.queryLocal();2992return this.getExtensionsToInstall(local);2993}29942995private async selectAndInstallExtensions(): Promise<void> {2996const quickPick = this.quickInputService.createQuickPick<IExtensionPickItem>();2997quickPick.busy = true;2998const disposable = quickPick.onDidAccept(() => {2999disposable.dispose();3000quickPick.hide();3001quickPick.dispose();3002this.onDidAccept(quickPick.selectedItems);3003});3004quickPick.show();3005const localExtensionsToInstall = await this.queryExtensionsToInstall();3006quickPick.busy = false;3007if (localExtensionsToInstall.length) {3008quickPick.title = this.getQuickPickTitle();3009quickPick.placeholder = localize('select extensions to install', "Select extensions to install");3010quickPick.canSelectMany = true;3011localExtensionsToInstall.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName));3012quickPick.items = localExtensionsToInstall.map<IExtensionPickItem>(extension => ({ extension, label: extension.displayName, description: extension.version }));3013} else {3014quickPick.hide();3015quickPick.dispose();3016this.notificationService.notify({3017severity: Severity.Info,3018message: localize('no local extensions', "There are no extensions to install.")3019});3020}3021}30223023private async onDidAccept(selectedItems: ReadonlyArray<IExtensionPickItem>): Promise<void> {3024if (selectedItems.length) {3025const localExtensionsToInstall = selectedItems.filter(r => !!r.extension).map(r => r.extension);3026if (localExtensionsToInstall.length) {3027await this.progressService.withProgress(3028{3029location: ProgressLocation.Notification,3030title: localize('installing extensions', "Installing Extensions...")3031},3032() => this.installExtensions(localExtensionsToInstall));3033this.notificationService.info(localize('finished installing', "Successfully installed extensions."));3034}3035}3036}30373038protected abstract getQuickPickTitle(): string;3039protected abstract getExtensionsToInstall(local: IExtension[]): IExtension[];3040protected abstract installExtensions(extensions: IExtension[]): Promise<void>;3041}30423043export class InstallLocalExtensionsInRemoteAction extends AbstractInstallExtensionsInServerAction {30443045constructor(3046@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,3047@IQuickInputService quickInputService: IQuickInputService,3048@IProgressService progressService: IProgressService,3049@INotificationService notificationService: INotificationService,3050@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,3051@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,3052@IInstantiationService private readonly instantiationService: IInstantiationService,3053@IFileService private readonly fileService: IFileService,3054@ILogService private readonly logService: ILogService,3055) {3056super('workbench.extensions.actions.installLocalExtensionsInRemote', extensionsWorkbenchService, quickInputService, notificationService, progressService);3057}30583059override get label(): string {3060if (this.extensionManagementServerService && this.extensionManagementServerService.remoteExtensionManagementServer) {3061return localize('select and install local extensions', "Install Local Extensions in '{0}'...", this.extensionManagementServerService.remoteExtensionManagementServer.label);3062}3063return '';3064}30653066protected getQuickPickTitle(): string {3067return localize('install local extensions title', "Install Local Extensions in '{0}'", this.extensionManagementServerService.remoteExtensionManagementServer!.label);3068}30693070protected getExtensionsToInstall(local: IExtension[]): IExtension[] {3071return local.filter(extension => {3072const action = this.instantiationService.createInstance(RemoteInstallAction, true);3073action.extension = extension;3074return action.enabled;3075});3076}30773078protected async installExtensions(localExtensionsToInstall: IExtension[]): Promise<void> {3079const galleryExtensions: IGalleryExtension[] = [];3080const vsixs: URI[] = [];3081const targetPlatform = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getTargetPlatform();3082await Promises.settled(localExtensionsToInstall.map(async extension => {3083if (this.extensionGalleryService.isEnabled()) {3084const gallery = (await this.extensionGalleryService.getExtensions([{ ...extension.identifier, preRelease: !!extension.local?.preRelease }], { targetPlatform, compatible: true }, CancellationToken.None))[0];3085if (gallery) {3086galleryExtensions.push(gallery);3087return;3088}3089}3090const vsix = await this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.zip(extension.local!);3091vsixs.push(vsix);3092}));30933094await Promises.settled(galleryExtensions.map(gallery => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(gallery)));3095try {3096await Promises.settled(vsixs.map(vsix => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.install(vsix)));3097} finally {3098try {3099await Promise.allSettled(vsixs.map(vsix => this.fileService.del(vsix)));3100} catch (error) {3101this.logService.error(error);3102}3103}3104}3105}31063107export class InstallRemoteExtensionsInLocalAction extends AbstractInstallExtensionsInServerAction {31083109constructor(3110id: string,3111@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,3112@IQuickInputService quickInputService: IQuickInputService,3113@IProgressService progressService: IProgressService,3114@INotificationService notificationService: INotificationService,3115@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,3116@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,3117@IFileService private readonly fileService: IFileService,3118@ILogService private readonly logService: ILogService,3119) {3120super(id, extensionsWorkbenchService, quickInputService, notificationService, progressService);3121}31223123override get label(): string {3124return localize('select and install remote extensions', "Install Remote Extensions Locally...");3125}31263127protected getQuickPickTitle(): string {3128return localize('install remote extensions', "Install Remote Extensions Locally");3129}31303131protected getExtensionsToInstall(local: IExtension[]): IExtension[] {3132return local.filter(extension =>3133extension.type === ExtensionType.User && extension.server !== this.extensionManagementServerService.localExtensionManagementServer3134&& !this.extensionsWorkbenchService.installed.some(e => e.server === this.extensionManagementServerService.localExtensionManagementServer && areSameExtensions(e.identifier, extension.identifier)));3135}31363137protected async installExtensions(extensions: IExtension[]): Promise<void> {3138const galleryExtensions: IGalleryExtension[] = [];3139const vsixs: URI[] = [];3140const targetPlatform = await this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.getTargetPlatform();3141await Promises.settled(extensions.map(async extension => {3142if (this.extensionGalleryService.isEnabled()) {3143const gallery = (await this.extensionGalleryService.getExtensions([{ ...extension.identifier, preRelease: !!extension.local?.preRelease }], { targetPlatform, compatible: true }, CancellationToken.None))[0];3144if (gallery) {3145galleryExtensions.push(gallery);3146return;3147}3148}3149const vsix = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.zip(extension.local!);3150vsixs.push(vsix);3151}));31523153await Promises.settled(galleryExtensions.map(gallery => this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.installFromGallery(gallery)));3154try {3155await Promises.settled(vsixs.map(vsix => this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.install(vsix)));3156} finally {3157try {3158await Promise.allSettled(vsixs.map(vsix => this.fileService.del(vsix)));3159} catch (error) {3160this.logService.error(error);3161}3162}3163}3164}31653166CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForLanguage', function (accessor: ServicesAccessor, fileExtension: string) {3167const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);3168return extensionsWorkbenchService.openSearch(`ext:${fileExtension.replace(/^\./, '')}`);3169});31703171export const showExtensionsWithIdsCommandId = 'workbench.extensions.action.showExtensionsWithIds';3172CommandsRegistry.registerCommand(showExtensionsWithIdsCommandId, function (accessor: ServicesAccessor, extensionIds: string[]) {3173const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);3174return extensionsWorkbenchService.openSearch(extensionIds.map(id => `@id:${id}`).join(' '));3175});31763177registerColor('extensionButton.background', {3178dark: buttonSecondaryBackground,3179light: buttonSecondaryBackground,3180hcDark: null,3181hcLight: null3182}, localize('extensionButtonBackground', "Button background color for extension actions."));31833184registerColor('extensionButton.foreground', {3185dark: buttonSecondaryForeground,3186light: buttonSecondaryForeground,3187hcDark: null,3188hcLight: null3189}, localize('extensionButtonForeground', "Button foreground color for extension actions."));31903191registerColor('extensionButton.hoverBackground', {3192dark: buttonSecondaryHoverBackground,3193light: buttonSecondaryHoverBackground,3194hcDark: null,3195hcLight: null3196}, localize('extensionButtonHoverBackground', "Button background hover color for extension actions."));31973198registerColor('extensionButton.border', {3199dark: buttonBorder,3200light: buttonBorder,3201hcDark: contrastBorder,3202hcLight: contrastBorder3203}, localize('extensionButtonBorder', "Button border color for extension actions."));32043205registerColor('extensionButton.separator', buttonSeparator, localize('extensionButtonSeparator', "Button separator color for extension actions"));32063207export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', {3208dark: buttonBackground,3209light: buttonBackground,3210hcDark: null,3211hcLight: null3212}, localize('extensionButtonProminentBackground', "Button background color for extension actions that stand out (e.g. install button)."));32133214registerColor('extensionButton.prominentForeground', {3215dark: buttonForeground,3216light: buttonForeground,3217hcDark: null,3218hcLight: null3219}, localize('extensionButtonProminentForeground', "Button foreground color for extension actions that stand out (e.g. install button)."));32203221registerColor('extensionButton.prominentHoverBackground', {3222dark: buttonHoverBackground,3223light: buttonHoverBackground,3224hcDark: null,3225hcLight: null3226}, localize('extensionButtonProminentHoverBackground', "Button background hover color for extension actions that stand out (e.g. install button)."));32273228registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {32293230const errorColor = theme.getColor(editorErrorForeground);3231if (errorColor) {3232collector.addRule(`.extension-editor .header .actions-status-container > .status ${ThemeIcon.asCSSSelector(errorIcon)} { color: ${errorColor}; }`);3233collector.addRule(`.extension-editor .body .subcontent .runtime-status ${ThemeIcon.asCSSSelector(errorIcon)} { color: ${errorColor}; }`);3234collector.addRule(`.monaco-hover.extension-hover .markdown-hover .hover-contents ${ThemeIcon.asCSSSelector(errorIcon)} { color: ${errorColor}; }`);3235}32363237const warningColor = theme.getColor(editorWarningForeground);3238if (warningColor) {3239collector.addRule(`.extension-editor .header .actions-status-container > .status ${ThemeIcon.asCSSSelector(warningIcon)} { color: ${warningColor}; }`);3240collector.addRule(`.extension-editor .body .subcontent .runtime-status ${ThemeIcon.asCSSSelector(warningIcon)} { color: ${warningColor}; }`);3241collector.addRule(`.monaco-hover.extension-hover .markdown-hover .hover-contents ${ThemeIcon.asCSSSelector(warningIcon)} { color: ${warningColor}; }`);3242}32433244const infoColor = theme.getColor(editorInfoForeground);3245if (infoColor) {3246collector.addRule(`.extension-editor .header .actions-status-container > .status ${ThemeIcon.asCSSSelector(infoIcon)} { color: ${infoColor}; }`);3247collector.addRule(`.extension-editor .body .subcontent .runtime-status ${ThemeIcon.asCSSSelector(infoIcon)} { color: ${infoColor}; }`);3248collector.addRule(`.monaco-hover.extension-hover .markdown-hover .hover-contents ${ThemeIcon.asCSSSelector(infoIcon)} { color: ${infoColor}; }`);3249}3250});325132523253