Path: blob/main/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
3296 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, registerColor, editorWarningForeground, editorInfoForeground, editorErrorForeground, buttonSeparator } 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 { 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.", `command:${showWindowLogActionId}`);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 PROMINENT_LABEL_ACTION_CLASS = `${ExtensionAction.LABEL_ACTION_CLASS} prominent`;293static readonly ICON_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} icon`;294295private _extension: IExtension | null = null;296get extension(): IExtension | null { return this._extension; }297set extension(extension: IExtension | null) { this._extension = extension; this.update(); }298299private _hidden: boolean = false;300get hidden(): boolean { return this._hidden; }301set hidden(hidden: boolean) {302if (this._hidden !== hidden) {303this._hidden = hidden;304this._onDidChange.fire({ hidden });305}306}307308protected override _setEnabled(value: boolean): void {309super._setEnabled(value);310if (this.hideOnDisabled) {311this.hidden = !value;312}313}314315protected hideOnDisabled: boolean = true;316317abstract update(): void;318}319320export class ButtonWithDropDownExtensionAction extends ExtensionAction {321322private primaryAction: IAction | undefined;323324readonly menuActionClassNames: string[] = [];325private _menuActions: IAction[] = [];326get menuActions(): IAction[] { return [...this._menuActions]; }327328override get extension(): IExtension | null {329return super.extension;330}331332override set extension(extension: IExtension | null) {333this.extensionActions.forEach(a => a.extension = extension);334super.extension = extension;335}336337protected readonly extensionActions: ExtensionAction[];338339constructor(340id: string,341clazz: string,342private readonly actionsGroups: ExtensionAction[][],343) {344clazz = `${clazz} action-dropdown`;345super(id, undefined, clazz);346this.menuActionClassNames = clazz.split(' ');347this.hideOnDisabled = false;348this.extensionActions = actionsGroups.flat();349this.update();350this._register(Event.any(...this.extensionActions.map(a => a.onDidChange))(() => this.update(true)));351this.extensionActions.forEach(a => this._register(a));352}353354update(donotUpdateActions?: boolean): void {355if (!donotUpdateActions) {356this.extensionActions.forEach(a => a.update());357}358359const actionsGroups = this.actionsGroups.map(actionsGroup => actionsGroup.filter(a => !a.hidden));360361let actions: IAction[] = [];362for (const visibleActions of actionsGroups) {363if (visibleActions.length) {364actions = [...actions, ...visibleActions, new Separator()];365}366}367actions = actions.length ? actions.slice(0, actions.length - 1) : actions;368369this.primaryAction = actions[0];370this._menuActions = actions.length > 1 ? actions : [];371this._onDidChange.fire({ menuActions: this._menuActions });372373if (this.primaryAction) {374this.hidden = false;375this.enabled = this.primaryAction.enabled;376this.label = this.getLabel(this.primaryAction as ExtensionAction);377this.tooltip = this.primaryAction.tooltip;378} else {379this.hidden = true;380this.enabled = false;381}382}383384override async run(): Promise<void> {385if (this.enabled) {386await this.primaryAction?.run();387}388}389390protected getLabel(action: ExtensionAction): string {391return action.label;392}393}394395export class ButtonWithDropdownExtensionActionViewItem extends ActionWithDropdownActionViewItem {396397constructor(398action: ButtonWithDropDownExtensionAction,399options: IActionViewItemOptions & IActionWithDropdownActionViewItemOptions,400contextMenuProvider: IContextMenuProvider401) {402super(null, action, options, contextMenuProvider);403this._register(action.onDidChange(e => {404if (e.hidden !== undefined || e.menuActions !== undefined) {405this.updateClass();406}407}));408}409410override render(container: HTMLElement): void {411super.render(container);412this.updateClass();413}414415protected override updateClass(): void {416super.updateClass();417if (this.element && this.dropdownMenuActionViewItem?.element) {418this.element.classList.toggle('hide', (<ButtonWithDropDownExtensionAction>this._action).hidden);419const isMenuEmpty = (<ButtonWithDropDownExtensionAction>this._action).menuActions.length === 0;420this.element.classList.toggle('empty', isMenuEmpty);421this.dropdownMenuActionViewItem.element.classList.toggle('hide', isMenuEmpty);422}423}424425}426427export class InstallAction extends ExtensionAction {428429static readonly CLASS = `${this.LABEL_ACTION_CLASS} prominent install`;430private static readonly HIDE = `${this.CLASS} hide`;431432protected _manifest: IExtensionManifest | null = null;433set manifest(manifest: IExtensionManifest | null) {434this._manifest = manifest;435this.updateLabel();436}437438private readonly updateThrottler = new Throttler();439public readonly options: InstallOptions;440441constructor(442options: InstallOptions,443@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,444@IInstantiationService private readonly instantiationService: IInstantiationService,445@IExtensionService private readonly runtimeExtensionService: IExtensionService,446@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,447@ILabelService private readonly labelService: ILabelService,448@IDialogService private readonly dialogService: IDialogService,449@IPreferencesService private readonly preferencesService: IPreferencesService,450@ITelemetryService private readonly telemetryService: ITelemetryService,451@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,452@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,453@IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService,454) {455super('extensions.install', localize('install', "Install"), InstallAction.CLASS, false);456this.hideOnDisabled = false;457this.options = { isMachineScoped: false, ...options };458this.update();459this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => this.update()));460this._register(this.labelService.onDidChangeFormatters(() => this.updateLabel(), this));461}462463update(): void {464this.updateThrottler.queue(() => this.computeAndUpdateEnablement());465}466467protected async computeAndUpdateEnablement(): Promise<void> {468this.enabled = false;469this.class = InstallAction.HIDE;470this.hidden = true;471if (!this.extension) {472return;473}474if (this.extension.isBuiltin) {475return;476}477if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {478return;479}480if (this.extension.state !== ExtensionState.Uninstalled) {481return;482}483if (this.options.installPreReleaseVersion && (!this.extension.hasPreReleaseVersion || this.allowedExtensionsService.isAllowed({ id: this.extension.identifier.id, publisherDisplayName: this.extension.publisherDisplayName, prerelease: true }) !== true)) {484return;485}486if (!this.options.installPreReleaseVersion && !this.extension.hasReleaseVersion) {487return;488}489this.hidden = false;490this.class = InstallAction.CLASS;491if (await this.extensionsWorkbenchService.canInstall(this.extension) === true) {492this.enabled = true;493this.updateLabel();494}495}496497override async run(): Promise<any> {498if (!this.extension) {499return;500}501502if (this.extension.gallery && !this.extension.gallery.isSigned && shouldRequireRepositorySignatureFor(this.extension.private, await this.extensionGalleryManifestService.getExtensionGalleryManifest())) {503const { result } = await this.dialogService.prompt({504type: Severity.Warning,505message: localize('not signed', "'{0}' is an extension from an unknown source. Are you sure you want to install?", this.extension.displayName),506detail: localize('not signed detail', "Extension is not signed."),507buttons: [508{509label: localize('install anyway', "Install Anyway"),510run: () => {511this.options.donotVerifySignature = true;512return true;513}514}515],516cancelButton: {517run: () => false518}519});520if (!result) {521return;522}523}524525if (this.extension.deprecationInfo) {526let detail: string | MarkdownString = localize('deprecated message', "This extension is deprecated as it is no longer being maintained.");527enum DeprecationChoice {528InstallAnyway = 0,529ShowAlternateExtension = 1,530ConfigureSettings = 2,531Cancel = 3532}533const buttons: IPromptButton<DeprecationChoice>[] = [534{535label: localize('install anyway', "Install Anyway"),536run: () => DeprecationChoice.InstallAnyway537}538];539540if (this.extension.deprecationInfo.extension) {541detail = localize('deprecated with alternate extension message', "This extension is deprecated. Use the {0} extension instead.", this.extension.deprecationInfo.extension.displayName);542543const alternateExtension = this.extension.deprecationInfo.extension;544buttons.push({545label: localize({ key: 'Show alternate extension', comment: ['&& denotes a mnemonic'] }, "&&Open {0}", this.extension.deprecationInfo.extension.displayName),546run: async () => {547const [extension] = await this.extensionsWorkbenchService.getExtensions([{ id: alternateExtension.id, preRelease: alternateExtension.preRelease }], CancellationToken.None);548await this.extensionsWorkbenchService.open(extension);549550return DeprecationChoice.ShowAlternateExtension;551}552});553} else if (this.extension.deprecationInfo.settings) {554detail = localize('deprecated with alternate settings message', "This extension is deprecated as this functionality is now built-in to VS Code.");555556const settings = this.extension.deprecationInfo.settings;557buttons.push({558label: localize({ key: 'configure in settings', comment: ['&& denotes a mnemonic'] }, "&&Configure Settings"),559run: async () => {560await this.preferencesService.openSettings({ query: settings.map(setting => `@id:${setting}`).join(' ') });561562return DeprecationChoice.ConfigureSettings;563}564});565} else if (this.extension.deprecationInfo.additionalInfo) {566detail = new MarkdownString(`${detail} ${this.extension.deprecationInfo.additionalInfo}`);567}568569const { result } = await this.dialogService.prompt({570type: Severity.Warning,571message: localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName),572detail: isString(detail) ? detail : undefined,573custom: isString(detail) ? undefined : {574markdownDetails: [{575markdown: detail576}]577},578buttons,579cancelButton: {580run: () => DeprecationChoice.Cancel581}582});583if (result !== DeprecationChoice.InstallAnyway) {584return;585}586}587588this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: this.options.installPreReleaseVersion });589590alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));591592/* __GDPR__593"extensions:action:install" : {594"owner": "sandy081",595"actionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },596"${include}": [597"${GalleryExtensionTelemetryData}"598]599}600*/601this.telemetryService.publicLog('extensions:action:install', { ...this.extension.telemetryData, actionId: this.id });602603const extension = await this.install(this.extension);604605if (extension?.local) {606alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName));607const runningExtension = await this.getRunningExtension(extension.local);608if (runningExtension && !(runningExtension.activationEvents && runningExtension.activationEvents.some(activationEent => activationEent.startsWith('onLanguage')))) {609const action = await this.getThemeAction(extension);610if (action) {611action.extension = extension;612try {613return action.run({ showCurrentTheme: true, ignoreFocusLost: true });614} finally {615action.dispose();616}617}618}619}620621}622623private async getThemeAction(extension: IExtension): Promise<ExtensionAction | undefined> {624const colorThemes = await this.workbenchThemeService.getColorThemes();625if (colorThemes.some(theme => isThemeFromExtension(theme, extension))) {626return this.instantiationService.createInstance(SetColorThemeAction);627}628const fileIconThemes = await this.workbenchThemeService.getFileIconThemes();629if (fileIconThemes.some(theme => isThemeFromExtension(theme, extension))) {630return this.instantiationService.createInstance(SetFileIconThemeAction);631}632const productIconThemes = await this.workbenchThemeService.getProductIconThemes();633if (productIconThemes.some(theme => isThemeFromExtension(theme, extension))) {634return this.instantiationService.createInstance(SetProductIconThemeAction);635}636return undefined;637}638639private async install(extension: IExtension): Promise<IExtension | undefined> {640try {641return await this.extensionsWorkbenchService.install(extension, this.options);642} catch (error) {643await this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, this.options, extension.latestVersion, InstallOperation.Install, error).run();644return undefined;645}646}647648private async getRunningExtension(extension: ILocalExtension): Promise<IExtensionDescription | null> {649const runningExtension = await this.runtimeExtensionService.getExtension(extension.identifier.id);650if (runningExtension) {651return runningExtension;652}653if (this.runtimeExtensionService.canAddExtension(toExtensionDescription(extension))) {654return new Promise<IExtensionDescription | null>((c, e) => {655const disposable = this.runtimeExtensionService.onDidChangeExtensions(async () => {656const runningExtension = await this.runtimeExtensionService.getExtension(extension.identifier.id);657if (runningExtension) {658disposable.dispose();659c(runningExtension);660}661});662});663}664return null;665}666667protected updateLabel(): void {668this.label = this.getLabel();669}670671getLabel(primary?: boolean): string {672if (this.extension?.isWorkspaceScoped && this.extension.resourceExtension && this.contextService.isInsideWorkspace(this.extension.resourceExtension.location)) {673return localize('install workspace version', "Install Workspace Extension");674}675/* install pre-release version */676if (this.options.installPreReleaseVersion && this.extension?.hasPreReleaseVersion) {677return primary ? localize('install pre-release', "Install Pre-Release") : localize('install pre-release version', "Install Pre-Release Version");678}679/* install released version that has a pre release version */680if (this.extension?.hasPreReleaseVersion) {681return primary ? localize('install', "Install") : localize('install release version', "Install Release Version");682}683return localize('install', "Install");684}685686}687688export class InstallDropdownAction extends ButtonWithDropDownExtensionAction {689690set manifest(manifest: IExtensionManifest | null) {691this.extensionActions.forEach(a => (<InstallAction>a).manifest = manifest);692this.update();693}694695constructor(696@IInstantiationService instantiationService: IInstantiationService,697@IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService,698) {699super(`extensions.installActions`, InstallAction.CLASS, [700[701instantiationService.createInstance(InstallAction, { installPreReleaseVersion: extensionManagementService.preferPreReleases }),702instantiationService.createInstance(InstallAction, { installPreReleaseVersion: !extensionManagementService.preferPreReleases }),703]704]);705}706707protected override getLabel(action: InstallAction): string {708return action.getLabel(true);709}710711}712713export class InstallingLabelAction extends ExtensionAction {714715private static readonly LABEL = localize('installing', "Installing");716private static readonly CLASS = `${ExtensionAction.LABEL_ACTION_CLASS} install installing`;717718constructor() {719super('extension.installing', InstallingLabelAction.LABEL, InstallingLabelAction.CLASS, false);720}721722update(): void {723this.class = `${InstallingLabelAction.CLASS}${this.extension && this.extension.state === ExtensionState.Installing ? '' : ' hide'}`;724}725}726727export abstract class InstallInOtherServerAction extends ExtensionAction {728729protected static readonly INSTALL_LABEL = localize('install', "Install");730protected static readonly INSTALLING_LABEL = localize('installing', "Installing");731732private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} prominent install-other-server`;733private static readonly InstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} install-other-server installing`;734735updateWhenCounterExtensionChanges: boolean = true;736737constructor(738id: string,739private readonly server: IExtensionManagementServer | null,740private readonly canInstallAnyWhere: boolean,741@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,742@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,743@IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService,744) {745super(id, InstallInOtherServerAction.INSTALL_LABEL, InstallInOtherServerAction.Class, false);746this.update();747}748749update(): void {750this.enabled = false;751this.class = InstallInOtherServerAction.Class;752753if (this.canInstall()) {754const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server === this.server)[0];755if (extensionInOtherServer) {756// Getting installed in other server757if (extensionInOtherServer.state === ExtensionState.Installing && !extensionInOtherServer.local) {758this.enabled = true;759this.label = InstallInOtherServerAction.INSTALLING_LABEL;760this.class = InstallInOtherServerAction.InstallingClass;761}762} else {763// Not installed in other server764this.enabled = true;765this.label = this.getInstallLabel();766}767}768}769770protected canInstall(): boolean {771// Disable if extension is not installed or not an user extension772if (773!this.extension774|| !this.server775|| !this.extension.local776|| this.extension.state !== ExtensionState.Installed777|| this.extension.type !== ExtensionType.User778|| this.extension.enablementState === EnablementState.DisabledByEnvironment || this.extension.enablementState === EnablementState.DisabledByTrustRequirement || this.extension.enablementState === EnablementState.DisabledByVirtualWorkspace779) {780return false;781}782783if (isLanguagePackExtension(this.extension.local.manifest)) {784return true;785}786787// Prefers to run on UI788if (this.server === this.extensionManagementServerService.localExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnUI(this.extension.local.manifest)) {789return true;790}791792// Prefers to run on Workspace793if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(this.extension.local.manifest)) {794return true;795}796797// Prefers to run on Web798if (this.server === this.extensionManagementServerService.webExtensionManagementServer && this.extensionManifestPropertiesService.prefersExecuteOnWeb(this.extension.local.manifest)) {799return true;800}801802if (this.canInstallAnyWhere) {803// Can run on UI804if (this.server === this.extensionManagementServerService.localExtensionManagementServer && this.extensionManifestPropertiesService.canExecuteOnUI(this.extension.local.manifest)) {805return true;806}807808// Can run on Workspace809if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManifestPropertiesService.canExecuteOnWorkspace(this.extension.local.manifest)) {810return true;811}812}813814return false;815}816817override async run(): Promise<void> {818if (!this.extension?.local) {819return;820}821if (!this.extension?.server) {822return;823}824if (!this.server) {825return;826}827this.extensionsWorkbenchService.open(this.extension);828alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));829return this.extensionsWorkbenchService.installInServer(this.extension, this.server);830}831832protected abstract getInstallLabel(): string;833}834835export class RemoteInstallAction extends InstallInOtherServerAction {836837constructor(838canInstallAnyWhere: boolean,839@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,840@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,841@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,842) {843super(`extensions.remoteinstall`, extensionManagementServerService.remoteExtensionManagementServer, canInstallAnyWhere, extensionsWorkbenchService, extensionManagementServerService, extensionManifestPropertiesService);844}845846protected getInstallLabel(): string {847return this.extensionManagementServerService.remoteExtensionManagementServer848? 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)849: InstallInOtherServerAction.INSTALL_LABEL;850}851852}853854export class LocalInstallAction extends InstallInOtherServerAction {855856constructor(857@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,858@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,859@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,860) {861super(`extensions.localinstall`, extensionManagementServerService.localExtensionManagementServer, false, extensionsWorkbenchService, extensionManagementServerService, extensionManifestPropertiesService);862}863864protected getInstallLabel(): string {865return localize('install locally', "Install Locally");866}867868}869870export class WebInstallAction extends InstallInOtherServerAction {871872constructor(873@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,874@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,875@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,876) {877super(`extensions.webInstall`, extensionManagementServerService.webExtensionManagementServer, false, extensionsWorkbenchService, extensionManagementServerService, extensionManifestPropertiesService);878}879880protected getInstallLabel(): string {881return localize('install browser', "Install in Browser");882}883884}885886export class UninstallAction extends ExtensionAction {887888static readonly UninstallLabel = localize('uninstallAction', "Uninstall");889private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling");890891static readonly UninstallClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall`;892private static readonly UnInstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall uninstalling`;893894constructor(895@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,896@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,897@IDialogService private readonly dialogService: IDialogService898) {899super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false);900this.update();901}902903update(): void {904if (!this.extension) {905this.enabled = false;906return;907}908909const state = this.extension.state;910911if (state === ExtensionState.Uninstalling) {912this.label = UninstallAction.UninstallingLabel;913this.class = UninstallAction.UnInstallingClass;914this.enabled = false;915return;916}917918this.label = this.extension.local?.isApplicationScoped && this.userDataProfilesService.profiles.length > 1 ? localize('uninstallAll', "Uninstall (All Profiles)") : UninstallAction.UninstallLabel;919this.class = UninstallAction.UninstallClass;920this.tooltip = UninstallAction.UninstallLabel;921922if (state !== ExtensionState.Installed) {923this.enabled = false;924return;925}926927if (this.extension.isBuiltin) {928this.enabled = false;929return;930}931932this.enabled = true;933}934935override async run(): Promise<any> {936if (!this.extension) {937return;938}939alert(localize('uninstallExtensionStart', "Uninstalling extension {0} started.", this.extension.displayName));940941try {942await this.extensionsWorkbenchService.uninstall(this.extension);943alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName));944} catch (error) {945if (!isCancellationError(error)) {946this.dialogService.error(getErrorMessage(error));947}948}949}950}951952export class UpdateAction extends ExtensionAction {953954private static readonly EnabledClass = `${this.LABEL_ACTION_CLASS} prominent update`;955private static readonly DisabledClass = `${this.EnabledClass} disabled`;956957private readonly updateThrottler = new Throttler();958959constructor(960private readonly verbose: boolean,961@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,962@IDialogService private readonly dialogService: IDialogService,963@IOpenerService private readonly openerService: IOpenerService,964@IInstantiationService private readonly instantiationService: IInstantiationService,965) {966super(`extensions.update`, localize('update', "Update"), UpdateAction.DisabledClass, false);967this.update();968}969970update(): void {971this.updateThrottler.queue(() => this.computeAndUpdateEnablement());972if (this.extension) {973this.label = this.verbose ? localize('update to', "Update to v{0}", this.extension.latestVersion) : localize('update', "Update");974}975}976977private async computeAndUpdateEnablement(): Promise<void> {978this.enabled = false;979this.class = UpdateAction.DisabledClass;980981if (!this.extension) {982return;983}984985if (this.extension.deprecationInfo) {986return;987}988989const canInstall = await this.extensionsWorkbenchService.canInstall(this.extension);990const isInstalled = this.extension.state === ExtensionState.Installed;991992this.enabled = canInstall === true && isInstalled && this.extension.outdated;993this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass;994}995996override async run(): Promise<any> {997if (!this.extension) {998return;999}10001001const consent = await this.extensionsWorkbenchService.shouldRequireConsentToUpdate(this.extension);1002if (consent) {1003const { result } = await this.dialogService.prompt<'update' | 'review' | 'cancel'>({1004type: 'warning',1005title: localize('updateExtensionConsentTitle', "Update {0} Extension", this.extension.displayName),1006message: localize('updateExtensionConsent', "{0}\n\nWould you like to update the extension?", consent),1007buttons: [{1008label: localize('update', "Update"),1009run: () => 'update'1010}, {1011label: localize('review', "Review"),1012run: () => 'review'1013}, {1014label: localize('cancel', "Cancel"),1015run: () => 'cancel'1016}]1017});1018if (result === 'cancel') {1019return;1020}1021if (result === 'review') {1022if (this.extension.hasChangelog()) {1023return this.extensionsWorkbenchService.open(this.extension, { tab: ExtensionEditorTab.Changelog });1024}1025if (this.extension.repository) {1026return this.openerService.open(this.extension.repository);1027}1028return this.extensionsWorkbenchService.open(this.extension);1029}1030}10311032const installOptions: InstallOptions = {};1033if (this.extension.local?.source === 'vsix' && this.extension.local.pinned) {1034installOptions.pinned = false;1035}1036if (this.extension.local?.preRelease) {1037installOptions.installPreReleaseVersion = true;1038}1039try {1040alert(localize('updateExtensionStart', "Updating extension {0} to version {1} started.", this.extension.displayName, this.extension.latestVersion));1041await this.extensionsWorkbenchService.install(this.extension, installOptions);1042alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", this.extension.displayName, this.extension.latestVersion));1043} catch (err) {1044this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension, installOptions, this.extension.latestVersion, InstallOperation.Update, err).run();1045}1046}1047}10481049export class ToggleAutoUpdateForExtensionAction extends ExtensionAction {10501051static readonly ID = 'workbench.extensions.action.toggleAutoUpdateForExtension';1052static readonly LABEL = localize2('enableAutoUpdateLabel', "Auto Update");10531054private static readonly EnabledClass = `${ExtensionAction.EXTENSION_ACTION_CLASS} auto-update`;1055private static readonly DisabledClass = `${this.EnabledClass} hide`;10561057constructor(1058@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1059@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1060@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,1061@IConfigurationService configurationService: IConfigurationService,1062) {1063super(ToggleAutoUpdateForExtensionAction.ID, ToggleAutoUpdateForExtensionAction.LABEL.value, ToggleAutoUpdateForExtensionAction.DisabledClass);1064this._register(configurationService.onDidChangeConfiguration(e => {1065if (e.affectsConfiguration(AutoUpdateConfigurationKey)) {1066this.update();1067}1068}));1069this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(e => this.update()));1070this.update();1071}10721073override update() {1074this.enabled = false;1075this.class = ToggleAutoUpdateForExtensionAction.DisabledClass;1076if (!this.extension) {1077return;1078}1079if (this.extension.isBuiltin) {1080return;1081}1082if (this.extension.deprecationInfo?.disallowInstall) {1083return;1084}10851086const extension = this.extension.local ?? this.extension.gallery;1087if (extension && this.allowedExtensionsService.isAllowed(extension) !== true) {1088return;1089}1090if (this.extensionsWorkbenchService.getAutoUpdateValue() === 'onlyEnabledExtensions' && !this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState)) {1091return;1092}1093this.enabled = true;1094this.class = ToggleAutoUpdateForExtensionAction.EnabledClass;1095this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);1096}10971098override async run(): Promise<any> {1099if (!this.extension) {1100return;1101}11021103const enableAutoUpdate = !this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);1104await this.extensionsWorkbenchService.updateAutoUpdateEnablementFor(this.extension, enableAutoUpdate);11051106if (enableAutoUpdate) {1107alert(localize('enableAutoUpdate', "Enabled auto updates for", this.extension.displayName));1108} else {1109alert(localize('disableAutoUpdate', "Disabled auto updates for", this.extension.displayName));1110}1111}1112}11131114export class ToggleAutoUpdatesForPublisherAction extends ExtensionAction {11151116static readonly ID = 'workbench.extensions.action.toggleAutoUpdatesForPublisher';1117static readonly LABEL = localize('toggleAutoUpdatesForPublisherLabel', "Auto Update All (From Publisher)");11181119constructor(1120@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService1121) {1122super(ToggleAutoUpdatesForPublisherAction.ID, ToggleAutoUpdatesForPublisherAction.LABEL);1123}11241125override update() { }11261127override async run(): Promise<any> {1128if (!this.extension) {1129return;1130}1131alert(localize('ignoreExtensionUpdatePublisher', "Ignoring updates published by {0}.", this.extension.publisherDisplayName));1132const enableAutoUpdate = !this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension.publisher);1133await this.extensionsWorkbenchService.updateAutoUpdateEnablementFor(this.extension.publisher, enableAutoUpdate);1134if (enableAutoUpdate) {1135alert(localize('enableAutoUpdate', "Enabled auto updates for", this.extension.displayName));1136} else {1137alert(localize('disableAutoUpdate', "Disabled auto updates for", this.extension.displayName));1138}1139}1140}11411142export class MigrateDeprecatedExtensionAction extends ExtensionAction {11431144private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} migrate`;1145private static readonly DisabledClass = `${this.EnabledClass} disabled`;11461147constructor(1148private readonly small: boolean,1149@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService1150) {1151super('extensionsAction.migrateDeprecatedExtension', localize('migrateExtension', "Migrate"), MigrateDeprecatedExtensionAction.DisabledClass, false);1152this.update();1153}11541155update(): void {1156this.enabled = false;1157this.class = MigrateDeprecatedExtensionAction.DisabledClass;1158if (!this.extension?.local) {1159return;1160}1161if (this.extension.state !== ExtensionState.Installed) {1162return;1163}1164if (!this.extension.deprecationInfo?.extension) {1165return;1166}1167const id = this.extension.deprecationInfo.extension.id;1168if (this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, { id }))) {1169return;1170}1171this.enabled = true;1172this.class = MigrateDeprecatedExtensionAction.EnabledClass;1173this.tooltip = localize('migrate to', "Migrate to {0}", this.extension.deprecationInfo.extension.displayName);1174this.label = this.small ? localize('migrate', "Migrate") : this.tooltip;1175}11761177override async run(): Promise<any> {1178if (!this.extension?.deprecationInfo?.extension) {1179return;1180}1181const local = this.extension.local;1182await this.extensionsWorkbenchService.uninstall(this.extension);1183const [extension] = await this.extensionsWorkbenchService.getExtensions([{ id: this.extension.deprecationInfo.extension.id, preRelease: this.extension.deprecationInfo?.extension?.preRelease }], CancellationToken.None);1184await this.extensionsWorkbenchService.install(extension, { isMachineScoped: local?.isMachineScoped });1185}1186}11871188export abstract class DropDownExtensionAction extends ExtensionAction {11891190constructor(1191id: string,1192label: string,1193cssClass: string,1194enabled: boolean,1195@IInstantiationService protected instantiationService: IInstantiationService1196) {1197super(id, label, cssClass, enabled);1198}11991200private _actionViewItem: DropDownExtensionActionViewItem | null = null;1201createActionViewItem(options: IActionViewItemOptions): DropDownExtensionActionViewItem {1202this._actionViewItem = this.instantiationService.createInstance(DropDownExtensionActionViewItem, this, options);1203return this._actionViewItem;1204}12051206public override run(actionGroups: IAction[][]): Promise<any> {1207this._actionViewItem?.showMenu(actionGroups);1208return Promise.resolve();1209}1210}12111212export class DropDownExtensionActionViewItem extends ActionViewItem {12131214constructor(1215action: IAction,1216options: IActionViewItemOptions,1217@IContextMenuService private readonly contextMenuService: IContextMenuService1218) {1219super(null, action, { ...options, icon: true, label: true });1220}12211222public showMenu(menuActionGroups: IAction[][]): void {1223if (this.element) {1224const actions = this.getActions(menuActionGroups);1225const elementPosition = DOM.getDomNodePagePosition(this.element);1226const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 };1227this.contextMenuService.showContextMenu({1228getAnchor: () => anchor,1229getActions: () => actions,1230actionRunner: this.actionRunner,1231onHide: () => disposeIfDisposable(actions)1232});1233}1234}12351236private getActions(menuActionGroups: IAction[][]): IAction[] {1237let actions: IAction[] = [];1238for (const menuActions of menuActionGroups) {1239actions = [...actions, ...menuActions, new Separator()];1240}1241return actions.length ? actions.slice(0, actions.length - 1) : actions;1242}1243}12441245async function getContextMenuActionsGroups(extension: IExtension | undefined | null, contextKeyService: IContextKeyService, instantiationService: IInstantiationService): Promise<[string, Array<MenuItemAction | SubmenuItemAction>][]> {1246return instantiationService.invokeFunction(async accessor => {1247const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);1248const extensionEnablementService = accessor.get(IWorkbenchExtensionEnablementService);1249const menuService = accessor.get(IMenuService);1250const extensionRecommendationsService = accessor.get(IExtensionRecommendationsService);1251const extensionIgnoredRecommendationsService = accessor.get(IExtensionIgnoredRecommendationsService);1252const workbenchThemeService = accessor.get(IWorkbenchThemeService);1253const authenticationUsageService = accessor.get(IAuthenticationUsageService);1254const allowedExtensionsService = accessor.get(IAllowedExtensionsService);1255const cksOverlay: [string, any][] = [];12561257if (extension) {1258cksOverlay.push(['extension', extension.identifier.id]);1259cksOverlay.push(['isBuiltinExtension', extension.isBuiltin]);1260cksOverlay.push(['isDefaultApplicationScopedExtension', extension.local && isApplicationScopedExtension(extension.local.manifest)]);1261cksOverlay.push(['isApplicationScopedExtension', extension.local && extension.local.isApplicationScoped]);1262cksOverlay.push(['isWorkspaceScopedExtension', extension.isWorkspaceScoped]);1263cksOverlay.push(['isGalleryExtension', !!extension.identifier.uuid]);1264if (extension.local) {1265cksOverlay.push(['extensionSource', extension.local.source]);1266}1267cksOverlay.push(['extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration]);1268cksOverlay.push(['extensionHasKeybindings', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.keybindings]);1269cksOverlay.push(['extensionHasCommands', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes?.commands]);1270cksOverlay.push(['isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]]);1271cksOverlay.push(['isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace]);1272cksOverlay.push(['isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())]);1273cksOverlay.push(['isExtensionPinned', extension.pinned]);1274cksOverlay.push(['isExtensionEnabled', extensionEnablementService.isEnabledEnablementState(extension.enablementState)]);1275switch (extension.state) {1276case ExtensionState.Installing:1277cksOverlay.push(['extensionStatus', 'installing']);1278break;1279case ExtensionState.Installed:1280cksOverlay.push(['extensionStatus', 'installed']);1281break;1282case ExtensionState.Uninstalling:1283cksOverlay.push(['extensionStatus', 'uninstalling']);1284break;1285case ExtensionState.Uninstalled:1286cksOverlay.push(['extensionStatus', 'uninstalled']);1287break;1288}1289cksOverlay.push(['installedExtensionIsPreReleaseVersion', !!extension.local?.isPreReleaseVersion]);1290cksOverlay.push(['installedExtensionIsOptedToPreRelease', !!extension.local?.preRelease]);1291cksOverlay.push(['galleryExtensionIsPreReleaseVersion', !!extension.gallery?.properties.isPreReleaseVersion]);1292cksOverlay.push(['galleryExtensionHasPreReleaseVersion', extension.gallery?.hasPreReleaseVersion]);1293cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]);1294cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]);1295cksOverlay.push(['extensionDisallowInstall', extension.isMalicious || extension.deprecationInfo?.disallowInstall]);1296cksOverlay.push(['isExtensionAllowed', allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName }) === true]);1297cksOverlay.push(['isPreReleaseExtensionAllowed', allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName, prerelease: true }) === true]);1298cksOverlay.push(['extensionIsUnsigned', extension.gallery && !extension.gallery.isSigned]);1299cksOverlay.push(['extensionIsPrivate', extension.gallery?.private]);13001301const [colorThemes, fileIconThemes, productIconThemes, extensionUsesAuth] = await Promise.all([workbenchThemeService.getColorThemes(), workbenchThemeService.getFileIconThemes(), workbenchThemeService.getProductIconThemes(), authenticationUsageService.extensionUsesAuth(extension.identifier.id.toLowerCase())]);1302cksOverlay.push(['extensionHasColorThemes', colorThemes.some(theme => isThemeFromExtension(theme, extension))]);1303cksOverlay.push(['extensionHasFileIconThemes', fileIconThemes.some(theme => isThemeFromExtension(theme, extension))]);1304cksOverlay.push(['extensionHasProductIconThemes', productIconThemes.some(theme => isThemeFromExtension(theme, extension))]);1305cksOverlay.push(['extensionHasAccountPreferences', extensionUsesAuth]);13061307cksOverlay.push(['canSetLanguage', extensionsWorkbenchService.canSetLanguage(extension)]);1308cksOverlay.push(['isActiveLanguagePackExtension', extension.gallery && language === getLocale(extension.gallery)]);1309}13101311const actionsGroups = menuService.getMenuActions(MenuId.ExtensionContext, contextKeyService.createOverlay(cksOverlay), { shouldForwardArgs: true });1312return actionsGroups;1313});1314}13151316function toActions(actionsGroups: [string, Array<MenuItemAction | SubmenuItemAction>][], instantiationService: IInstantiationService): IAction[][] {1317const result: IAction[][] = [];1318for (const [, actions] of actionsGroups) {1319result.push(actions.map(action => {1320if (action instanceof SubmenuAction) {1321return action;1322}1323return instantiationService.createInstance(MenuItemExtensionAction, action);1324}));1325}1326return result;1327}132813291330export async function getContextMenuActions(extension: IExtension | undefined | null, contextKeyService: IContextKeyService, instantiationService: IInstantiationService): Promise<IAction[][]> {1331const actionsGroups = await getContextMenuActionsGroups(extension, contextKeyService, instantiationService);1332return toActions(actionsGroups, instantiationService);1333}13341335export class ManageExtensionAction extends DropDownExtensionAction {13361337static readonly ID = 'extensions.manage';13381339private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage ` + ThemeIcon.asClassName(manageExtensionIcon);1340private static readonly HideManageExtensionClass = `${this.Class} hide`;13411342constructor(1343@IInstantiationService instantiationService: IInstantiationService,1344@IExtensionService private readonly extensionService: IExtensionService,1345@IContextKeyService private readonly contextKeyService: IContextKeyService,1346) {13471348super(ManageExtensionAction.ID, '', '', true, instantiationService);13491350this.tooltip = localize('manage', "Manage");13511352this.update();1353}13541355async getActionGroups(): Promise<IAction[][]> {1356const groups: IAction[][] = [];1357const contextMenuActionsGroups = await getContextMenuActionsGroups(this.extension, this.contextKeyService, this.instantiationService);1358const themeActions: IAction[] = [], installActions: IAction[] = [], updateActions: IAction[] = [], otherActionGroups: IAction[][] = [];1359for (const [group, actions] of contextMenuActionsGroups) {1360if (group === INSTALL_ACTIONS_GROUP) {1361installActions.push(...toActions([[group, actions]], this.instantiationService)[0]);1362} else if (group === UPDATE_ACTIONS_GROUP) {1363updateActions.push(...toActions([[group, actions]], this.instantiationService)[0]);1364} else if (group === THEME_ACTIONS_GROUP) {1365themeActions.push(...toActions([[group, actions]], this.instantiationService)[0]);1366} else {1367otherActionGroups.push(...toActions([[group, actions]], this.instantiationService));1368}1369}13701371if (themeActions.length) {1372groups.push(themeActions);1373}13741375groups.push([1376this.instantiationService.createInstance(EnableGloballyAction),1377this.instantiationService.createInstance(EnableForWorkspaceAction)1378]);1379groups.push([1380this.instantiationService.createInstance(DisableGloballyAction),1381this.instantiationService.createInstance(DisableForWorkspaceAction)1382]);1383if (updateActions.length) {1384groups.push(updateActions);1385}1386groups.push([1387...(installActions.length ? installActions : []),1388this.instantiationService.createInstance(InstallAnotherVersionAction, this.extension, false),1389this.instantiationService.createInstance(UninstallAction),1390]);13911392otherActionGroups.forEach(actions => groups.push(actions));13931394groups.forEach(group => group.forEach(extensionAction => {1395if (extensionAction instanceof ExtensionAction) {1396extensionAction.extension = this.extension;1397}1398}));13991400return groups;1401}14021403override async run(): Promise<any> {1404await this.extensionService.whenInstalledExtensionsRegistered();1405return super.run(await this.getActionGroups());1406}14071408update(): void {1409this.class = ManageExtensionAction.HideManageExtensionClass;1410this.enabled = false;1411if (this.extension) {1412const state = this.extension.state;1413this.enabled = state === ExtensionState.Installed;1414this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass;1415}1416}1417}14181419export class ExtensionEditorManageExtensionAction extends DropDownExtensionAction {14201421constructor(1422private readonly contextKeyService: IContextKeyService,1423instantiationService: IInstantiationService1424) {1425super('extensionEditor.manageExtension', '', `${ExtensionAction.ICON_ACTION_CLASS} manage ${ThemeIcon.asClassName(manageExtensionIcon)}`, true, instantiationService);1426this.tooltip = localize('manage', "Manage");1427}14281429update(): void { }14301431override async run(): Promise<any> {1432const actionGroups: IAction[][] = [];1433(await getContextMenuActions(this.extension, this.contextKeyService, this.instantiationService)).forEach(actions => actionGroups.push(actions));1434actionGroups.forEach(group => group.forEach(extensionAction => {1435if (extensionAction instanceof ExtensionAction) {1436extensionAction.extension = this.extension;1437}1438}));1439return super.run(actionGroups);1440}14411442}14431444export class MenuItemExtensionAction extends ExtensionAction {14451446constructor(1447private readonly action: IAction,1448@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1449) {1450super(action.id, action.label);1451}14521453override get enabled(): boolean {1454return this.action.enabled;1455}14561457override set enabled(value: boolean) {1458this.action.enabled = value;1459}14601461update() {1462if (!this.extension) {1463return;1464}1465if (this.action.id === TOGGLE_IGNORE_EXTENSION_ACTION_ID) {1466this.checked = !this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension);1467} else if (this.action.id === ToggleAutoUpdateForExtensionAction.ID) {1468this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);1469} else if (this.action.id === ToggleAutoUpdatesForPublisherAction.ID) {1470this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension.publisher);1471} else {1472this.checked = this.action.checked;1473}1474}14751476override async run(): Promise<void> {1477if (this.extension) {1478const id = this.extension.local ? getExtensionId(this.extension.local.manifest.publisher, this.extension.local.manifest.name)1479: this.extension.gallery ? getExtensionId(this.extension.gallery.publisher, this.extension.gallery.name)1480: this.extension.identifier.id;1481const extensionArg: IExtensionArg = {1482id: this.extension.identifier.id,1483version: this.extension.version,1484location: this.extension.local?.location,1485galleryLink: this.extension.url1486};1487await this.action.run(id, extensionArg);1488}1489}1490}14911492export class TogglePreReleaseExtensionAction extends ExtensionAction {14931494static readonly ID = 'workbench.extensions.action.togglePreRlease';1495static readonly LABEL = localize('togglePreRleaseLabel', "Pre-Release");14961497private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} pre-release`;1498private static readonly DisabledClass = `${this.EnabledClass} hide`;14991500constructor(1501@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1502@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,1503) {1504super(TogglePreReleaseExtensionAction.ID, TogglePreReleaseExtensionAction.LABEL, TogglePreReleaseExtensionAction.DisabledClass);1505this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => this.update()));1506this.update();1507}15081509override update() {1510this.enabled = false;1511this.class = TogglePreReleaseExtensionAction.DisabledClass;1512if (!this.extension) {1513return;1514}1515if (this.extension.isBuiltin) {1516return;1517}1518if (this.extension.state !== ExtensionState.Installed) {1519return;1520}1521if (!this.extension.hasPreReleaseVersion) {1522return;1523}1524if (!this.extension.gallery) {1525return;1526}1527if (this.extension.preRelease) {1528if (!this.extension.isPreReleaseVersion) {1529return;1530}1531if (this.allowedExtensionsService.isAllowed({ id: this.extension.identifier.id, publisherDisplayName: this.extension.publisherDisplayName }) !== true) {1532return;1533}1534}1535if (!this.extension.preRelease) {1536if (!this.extension.gallery.hasPreReleaseVersion) {1537return;1538}1539if (this.allowedExtensionsService.isAllowed(this.extension.gallery) !== true) {1540return;1541}1542}1543this.enabled = true;1544this.class = TogglePreReleaseExtensionAction.EnabledClass;15451546if (this.extension.preRelease) {1547this.label = localize('togglePreRleaseDisableLabel', "Switch to Release Version");1548this.tooltip = localize('togglePreRleaseDisableTooltip', "This will switch and enable updates to release versions");1549} else {1550this.label = localize('switchToPreReleaseLabel', "Switch to Pre-Release Version");1551this.tooltip = localize('switchToPreReleaseTooltip', "This will switch to pre-release version and enable updates to latest version always");1552}1553}15541555override async run(): Promise<any> {1556if (!this.extension) {1557return;1558}1559this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: !this.extension.preRelease });1560await this.extensionsWorkbenchService.togglePreRelease(this.extension);1561}1562}15631564export class InstallAnotherVersionAction extends ExtensionAction {15651566static readonly ID = 'workbench.extensions.action.install.anotherVersion';1567static readonly LABEL = localize('install another version', "Install Specific Version...");15681569constructor(1570extension: IExtension | null,1571private readonly whenInstalled: boolean,1572@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1573@IWorkbenchExtensionManagementService private readonly extensionManagementService: IWorkbenchExtensionManagementService,1574@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,1575@IQuickInputService private readonly quickInputService: IQuickInputService,1576@IInstantiationService private readonly instantiationService: IInstantiationService,1577@IDialogService private readonly dialogService: IDialogService,1578@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,1579) {1580super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1581this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => this.update()));1582this.extension = extension;1583this.update();1584}15851586update(): void {1587this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.identifier.uuid && !this.extension.deprecationInfo1588&& this.allowedExtensionsService.isAllowed({ id: this.extension.identifier.id, publisherDisplayName: this.extension.publisherDisplayName }) === true;1589if (this.enabled && this.whenInstalled) {1590this.enabled = !!this.extension?.local && !!this.extension.server && this.extension.state === ExtensionState.Installed;1591}1592}15931594override async run(): Promise<any> {1595if (!this.enabled) {1596return;1597}1598if (!this.extension) {1599return;1600}1601const targetPlatform = this.extension.server ? await this.extension.server.extensionManagementService.getTargetPlatform() : await this.extensionManagementService.getTargetPlatform();1602const allVersions = await this.extensionGalleryService.getAllCompatibleVersions(this.extension.identifier, this.extension.local?.preRelease ?? this.extension.gallery?.properties.isPreReleaseVersion ?? false, targetPlatform);1603if (!allVersions.length) {1604await this.dialogService.info(localize('no versions', "This extension has no other versions."));1605return;1606}16071608const picks = allVersions.map((v, i) => {1609return {1610id: v.version,1611label: v.version,1612description: `${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")})` : ''}`,1613ariaLabel: `${v.isPreReleaseVersion ? 'Pre-Release version' : 'Release version'} ${v.version}`,1614isPreReleaseVersion: v.isPreReleaseVersion1615};1616});1617const pick = await this.quickInputService.pick(picks,1618{1619placeHolder: localize('selectVersion', "Select Version to Install"),1620matchOnDetail: true1621});1622if (pick) {1623if (this.extension.local?.manifest.version === pick.id) {1624return;1625}1626const options = { installPreReleaseVersion: pick.isPreReleaseVersion, version: pick.id };1627try {1628await this.extensionsWorkbenchService.install(this.extension, options);1629} catch (error) {1630this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension, options, pick.id, InstallOperation.Install, error).run();1631}1632}1633return null;1634}16351636}16371638export class EnableForWorkspaceAction extends ExtensionAction {16391640static readonly ID = 'extensions.enableForWorkspace';1641static readonly LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)");16421643constructor(1644@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1645@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService1646) {1647super(EnableForWorkspaceAction.ID, EnableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1648this.tooltip = localize('enableForWorkspaceActionToolTip', "Enable this extension only in this workspace");1649this.update();1650}16511652update(): void {1653this.enabled = false;1654if (this.extension && this.extension.local && !this.extension.isWorkspaceScoped) {1655this.enabled = this.extension.state === ExtensionState.Installed1656&& !this.extensionEnablementService.isEnabled(this.extension.local)1657&& this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local);1658}1659}16601661override async run(): Promise<any> {1662if (!this.extension) {1663return;1664}1665return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.EnabledWorkspace);1666}1667}16681669export class EnableGloballyAction extends ExtensionAction {16701671static readonly ID = 'extensions.enableGlobally';1672static readonly LABEL = localize('enableGloballyAction', "Enable");16731674constructor(1675@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1676@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService1677) {1678super(EnableGloballyAction.ID, EnableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1679this.tooltip = localize('enableGloballyActionToolTip', "Enable this extension");1680this.update();1681}16821683update(): void {1684this.enabled = false;1685if (this.extension && this.extension.local && !this.extension.isWorkspaceScoped) {1686this.enabled = this.extension.state === ExtensionState.Installed1687&& this.extensionEnablementService.isDisabledGlobally(this.extension.local)1688&& this.extensionEnablementService.canChangeEnablement(this.extension.local);1689}1690}16911692override async run(): Promise<any> {1693if (!this.extension) {1694return;1695}1696return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.EnabledGlobally);1697}1698}16991700export class DisableForWorkspaceAction extends ExtensionAction {17011702static readonly ID = 'extensions.disableForWorkspace';1703static readonly LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)");17041705constructor(1706@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,1707@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1708@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1709@IExtensionService private readonly extensionService: IExtensionService,1710) {1711super(DisableForWorkspaceAction.ID, DisableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1712this.tooltip = localize('disableForWorkspaceActionToolTip', "Disable this extension only in this workspace");1713this.update();1714this._register(this.extensionService.onDidChangeExtensions(() => this.update()));1715}17161717update(): void {1718this.enabled = false;1719if (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)) {1720this.enabled = this.extension.state === ExtensionState.Installed1721&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)1722&& this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local);1723}1724}17251726override async run(): Promise<any> {1727if (!this.extension) {1728return;1729}1730return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.DisabledWorkspace);1731}1732}17331734export class DisableGloballyAction extends ExtensionAction {17351736static readonly ID = 'extensions.disableGlobally';1737static readonly LABEL = localize('disableGloballyAction', "Disable");17381739constructor(1740@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1741@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1742@IExtensionService private readonly extensionService: IExtensionService,1743) {1744super(DisableGloballyAction.ID, DisableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);1745this.tooltip = localize('disableGloballyActionToolTip', "Disable this extension");1746this.update();1747this._register(this.extensionService.onDidChangeExtensions(() => this.update()));1748}17491750update(): void {1751this.enabled = false;1752if (this.extension && this.extension.local && !this.extension.isWorkspaceScoped && this.extensionService.extensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))) {1753this.enabled = this.extension.state === ExtensionState.Installed1754&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)1755&& this.extensionEnablementService.canChangeEnablement(this.extension.local);1756}1757}17581759override async run(): Promise<any> {1760if (!this.extension) {1761return;1762}1763return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.DisabledGlobally);1764}1765}17661767export class EnableDropDownAction extends ButtonWithDropDownExtensionAction {17681769constructor(1770@IInstantiationService instantiationService: IInstantiationService1771) {1772super('extensions.enable', ExtensionAction.LABEL_ACTION_CLASS, [1773[1774instantiationService.createInstance(EnableGloballyAction),1775instantiationService.createInstance(EnableForWorkspaceAction)1776]1777]);1778}1779}17801781export class DisableDropDownAction extends ButtonWithDropDownExtensionAction {17821783constructor(1784@IInstantiationService instantiationService: IInstantiationService1785) {1786super('extensions.disable', ExtensionAction.LABEL_ACTION_CLASS, [[1787instantiationService.createInstance(DisableGloballyAction),1788instantiationService.createInstance(DisableForWorkspaceAction)1789]]);1790}17911792}17931794export class ExtensionRuntimeStateAction extends ExtensionAction {17951796private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} reload`;1797private static readonly DisabledClass = `${this.EnabledClass} disabled`;17981799updateWhenCounterExtensionChanges: boolean = true;18001801constructor(1802@IHostService private readonly hostService: IHostService,1803@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,1804@IUpdateService private readonly updateService: IUpdateService,1805@IExtensionService private readonly extensionService: IExtensionService,1806@IProductService private readonly productService: IProductService,1807@ITelemetryService private readonly telemetryService: ITelemetryService,1808) {1809super('extensions.runtimeState', '', ExtensionRuntimeStateAction.DisabledClass, false);1810this._register(this.extensionService.onDidChangeExtensions(() => this.update()));1811this.update();1812}18131814update(): void {1815this.enabled = false;1816this.tooltip = '';1817this.class = ExtensionRuntimeStateAction.DisabledClass;18181819if (!this.extension) {1820return;1821}18221823const state = this.extension.state;1824if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {1825return;1826}18271828if (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) {1829return;1830}18311832const runtimeState = this.extension.runtimeState;1833if (!runtimeState) {1834return;1835}18361837this.enabled = true;1838this.class = ExtensionRuntimeStateAction.EnabledClass;1839this.tooltip = runtimeState.reason;1840this.label = runtimeState.action === ExtensionRuntimeActionType.ReloadWindow ? localize('reload window', 'Reload Window')1841: runtimeState.action === ExtensionRuntimeActionType.RestartExtensions ? localize('restart extensions', 'Restart Extensions')1842: runtimeState.action === ExtensionRuntimeActionType.QuitAndInstall ? localize('restart product', 'Restart to Update')1843: runtimeState.action === ExtensionRuntimeActionType.ApplyUpdate || runtimeState.action === ExtensionRuntimeActionType.DownloadUpdate ? localize('update product', 'Update {0}', this.productService.nameShort) : '';1844}18451846override async run(): Promise<any> {1847const runtimeState = this.extension?.runtimeState;1848if (!runtimeState?.action) {1849return;1850}18511852type ExtensionRuntimeStateActionClassification = {1853owner: 'sandy081';1854comment: 'Extension runtime state action event';1855action: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Executed action' };1856};1857type ExtensionRuntimeStateActionEvent = {1858action: string;1859};1860this.telemetryService.publicLog2<ExtensionRuntimeStateActionEvent, ExtensionRuntimeStateActionClassification>('extensions:runtimestate:action', {1861action: runtimeState.action1862});18631864if (runtimeState?.action === ExtensionRuntimeActionType.ReloadWindow) {1865return this.hostService.reload();1866}18671868else if (runtimeState?.action === ExtensionRuntimeActionType.RestartExtensions) {1869return this.extensionsWorkbenchService.updateRunningExtensions();1870}18711872else if (runtimeState?.action === ExtensionRuntimeActionType.DownloadUpdate) {1873return this.updateService.downloadUpdate();1874}18751876else if (runtimeState?.action === ExtensionRuntimeActionType.ApplyUpdate) {1877return this.updateService.applyUpdate();1878}18791880else if (runtimeState?.action === ExtensionRuntimeActionType.QuitAndInstall) {1881return this.updateService.quitAndInstall();1882}18831884}1885}18861887function isThemeFromExtension(theme: IWorkbenchTheme, extension: IExtension | undefined | null): boolean {1888return !!(extension && theme.extensionData && ExtensionIdentifier.equals(theme.extensionData.extensionId, extension.identifier.id));1889}18901891function getQuickPickEntries(themes: IWorkbenchTheme[], currentTheme: IWorkbenchTheme, extension: IExtension | null | undefined, showCurrentTheme: boolean): QuickPickItem[] {1892const picks: QuickPickItem[] = [];1893for (const theme of themes) {1894if (isThemeFromExtension(theme, extension) && !(showCurrentTheme && theme === currentTheme)) {1895picks.push({ label: theme.label, id: theme.id });1896}1897}1898if (showCurrentTheme) {1899picks.push({ type: 'separator', label: localize('current', "current") });1900picks.push({ label: currentTheme.label, id: currentTheme.id });1901}1902return picks;1903}19041905export class SetColorThemeAction extends ExtensionAction {19061907static readonly ID = 'workbench.extensions.action.setColorTheme';1908static readonly TITLE = localize2('workbench.extensions.action.setColorTheme', 'Set Color Theme');19091910private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;1911private static readonly DisabledClass = `${this.EnabledClass} disabled`;19121913constructor(1914@IExtensionService extensionService: IExtensionService,1915@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,1916@IQuickInputService private readonly quickInputService: IQuickInputService,1917@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1918) {1919super(SetColorThemeAction.ID, SetColorThemeAction.TITLE.value, SetColorThemeAction.DisabledClass, false);1920this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidColorThemeChange)(() => this.update(), this));1921this.update();1922}19231924update(): void {1925this.workbenchThemeService.getColorThemes().then(colorThemes => {1926this.enabled = this.computeEnablement(colorThemes);1927this.class = this.enabled ? SetColorThemeAction.EnabledClass : SetColorThemeAction.DisabledClass;1928});1929}19301931private computeEnablement(colorThemes: IWorkbenchColorTheme[]): boolean {1932return !!this.extension && this.extension.state === ExtensionState.Installed && this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState) && colorThemes.some(th => isThemeFromExtension(th, this.extension));1933}19341935override async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean; ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {1936const colorThemes = await this.workbenchThemeService.getColorThemes();19371938if (!this.computeEnablement(colorThemes)) {1939return;1940}1941const currentTheme = this.workbenchThemeService.getColorTheme();19421943const delayer = new Delayer<any>(100);1944const picks = getQuickPickEntries(colorThemes, currentTheme, this.extension, showCurrentTheme);1945const pickedTheme = await this.quickInputService.pick(1946picks,1947{1948placeHolder: localize('select color theme', "Select Color Theme"),1949onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setColorTheme(item.id, undefined)),1950ignoreFocusLost1951});1952return this.workbenchThemeService.setColorTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');1953}1954}19551956export class SetFileIconThemeAction extends ExtensionAction {19571958static readonly ID = 'workbench.extensions.action.setFileIconTheme';1959static readonly TITLE = localize2('workbench.extensions.action.setFileIconTheme', 'Set File Icon Theme');19601961private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;1962private static readonly DisabledClass = `${this.EnabledClass} disabled`;19631964constructor(1965@IExtensionService extensionService: IExtensionService,1966@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,1967@IQuickInputService private readonly quickInputService: IQuickInputService,1968@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,1969) {1970super(SetFileIconThemeAction.ID, SetFileIconThemeAction.TITLE.value, SetFileIconThemeAction.DisabledClass, false);1971this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidFileIconThemeChange)(() => this.update(), this));1972this.update();1973}19741975update(): void {1976this.workbenchThemeService.getFileIconThemes().then(fileIconThemes => {1977this.enabled = this.computeEnablement(fileIconThemes);1978this.class = this.enabled ? SetFileIconThemeAction.EnabledClass : SetFileIconThemeAction.DisabledClass;1979});1980}19811982private computeEnablement(colorThemfileIconThemess: IWorkbenchFileIconTheme[]): boolean {1983return !!this.extension && this.extension.state === ExtensionState.Installed && this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState) && colorThemfileIconThemess.some(th => isThemeFromExtension(th, this.extension));1984}19851986override async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean; ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {1987const fileIconThemes = await this.workbenchThemeService.getFileIconThemes();1988if (!this.computeEnablement(fileIconThemes)) {1989return;1990}1991const currentTheme = this.workbenchThemeService.getFileIconTheme();19921993const delayer = new Delayer<any>(100);1994const picks = getQuickPickEntries(fileIconThemes, currentTheme, this.extension, showCurrentTheme);1995const pickedTheme = await this.quickInputService.pick(1996picks,1997{1998placeHolder: localize('select file icon theme', "Select File Icon Theme"),1999onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setFileIconTheme(item.id, undefined)),2000ignoreFocusLost2001});2002return this.workbenchThemeService.setFileIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');2003}2004}20052006export class SetProductIconThemeAction extends ExtensionAction {20072008static readonly ID = 'workbench.extensions.action.setProductIconTheme';2009static readonly TITLE = localize2('workbench.extensions.action.setProductIconTheme', 'Set Product Icon Theme');20102011private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;2012private static readonly DisabledClass = `${this.EnabledClass} disabled`;20132014constructor(2015@IExtensionService extensionService: IExtensionService,2016@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,2017@IQuickInputService private readonly quickInputService: IQuickInputService,2018@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,2019) {2020super(SetProductIconThemeAction.ID, SetProductIconThemeAction.TITLE.value, SetProductIconThemeAction.DisabledClass, false);2021this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidProductIconThemeChange)(() => this.update(), this));2022this.update();2023}20242025update(): void {2026this.workbenchThemeService.getProductIconThemes().then(productIconThemes => {2027this.enabled = this.computeEnablement(productIconThemes);2028this.class = this.enabled ? SetProductIconThemeAction.EnabledClass : SetProductIconThemeAction.DisabledClass;2029});2030}20312032private computeEnablement(productIconThemes: IWorkbenchProductIconTheme[]): boolean {2033return !!this.extension && this.extension.state === ExtensionState.Installed && this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState) && productIconThemes.some(th => isThemeFromExtension(th, this.extension));2034}20352036override async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean; ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {2037const productIconThemes = await this.workbenchThemeService.getProductIconThemes();2038if (!this.computeEnablement(productIconThemes)) {2039return;2040}20412042const currentTheme = this.workbenchThemeService.getProductIconTheme();20432044const delayer = new Delayer<any>(100);2045const picks = getQuickPickEntries(productIconThemes, currentTheme, this.extension, showCurrentTheme);2046const pickedTheme = await this.quickInputService.pick(2047picks,2048{2049placeHolder: localize('select product icon theme', "Select Product Icon Theme"),2050onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setProductIconTheme(item.id, undefined)),2051ignoreFocusLost2052});2053return this.workbenchThemeService.setProductIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');2054}2055}20562057export class SetLanguageAction extends ExtensionAction {20582059static readonly ID = 'workbench.extensions.action.setDisplayLanguage';2060static readonly TITLE = localize2('workbench.extensions.action.setDisplayLanguage', 'Set Display Language');20612062private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} language`;2063private static readonly DisabledClass = `${this.EnabledClass} disabled`;20642065constructor(2066@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2067) {2068super(SetLanguageAction.ID, SetLanguageAction.TITLE.value, SetLanguageAction.DisabledClass, false);2069this.update();2070}20712072update(): void {2073this.enabled = false;2074this.class = SetLanguageAction.DisabledClass;2075if (!this.extension) {2076return;2077}2078if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {2079return;2080}2081if (this.extension.gallery && language === getLocale(this.extension.gallery)) {2082return;2083}2084this.enabled = true;2085this.class = SetLanguageAction.EnabledClass;2086}20872088override async run(): Promise<any> {2089return this.extension && this.extensionsWorkbenchService.setLanguage(this.extension);2090}2091}20922093export class ClearLanguageAction extends ExtensionAction {20942095static readonly ID = 'workbench.extensions.action.clearLanguage';2096static readonly TITLE = localize2('workbench.extensions.action.clearLanguage', 'Clear Display Language');20972098private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} language`;2099private static readonly DisabledClass = `${this.EnabledClass} disabled`;21002101constructor(2102@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2103@ILocaleService private readonly localeService: ILocaleService,2104) {2105super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);2106this.update();2107}21082109update(): void {2110this.enabled = false;2111this.class = ClearLanguageAction.DisabledClass;2112if (!this.extension) {2113return;2114}2115if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {2116return;2117}2118if (this.extension.gallery && language !== getLocale(this.extension.gallery)) {2119return;2120}2121this.enabled = true;2122this.class = ClearLanguageAction.EnabledClass;2123}21242125override async run(): Promise<any> {2126return this.extension && this.localeService.clearLocalePreference();2127}2128}21292130export class ShowRecommendedExtensionAction extends Action {21312132static readonly ID = 'workbench.extensions.action.showRecommendedExtension';2133static readonly LABEL = localize('showRecommendedExtension', "Show Recommended Extension");21342135private extensionId: string;21362137constructor(2138extensionId: string,2139@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,2140) {2141super(ShowRecommendedExtensionAction.ID, ShowRecommendedExtensionAction.LABEL, undefined, false);2142this.extensionId = extensionId;2143}21442145override async run(): Promise<any> {2146await this.extensionWorkbenchService.openSearch(`@id:${this.extensionId}`);2147const [extension] = await this.extensionWorkbenchService.getExtensions([{ id: this.extensionId }], { source: 'install-recommendation' }, CancellationToken.None);2148if (extension) {2149return this.extensionWorkbenchService.open(extension);2150}2151return null;2152}2153}21542155export class InstallRecommendedExtensionAction extends Action {21562157static readonly ID = 'workbench.extensions.action.installRecommendedExtension';2158static readonly LABEL = localize('installRecommendedExtension', "Install Recommended Extension");21592160private extensionId: string;21612162constructor(2163extensionId: string,2164@IInstantiationService private readonly instantiationService: IInstantiationService,2165@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,2166) {2167super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, undefined, false);2168this.extensionId = extensionId;2169}21702171override async run(): Promise<any> {2172await this.extensionWorkbenchService.openSearch(`@id:${this.extensionId}`);2173const [extension] = await this.extensionWorkbenchService.getExtensions([{ id: this.extensionId }], { source: 'install-recommendation' }, CancellationToken.None);2174if (extension) {2175await this.extensionWorkbenchService.open(extension);2176try {2177await this.extensionWorkbenchService.install(extension);2178} catch (err) {2179this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, undefined, extension.latestVersion, InstallOperation.Install, err).run();2180}2181}2182}2183}21842185export class IgnoreExtensionRecommendationAction extends Action {21862187static readonly ID = 'extensions.ignore';21882189private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} ignore`;21902191constructor(2192private readonly extension: IExtension,2193@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,2194) {2195super(IgnoreExtensionRecommendationAction.ID, 'Ignore Recommendation');21962197this.class = IgnoreExtensionRecommendationAction.Class;2198this.tooltip = localize('ignoreExtensionRecommendation', "Do not recommend this extension again");2199this.enabled = true;2200}22012202public override run(): Promise<any> {2203this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, true);2204return Promise.resolve();2205}2206}22072208export class UndoIgnoreExtensionRecommendationAction extends Action {22092210static readonly ID = 'extensions.ignore';22112212private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} undo-ignore`;22132214constructor(2215private readonly extension: IExtension,2216@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,2217) {2218super(UndoIgnoreExtensionRecommendationAction.ID, 'Undo');22192220this.class = UndoIgnoreExtensionRecommendationAction.Class;2221this.tooltip = localize('undo', "Undo");2222this.enabled = true;2223}22242225public override run(): Promise<any> {2226this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, false);2227return Promise.resolve();2228}2229}22302231export abstract class AbstractConfigureRecommendedExtensionsAction extends Action {22322233constructor(2234id: string,2235label: string,2236@IWorkspaceContextService protected contextService: IWorkspaceContextService,2237@IFileService private readonly fileService: IFileService,2238@ITextFileService private readonly textFileService: ITextFileService,2239@IEditorService protected editorService: IEditorService,2240@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,2241@ITextModelService private readonly textModelResolverService: ITextModelService2242) {2243super(id, label);2244}22452246protected openExtensionsFile(extensionsFileResource: URI): Promise<any> {2247return this.getOrCreateExtensionsFile(extensionsFileResource)2248.then(({ created, content }) =>2249this.getSelectionPosition(content, extensionsFileResource, ['recommendations'])2250.then(selection => this.editorService.openEditor({2251resource: extensionsFileResource,2252options: {2253pinned: created,2254selection2255}2256})),2257error => Promise.reject(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error))));2258}22592260protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<any> {2261return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)2262.then(content => this.getSelectionPosition(content.value.toString(), content.resource, ['extensions', 'recommendations']))2263.then(selection => this.editorService.openEditor({2264resource: workspaceConfigurationFile,2265options: {2266selection,2267forceReload: true // because content has changed2268}2269}));2270}22712272private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<IFileContent> {2273return Promise.resolve(this.fileService.readFile(workspaceConfigurationFile))2274.then(content => {2275const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value.toString())['extensions'];2276if (!workspaceRecommendations || !workspaceRecommendations.recommendations) {2277return this.jsonEditingService.write(workspaceConfigurationFile, [{ path: ['extensions'], value: { recommendations: [] } }], true)2278.then(() => this.fileService.readFile(workspaceConfigurationFile));2279}2280return content;2281});2282}22832284private getSelectionPosition(content: string, resource: URI, path: json.JSONPath): Promise<ITextEditorSelection | undefined> {2285const tree = json.parseTree(content);2286const node = json.findNodeAtLocation(tree, path);2287if (node && node.parent && node.parent.children) {2288const recommendationsValueNode = node.parent.children[1];2289const lastExtensionNode = recommendationsValueNode.children && recommendationsValueNode.children.length ? recommendationsValueNode.children[recommendationsValueNode.children.length - 1] : null;2290const offset = lastExtensionNode ? lastExtensionNode.offset + lastExtensionNode.length : recommendationsValueNode.offset + 1;2291return Promise.resolve(this.textModelResolverService.createModelReference(resource))2292.then(reference => {2293const position = reference.object.textEditorModel.getPositionAt(offset);2294reference.dispose();2295return {2296startLineNumber: position.lineNumber,2297startColumn: position.column,2298endLineNumber: position.lineNumber,2299endColumn: position.column,2300};2301});2302}2303return Promise.resolve(undefined);2304}23052306private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean; extensionsFileResource: URI; content: string }> {2307return Promise.resolve(this.fileService.readFile(extensionsFileResource)).then(content => {2308return { created: false, extensionsFileResource, content: content.value.toString() };2309}, err => {2310return this.textFileService.write(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {2311return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };2312});2313});2314}2315}23162317export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {23182319static readonly ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions';2320static readonly LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)");23212322constructor(2323id: string,2324label: string,2325@IFileService fileService: IFileService,2326@ITextFileService textFileService: ITextFileService,2327@IWorkspaceContextService contextService: IWorkspaceContextService,2328@IEditorService editorService: IEditorService,2329@IJSONEditingService jsonEditingService: IJSONEditingService,2330@ITextModelService textModelResolverService: ITextModelService2331) {2332super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);2333this._register(this.contextService.onDidChangeWorkbenchState(() => this.update(), this));2334this.update();2335}23362337private update(): void {2338this.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;2339}23402341public override run(): Promise<void> {2342switch (this.contextService.getWorkbenchState()) {2343case WorkbenchState.FOLDER:2344return this.openExtensionsFile(this.contextService.getWorkspace().folders[0].toResource(EXTENSIONS_CONFIG));2345case WorkbenchState.WORKSPACE:2346return this.openWorkspaceConfigurationFile(this.contextService.getWorkspace().configuration!);2347}2348return Promise.resolve();2349}2350}23512352export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {23532354static readonly ID = 'workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions';2355static readonly LABEL = localize('configureWorkspaceFolderRecommendedExtensions', "Configure Recommended Extensions (Workspace Folder)");23562357constructor(2358id: string,2359label: string,2360@IFileService fileService: IFileService,2361@ITextFileService textFileService: ITextFileService,2362@IWorkspaceContextService contextService: IWorkspaceContextService,2363@IEditorService editorService: IEditorService,2364@IJSONEditingService jsonEditingService: IJSONEditingService,2365@ITextModelService textModelResolverService: ITextModelService,2366@ICommandService private readonly commandService: ICommandService2367) {2368super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);2369}23702371public override run(): Promise<any> {2372const folderCount = this.contextService.getWorkspace().folders.length;2373const pickFolderPromise = folderCount === 1 ? Promise.resolve(this.contextService.getWorkspace().folders[0]) : this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID);2374return Promise.resolve(pickFolderPromise)2375.then(workspaceFolder => {2376if (workspaceFolder) {2377return this.openExtensionsFile(workspaceFolder.toResource(EXTENSIONS_CONFIG));2378}2379return null;2380});2381}2382}23832384export class ExtensionStatusLabelAction extends Action implements IExtensionContainer {23852386private static readonly ENABLED_CLASS = `${ExtensionAction.TEXT_ACTION_CLASS} extension-status-label`;2387private static readonly DISABLED_CLASS = `${this.ENABLED_CLASS} hide`;23882389private initialStatus: ExtensionState | null = null;2390private status: ExtensionState | null = null;2391private version: string | null = null;2392private enablementState: EnablementState | null = null;23932394private _extension: IExtension | null = null;2395get extension(): IExtension | null { return this._extension; }2396set extension(extension: IExtension | null) {2397if (!(this._extension && extension && areSameExtensions(this._extension.identifier, extension.identifier))) {2398// Different extension. Reset2399this.initialStatus = null;2400this.status = null;2401this.enablementState = null;2402}2403this._extension = extension;2404this.update();2405}24062407constructor(2408@IExtensionService private readonly extensionService: IExtensionService,2409@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,2410@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService2411) {2412super('extensions.action.statusLabel', '', ExtensionStatusLabelAction.DISABLED_CLASS, false);2413}24142415update(): void {2416const label = this.computeLabel();2417this.label = label || '';2418this.class = label ? ExtensionStatusLabelAction.ENABLED_CLASS : ExtensionStatusLabelAction.DISABLED_CLASS;2419}24202421private computeLabel(): string | null {2422if (!this.extension) {2423return null;2424}24252426const currentStatus = this.status;2427const currentVersion = this.version;2428const currentEnablementState = this.enablementState;2429this.status = this.extension.state;2430this.version = this.extension.version;2431if (this.initialStatus === null) {2432this.initialStatus = this.status;2433}2434this.enablementState = this.extension.enablementState;24352436const canAddExtension = () => {2437const runningExtension = this.extensionService.extensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];2438if (this.extension!.local) {2439if (runningExtension && this.extension!.version === runningExtension.version) {2440return true;2441}2442return this.extensionService.canAddExtension(toExtensionDescription(this.extension!.local));2443}2444return false;2445};2446const canRemoveExtension = () => {2447if (this.extension!.local) {2448if (this.extensionService.extensions.every(e => !(areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.extension!.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(e))))) {2449return true;2450}2451return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension!.local));2452}2453return false;2454};24552456if (currentStatus !== null) {2457if (currentStatus === ExtensionState.Installing && this.status === ExtensionState.Installed) {2458if (this.initialStatus === ExtensionState.Uninstalled && canAddExtension()) {2459return localize('installed', "Installed");2460}2461if (this.initialStatus === ExtensionState.Installed && this.version !== currentVersion && canAddExtension()) {2462return localize('updated', "Updated");2463}2464return null;2465}2466if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) {2467this.initialStatus = this.status;2468return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null;2469}2470}24712472if (currentEnablementState !== null) {2473const currentlyEnabled = this.extensionEnablementService.isEnabledEnablementState(currentEnablementState);2474const enabled = this.extensionEnablementService.isEnabledEnablementState(this.enablementState);2475if (!currentlyEnabled && enabled) {2476return canAddExtension() ? localize('enabled', "Enabled") : null;2477}2478if (currentlyEnabled && !enabled) {2479return canRemoveExtension() ? localize('disabled', "Disabled") : null;2480}24812482}24832484return null;2485}24862487override run(): Promise<any> {2488return Promise.resolve();2489}24902491}24922493export class ToggleSyncExtensionAction extends DropDownExtensionAction {24942495private static readonly IGNORED_SYNC_CLASS = `${ExtensionAction.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncIgnoredIcon)}`;2496private static readonly SYNC_CLASS = `${this.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncEnabledIcon)}`;24972498constructor(2499@IConfigurationService private readonly configurationService: IConfigurationService,2500@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2501@IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService,2502@IInstantiationService instantiationService: IInstantiationService,2503) {2504super('extensions.sync', '', ToggleSyncExtensionAction.SYNC_CLASS, false, instantiationService);2505this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('settingsSync.ignoredExtensions'))(() => this.update()));2506this._register(userDataSyncEnablementService.onDidChangeEnablement(() => this.update()));2507this.update();2508}25092510update(): void {2511this.enabled = !!this.extension && this.userDataSyncEnablementService.isEnabled() && this.extension.state === ExtensionState.Installed;2512if (this.extension) {2513const isIgnored = this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension);2514this.class = isIgnored ? ToggleSyncExtensionAction.IGNORED_SYNC_CLASS : ToggleSyncExtensionAction.SYNC_CLASS;2515this.tooltip = isIgnored ? localize('ignored', "This extension is ignored during sync") : localize('synced', "This extension is synced");2516}2517}25182519override async run(): Promise<any> {2520return super.run([2521[2522new Action(2523'extensions.syncignore',2524this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension!) ? localize('sync', "Sync this extension") : localize('do not sync', "Do not sync this extension")2525, undefined, true, () => this.extensionsWorkbenchService.toggleExtensionIgnoredToSync(this.extension!))2526]2527]);2528}2529}25302531export type ExtensionStatus = { readonly message: IMarkdownString; readonly icon?: ThemeIcon };25322533export class ExtensionStatusAction extends ExtensionAction {25342535private static readonly CLASS = `${ExtensionAction.ICON_ACTION_CLASS} extension-status`;25362537updateWhenCounterExtensionChanges: boolean = true;25382539private _status: ExtensionStatus[] = [];2540get status(): ExtensionStatus[] { return this._status; }25412542private readonly _onDidChangeStatus = this._register(new Emitter<void>());2543readonly onDidChangeStatus = this._onDidChangeStatus.event;25442545private readonly updateThrottler = new Throttler();25462547constructor(2548@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,2549@ILabelService private readonly labelService: ILabelService,2550@ICommandService private readonly commandService: ICommandService,2551@IWorkspaceTrustEnablementService private readonly workspaceTrustEnablementService: IWorkspaceTrustEnablementService,2552@IWorkspaceTrustManagementService private readonly workspaceTrustService: IWorkspaceTrustManagementService,2553@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2554@IExtensionService private readonly extensionService: IExtensionService,2555@IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService,2556@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,2557@IProductService private readonly productService: IProductService,2558@IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService,2559@IWorkbenchExtensionEnablementService private readonly workbenchExtensionEnablementService: IWorkbenchExtensionEnablementService,2560@IExtensionFeaturesManagementService private readonly extensionFeaturesManagementService: IExtensionFeaturesManagementService,2561@IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService,2562) {2563super('extensions.status', '', `${ExtensionStatusAction.CLASS} hide`, false);2564this._register(this.labelService.onDidChangeFormatters(() => this.update(), this));2565this._register(this.extensionService.onDidChangeExtensions(() => this.update()));2566this._register(this.extensionFeaturesManagementService.onDidChangeAccessData(() => this.update()));2567this._register(allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => this.update()));2568this.update();2569}25702571update(): void {2572this.updateThrottler.queue(() => this.computeAndUpdateStatus());2573}25742575private async computeAndUpdateStatus(): Promise<void> {2576this.updateStatus(undefined, true);2577this.enabled = false;25782579if (!this.extension) {2580return;2581}25822583if (this.extension.isMalicious) {2584this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('malicious tooltip', "This extension was reported to be problematic.")) }, true);2585return;2586}25872588if (this.extension.state === ExtensionState.Uninstalled && this.extension.gallery && !this.extension.gallery.isSigned && shouldRequireRepositorySignatureFor(this.extension.private, await this.extensionGalleryManifestService.getExtensionGalleryManifest())) {2589this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('not signed tooltip', "This extension is not signed by the Extension Marketplace.")) }, true);2590return;2591}25922593if (this.extension.deprecationInfo) {2594if (this.extension.deprecationInfo.extension) {2595const link = `[${this.extension.deprecationInfo.extension.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.extension.id]))}`)})`;2596this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate extension tooltip', "This extension is deprecated. Use the {0} extension instead.", link)) }, true);2597} else if (this.extension.deprecationInfo.settings) {2598const link = `[${localize('settings', "settings")}](${URI.parse(`command:workbench.action.openSettings?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.settings.map(setting => `@id:${setting}`).join(' ')]))}`)})`;2599this.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);2600} else {2601const message = new MarkdownString(localize('deprecated tooltip', "This extension is deprecated as it is no longer being maintained."));2602if (this.extension.deprecationInfo.additionalInfo) {2603message.appendMarkdown(` ${this.extension.deprecationInfo.additionalInfo}`);2604}2605this.updateStatus({ icon: warningIcon, message }, true);2606}2607return;2608}26092610if (this.extension.missingFromGallery) {2611this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('missing from gallery tooltip', "This extension is no longer available on the Extension Marketplace.")) }, true);2612return;2613}26142615if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {2616return;2617}26182619if (this.extension.outdated) {2620const message = await this.extensionsWorkbenchService.shouldRequireConsentToUpdate(this.extension);2621if (message) {2622const markdown = new MarkdownString();2623markdown.appendMarkdown(`${message} `);2624markdown.appendMarkdown(2625localize('auto update message', "Please [review the extension]({0}) and update it manually.",2626this.extension.hasChangelog()2627? URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.identifier.id, ExtensionEditorTab.Changelog]))}`).toString()2628: this.extension.repository2629? this.extension.repository2630: URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.identifier.id]))}`).toString()2631));2632this.updateStatus({ icon: warningIcon, message: markdown }, true);2633}2634}26352636if (this.extension.gallery && this.extension.state === ExtensionState.Uninstalled) {2637const result = await this.extensionsWorkbenchService.canInstall(this.extension);2638if (result !== true) {2639this.updateStatus({ icon: warningIcon, message: result }, true);2640return;2641}2642}26432644if (!this.extension.local ||2645!this.extension.server ||2646this.extension.state !== ExtensionState.Installed2647) {2648return;2649}26502651// Extension is disabled by allowed list2652if (this.extension.enablementState === EnablementState.DisabledByAllowlist) {2653const result = this.allowedExtensionsService.isAllowed(this.extension.local);2654if (result !== true) {2655this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('disabled - not allowed', "This extension is disabled because {0}", result.value)) }, true);2656return;2657}2658}26592660// Extension is disabled by environment2661if (this.extension.enablementState === EnablementState.DisabledByEnvironment) {2662this.updateStatus({ message: new MarkdownString(localize('disabled by environment', "This extension is disabled by the environment.")) }, true);2663return;2664}26652666// Extension is enabled by environment2667if (this.extension.enablementState === EnablementState.EnabledByEnvironment) {2668this.updateStatus({ message: new MarkdownString(localize('enabled by environment', "This extension is enabled because it is required in the current environment.")) }, true);2669return;2670}26712672// Extension is disabled by virtual workspace2673if (this.extension.enablementState === EnablementState.DisabledByVirtualWorkspace) {2674const details = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.virtualWorkspaces);2675this.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);2676return;2677}26782679// Limited support in Virtual Workspace2680if (isVirtualWorkspace(this.contextService.getWorkspace())) {2681const virtualSupportType = this.extensionManifestPropertiesService.getExtensionVirtualWorkspaceSupportType(this.extension.local.manifest);2682const details = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.virtualWorkspaces);2683if (virtualSupportType === 'limited' || details) {2684this.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);2685return;2686}2687}26882689if (!this.workspaceTrustService.isWorkspaceTrusted() &&2690// Extension is disabled by untrusted workspace2691(this.extension.enablementState === EnablementState.DisabledByTrustRequirement ||2692// All disabled dependencies of the extension are disabled by untrusted workspace2693(this.extension.enablementState === EnablementState.DisabledByExtensionDependency && this.workbenchExtensionEnablementService.getDependenciesEnablementStates(this.extension.local).every(([, enablementState]) => this.workbenchExtensionEnablementService.isEnabledEnablementState(enablementState) || enablementState === EnablementState.DisabledByTrustRequirement)))) {2694this.enabled = true;2695const untrustedDetails = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.untrustedWorkspaces);2696this.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);2697return;2698}26992700// Limited support in Untrusted Workspace2701if (this.workspaceTrustEnablementService.isWorkspaceTrustEnabled() && !this.workspaceTrustService.isWorkspaceTrusted()) {2702const untrustedSupportType = this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(this.extension.local.manifest);2703const untrustedDetails = getWorkspaceSupportTypeMessage(this.extension.local.manifest.capabilities?.untrustedWorkspaces);2704if (untrustedSupportType === 'limited' || untrustedDetails) {2705this.enabled = true;2706this.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);2707return;2708}2709}27102711// Extension is disabled by extension kind2712if (this.extension.enablementState === EnablementState.DisabledByExtensionKind) {2713if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {2714let message;2715// Extension on Local Server2716if (this.extensionManagementServerService.localExtensionManagementServer === this.extension.server) {2717if (this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(this.extension.local.manifest)) {2718if (this.extensionManagementServerService.remoteExtensionManagementServer) {2719message = 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)`);2720}2721}2722}2723// Extension on Remote Server2724else if (this.extensionManagementServerService.remoteExtensionManagementServer === this.extension.server) {2725if (this.extensionManifestPropertiesService.prefersExecuteOnUI(this.extension.local.manifest)) {2726if (this.extensionManagementServerService.localExtensionManagementServer) {2727message = 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)`);2728} else if (isWeb) {2729message = 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)`);2730}2731}2732}2733// Extension on Web Server2734else if (this.extensionManagementServerService.webExtensionManagementServer === this.extension.server) {2735message = 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)`);2736}2737if (message) {2738this.updateStatus({ icon: warningIcon, message }, true);2739}2740return;2741}2742}27432744const extensionId = new ExtensionIdentifier(this.extension.identifier.id);2745const features = Registry.as<IExtensionFeaturesRegistry>(Extensions.ExtensionFeaturesRegistry).getExtensionFeatures();2746for (const feature of features) {2747const status = this.extensionFeaturesManagementService.getAccessData(extensionId, feature.id)?.current?.status;2748const manageAccessLink = `[${localize('manage access', 'Manage Access')}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.identifier.id, ExtensionEditorTab.Features, false, feature.id]))}`)})`;2749if (status?.severity === Severity.Error) {2750this.updateStatus({ icon: errorIcon, message: new MarkdownString().appendText(status.message).appendMarkdown(` ${manageAccessLink}`) }, true);2751return;2752}2753if (status?.severity === Severity.Warning) {2754this.updateStatus({ icon: warningIcon, message: new MarkdownString().appendText(status.message).appendMarkdown(` ${manageAccessLink}`) }, true);2755return;2756}2757}27582759// Remote Workspace2760if (this.extensionManagementServerService.remoteExtensionManagementServer) {2761if (isLanguagePackExtension(this.extension.local.manifest)) {2762if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {2763const message = this.extension.server === this.extensionManagementServerService.localExtensionManagementServer2764? 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))2765: new MarkdownString(localize('Install language pack also locally', "Install the language pack extension locally to enable it there also."));2766this.updateStatus({ icon: infoIcon, message }, true);2767}2768return;2769}27702771const runningExtension = this.extensionService.extensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];2772const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)) : null;2773if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {2774if (this.extensionManifestPropertiesService.prefersExecuteOnWorkspace(this.extension.local.manifest)) {2775this.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);2776}2777return;2778}27792780if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {2781if (this.extensionManifestPropertiesService.prefersExecuteOnUI(this.extension.local.manifest)) {2782this.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);2783}2784return;2785}27862787if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.webExtensionManagementServer) {2788if (this.extensionManifestPropertiesService.canExecuteOnWeb(this.extension.local.manifest)) {2789this.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);2790}2791return;2792}2793}27942795// Extension is disabled by its dependency2796if (this.extension.enablementState === EnablementState.DisabledByExtensionDependency) {2797this.updateStatus({2798icon: warningIcon,2799message: new MarkdownString(localize('extension disabled because of dependency', "This extension depends on an extension that is disabled."))2800.appendMarkdown(` [${localize('dependencies', "Show Dependencies")}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.identifier.id, ExtensionEditorTab.Dependencies]))}`)})`)2801}, true);2802return;2803}28042805if (!this.extension.local.isValid) {2806const errors = this.extension.local.validations.filter(([severity]) => severity === Severity.Error).map(([, message]) => message);2807this.updateStatus({ icon: warningIcon, message: new MarkdownString(errors.join(' ').trim()) }, true);2808return;2809}28102811const isEnabled = this.workbenchExtensionEnablementService.isEnabled(this.extension.local);2812const isRunning = this.extensionService.extensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier));28132814if (!this.extension.isWorkspaceScoped && isEnabled && isRunning) {2815if (this.extension.enablementState === EnablementState.EnabledWorkspace) {2816this.updateStatus({ message: new MarkdownString(localize('workspace enabled', "This extension is enabled for this workspace by the user.")) }, true);2817return;2818}2819if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {2820if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {2821this.updateStatus({ message: new MarkdownString(localize('extension enabled on remote', "Extension is enabled on '{0}'", this.extension.server.label)) }, true);2822return;2823}2824}2825if (this.extension.enablementState === EnablementState.EnabledGlobally) {2826return;2827}2828}28292830if (!isEnabled && !isRunning) {2831if (this.extension.enablementState === EnablementState.DisabledGlobally) {2832this.updateStatus({ message: new MarkdownString(localize('globally disabled', "This extension is disabled globally by the user.")) }, true);2833return;2834}2835if (this.extension.enablementState === EnablementState.DisabledWorkspace) {2836this.updateStatus({ message: new MarkdownString(localize('workspace disabled', "This extension is disabled for this workspace by the user.")) }, true);2837return;2838}2839}2840}28412842private updateStatus(status: ExtensionStatus | undefined, updateClass: boolean): void {2843if (status) {2844if (this._status.some(s => s.message.value === status.message.value && s.icon?.id === status.icon?.id)) {2845return;2846}2847} else {2848if (this._status.length === 0) {2849return;2850}2851this._status = [];2852}28532854if (status) {2855this._status.push(status);2856this._status.sort((a, b) =>2857b.icon === trustIcon ? -1 :2858a.icon === trustIcon ? 1 :2859b.icon === errorIcon ? -1 :2860a.icon === errorIcon ? 1 :2861b.icon === warningIcon ? -1 :2862a.icon === warningIcon ? 1 :2863b.icon === infoIcon ? -1 :2864a.icon === infoIcon ? 1 :286502866);2867}28682869if (updateClass) {2870if (status?.icon === errorIcon) {2871this.class = `${ExtensionStatusAction.CLASS} extension-status-error ${ThemeIcon.asClassName(errorIcon)}`;2872}2873else if (status?.icon === warningIcon) {2874this.class = `${ExtensionStatusAction.CLASS} extension-status-warning ${ThemeIcon.asClassName(warningIcon)}`;2875}2876else if (status?.icon === infoIcon) {2877this.class = `${ExtensionStatusAction.CLASS} extension-status-info ${ThemeIcon.asClassName(infoIcon)}`;2878}2879else if (status?.icon === trustIcon) {2880this.class = `${ExtensionStatusAction.CLASS} ${ThemeIcon.asClassName(trustIcon)}`;2881}2882else {2883this.class = `${ExtensionStatusAction.CLASS} hide`;2884}2885}2886this._onDidChangeStatus.fire();2887}28882889override async run(): Promise<any> {2890if (this._status[0]?.icon === trustIcon) {2891return this.commandService.executeCommand('workbench.trust.manage');2892}2893}2894}28952896export class InstallSpecificVersionOfExtensionAction extends Action {28972898static readonly ID = 'workbench.extensions.action.install.specificVersion';2899static readonly LABEL = localize('install previous version', "Install Specific Version of Extension...");29002901constructor(2902id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL,2903@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2904@IQuickInputService private readonly quickInputService: IQuickInputService,2905@IInstantiationService private readonly instantiationService: IInstantiationService,2906@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,2907) {2908super(id, label);2909}29102911override get enabled(): boolean {2912return this.extensionsWorkbenchService.local.some(l => this.isEnabled(l));2913}29142915override async run(): Promise<any> {2916const extensionPick = await this.quickInputService.pick(this.getExtensionEntries(), { placeHolder: localize('selectExtension', "Select Extension"), matchOnDetail: true });2917if (extensionPick && extensionPick.extension) {2918const action = this.instantiationService.createInstance(InstallAnotherVersionAction, extensionPick.extension, true);2919await action.run();2920await this.extensionsWorkbenchService.openSearch(extensionPick.extension.identifier.id);2921}2922}29232924private isEnabled(extension: IExtension): boolean {2925const action = this.instantiationService.createInstance(InstallAnotherVersionAction, extension, true);2926return action.enabled && !!extension.local && this.extensionEnablementService.isEnabled(extension.local);2927}29282929private async getExtensionEntries(): Promise<IExtensionPickItem[]> {2930const installed = await this.extensionsWorkbenchService.queryLocal();2931const entries: IExtensionPickItem[] = [];2932for (const extension of installed) {2933if (this.isEnabled(extension)) {2934entries.push({2935id: extension.identifier.id,2936label: extension.displayName || extension.identifier.id,2937description: extension.identifier.id,2938extension,2939});2940}2941}2942return entries.sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName));2943}2944}29452946interface IExtensionPickItem extends IQuickPickItem {2947extension: IExtension;2948}29492950export abstract class AbstractInstallExtensionsInServerAction extends Action {29512952private extensions: IExtension[] | undefined = undefined;29532954constructor(2955id: string,2956@IExtensionsWorkbenchService protected readonly extensionsWorkbenchService: IExtensionsWorkbenchService,2957@IQuickInputService private readonly quickInputService: IQuickInputService,2958@INotificationService private readonly notificationService: INotificationService,2959@IProgressService private readonly progressService: IProgressService,2960) {2961super(id);2962this.update();2963this.extensionsWorkbenchService.queryLocal().then(() => this.updateExtensions());2964this._register(this.extensionsWorkbenchService.onChange(() => {2965if (this.extensions) {2966this.updateExtensions();2967}2968}));2969}29702971private updateExtensions(): void {2972this.extensions = this.extensionsWorkbenchService.local;2973this.update();2974}29752976private update(): void {2977this.enabled = !!this.extensions && this.getExtensionsToInstall(this.extensions).length > 0;2978this.tooltip = this.label;2979}29802981override async run(): Promise<void> {2982return this.selectAndInstallExtensions();2983}29842985private async queryExtensionsToInstall(): Promise<IExtension[]> {2986const local = await this.extensionsWorkbenchService.queryLocal();2987return this.getExtensionsToInstall(local);2988}29892990private async selectAndInstallExtensions(): Promise<void> {2991const quickPick = this.quickInputService.createQuickPick<IExtensionPickItem>();2992quickPick.busy = true;2993const disposable = quickPick.onDidAccept(() => {2994disposable.dispose();2995quickPick.hide();2996quickPick.dispose();2997this.onDidAccept(quickPick.selectedItems);2998});2999quickPick.show();3000const localExtensionsToInstall = await this.queryExtensionsToInstall();3001quickPick.busy = false;3002if (localExtensionsToInstall.length) {3003quickPick.title = this.getQuickPickTitle();3004quickPick.placeholder = localize('select extensions to install', "Select extensions to install");3005quickPick.canSelectMany = true;3006localExtensionsToInstall.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName));3007quickPick.items = localExtensionsToInstall.map<IExtensionPickItem>(extension => ({ extension, label: extension.displayName, description: extension.version }));3008} else {3009quickPick.hide();3010quickPick.dispose();3011this.notificationService.notify({3012severity: Severity.Info,3013message: localize('no local extensions', "There are no extensions to install.")3014});3015}3016}30173018private async onDidAccept(selectedItems: ReadonlyArray<IExtensionPickItem>): Promise<void> {3019if (selectedItems.length) {3020const localExtensionsToInstall = selectedItems.filter(r => !!r.extension).map(r => r.extension);3021if (localExtensionsToInstall.length) {3022await this.progressService.withProgress(3023{3024location: ProgressLocation.Notification,3025title: localize('installing extensions', "Installing Extensions...")3026},3027() => this.installExtensions(localExtensionsToInstall));3028this.notificationService.info(localize('finished installing', "Successfully installed extensions."));3029}3030}3031}30323033protected abstract getQuickPickTitle(): string;3034protected abstract getExtensionsToInstall(local: IExtension[]): IExtension[];3035protected abstract installExtensions(extensions: IExtension[]): Promise<void>;3036}30373038export class InstallLocalExtensionsInRemoteAction extends AbstractInstallExtensionsInServerAction {30393040constructor(3041@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,3042@IQuickInputService quickInputService: IQuickInputService,3043@IProgressService progressService: IProgressService,3044@INotificationService notificationService: INotificationService,3045@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,3046@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,3047@IInstantiationService private readonly instantiationService: IInstantiationService,3048@IFileService private readonly fileService: IFileService,3049@ILogService private readonly logService: ILogService,3050) {3051super('workbench.extensions.actions.installLocalExtensionsInRemote', extensionsWorkbenchService, quickInputService, notificationService, progressService);3052}30533054override get label(): string {3055if (this.extensionManagementServerService && this.extensionManagementServerService.remoteExtensionManagementServer) {3056return localize('select and install local extensions', "Install Local Extensions in '{0}'...", this.extensionManagementServerService.remoteExtensionManagementServer.label);3057}3058return '';3059}30603061protected getQuickPickTitle(): string {3062return localize('install local extensions title', "Install Local Extensions in '{0}'", this.extensionManagementServerService.remoteExtensionManagementServer!.label);3063}30643065protected getExtensionsToInstall(local: IExtension[]): IExtension[] {3066return local.filter(extension => {3067const action = this.instantiationService.createInstance(RemoteInstallAction, true);3068action.extension = extension;3069return action.enabled;3070});3071}30723073protected async installExtensions(localExtensionsToInstall: IExtension[]): Promise<void> {3074const galleryExtensions: IGalleryExtension[] = [];3075const vsixs: URI[] = [];3076const targetPlatform = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getTargetPlatform();3077await Promises.settled(localExtensionsToInstall.map(async extension => {3078if (this.extensionGalleryService.isEnabled()) {3079const gallery = (await this.extensionGalleryService.getExtensions([{ ...extension.identifier, preRelease: !!extension.local?.preRelease }], { targetPlatform, compatible: true }, CancellationToken.None))[0];3080if (gallery) {3081galleryExtensions.push(gallery);3082return;3083}3084}3085const vsix = await this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.zip(extension.local!);3086vsixs.push(vsix);3087}));30883089await Promises.settled(galleryExtensions.map(gallery => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(gallery)));3090try {3091await Promises.settled(vsixs.map(vsix => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.install(vsix)));3092} finally {3093try {3094await Promise.allSettled(vsixs.map(vsix => this.fileService.del(vsix)));3095} catch (error) {3096this.logService.error(error);3097}3098}3099}3100}31013102export class InstallRemoteExtensionsInLocalAction extends AbstractInstallExtensionsInServerAction {31033104constructor(3105id: string,3106@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,3107@IQuickInputService quickInputService: IQuickInputService,3108@IProgressService progressService: IProgressService,3109@INotificationService notificationService: INotificationService,3110@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,3111@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,3112@IFileService private readonly fileService: IFileService,3113@ILogService private readonly logService: ILogService,3114) {3115super(id, extensionsWorkbenchService, quickInputService, notificationService, progressService);3116}31173118override get label(): string {3119return localize('select and install remote extensions', "Install Remote Extensions Locally...");3120}31213122protected getQuickPickTitle(): string {3123return localize('install remote extensions', "Install Remote Extensions Locally");3124}31253126protected getExtensionsToInstall(local: IExtension[]): IExtension[] {3127return local.filter(extension =>3128extension.type === ExtensionType.User && extension.server !== this.extensionManagementServerService.localExtensionManagementServer3129&& !this.extensionsWorkbenchService.installed.some(e => e.server === this.extensionManagementServerService.localExtensionManagementServer && areSameExtensions(e.identifier, extension.identifier)));3130}31313132protected async installExtensions(extensions: IExtension[]): Promise<void> {3133const galleryExtensions: IGalleryExtension[] = [];3134const vsixs: URI[] = [];3135const targetPlatform = await this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.getTargetPlatform();3136await Promises.settled(extensions.map(async extension => {3137if (this.extensionGalleryService.isEnabled()) {3138const gallery = (await this.extensionGalleryService.getExtensions([{ ...extension.identifier, preRelease: !!extension.local?.preRelease }], { targetPlatform, compatible: true }, CancellationToken.None))[0];3139if (gallery) {3140galleryExtensions.push(gallery);3141return;3142}3143}3144const vsix = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.zip(extension.local!);3145vsixs.push(vsix);3146}));31473148await Promises.settled(galleryExtensions.map(gallery => this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.installFromGallery(gallery)));3149try {3150await Promises.settled(vsixs.map(vsix => this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.install(vsix)));3151} finally {3152try {3153await Promise.allSettled(vsixs.map(vsix => this.fileService.del(vsix)));3154} catch (error) {3155this.logService.error(error);3156}3157}3158}3159}31603161CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForLanguage', function (accessor: ServicesAccessor, fileExtension: string) {3162const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);3163return extensionsWorkbenchService.openSearch(`ext:${fileExtension.replace(/^\./, '')}`);3164});31653166export const showExtensionsWithIdsCommandId = 'workbench.extensions.action.showExtensionsWithIds';3167CommandsRegistry.registerCommand(showExtensionsWithIdsCommandId, function (accessor: ServicesAccessor, extensionIds: string[]) {3168const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);3169return extensionsWorkbenchService.openSearch(extensionIds.map(id => `@id:${id}`).join(' '));3170});31713172registerColor('extensionButton.background', {3173dark: buttonBackground,3174light: buttonBackground,3175hcDark: null,3176hcLight: null3177}, localize('extensionButtonBackground', "Button background color for extension actions."));31783179registerColor('extensionButton.foreground', {3180dark: buttonForeground,3181light: buttonForeground,3182hcDark: null,3183hcLight: null3184}, localize('extensionButtonForeground', "Button foreground color for extension actions."));31853186registerColor('extensionButton.hoverBackground', {3187dark: buttonHoverBackground,3188light: buttonHoverBackground,3189hcDark: null,3190hcLight: null3191}, localize('extensionButtonHoverBackground', "Button background hover color for extension actions."));31923193registerColor('extensionButton.separator', buttonSeparator, localize('extensionButtonSeparator', "Button separator color for extension actions"));31943195export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', {3196dark: buttonBackground,3197light: buttonBackground,3198hcDark: null,3199hcLight: null3200}, localize('extensionButtonProminentBackground', "Button background color for extension actions that stand out (e.g. install button)."));32013202registerColor('extensionButton.prominentForeground', {3203dark: buttonForeground,3204light: buttonForeground,3205hcDark: null,3206hcLight: null3207}, localize('extensionButtonProminentForeground', "Button foreground color for extension actions that stand out (e.g. install button)."));32083209registerColor('extensionButton.prominentHoverBackground', {3210dark: buttonHoverBackground,3211light: buttonHoverBackground,3212hcDark: null,3213hcLight: null3214}, localize('extensionButtonProminentHoverBackground', "Button background hover color for extension actions that stand out (e.g. install button)."));32153216registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {32173218const errorColor = theme.getColor(editorErrorForeground);3219if (errorColor) {3220collector.addRule(`.extension-editor .header .actions-status-container > .status ${ThemeIcon.asCSSSelector(errorIcon)} { color: ${errorColor}; }`);3221collector.addRule(`.extension-editor .body .subcontent .runtime-status ${ThemeIcon.asCSSSelector(errorIcon)} { color: ${errorColor}; }`);3222collector.addRule(`.monaco-hover.extension-hover .markdown-hover .hover-contents ${ThemeIcon.asCSSSelector(errorIcon)} { color: ${errorColor}; }`);3223}32243225const warningColor = theme.getColor(editorWarningForeground);3226if (warningColor) {3227collector.addRule(`.extension-editor .header .actions-status-container > .status ${ThemeIcon.asCSSSelector(warningIcon)} { color: ${warningColor}; }`);3228collector.addRule(`.extension-editor .body .subcontent .runtime-status ${ThemeIcon.asCSSSelector(warningIcon)} { color: ${warningColor}; }`);3229collector.addRule(`.monaco-hover.extension-hover .markdown-hover .hover-contents ${ThemeIcon.asCSSSelector(warningIcon)} { color: ${warningColor}; }`);3230}32313232const infoColor = theme.getColor(editorInfoForeground);3233if (infoColor) {3234collector.addRule(`.extension-editor .header .actions-status-container > .status ${ThemeIcon.asCSSSelector(infoIcon)} { color: ${infoColor}; }`);3235collector.addRule(`.extension-editor .body .subcontent .runtime-status ${ThemeIcon.asCSSSelector(infoIcon)} { color: ${infoColor}; }`);3236collector.addRule(`.monaco-hover.extension-hover .markdown-hover .hover-contents ${ThemeIcon.asCSSSelector(infoIcon)} { color: ${infoColor}; }`);3237}3238});323932403241