Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts
5270 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IAction } from '../../../../base/common/actions.js';
7
import { CancellationToken } from '../../../../base/common/cancellation.js';
8
import { IStringDictionary } from '../../../../base/common/collections.js';
9
import { onUnexpectedError } from '../../../../base/common/errors.js';
10
import { Event } from '../../../../base/common/event.js';
11
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
12
import { mnemonicButtonLabel } from '../../../../base/common/labels.js';
13
import { Disposable, DisposableStore, IDisposable, isDisposable } from '../../../../base/common/lifecycle.js';
14
import { Schemas } from '../../../../base/common/network.js';
15
import { isNative, isWeb } from '../../../../base/common/platform.js';
16
import { PolicyCategory } from '../../../../base/common/policy.js';
17
import { URI, UriComponents } from '../../../../base/common/uri.js';
18
import { MultiCommand } from '../../../../editor/browser/editorExtensions.js';
19
import { CopyAction, CutAction, PasteAction } from '../../../../editor/contrib/clipboard/browser/clipboard.js';
20
import { localize, localize2 } from '../../../../nls.js';
21
import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';
22
import { Action2, IAction2Options, IMenuItem, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js';
23
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
24
import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';
25
import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js';
26
import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
27
import { IDialogService, IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js';
28
import { ExtensionGalleryManifestStatus, ExtensionGalleryResourceType, ExtensionGalleryServiceUrlConfigKey, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js';
29
import { EXTENSION_INSTALL_SOURCE_CONTEXT, ExtensionInstallSource, ExtensionRequestsTimeoutConfigKey, ExtensionsLocalizedLabel, FilterType, IExtensionGalleryService, IExtensionManagementService, PreferencesLocalizedLabel, SortBy, VerifyExtensionSignatureConfigKey } from '../../../../platform/extensionManagement/common/extensionManagement.js';
30
import { areSameExtensions, getIdAndVersion } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';
31
import { ExtensionStorageService } from '../../../../platform/extensionManagement/common/extensionStorage.js';
32
import { IExtensionRecommendationNotificationService } from '../../../../platform/extensionRecommendations/common/extensionRecommendations.js';
33
import { EXTENSION_CATEGORIES, ExtensionType } from '../../../../platform/extensions/common/extensions.js';
34
import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
35
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
36
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
37
import * as jsonContributionRegistry from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js';
38
import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';
39
import product from '../../../../platform/product/common/product.js';
40
import { IProductService } from '../../../../platform/product/common/productService.js';
41
import { ProgressLocation } from '../../../../platform/progress/common/progress.js';
42
import { Extensions, IQuickAccessRegistry } from '../../../../platform/quickinput/common/quickAccess.js';
43
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
44
import { Registry } from '../../../../platform/registry/common/platform.js';
45
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
46
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
47
import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js';
48
import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js';
49
import { Extensions as ConfigurationMigrationExtensions, IConfigurationMigrationRegistry } from '../../../common/configuration.js';
50
import { ResourceContextKey, WorkbenchStateContext } from '../../../common/contextkeys.js';
51
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, registerWorkbenchContribution2, Extensions as WorkbenchExtensions, WorkbenchPhase } from '../../../common/contributions.js';
52
import { EditorExtensions } from '../../../common/editor.js';
53
import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation } from '../../../common/views.js';
54
import { DEFAULT_ACCOUNT_SIGN_IN_COMMAND } from '../../../services/accounts/browser/defaultAccount.js';
55
import { IEditorService } from '../../../services/editor/common/editorService.js';
56
import { EnablementState, IExtensionManagementServerService, IPublisherInfo, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js';
57
import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js';
58
import { IWorkspaceExtensionsConfigService } from '../../../services/extensionRecommendations/common/workspaceExtensionsConfig.js';
59
import { IHostService } from '../../../services/host/browser/host.js';
60
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
61
import { IPreferencesService } from '../../../services/preferences/common/preferences.js';
62
import { CONTEXT_SYNC_ENABLEMENT } from '../../../services/userDataSync/common/userDataSync.js';
63
import { IViewsService } from '../../../services/views/common/viewsService.js';
64
import { WORKSPACE_TRUST_EXTENSION_SUPPORT } from '../../../services/workspaces/common/workspaceTrust.js';
65
import { ILanguageModelToolsService } from '../../chat/common/tools/languageModelToolsService.js';
66
import { CONTEXT_KEYBINDINGS_EDITOR } from '../../preferences/common/preferences.js';
67
import { IWebview } from '../../webview/browser/webview.js';
68
import { Query } from '../common/extensionQuery.js';
69
import { AutoRestartConfigurationKey, AutoUpdateConfigurationKey, CONTEXT_EXTENSIONS_GALLERY_STATUS, CONTEXT_HAS_GALLERY, DefaultViewsContext, ExtensionEditorTab, ExtensionRuntimeActionType, EXTENSIONS_CATEGORY, extensionsFilterSubMenu, extensionsSearchActionsMenu, HasOutdatedExtensionsContext, IExtensionArg, IExtensionsViewPaneContainer, IExtensionsWorkbenchService, INSTALL_ACTIONS_GROUP, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, IWorkspaceRecommendedExtensionsView, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, OUTDATED_EXTENSIONS_VIEW_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, TOGGLE_IGNORE_EXTENSION_ACTION_ID, UPDATE_ACTIONS_GROUP, VIEWLET_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID } from '../common/extensions.js';
70
import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from '../common/extensionsFileTemplate.js';
71
import { ExtensionsInput } from '../common/extensionsInput.js';
72
import { KeymapExtensions } from '../common/extensionsUtils.js';
73
import { SearchExtensionsTool, SearchExtensionsToolData } from '../common/searchExtensionsTool.js';
74
import { ShowRuntimeExtensionsAction } from './abstractRuntimeExtensionsEditor.js';
75
import { ExtensionEditor } from './extensionEditor.js';
76
import { ExtensionEnablementWorkspaceTrustTransitionParticipant } from './extensionEnablementWorkspaceTrustTransitionParticipant.js';
77
import { ExtensionRecommendationNotificationService } from './extensionRecommendationNotificationService.js';
78
import { ExtensionRecommendationsService } from './extensionRecommendationsService.js';
79
import { ClearLanguageAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction, InstallAction, InstallAnotherVersionAction, InstallSpecificVersionOfExtensionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction, TogglePreReleaseExtensionAction } from './extensionsActions.js';
80
import { ExtensionActivationProgress } from './extensionsActivationProgress.js';
81
import { ExtensionsCompletionItemsProvider } from './extensionsCompletionItemsProvider.js';
82
import { ExtensionDependencyChecker } from './extensionsDependencyChecker.js';
83
import { clearSearchResultsIcon, configureRecommendedIcon, extensionsViewIcon, filterIcon, installWorkspaceRecommendedIcon, refreshIcon } from './extensionsIcons.js';
84
import { InstallExtensionQuickAccessProvider, ManageExtensionsQuickAccessProvider } from './extensionsQuickAccess.js';
85
import { BuiltInExtensionsContext, ExtensionMarketplaceStatusUpdater, ExtensionsSearchValueContext, ExtensionsSortByContext, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, MaliciousExtensionChecker, RecommendedExtensionsContext, SearchHasTextContext, SearchMarketplaceExtensionsContext, StatusUpdater } from './extensionsViewlet.js';
86
import { ExtensionsWorkbenchService } from './extensionsWorkbenchService.js';
87
import './media/extensionManagement.css';
88
import { UnsupportedExtensionsMigrationContrib } from './unsupportedExtensionsMigrationContribution.js';
89
90
// Singletons
91
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService, InstantiationType.Eager /* Auto updates extensions */);
92
registerSingleton(IExtensionRecommendationNotificationService, ExtensionRecommendationNotificationService, InstantiationType.Delayed);
93
registerSingleton(IExtensionRecommendationsService, ExtensionRecommendationsService, InstantiationType.Eager /* Prompts recommendations in the background */);
94
95
// Quick Access
96
Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessProvider({
97
ctor: ManageExtensionsQuickAccessProvider,
98
prefix: ManageExtensionsQuickAccessProvider.PREFIX,
99
placeholder: localize('manageExtensionsQuickAccessPlaceholder', "Press Enter to manage extensions."),
100
helpEntries: [{ description: localize('manageExtensionsHelp', "Manage Extensions") }]
101
});
102
103
// Editor
104
Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane).registerEditorPane(
105
EditorPaneDescriptor.create(
106
ExtensionEditor,
107
ExtensionEditor.ID,
108
localize('extension', "Extension")
109
),
110
[
111
new SyncDescriptor(ExtensionsInput)
112
]);
113
114
export const VIEW_CONTAINER = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(
115
{
116
id: VIEWLET_ID,
117
title: localize2('extensions', "Extensions"),
118
openCommandActionDescriptor: {
119
id: VIEWLET_ID,
120
mnemonicTitle: localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions"),
121
keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyX },
122
order: 4,
123
},
124
ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer),
125
icon: extensionsViewIcon,
126
order: 4,
127
rejectAddedViews: true,
128
alwaysUseContainerInfo: true,
129
}, ViewContainerLocation.Sidebar);
130
131
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
132
.registerConfiguration({
133
id: 'extensions',
134
order: 30,
135
title: localize('extensionsConfigurationTitle', "Extensions"),
136
type: 'object',
137
properties: {
138
'extensions.autoUpdate': {
139
enum: [true, 'onlyEnabledExtensions', false,],
140
enumItemLabels: [
141
localize('all', "All Extensions"),
142
localize('enabled', "Only Enabled Extensions"),
143
localize('none', "None"),
144
],
145
enumDescriptions: [
146
localize('extensions.autoUpdate.true', 'Download and install updates automatically for all extensions.'),
147
localize('extensions.autoUpdate.enabled', 'Download and install updates automatically only for enabled extensions.'),
148
localize('extensions.autoUpdate.false', 'Extensions are not automatically updated.'),
149
],
150
description: localize('extensions.autoUpdate', "Controls the automatic update behavior of extensions. The updates are fetched from a Microsoft online service."),
151
default: true,
152
scope: ConfigurationScope.APPLICATION,
153
tags: ['usesOnlineServices']
154
},
155
'extensions.autoCheckUpdates': {
156
type: 'boolean',
157
description: localize('extensionsCheckUpdates', "When enabled, automatically checks extensions for updates. If an extension has an update, it is marked as outdated in the Extensions view. The updates are fetched from a Microsoft online service."),
158
default: true,
159
scope: ConfigurationScope.APPLICATION,
160
tags: ['usesOnlineServices']
161
},
162
'extensions.ignoreRecommendations': {
163
type: 'boolean',
164
description: localize('extensionsIgnoreRecommendations', "When enabled, the notifications for extension recommendations will not be shown."),
165
default: false
166
},
167
'extensions.showRecommendationsOnlyOnDemand': {
168
type: 'boolean',
169
deprecationMessage: localize('extensionsShowRecommendationsOnlyOnDemand_Deprecated', "This setting is deprecated. Use extensions.ignoreRecommendations setting to control recommendation notifications. Use Extensions view's visibility actions to hide Recommended view by default."),
170
default: false,
171
tags: ['usesOnlineServices']
172
},
173
'extensions.closeExtensionDetailsOnViewChange': {
174
type: 'boolean',
175
description: localize('extensionsCloseExtensionDetailsOnViewChange', "When enabled, editors with extension details will be automatically closed upon navigating away from the Extensions View."),
176
default: false
177
},
178
'extensions.confirmedUriHandlerExtensionIds': {
179
type: 'array',
180
items: {
181
type: 'string'
182
},
183
description: localize('handleUriConfirmedExtensions', "When an extension is listed here, a confirmation prompt will not be shown when that extension handles a URI."),
184
default: [],
185
scope: ConfigurationScope.APPLICATION
186
},
187
'extensions.webWorker': {
188
type: ['boolean', 'string'],
189
enum: [true, false, 'auto'],
190
enumDescriptions: [
191
localize('extensionsWebWorker.true', "The Web Worker Extension Host will always be launched."),
192
localize('extensionsWebWorker.false', "The Web Worker Extension Host will never be launched."),
193
localize('extensionsWebWorker.auto', "The Web Worker Extension Host will be launched when a web extension needs it."),
194
],
195
description: localize('extensionsWebWorker', "Enable web worker extension host."),
196
default: 'auto'
197
},
198
'extensions.supportVirtualWorkspaces': {
199
type: 'object',
200
markdownDescription: localize('extensions.supportVirtualWorkspaces', "Override the virtual workspaces support of an extension."),
201
patternProperties: {
202
'([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$': {
203
type: 'boolean',
204
default: false
205
}
206
},
207
additionalProperties: false,
208
default: {},
209
defaultSnippets: [{
210
'body': {
211
'pub.name': false
212
}
213
}]
214
},
215
'extensions.experimental.affinity': {
216
type: 'object',
217
markdownDescription: localize('extensions.affinity', "Configure an extension to execute in a different extension host process."),
218
patternProperties: {
219
'([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$': {
220
type: 'integer',
221
default: 1
222
}
223
},
224
additionalProperties: false,
225
default: {},
226
defaultSnippets: [{
227
'body': {
228
'pub.name': 1
229
}
230
}]
231
},
232
[WORKSPACE_TRUST_EXTENSION_SUPPORT]: {
233
type: 'object',
234
scope: ConfigurationScope.APPLICATION,
235
markdownDescription: localize('extensions.supportUntrustedWorkspaces', "Override the untrusted workspace support of an extension. Extensions using `true` will always be enabled. Extensions using `limited` will always be enabled, and the extension will hide functionality that requires trust. Extensions using `false` will only be enabled only when the workspace is trusted."),
236
patternProperties: {
237
'([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$': {
238
type: 'object',
239
properties: {
240
'supported': {
241
type: ['boolean', 'string'],
242
enum: [true, false, 'limited'],
243
enumDescriptions: [
244
localize('extensions.supportUntrustedWorkspaces.true', "Extension will always be enabled."),
245
localize('extensions.supportUntrustedWorkspaces.false', "Extension will only be enabled only when the workspace is trusted."),
246
localize('extensions.supportUntrustedWorkspaces.limited', "Extension will always be enabled, and the extension will hide functionality requiring trust."),
247
],
248
description: localize('extensions.supportUntrustedWorkspaces.supported', "Defines the untrusted workspace support setting for the extension."),
249
},
250
'version': {
251
type: 'string',
252
description: localize('extensions.supportUntrustedWorkspaces.version', "Defines the version of the extension for which the override should be applied. If not specified, the override will be applied independent of the extension version."),
253
}
254
}
255
}
256
}
257
},
258
'extensions.experimental.deferredStartupFinishedActivation': {
259
type: 'boolean',
260
description: localize('extensionsDeferredStartupFinishedActivation', "When enabled, extensions which declare the `onStartupFinished` activation event will be activated after a timeout."),
261
default: false
262
},
263
'extensions.experimental.issueQuickAccess': {
264
type: 'boolean',
265
description: localize('extensionsInQuickAccess', "When enabled, extensions can be searched for via Quick Access and report issues from there."),
266
default: true
267
},
268
[VerifyExtensionSignatureConfigKey]: {
269
type: 'boolean',
270
description: localize('extensions.verifySignature', "When enabled, extensions are verified to be signed before getting installed."),
271
default: true,
272
scope: ConfigurationScope.APPLICATION,
273
included: isNative
274
},
275
[AutoRestartConfigurationKey]: {
276
type: 'boolean',
277
description: localize('autoRestart', "If activated, extensions will automatically restart following an update if the window is not in focus. There can be a data loss if you have open Notebooks or Custom Editors."),
278
default: false,
279
included: product.quality !== 'stable'
280
},
281
[ExtensionGalleryServiceUrlConfigKey]: {
282
type: 'string',
283
description: localize('extensions.gallery.serviceUrl', "Configure the Marketplace service URL to connect to"),
284
default: '',
285
scope: ConfigurationScope.APPLICATION,
286
tags: ['usesOnlineServices'],
287
included: false,
288
policy: {
289
name: 'ExtensionGalleryServiceUrl',
290
category: PolicyCategory.Extensions,
291
minimumVersion: '1.99',
292
localization: {
293
description: {
294
key: 'extensions.gallery.serviceUrl',
295
value: localize('extensions.gallery.serviceUrl', "Configure the Marketplace service URL to connect to"),
296
}
297
}
298
},
299
},
300
'extensions.supportNodeGlobalNavigator': {
301
type: 'boolean',
302
description: localize('extensionsSupportNodeGlobalNavigator', "When enabled, Node.js navigator object is exposed on the global scope."),
303
default: false,
304
},
305
[ExtensionRequestsTimeoutConfigKey]: {
306
type: 'number',
307
description: localize('extensionsRequestTimeout', "Controls the timeout in milliseconds for HTTP requests made when fetching extensions from the Marketplace"),
308
default: 60_000,
309
scope: ConfigurationScope.APPLICATION,
310
tags: ['advanced', 'usesOnlineServices']
311
},
312
}
313
});
314
315
const jsonRegistry = <jsonContributionRegistry.IJSONContributionRegistry>Registry.as(jsonContributionRegistry.Extensions.JSONContribution);
316
jsonRegistry.registerSchema(ExtensionsConfigurationSchemaId, ExtensionsConfigurationSchema);
317
318
// Register Commands
319
CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean, feature?: string) => {
320
const extensionService = accessor.get(IExtensionsWorkbenchService);
321
const extension = extensionService.local.find(e => areSameExtensions(e.identifier, { id: extensionId }));
322
if (extension) {
323
extensionService.open(extension, { tab, preserveFocus, feature });
324
} else {
325
throw new Error(localize('notFound', "Extension '{0}' not found.", extensionId));
326
}
327
});
328
329
CommandsRegistry.registerCommand('extension.open', async (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean, feature?: string, sideByside?: boolean) => {
330
const extensionService = accessor.get(IExtensionsWorkbenchService);
331
const commandService = accessor.get(ICommandService);
332
333
const [extension] = await extensionService.getExtensions([{ id: extensionId }], CancellationToken.None);
334
if (extension) {
335
return extensionService.open(extension, { tab, preserveFocus, feature, sideByside });
336
}
337
338
return commandService.executeCommand('_extensions.manage', extensionId, tab, preserveFocus, feature);
339
});
340
341
CommandsRegistry.registerCommand({
342
id: 'workbench.extensions.installExtension',
343
metadata: {
344
description: localize('workbench.extensions.installExtension.description', "Install the given extension"),
345
args: [
346
{
347
name: 'extensionIdOrVSIXUri',
348
description: localize('workbench.extensions.installExtension.arg.decription', "Extension id or VSIX resource uri"),
349
constraint: (value: any) => typeof value === 'string' || value instanceof URI,
350
},
351
{
352
name: 'options',
353
description: '(optional) Options for installing the extension. Object with the following properties: ' +
354
'`installOnlyNewlyAddedFromExtensionPackVSIX`: When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only when installing VSIX. ',
355
isOptional: true,
356
schema: {
357
'type': 'object',
358
'properties': {
359
'installOnlyNewlyAddedFromExtensionPackVSIX': {
360
'type': 'boolean',
361
'description': localize('workbench.extensions.installExtension.option.installOnlyNewlyAddedFromExtensionPackVSIX', "When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only while installing a VSIX."),
362
default: false
363
},
364
'installPreReleaseVersion': {
365
'type': 'boolean',
366
'description': localize('workbench.extensions.installExtension.option.installPreReleaseVersion', "When enabled, VS Code installs the pre-release version of the extension if available."),
367
default: false
368
},
369
'donotSync': {
370
'type': 'boolean',
371
'description': localize('workbench.extensions.installExtension.option.donotSync', "When enabled, VS Code do not sync this extension when Settings Sync is on."),
372
default: false
373
},
374
'justification': {
375
'type': ['string', 'object'],
376
'description': localize('workbench.extensions.installExtension.option.justification', "Justification for installing the extension. This is a string or an object that can be used to pass any information to the installation handlers. i.e. `{reason: 'This extension wants to open a URI', action: 'Open URI'}` will show a message box with the reason and action upon install."),
377
},
378
'enable': {
379
'type': 'boolean',
380
'description': localize('workbench.extensions.installExtension.option.enable', "When enabled, the extension will be enabled if it is installed but disabled. If the extension is already enabled, this has no effect."),
381
default: false
382
},
383
'context': {
384
'type': 'object',
385
'description': localize('workbench.extensions.installExtension.option.context', "Context for the installation. This is a JSON object that can be used to pass any information to the installation handlers. i.e. `{skipWalkthrough: true}` will skip opening the walkthrough upon install."),
386
}
387
}
388
}
389
}
390
]
391
},
392
handler: async (
393
accessor,
394
arg: string | UriComponents,
395
options?: {
396
installOnlyNewlyAddedFromExtensionPackVSIX?: boolean;
397
installPreReleaseVersion?: boolean;
398
donotSync?: boolean;
399
justification?: string | { reason: string; action: string };
400
enable?: boolean;
401
context?: IStringDictionary<any>;
402
}) => {
403
const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);
404
const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService);
405
const extensionGalleryService = accessor.get(IExtensionGalleryService);
406
try {
407
if (typeof arg === 'string') {
408
const [id, version] = getIdAndVersion(arg);
409
const extension = extensionsWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id, uuid: version }));
410
if (extension?.enablementState === EnablementState.DisabledByExtensionKind) {
411
const [gallery] = await extensionGalleryService.getExtensions([{ id, preRelease: options?.installPreReleaseVersion }], CancellationToken.None);
412
if (!gallery) {
413
throw new Error(localize('notFound', "Extension '{0}' not found.", arg));
414
}
415
await extensionManagementService.installFromGallery(gallery, {
416
isMachineScoped: options?.donotSync ? true : undefined, /* do not allow syncing extensions automatically while installing through the command */
417
installPreReleaseVersion: options?.installPreReleaseVersion,
418
installGivenVersion: !!version,
419
context: { ...options?.context, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.COMMAND },
420
});
421
} else {
422
await extensionsWorkbenchService.install(id, {
423
version,
424
installPreReleaseVersion: options?.installPreReleaseVersion,
425
context: { ...options?.context, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.COMMAND },
426
justification: options?.justification,
427
enable: options?.enable,
428
isMachineScoped: options?.donotSync ? true : undefined, /* do not allow syncing extensions automatically while installing through the command */
429
}, ProgressLocation.Notification);
430
}
431
} else {
432
const vsix = URI.revive(arg);
433
await extensionsWorkbenchService.install(vsix, { installGivenVersion: true });
434
}
435
} catch (e) {
436
onUnexpectedError(e);
437
throw e;
438
}
439
}
440
});
441
442
CommandsRegistry.registerCommand({
443
id: 'workbench.extensions.uninstallExtension',
444
metadata: {
445
description: localize('workbench.extensions.uninstallExtension.description', "Uninstall the given extension"),
446
args: [
447
{
448
name: localize('workbench.extensions.uninstallExtension.arg.name', "Id of the extension to uninstall"),
449
schema: {
450
'type': 'string'
451
}
452
}
453
]
454
},
455
handler: async (accessor, id: string) => {
456
if (!id) {
457
throw new Error(localize('id required', "Extension id required."));
458
}
459
const extensionManagementService = accessor.get(IExtensionManagementService);
460
const installed = await extensionManagementService.getInstalled();
461
const [extensionToUninstall] = installed.filter(e => areSameExtensions(e.identifier, { id }));
462
if (!extensionToUninstall) {
463
throw new Error(localize('notInstalled', "Extension '{0}' is not installed. Make sure you use the full extension ID, including the publisher, e.g.: ms-dotnettools.csharp.", id));
464
}
465
if (extensionToUninstall.isBuiltin) {
466
throw new Error(localize('builtin', "Extension '{0}' is a Built-in extension and cannot be uninstalled", id));
467
}
468
469
try {
470
await extensionManagementService.uninstall(extensionToUninstall);
471
} catch (e) {
472
onUnexpectedError(e);
473
throw e;
474
}
475
}
476
});
477
478
CommandsRegistry.registerCommand({
479
id: 'workbench.extensions.search',
480
metadata: {
481
description: localize('workbench.extensions.search.description', "Search for a specific extension"),
482
args: [
483
{
484
name: localize('workbench.extensions.search.arg.name', "Query to use in search"),
485
schema: { 'type': 'string' }
486
}
487
]
488
},
489
handler: async (accessor, query: string = '') => {
490
return accessor.get(IExtensionsWorkbenchService).openSearch(query);
491
}
492
});
493
494
function overrideActionForActiveExtensionEditorWebview(command: MultiCommand | undefined, f: (webview: IWebview) => void) {
495
command?.addImplementation(105, 'extensions-editor', (accessor) => {
496
const editorService = accessor.get(IEditorService);
497
const editor = editorService.activeEditorPane;
498
if (editor instanceof ExtensionEditor) {
499
if (editor.activeWebview?.isFocused) {
500
f(editor.activeWebview);
501
return true;
502
}
503
}
504
return false;
505
});
506
}
507
508
overrideActionForActiveExtensionEditorWebview(CopyAction, webview => webview.copy());
509
overrideActionForActiveExtensionEditorWebview(CutAction, webview => webview.cut());
510
overrideActionForActiveExtensionEditorWebview(PasteAction, webview => webview.paste());
511
512
// Contexts
513
export const CONTEXT_HAS_LOCAL_SERVER = new RawContextKey<boolean>('hasLocalServer', false);
514
export const CONTEXT_HAS_REMOTE_SERVER = new RawContextKey<boolean>('hasRemoteServer', false);
515
export const CONTEXT_HAS_WEB_SERVER = new RawContextKey<boolean>('hasWebServer', false);
516
const CONTEXT_GALLERY_SORT_CAPABILITIES = new RawContextKey<string>('gallerySortCapabilities', '');
517
const CONTEXT_GALLERY_FILTER_CAPABILITIES = new RawContextKey<string>('galleryFilterCapabilities', '');
518
const CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED = new RawContextKey<boolean>('galleryAllPublicRepositorySigned', false);
519
const CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED = new RawContextKey<boolean>('galleryAllPrivateRepositorySigned', false);
520
const CONTEXT_GALLERY_HAS_EXTENSION_LINK = new RawContextKey<boolean>('galleryHasExtensionLink', false);
521
522
async function runAction(action: IAction): Promise<void> {
523
try {
524
await action.run();
525
} finally {
526
if (isDisposable(action)) {
527
action.dispose();
528
}
529
}
530
}
531
532
type IExtensionActionOptions = IAction2Options & {
533
menuTitles?: { [id: string]: string };
534
run(accessor: ServicesAccessor, ...args: unknown[]): Promise<any>;
535
};
536
537
class ExtensionsContributions extends Disposable implements IWorkbenchContribution {
538
539
constructor(
540
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
541
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
542
@IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService,
543
@IContextKeyService private readonly contextKeyService: IContextKeyService,
544
@IViewsService private readonly viewsService: IViewsService,
545
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
546
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
547
@IInstantiationService private readonly instantiationService: IInstantiationService,
548
@IDialogService private readonly dialogService: IDialogService,
549
@ICommandService private readonly commandService: ICommandService,
550
@IProductService private readonly productService: IProductService,
551
) {
552
super();
553
const hasLocalServerContext = CONTEXT_HAS_LOCAL_SERVER.bindTo(contextKeyService);
554
if (this.extensionManagementServerService.localExtensionManagementServer) {
555
hasLocalServerContext.set(true);
556
}
557
558
const hasRemoteServerContext = CONTEXT_HAS_REMOTE_SERVER.bindTo(contextKeyService);
559
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
560
hasRemoteServerContext.set(true);
561
}
562
563
const hasWebServerContext = CONTEXT_HAS_WEB_SERVER.bindTo(contextKeyService);
564
if (this.extensionManagementServerService.webExtensionManagementServer) {
565
hasWebServerContext.set(true);
566
}
567
568
this.updateExtensionGalleryStatusContexts();
569
this._register(extensionGalleryManifestService.onDidChangeExtensionGalleryManifestStatus(() => this.updateExtensionGalleryStatusContexts()));
570
extensionGalleryManifestService.getExtensionGalleryManifest()
571
.then(extensionGalleryManifest => {
572
this.updateGalleryCapabilitiesContexts(extensionGalleryManifest);
573
this._register(extensionGalleryManifestService.onDidChangeExtensionGalleryManifest(extensionGalleryManifest => this.updateGalleryCapabilitiesContexts(extensionGalleryManifest)));
574
});
575
this.registerGlobalActions();
576
this.registerContextMenuActions();
577
this.registerQuickAccessProvider();
578
}
579
580
private async updateExtensionGalleryStatusContexts(): Promise<void> {
581
CONTEXT_HAS_GALLERY.bindTo(this.contextKeyService).set(this.extensionGalleryManifestService.extensionGalleryManifestStatus === ExtensionGalleryManifestStatus.Available);
582
CONTEXT_EXTENSIONS_GALLERY_STATUS.bindTo(this.contextKeyService).set(this.extensionGalleryManifestService.extensionGalleryManifestStatus);
583
}
584
585
private async updateGalleryCapabilitiesContexts(extensionGalleryManifest: IExtensionGalleryManifest | null): Promise<void> {
586
CONTEXT_GALLERY_SORT_CAPABILITIES.bindTo(this.contextKeyService).set(`_${extensionGalleryManifest?.capabilities.extensionQuery.sorting?.map(s => s.name)?.join('_')}_UpdateDate_`);
587
CONTEXT_GALLERY_FILTER_CAPABILITIES.bindTo(this.contextKeyService).set(`_${extensionGalleryManifest?.capabilities.extensionQuery.filtering?.map(s => s.name)?.join('_')}_`);
588
CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED.bindTo(this.contextKeyService).set(!!extensionGalleryManifest?.capabilities?.signing?.allPublicRepositorySigned);
589
CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED.bindTo(this.contextKeyService).set(!!extensionGalleryManifest?.capabilities?.signing?.allPrivateRepositorySigned);
590
CONTEXT_GALLERY_HAS_EXTENSION_LINK.bindTo(this.contextKeyService).set(!!(extensionGalleryManifest && getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionDetailsViewUri)));
591
}
592
593
private registerQuickAccessProvider(): void {
594
if (this.extensionManagementServerService.localExtensionManagementServer
595
|| this.extensionManagementServerService.remoteExtensionManagementServer
596
|| this.extensionManagementServerService.webExtensionManagementServer
597
) {
598
Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessProvider({
599
ctor: InstallExtensionQuickAccessProvider,
600
prefix: InstallExtensionQuickAccessProvider.PREFIX,
601
placeholder: localize('installExtensionQuickAccessPlaceholder', "Type the name of an extension to install or search."),
602
helpEntries: [{ description: localize('installExtensionQuickAccessHelp', "Install or Search Extensions") }]
603
});
604
}
605
}
606
607
// Global actions
608
private registerGlobalActions(): void {
609
this._register(MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
610
command: {
611
id: VIEWLET_ID,
612
title: localize({ key: 'miPreferencesExtensions', comment: ['&& denotes a mnemonic'] }, "&&Extensions")
613
},
614
group: '2_configuration',
615
order: 3
616
}));
617
this._register(MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
618
command: {
619
id: VIEWLET_ID,
620
title: localize('showExtensions', "Extensions")
621
},
622
group: '2_configuration',
623
order: 3
624
}));
625
626
this.registerExtensionAction({
627
id: 'workbench.extensions.action.focusExtensionsView',
628
title: localize2('focusExtensions', 'Focus on Extensions View'),
629
category: ExtensionsLocalizedLabel,
630
f1: true,
631
run: async (accessor: ServicesAccessor) => {
632
await accessor.get(IExtensionsWorkbenchService).openSearch('');
633
}
634
});
635
636
this.registerExtensionAction({
637
id: 'workbench.extensions.action.installExtensions',
638
title: localize2('installExtensions', 'Install Extensions'),
639
category: ExtensionsLocalizedLabel,
640
menu: {
641
id: MenuId.CommandPalette,
642
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
643
},
644
run: async (accessor: ServicesAccessor) => {
645
accessor.get(IViewsService).openViewContainer(VIEWLET_ID, true);
646
}
647
});
648
649
this.registerExtensionAction({
650
id: 'workbench.extensions.action.showRecommendedKeymapExtensions',
651
title: localize2('showRecommendedKeymapExtensionsShort', 'Keymaps'),
652
category: PreferencesLocalizedLabel,
653
menu: [{
654
id: MenuId.CommandPalette,
655
when: CONTEXT_HAS_GALLERY
656
}, {
657
id: MenuId.EditorTitle,
658
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_HAS_GALLERY),
659
group: '2_keyboard_discover_actions'
660
}],
661
menuTitles: {
662
[MenuId.EditorTitle.id]: localize('importKeyboardShortcutsFroms', "Migrate Keyboard Shortcuts from...")
663
},
664
run: () => this.extensionsWorkbenchService.openSearch('@recommended:keymaps ')
665
});
666
667
this.registerExtensionAction({
668
id: 'workbench.extensions.action.showLanguageExtensions',
669
title: localize2('showLanguageExtensionsShort', 'Language Extensions'),
670
category: PreferencesLocalizedLabel,
671
menu: {
672
id: MenuId.CommandPalette,
673
when: CONTEXT_HAS_GALLERY
674
},
675
run: () => this.extensionsWorkbenchService.openSearch('@recommended:languages ')
676
});
677
678
this.registerExtensionAction({
679
id: 'workbench.extensions.action.checkForUpdates',
680
title: localize2('checkForUpdates', 'Check for Extension Updates'),
681
category: ExtensionsLocalizedLabel,
682
menu: [{
683
id: MenuId.CommandPalette,
684
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
685
}, {
686
id: MenuId.ViewContainerTitle,
687
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY),
688
group: '1_updates',
689
order: 1
690
}],
691
run: async () => {
692
await this.extensionsWorkbenchService.checkForUpdates();
693
const outdated = this.extensionsWorkbenchService.outdated;
694
if (outdated.length) {
695
return this.extensionsWorkbenchService.openSearch('@outdated ');
696
} else {
697
return this.dialogService.info(localize('noUpdatesAvailable', "All extensions are up to date."));
698
}
699
}
700
});
701
702
const enableAutoUpdateWhenCondition = ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false);
703
this.registerExtensionAction({
704
id: 'workbench.extensions.action.enableAutoUpdate',
705
title: localize2('enableAutoUpdate', 'Enable Auto Update for All Extensions'),
706
category: ExtensionsLocalizedLabel,
707
precondition: enableAutoUpdateWhenCondition,
708
menu: [{
709
id: MenuId.ViewContainerTitle,
710
order: 5,
711
group: '1_updates',
712
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), enableAutoUpdateWhenCondition)
713
}, {
714
id: MenuId.CommandPalette,
715
}],
716
run: (accessor: ServicesAccessor) => accessor.get(IExtensionsWorkbenchService).updateAutoUpdateForAllExtensions(true)
717
});
718
719
const disableAutoUpdateWhenCondition = ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, false);
720
this.registerExtensionAction({
721
id: 'workbench.extensions.action.disableAutoUpdate',
722
title: localize2('disableAutoUpdate', 'Disable Auto Update for All Extensions'),
723
precondition: disableAutoUpdateWhenCondition,
724
category: ExtensionsLocalizedLabel,
725
menu: [{
726
id: MenuId.ViewContainerTitle,
727
order: 5,
728
group: '1_updates',
729
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), disableAutoUpdateWhenCondition)
730
}, {
731
id: MenuId.CommandPalette,
732
}],
733
run: (accessor: ServicesAccessor) => accessor.get(IExtensionsWorkbenchService).updateAutoUpdateForAllExtensions(false)
734
});
735
736
this.registerExtensionAction({
737
id: 'workbench.extensions.action.updateAllExtensions',
738
title: localize2('updateAll', 'Update All Extensions'),
739
category: ExtensionsLocalizedLabel,
740
precondition: HasOutdatedExtensionsContext,
741
menu: [
742
{
743
id: MenuId.CommandPalette,
744
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
745
}, {
746
id: MenuId.ViewContainerTitle,
747
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`).negate(), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'))),
748
group: '1_updates',
749
order: 2
750
}, {
751
id: MenuId.ViewTitle,
752
when: ContextKeyExpr.equals('view', OUTDATED_EXTENSIONS_VIEW_ID),
753
group: 'navigation',
754
order: 1
755
}
756
],
757
icon: installWorkspaceRecommendedIcon,
758
run: async () => {
759
await this.extensionsWorkbenchService.updateAll();
760
}
761
});
762
763
this.registerExtensionAction({
764
id: 'workbench.extensions.action.enableAll',
765
title: localize2('enableAll', 'Enable All Extensions'),
766
category: ExtensionsLocalizedLabel,
767
menu: [{
768
id: MenuId.CommandPalette,
769
when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)
770
}, {
771
id: MenuId.ViewContainerTitle,
772
when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID),
773
group: '2_enablement',
774
order: 1
775
}],
776
run: async () => {
777
const extensionsToEnable = this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local));
778
if (extensionsToEnable.length) {
779
await this.extensionsWorkbenchService.setEnablement(extensionsToEnable, EnablementState.EnabledGlobally);
780
}
781
}
782
});
783
784
this.registerExtensionAction({
785
id: 'workbench.extensions.action.enableAllWorkspace',
786
title: localize2('enableAllWorkspace', 'Enable All Extensions for this Workspace'),
787
category: ExtensionsLocalizedLabel,
788
menu: {
789
id: MenuId.CommandPalette,
790
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
791
},
792
run: async () => {
793
const extensionsToEnable = this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local));
794
if (extensionsToEnable.length) {
795
await this.extensionsWorkbenchService.setEnablement(extensionsToEnable, EnablementState.EnabledWorkspace);
796
}
797
}
798
});
799
800
this.registerExtensionAction({
801
id: 'workbench.extensions.action.disableAll',
802
title: localize2('disableAll', 'Disable All Installed Extensions'),
803
category: ExtensionsLocalizedLabel,
804
menu: [{
805
id: MenuId.CommandPalette,
806
when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)
807
}, {
808
id: MenuId.ViewContainerTitle,
809
when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID),
810
group: '2_enablement',
811
order: 2
812
}],
813
run: async () => {
814
const extensionsToDisable = this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local));
815
if (extensionsToDisable.length) {
816
await this.extensionsWorkbenchService.setEnablement(extensionsToDisable, EnablementState.DisabledGlobally);
817
}
818
}
819
});
820
821
this.registerExtensionAction({
822
id: 'workbench.extensions.action.disableAllWorkspace',
823
title: localize2('disableAllWorkspace', 'Disable All Installed Extensions for this Workspace'),
824
category: ExtensionsLocalizedLabel,
825
menu: {
826
id: MenuId.CommandPalette,
827
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
828
},
829
run: async () => {
830
const extensionsToDisable = this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local));
831
if (extensionsToDisable.length) {
832
await this.extensionsWorkbenchService.setEnablement(extensionsToDisable, EnablementState.DisabledWorkspace);
833
}
834
}
835
});
836
837
this.registerExtensionAction({
838
id: SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID,
839
title: localize2('InstallFromVSIX', 'Install from VSIX...'),
840
category: ExtensionsLocalizedLabel,
841
menu: [{
842
id: MenuId.CommandPalette,
843
when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)
844
}, {
845
id: MenuId.ViewContainerTitle,
846
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)),
847
group: '3_install',
848
order: 1
849
}],
850
run: async (accessor: ServicesAccessor) => {
851
const fileDialogService = accessor.get(IFileDialogService);
852
const commandService = accessor.get(ICommandService);
853
const vsixPaths = await fileDialogService.showOpenDialog({
854
title: localize('installFromVSIX', "Install from VSIX"),
855
filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }],
856
canSelectFiles: true,
857
canSelectMany: true,
858
openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install"))
859
});
860
if (vsixPaths) {
861
await commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths);
862
}
863
}
864
});
865
866
this.registerExtensionAction({
867
id: INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID,
868
title: localize('installVSIX', "Install Extension VSIX"),
869
menu: [{
870
id: MenuId.ExplorerContext,
871
group: 'extensions',
872
when: ContextKeyExpr.and(ResourceContextKey.Extension.isEqualTo('.vsix'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)),
873
}],
874
run: async (accessor: ServicesAccessor, resources: URI[] | URI) => {
875
const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);
876
const hostService = accessor.get(IHostService);
877
const notificationService = accessor.get(INotificationService);
878
879
const vsixs = Array.isArray(resources) ? resources : [resources];
880
const result = await Promise.allSettled(vsixs.map(async (vsix) => await extensionsWorkbenchService.install(vsix, { installGivenVersion: true })));
881
let error: Error | undefined, requireReload = false, requireRestart = false;
882
for (const r of result) {
883
if (r.status === 'rejected') {
884
error = new Error(r.reason);
885
break;
886
}
887
requireReload = requireReload || r.value.runtimeState?.action === ExtensionRuntimeActionType.ReloadWindow;
888
requireRestart = requireRestart || r.value.runtimeState?.action === ExtensionRuntimeActionType.RestartExtensions;
889
}
890
if (error) {
891
throw error;
892
}
893
if (requireReload) {
894
notificationService.prompt(
895
Severity.Info,
896
vsixs.length > 1 ? localize('InstallVSIXs.successReload', "Completed installing extensions. Please reload Visual Studio Code to enable them.")
897
: localize('InstallVSIXAction.successReload', "Completed installing extension. Please reload Visual Studio Code to enable it."),
898
[{
899
label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
900
run: () => hostService.reload()
901
}]
902
);
903
}
904
else if (requireRestart) {
905
notificationService.prompt(
906
Severity.Info,
907
vsixs.length > 1 ? localize('InstallVSIXs.successRestart', "Completed installing extensions. Please restart extensions to enable them.")
908
: localize('InstallVSIXAction.successRestart', "Completed installing extension. Please restart extensions to enable it."),
909
[{
910
label: localize('InstallVSIXAction.restartExtensions', "Restart Extensions"),
911
run: () => extensionsWorkbenchService.updateRunningExtensions()
912
}]
913
);
914
}
915
else {
916
notificationService.prompt(
917
Severity.Info,
918
vsixs.length > 1 ? localize('InstallVSIXs.successNoReload', "Completed installing extensions.") : localize('InstallVSIXAction.successNoReload', "Completed installing extension."),
919
[]
920
);
921
}
922
}
923
});
924
925
this.registerExtensionAction({
926
id: 'workbench.extensions.action.installExtensionFromLocation',
927
title: localize2('installExtensionFromLocation', 'Install Extension from Location...'),
928
category: Categories.Developer,
929
menu: [{
930
id: MenuId.CommandPalette,
931
when: ContextKeyExpr.or(CONTEXT_HAS_WEB_SERVER, CONTEXT_HAS_LOCAL_SERVER)
932
}],
933
run: async (accessor: ServicesAccessor) => {
934
const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService);
935
if (isWeb) {
936
return new Promise<void>((c, e) => {
937
const quickInputService = accessor.get(IQuickInputService);
938
const disposables = new DisposableStore();
939
const quickPick = disposables.add(quickInputService.createQuickPick());
940
quickPick.title = localize('installFromLocation', "Install Extension from Location");
941
quickPick.customButton = true;
942
quickPick.customLabel = localize('install button', "Install");
943
quickPick.placeholder = localize('installFromLocationPlaceHolder', "Location of the web extension");
944
quickPick.ignoreFocusOut = true;
945
disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(async () => {
946
quickPick.hide();
947
if (quickPick.value) {
948
try {
949
await extensionManagementService.installFromLocation(URI.parse(quickPick.value));
950
} catch (error) {
951
e(error);
952
return;
953
}
954
}
955
c();
956
}));
957
disposables.add(quickPick.onDidHide(() => disposables.dispose()));
958
quickPick.show();
959
});
960
} else {
961
const fileDialogService = accessor.get(IFileDialogService);
962
const extensionLocation = await fileDialogService.showOpenDialog({
963
canSelectFolders: true,
964
canSelectFiles: false,
965
canSelectMany: false,
966
title: localize('installFromLocation', "Install Extension from Location"),
967
});
968
if (extensionLocation?.[0]) {
969
await extensionManagementService.installFromLocation(extensionLocation[0]);
970
}
971
}
972
}
973
});
974
975
MenuRegistry.appendMenuItem(extensionsSearchActionsMenu, {
976
submenu: extensionsFilterSubMenu,
977
title: localize('filterExtensions', "Filter Extensions..."),
978
group: 'navigation',
979
order: 2,
980
icon: filterIcon,
981
});
982
983
const showFeaturedExtensionsId = 'extensions.filter.featured';
984
const featuresExtensionsWhenContext = ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.regex(CONTEXT_GALLERY_FILTER_CAPABILITIES.key, new RegExp(`_${FilterType.Featured}_`)));
985
this.registerExtensionAction({
986
id: showFeaturedExtensionsId,
987
title: localize2('showFeaturedExtensions', 'Show Featured Extensions'),
988
category: ExtensionsLocalizedLabel,
989
menu: [{
990
id: MenuId.CommandPalette,
991
when: featuresExtensionsWhenContext
992
}, {
993
id: extensionsFilterSubMenu,
994
when: featuresExtensionsWhenContext,
995
group: '1_predefined',
996
order: 1,
997
}],
998
menuTitles: {
999
[extensionsFilterSubMenu.id]: localize('featured filter', "Featured")
1000
},
1001
run: () => this.extensionsWorkbenchService.openSearch('@featured ')
1002
});
1003
1004
this.registerExtensionAction({
1005
id: 'workbench.extensions.action.showPopularExtensions',
1006
title: localize2('showPopularExtensions', 'Show Popular Extensions'),
1007
category: ExtensionsLocalizedLabel,
1008
menu: [{
1009
id: MenuId.CommandPalette,
1010
when: CONTEXT_HAS_GALLERY
1011
}, {
1012
id: extensionsFilterSubMenu,
1013
when: CONTEXT_HAS_GALLERY,
1014
group: '1_predefined',
1015
order: 2,
1016
}],
1017
menuTitles: {
1018
[extensionsFilterSubMenu.id]: localize('most popular filter', "Most Popular")
1019
},
1020
run: () => this.extensionsWorkbenchService.openSearch('@popular ')
1021
});
1022
1023
this.registerExtensionAction({
1024
id: 'workbench.extensions.action.showRecommendedExtensions',
1025
title: localize2('showRecommendedExtensions', 'Show Recommended Extensions'),
1026
category: ExtensionsLocalizedLabel,
1027
menu: [{
1028
id: MenuId.CommandPalette,
1029
when: CONTEXT_HAS_GALLERY
1030
}, {
1031
id: extensionsFilterSubMenu,
1032
when: CONTEXT_HAS_GALLERY,
1033
group: '1_predefined',
1034
order: 2,
1035
}],
1036
menuTitles: {
1037
[extensionsFilterSubMenu.id]: localize('most popular recommended', "Recommended")
1038
},
1039
run: () => this.extensionsWorkbenchService.openSearch('@recommended ')
1040
});
1041
1042
this.registerExtensionAction({
1043
id: 'workbench.extensions.action.recentlyPublishedExtensions',
1044
title: localize2('recentlyPublishedExtensions', 'Show Recently Published Extensions'),
1045
category: ExtensionsLocalizedLabel,
1046
menu: [{
1047
id: MenuId.CommandPalette,
1048
when: CONTEXT_HAS_GALLERY
1049
}, {
1050
id: extensionsFilterSubMenu,
1051
when: CONTEXT_HAS_GALLERY,
1052
group: '1_predefined',
1053
order: 2,
1054
}],
1055
menuTitles: {
1056
[extensionsFilterSubMenu.id]: localize('recently published filter', "Recently Published")
1057
},
1058
run: () => this.extensionsWorkbenchService.openSearch('@recentlyPublished ')
1059
});
1060
1061
const extensionsCategoryFilterSubMenu = new MenuId('extensionsCategoryFilterSubMenu');
1062
MenuRegistry.appendMenuItem(extensionsFilterSubMenu, {
1063
submenu: extensionsCategoryFilterSubMenu,
1064
title: localize('filter by category', "Category"),
1065
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.regex(CONTEXT_GALLERY_FILTER_CAPABILITIES.key, new RegExp(`_${FilterType.Category}_`))),
1066
group: '2_categories',
1067
order: 1,
1068
});
1069
1070
EXTENSION_CATEGORIES.forEach((category, index) => {
1071
this.registerExtensionAction({
1072
id: `extensions.actions.searchByCategory.${category}`,
1073
title: category,
1074
menu: [{
1075
id: extensionsCategoryFilterSubMenu,
1076
when: CONTEXT_HAS_GALLERY,
1077
order: index,
1078
}],
1079
run: () => this.extensionsWorkbenchService.openSearch(`@category:"${category.toLowerCase()}"`)
1080
});
1081
});
1082
1083
this.registerExtensionAction({
1084
id: 'workbench.extensions.action.installedExtensions',
1085
title: localize2('installedExtensions', 'Show Installed Extensions'),
1086
category: ExtensionsLocalizedLabel,
1087
f1: true,
1088
menu: [{
1089
id: extensionsFilterSubMenu,
1090
group: '3_installed',
1091
order: 1,
1092
}],
1093
menuTitles: {
1094
[extensionsFilterSubMenu.id]: localize('installed filter', "Installed")
1095
},
1096
run: () => this.extensionsWorkbenchService.openSearch('@installed ')
1097
});
1098
1099
this.registerExtensionAction({
1100
id: 'workbench.extensions.action.listBuiltInExtensions',
1101
title: localize2('showBuiltInExtensions', 'Show Built-in Extensions'),
1102
category: ExtensionsLocalizedLabel,
1103
menu: [{
1104
id: MenuId.CommandPalette,
1105
when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)
1106
}, {
1107
id: extensionsFilterSubMenu,
1108
group: '3_installed',
1109
order: 3,
1110
}],
1111
menuTitles: {
1112
[extensionsFilterSubMenu.id]: localize('builtin filter', "Built-in")
1113
},
1114
run: () => this.extensionsWorkbenchService.openSearch('@builtin ')
1115
});
1116
1117
this.registerExtensionAction({
1118
id: 'workbench.extensions.action.extensionUpdates',
1119
title: localize2('extensionUpdates', 'Show Extension Updates'),
1120
category: ExtensionsLocalizedLabel,
1121
precondition: CONTEXT_HAS_GALLERY,
1122
f1: true,
1123
menu: [{
1124
id: extensionsFilterSubMenu,
1125
group: '3_installed',
1126
when: CONTEXT_HAS_GALLERY,
1127
order: 2,
1128
}],
1129
menuTitles: {
1130
[extensionsFilterSubMenu.id]: localize('extension updates filter', "Updates")
1131
},
1132
run: () => this.extensionsWorkbenchService.openSearch('@updates')
1133
});
1134
1135
this.registerExtensionAction({
1136
id: LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID,
1137
title: localize2('showWorkspaceUnsupportedExtensions', 'Show Extensions Unsupported By Workspace'),
1138
category: ExtensionsLocalizedLabel,
1139
menu: [{
1140
id: MenuId.CommandPalette,
1141
when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER),
1142
}, {
1143
id: extensionsFilterSubMenu,
1144
group: '3_installed',
1145
order: 6,
1146
when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER),
1147
}],
1148
menuTitles: {
1149
[extensionsFilterSubMenu.id]: localize('workspace unsupported filter', "Workspace Unsupported")
1150
},
1151
run: () => this.extensionsWorkbenchService.openSearch('@workspaceUnsupported')
1152
});
1153
1154
this.registerExtensionAction({
1155
id: 'workbench.extensions.action.showEnabledExtensions',
1156
title: localize2('showEnabledExtensions', 'Show Enabled Extensions'),
1157
category: ExtensionsLocalizedLabel,
1158
menu: [{
1159
id: MenuId.CommandPalette,
1160
when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)
1161
}, {
1162
id: extensionsFilterSubMenu,
1163
group: '3_installed',
1164
order: 4,
1165
}],
1166
menuTitles: {
1167
[extensionsFilterSubMenu.id]: localize('enabled filter', "Enabled")
1168
},
1169
run: () => this.extensionsWorkbenchService.openSearch('@enabled ')
1170
});
1171
1172
this.registerExtensionAction({
1173
id: 'workbench.extensions.action.showDisabledExtensions',
1174
title: localize2('showDisabledExtensions', 'Show Disabled Extensions'),
1175
category: ExtensionsLocalizedLabel,
1176
menu: [{
1177
id: MenuId.CommandPalette,
1178
when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)
1179
}, {
1180
id: extensionsFilterSubMenu,
1181
group: '3_installed',
1182
order: 5,
1183
}],
1184
menuTitles: {
1185
[extensionsFilterSubMenu.id]: localize('disabled filter', "Disabled")
1186
},
1187
run: () => this.extensionsWorkbenchService.openSearch('@disabled ')
1188
});
1189
1190
const extensionsSortSubMenu = new MenuId('extensionsSortSubMenu');
1191
MenuRegistry.appendMenuItem(extensionsFilterSubMenu, {
1192
submenu: extensionsSortSubMenu,
1193
title: localize('sorty by', "Sort By"),
1194
when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext)),
1195
group: '4_sort',
1196
order: 1,
1197
});
1198
1199
[
1200
{ id: 'installs', title: localize('sort by installs', "Install Count"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.InstallCount },
1201
{ id: 'rating', title: localize('sort by rating', "Rating"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.WeightedRating },
1202
{ id: 'name', title: localize('sort by name', "Name"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.Title },
1203
{ id: 'publishedDate', title: localize('sort by published date', "Published Date"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.PublishedDate },
1204
{ id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate(), BuiltInExtensionsContext.negate()), sortCapability: 'UpdateDate' },
1205
].map(({ id, title, precondition, sortCapability }, index) => {
1206
const sortCapabilityContext = ContextKeyExpr.regex(CONTEXT_GALLERY_SORT_CAPABILITIES.key, new RegExp(`_${sortCapability}_`));
1207
this.registerExtensionAction({
1208
id: `extensions.sort.${id}`,
1209
title,
1210
precondition: ContextKeyExpr.and(precondition, ContextKeyExpr.regex(ExtensionsSearchValueContext.key, /^@contribute:/).negate(), sortCapabilityContext),
1211
menu: [{
1212
id: extensionsSortSubMenu,
1213
when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext), sortCapabilityContext),
1214
order: index,
1215
}],
1216
toggled: ExtensionsSortByContext.isEqualTo(id),
1217
run: async () => {
1218
const extensionsViewPaneContainer = ((await this.viewsService.openViewContainer(VIEWLET_ID, true))?.getViewPaneContainer()) as IExtensionsViewPaneContainer | undefined;
1219
const currentQuery = Query.parse(extensionsViewPaneContainer?.searchValue ?? '');
1220
extensionsViewPaneContainer?.search(new Query(currentQuery.value, id).toString());
1221
extensionsViewPaneContainer?.focus();
1222
}
1223
});
1224
});
1225
1226
this.registerExtensionAction({
1227
id: 'workbench.extensions.action.clearExtensionsSearchResults',
1228
title: localize2('clearExtensionsSearchResults', 'Clear Extensions Search Results'),
1229
category: ExtensionsLocalizedLabel,
1230
icon: clearSearchResultsIcon,
1231
f1: true,
1232
precondition: SearchHasTextContext,
1233
menu: {
1234
id: extensionsSearchActionsMenu,
1235
group: 'navigation',
1236
order: 1,
1237
},
1238
run: async (accessor: ServicesAccessor) => {
1239
const viewPaneContainer = accessor.get(IViewsService).getActiveViewPaneContainerWithId(VIEWLET_ID);
1240
if (viewPaneContainer) {
1241
const extensionsViewPaneContainer = viewPaneContainer as IExtensionsViewPaneContainer;
1242
extensionsViewPaneContainer.search('');
1243
extensionsViewPaneContainer.focus();
1244
}
1245
}
1246
});
1247
1248
this.registerExtensionAction({
1249
id: 'workbench.extensions.action.refreshExtension',
1250
title: localize2('refreshExtension', 'Refresh'),
1251
category: ExtensionsLocalizedLabel,
1252
icon: refreshIcon,
1253
f1: true,
1254
menu: {
1255
id: MenuId.ViewContainerTitle,
1256
when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID),
1257
group: 'navigation',
1258
order: 2
1259
},
1260
run: async (accessor: ServicesAccessor) => {
1261
const viewPaneContainer = accessor.get(IViewsService).getActiveViewPaneContainerWithId(VIEWLET_ID);
1262
if (viewPaneContainer) {
1263
await (viewPaneContainer as IExtensionsViewPaneContainer).refresh();
1264
}
1265
}
1266
});
1267
1268
this.registerExtensionAction({
1269
id: 'workbench.extensions.action.installWorkspaceRecommendedExtensions',
1270
title: localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"),
1271
icon: installWorkspaceRecommendedIcon,
1272
menu: {
1273
id: MenuId.ViewTitle,
1274
when: ContextKeyExpr.equals('view', WORKSPACE_RECOMMENDATIONS_VIEW_ID),
1275
group: 'navigation',
1276
order: 1
1277
},
1278
run: async (accessor: ServicesAccessor) => {
1279
const view = accessor.get(IViewsService).getActiveViewWithId(WORKSPACE_RECOMMENDATIONS_VIEW_ID) as IWorkspaceRecommendedExtensionsView;
1280
return view.installWorkspaceRecommendations();
1281
}
1282
});
1283
1284
this.registerExtensionAction({
1285
id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID,
1286
title: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL,
1287
icon: configureRecommendedIcon,
1288
menu: [{
1289
id: MenuId.CommandPalette,
1290
when: WorkbenchStateContext.notEqualsTo('empty'),
1291
}, {
1292
id: MenuId.ViewTitle,
1293
when: ContextKeyExpr.equals('view', WORKSPACE_RECOMMENDATIONS_VIEW_ID),
1294
group: 'navigation',
1295
order: 2
1296
}],
1297
run: () => runAction(this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL))
1298
});
1299
1300
this.registerExtensionAction({
1301
id: InstallSpecificVersionOfExtensionAction.ID,
1302
title: { value: InstallSpecificVersionOfExtensionAction.LABEL, original: 'Install Specific Version of Extension...' },
1303
category: ExtensionsLocalizedLabel,
1304
menu: {
1305
id: MenuId.CommandPalette,
1306
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
1307
},
1308
run: () => runAction(this.instantiationService.createInstance(InstallSpecificVersionOfExtensionAction, InstallSpecificVersionOfExtensionAction.ID, InstallSpecificVersionOfExtensionAction.LABEL))
1309
});
1310
}
1311
1312
// Extension Context Menu
1313
private registerContextMenuActions(): void {
1314
1315
this.registerExtensionAction({
1316
id: SetColorThemeAction.ID,
1317
title: SetColorThemeAction.TITLE,
1318
menu: {
1319
id: MenuId.ExtensionContext,
1320
group: THEME_ACTIONS_GROUP,
1321
order: 0,
1322
when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasColorThemes'))
1323
},
1324
run: async (accessor: ServicesAccessor, extensionId: string) => {
1325
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1326
const instantiationService = accessor.get(IInstantiationService);
1327
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId }));
1328
if (extension) {
1329
const action = instantiationService.createInstance(SetColorThemeAction);
1330
action.extension = extension;
1331
return action.run();
1332
}
1333
}
1334
});
1335
1336
this.registerExtensionAction({
1337
id: SetFileIconThemeAction.ID,
1338
title: SetFileIconThemeAction.TITLE,
1339
menu: {
1340
id: MenuId.ExtensionContext,
1341
group: THEME_ACTIONS_GROUP,
1342
order: 0,
1343
when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasFileIconThemes'))
1344
},
1345
run: async (accessor: ServicesAccessor, extensionId: string) => {
1346
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1347
const instantiationService = accessor.get(IInstantiationService);
1348
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId }));
1349
if (extension) {
1350
const action = instantiationService.createInstance(SetFileIconThemeAction);
1351
action.extension = extension;
1352
return action.run();
1353
}
1354
}
1355
});
1356
1357
this.registerExtensionAction({
1358
id: SetProductIconThemeAction.ID,
1359
title: SetProductIconThemeAction.TITLE,
1360
menu: {
1361
id: MenuId.ExtensionContext,
1362
group: THEME_ACTIONS_GROUP,
1363
order: 0,
1364
when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasProductIconThemes'))
1365
},
1366
run: async (accessor: ServicesAccessor, extensionId: string) => {
1367
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1368
const instantiationService = accessor.get(IInstantiationService);
1369
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId }));
1370
if (extension) {
1371
const action = instantiationService.createInstance(SetProductIconThemeAction);
1372
action.extension = extension;
1373
return action.run();
1374
}
1375
}
1376
});
1377
1378
this.registerExtensionAction({
1379
id: 'workbench.extensions.action.showPreReleaseVersion',
1380
title: localize2('show pre-release version', 'Show Pre-Release Version'),
1381
menu: {
1382
id: MenuId.ExtensionContext,
1383
group: INSTALL_ACTIONS_GROUP,
1384
order: 0,
1385
when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension'))
1386
},
1387
run: async (accessor: ServicesAccessor, extensionId: string) => {
1388
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1389
const extension = (await extensionWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0];
1390
extensionWorkbenchService.open(extension, { showPreReleaseVersion: true });
1391
}
1392
});
1393
1394
this.registerExtensionAction({
1395
id: 'workbench.extensions.action.showReleasedVersion',
1396
title: localize2('show released version', 'Show Release Version'),
1397
menu: {
1398
id: MenuId.ExtensionContext,
1399
group: INSTALL_ACTIONS_GROUP,
1400
order: 1,
1401
when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension'))
1402
},
1403
run: async (accessor: ServicesAccessor, extensionId: string) => {
1404
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1405
const extension = (await extensionWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0];
1406
extensionWorkbenchService.open(extension, { showPreReleaseVersion: false });
1407
}
1408
});
1409
1410
this.registerExtensionAction({
1411
id: ToggleAutoUpdateForExtensionAction.ID,
1412
title: ToggleAutoUpdateForExtensionAction.LABEL,
1413
category: ExtensionsLocalizedLabel,
1414
precondition: ContextKeyExpr.and(ContextKeyExpr.or(ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.equals('isExtensionEnabled', true)), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isExtensionAllowed')),
1415
menu: {
1416
id: MenuId.ExtensionContext,
1417
group: UPDATE_ACTIONS_GROUP,
1418
order: 1,
1419
when: ContextKeyExpr.and(
1420
ContextKeyExpr.not('inExtensionEditor'),
1421
ContextKeyExpr.equals('extensionStatus', 'installed'),
1422
ContextKeyExpr.not('isBuiltinExtension'),
1423
)
1424
},
1425
run: async (accessor: ServicesAccessor, id: string) => {
1426
const instantiationService = accessor.get(IInstantiationService);
1427
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1428
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id }));
1429
if (extension) {
1430
const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction);
1431
action.extension = extension;
1432
return action.run();
1433
}
1434
}
1435
});
1436
1437
this.registerExtensionAction({
1438
id: ToggleAutoUpdatesForPublisherAction.ID,
1439
title: { value: ToggleAutoUpdatesForPublisherAction.LABEL, original: 'Auto Update (Publisher)' },
1440
category: ExtensionsLocalizedLabel,
1441
precondition: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false),
1442
menu: {
1443
id: MenuId.ExtensionContext,
1444
group: UPDATE_ACTIONS_GROUP,
1445
order: 2,
1446
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'))
1447
},
1448
run: async (accessor: ServicesAccessor, id: string) => {
1449
const instantiationService = accessor.get(IInstantiationService);
1450
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1451
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id }));
1452
if (extension) {
1453
const action = instantiationService.createInstance(ToggleAutoUpdatesForPublisherAction);
1454
action.extension = extension;
1455
return action.run();
1456
}
1457
}
1458
});
1459
1460
this.registerExtensionAction({
1461
id: 'workbench.extensions.action.switchToPreRlease',
1462
title: localize('enablePreRleaseLabel', "Switch to Pre-Release Version"),
1463
category: ExtensionsLocalizedLabel,
1464
menu: {
1465
id: MenuId.ExtensionContext,
1466
group: INSTALL_ACTIONS_GROUP,
1467
order: 2,
1468
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'))
1469
},
1470
run: async (accessor: ServicesAccessor, id: string) => {
1471
const instantiationService = accessor.get(IInstantiationService);
1472
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1473
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id }));
1474
if (extension) {
1475
const action = instantiationService.createInstance(TogglePreReleaseExtensionAction);
1476
action.extension = extension;
1477
return action.run();
1478
}
1479
}
1480
});
1481
1482
this.registerExtensionAction({
1483
id: 'workbench.extensions.action.switchToRelease',
1484
title: localize('disablePreRleaseLabel', "Switch to Release Version"),
1485
category: ExtensionsLocalizedLabel,
1486
menu: {
1487
id: MenuId.ExtensionContext,
1488
group: INSTALL_ACTIONS_GROUP,
1489
order: 2,
1490
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'))
1491
},
1492
run: async (accessor: ServicesAccessor, id: string) => {
1493
const instantiationService = accessor.get(IInstantiationService);
1494
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1495
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id }));
1496
if (extension) {
1497
const action = instantiationService.createInstance(TogglePreReleaseExtensionAction);
1498
action.extension = extension;
1499
return action.run();
1500
}
1501
}
1502
});
1503
1504
this.registerExtensionAction({
1505
id: ClearLanguageAction.ID,
1506
title: ClearLanguageAction.TITLE,
1507
menu: {
1508
id: MenuId.ExtensionContext,
1509
group: INSTALL_ACTIONS_GROUP,
1510
order: 0,
1511
when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.has('canSetLanguage'), ContextKeyExpr.has('isActiveLanguagePackExtension'))
1512
},
1513
run: async (accessor: ServicesAccessor, extensionId: string) => {
1514
const instantiationService = accessor.get(IInstantiationService);
1515
const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);
1516
const extension = (await extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0];
1517
const action = instantiationService.createInstance(ClearLanguageAction);
1518
action.extension = extension;
1519
return action.run();
1520
}
1521
});
1522
1523
this.registerExtensionAction({
1524
id: 'workbench.extensions.action.installUnsigned',
1525
title: localize('install', "Install"),
1526
menu: {
1527
id: MenuId.ExtensionContext,
1528
group: '0_install',
1529
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('extensionIsUnsigned'),
1530
ContextKeyExpr.or(ContextKeyExpr.and(CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED, ContextKeyExpr.not('extensionIsPrivate')), ContextKeyExpr.and(CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED, ContextKeyExpr.has('extensionIsPrivate')))),
1531
order: 1
1532
},
1533
run: async (accessor: ServicesAccessor, extensionId: string) => {
1534
const instantiationService = accessor.get(IInstantiationService);
1535
const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0]
1536
|| (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0];
1537
if (extension) {
1538
const action = instantiationService.createInstance(InstallAction, { installPreReleaseVersion: this.extensionManagementService.preferPreReleases });
1539
action.extension = extension;
1540
return action.run();
1541
}
1542
}
1543
});
1544
1545
this.registerExtensionAction({
1546
id: 'workbench.extensions.action.installAndDonotSync',
1547
title: localize('install installAndDonotSync', "Install (Do not Sync)"),
1548
menu: {
1549
id: MenuId.ExtensionContext,
1550
group: '0_install',
1551
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall'), CONTEXT_SYNC_ENABLEMENT),
1552
order: 1
1553
},
1554
run: async (accessor: ServicesAccessor, extensionId: string) => {
1555
const instantiationService = accessor.get(IInstantiationService);
1556
const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0]
1557
|| (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0];
1558
if (extension) {
1559
const action = instantiationService.createInstance(InstallAction, {
1560
installPreReleaseVersion: this.extensionManagementService.preferPreReleases,
1561
isMachineScoped: true,
1562
});
1563
action.extension = extension;
1564
return action.run();
1565
}
1566
}
1567
});
1568
1569
this.registerExtensionAction({
1570
id: 'workbench.extensions.action.installPrereleaseAndDonotSync',
1571
title: localize('installPrereleaseAndDonotSync', "Install Pre-Release (Do not Sync)"),
1572
menu: {
1573
id: MenuId.ExtensionContext,
1574
group: '0_install',
1575
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall'), CONTEXT_SYNC_ENABLEMENT),
1576
order: 2
1577
},
1578
run: async (accessor: ServicesAccessor, extensionId: string) => {
1579
const instantiationService = accessor.get(IInstantiationService);
1580
const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0]
1581
|| (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0];
1582
if (extension) {
1583
const action = instantiationService.createInstance(InstallAction, {
1584
isMachineScoped: true,
1585
preRelease: true
1586
});
1587
action.extension = extension;
1588
return action.run();
1589
}
1590
}
1591
});
1592
1593
this.registerExtensionAction({
1594
id: InstallAnotherVersionAction.ID,
1595
title: InstallAnotherVersionAction.LABEL,
1596
menu: {
1597
id: MenuId.ExtensionContext,
1598
group: '0_install',
1599
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall')),
1600
order: 3
1601
},
1602
run: async (accessor: ServicesAccessor, extensionId: string) => {
1603
const instantiationService = accessor.get(IInstantiationService);
1604
const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0]
1605
|| (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0];
1606
if (extension) {
1607
return instantiationService.createInstance(InstallAnotherVersionAction, extension, false).run();
1608
}
1609
}
1610
});
1611
1612
this.registerExtensionAction({
1613
id: 'workbench.extensions.action.copyExtension',
1614
title: localize2('workbench.extensions.action.copyExtension', 'Copy'),
1615
menu: {
1616
id: MenuId.ExtensionContext,
1617
group: '1_copy'
1618
},
1619
run: async (accessor: ServicesAccessor, extensionId: string) => {
1620
const clipboardService = accessor.get(IClipboardService);
1621
const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0]
1622
|| (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0];
1623
if (extension) {
1624
const name = localize('extensionInfoName', 'Name: {0}', extension.displayName);
1625
const id = localize('extensionInfoId', 'Id: {0}', extensionId);
1626
const description = localize('extensionInfoDescription', 'Description: {0}', extension.description);
1627
const verision = localize('extensionInfoVersion', 'Version: {0}', extension.version);
1628
const publisher = localize('extensionInfoPublisher', 'Publisher: {0}', extension.publisherDisplayName);
1629
const link = extension.url ? localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link: {0}', `${extension.url}`) : null;
1630
const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`;
1631
await clipboardService.writeText(clipboardStr);
1632
}
1633
}
1634
});
1635
1636
this.registerExtensionAction({
1637
id: 'workbench.extensions.action.copyExtensionId',
1638
title: localize2('workbench.extensions.action.copyExtensionId', 'Copy Extension ID'),
1639
menu: {
1640
id: MenuId.ExtensionContext,
1641
group: '1_copy'
1642
},
1643
run: async (accessor: ServicesAccessor, id: string) => accessor.get(IClipboardService).writeText(id)
1644
});
1645
1646
this.registerExtensionAction({
1647
id: 'workbench.extensions.action.copyLink',
1648
title: localize2('workbench.extensions.action.copyLink', 'Copy Link'),
1649
menu: {
1650
id: MenuId.ExtensionContext,
1651
group: '1_copy',
1652
when: ContextKeyExpr.and(ContextKeyExpr.has('isGalleryExtension'), CONTEXT_GALLERY_HAS_EXTENSION_LINK),
1653
},
1654
run: async (accessor: ServicesAccessor, _, extension: IExtensionArg) => {
1655
const clipboardService = accessor.get(IClipboardService);
1656
if (extension.galleryLink) {
1657
await clipboardService.writeText(extension.galleryLink);
1658
}
1659
}
1660
});
1661
1662
this.registerExtensionAction({
1663
id: 'workbench.extensions.action.configure',
1664
title: localize2('workbench.extensions.action.configure', 'Settings'),
1665
menu: {
1666
id: MenuId.ExtensionContext,
1667
group: '2_configure',
1668
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')),
1669
order: 1
1670
},
1671
run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openSettings({ jsonEditor: false, query: `@ext:${id}` })
1672
});
1673
1674
this.registerExtensionAction({
1675
id: 'workbench.extensions.action.download',
1676
title: localize('download VSIX', "Download VSIX"),
1677
menu: {
1678
id: MenuId.ExtensionContext,
1679
when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')),
1680
order: this.productService.quality === 'stable' ? 0 : 1
1681
},
1682
run: async (accessor: ServicesAccessor, extensionId: string) => {
1683
accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'release');
1684
}
1685
});
1686
1687
this.registerExtensionAction({
1688
id: 'workbench.extensions.action.downloadPreRelease',
1689
title: localize('download pre-release', "Download Pre-Release VSIX"),
1690
menu: {
1691
id: MenuId.ExtensionContext,
1692
when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion')),
1693
order: this.productService.quality === 'stable' ? 1 : 0
1694
},
1695
run: async (accessor: ServicesAccessor, extensionId: string) => {
1696
accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'prerelease');
1697
}
1698
});
1699
1700
this.registerExtensionAction({
1701
id: 'workbench.extensions.action.downloadSpecificVersion',
1702
title: localize('download specific version', "Download Specific Version VSIX..."),
1703
menu: {
1704
id: MenuId.ExtensionContext,
1705
when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')),
1706
order: 2
1707
},
1708
run: async (accessor: ServicesAccessor, extensionId: string) => {
1709
accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'any');
1710
}
1711
});
1712
1713
this.registerExtensionAction({
1714
id: 'workbench.extensions.action.manageAccountPreferences',
1715
title: localize2('workbench.extensions.action.changeAccountPreference', "Account Preferences"),
1716
menu: {
1717
id: MenuId.ExtensionContext,
1718
group: '2_configure',
1719
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasAccountPreferences')),
1720
order: 2,
1721
},
1722
run: (accessor: ServicesAccessor, id: string) => accessor.get(ICommandService).executeCommand('_manageAccountPreferencesForExtension', id)
1723
});
1724
1725
this.registerExtensionAction({
1726
id: 'workbench.extensions.action.configureKeybindings',
1727
title: localize2('workbench.extensions.action.configureKeybindings', 'Keyboard Shortcuts'),
1728
menu: {
1729
id: MenuId.ExtensionContext,
1730
group: '2_configure',
1731
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasKeybindings')),
1732
order: 2
1733
},
1734
run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query: `@ext:${id}` })
1735
});
1736
1737
this.registerExtensionAction({
1738
id: 'workbench.extensions.action.toggleApplyToAllProfiles',
1739
title: localize2('workbench.extensions.action.toggleApplyToAllProfiles', "Apply Extension to all Profiles"),
1740
toggled: ContextKeyExpr.has('isApplicationScopedExtension'),
1741
menu: {
1742
id: MenuId.ExtensionContext,
1743
group: '2_configure',
1744
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('isDefaultApplicationScopedExtension').negate(), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.equals('isWorkspaceScopedExtension', false)),
1745
order: 3
1746
},
1747
run: async (accessor: ServicesAccessor, _: string, extensionArg: IExtensionArg) => {
1748
const uriIdentityService = accessor.get(IUriIdentityService);
1749
const extension = extensionArg.location ? this.extensionsWorkbenchService.installed.find(e => uriIdentityService.extUri.isEqual(e.local?.location, extensionArg.location)) : undefined;
1750
if (extension) {
1751
return this.extensionsWorkbenchService.toggleApplyExtensionToAllProfiles(extension);
1752
}
1753
}
1754
});
1755
1756
this.registerExtensionAction({
1757
id: TOGGLE_IGNORE_EXTENSION_ACTION_ID,
1758
title: localize2('workbench.extensions.action.toggleIgnoreExtension', "Sync This Extension"),
1759
menu: {
1760
id: MenuId.ExtensionContext,
1761
group: '2_configure',
1762
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), CONTEXT_SYNC_ENABLEMENT, ContextKeyExpr.equals('isWorkspaceScopedExtension', false)),
1763
order: 4
1764
},
1765
run: async (accessor: ServicesAccessor, id: string) => {
1766
const extension = this.extensionsWorkbenchService.local.find(e => areSameExtensions({ id }, e.identifier));
1767
if (extension) {
1768
return this.extensionsWorkbenchService.toggleExtensionIgnoredToSync(extension);
1769
}
1770
}
1771
});
1772
1773
this.registerExtensionAction({
1774
id: 'workbench.extensions.action.ignoreRecommendation',
1775
title: localize2('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"),
1776
menu: {
1777
id: MenuId.ExtensionContext,
1778
group: '3_recommendations',
1779
when: ContextKeyExpr.has('isExtensionRecommended'),
1780
order: 1
1781
},
1782
run: async (accessor: ServicesAccessor, id: string) => accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, true)
1783
});
1784
1785
this.registerExtensionAction({
1786
id: 'workbench.extensions.action.undoIgnoredRecommendation',
1787
title: localize2('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"),
1788
menu: {
1789
id: MenuId.ExtensionContext,
1790
group: '3_recommendations',
1791
when: ContextKeyExpr.has('isUserIgnoredRecommendation'),
1792
order: 1
1793
},
1794
run: async (accessor: ServicesAccessor, id: string) => accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, false)
1795
});
1796
1797
this.registerExtensionAction({
1798
id: 'workbench.extensions.action.addExtensionToWorkspaceRecommendations',
1799
title: localize2('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"),
1800
menu: {
1801
id: MenuId.ExtensionContext,
1802
group: '3_recommendations',
1803
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate(), ContextKeyExpr.notEquals('extensionSource', 'resource')),
1804
order: 2
1805
},
1806
run: (accessor: ServicesAccessor, id: string) => accessor.get(IWorkspaceExtensionsConfigService).toggleRecommendation(id)
1807
});
1808
1809
this.registerExtensionAction({
1810
id: 'workbench.extensions.action.removeExtensionFromWorkspaceRecommendations',
1811
title: localize2('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"),
1812
menu: {
1813
id: MenuId.ExtensionContext,
1814
group: '3_recommendations',
1815
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended')),
1816
order: 2
1817
},
1818
run: (accessor: ServicesAccessor, id: string) => accessor.get(IWorkspaceExtensionsConfigService).toggleRecommendation(id)
1819
});
1820
1821
this.registerExtensionAction({
1822
id: 'workbench.extensions.action.addToWorkspaceRecommendations',
1823
title: localize2('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"),
1824
category: EXTENSIONS_CATEGORY,
1825
menu: {
1826
id: MenuId.CommandPalette,
1827
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)),
1828
},
1829
async run(accessor: ServicesAccessor): Promise<any> {
1830
const editorService = accessor.get(IEditorService);
1831
const workspaceExtensionsConfigService = accessor.get(IWorkspaceExtensionsConfigService);
1832
if (!(editorService.activeEditor instanceof ExtensionsInput)) {
1833
return;
1834
}
1835
const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase();
1836
const recommendations = await workspaceExtensionsConfigService.getRecommendations();
1837
if (recommendations.includes(extensionId)) {
1838
return;
1839
}
1840
await workspaceExtensionsConfigService.toggleRecommendation(extensionId);
1841
}
1842
});
1843
1844
this.registerExtensionAction({
1845
id: 'workbench.extensions.action.addToWorkspaceFolderRecommendations',
1846
title: localize2('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"),
1847
category: EXTENSIONS_CATEGORY,
1848
menu: {
1849
id: MenuId.CommandPalette,
1850
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)),
1851
},
1852
run: () => this.commandService.executeCommand('workbench.extensions.action.addToWorkspaceRecommendations')
1853
});
1854
1855
this.registerExtensionAction({
1856
id: 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations',
1857
title: localize2('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"),
1858
category: EXTENSIONS_CATEGORY,
1859
menu: {
1860
id: MenuId.CommandPalette,
1861
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)),
1862
},
1863
async run(accessor: ServicesAccessor): Promise<any> {
1864
const editorService = accessor.get(IEditorService);
1865
const workspaceExtensionsConfigService = accessor.get(IWorkspaceExtensionsConfigService);
1866
if (!(editorService.activeEditor instanceof ExtensionsInput)) {
1867
return;
1868
}
1869
const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase();
1870
const unwantedRecommendations = await workspaceExtensionsConfigService.getUnwantedRecommendations();
1871
if (unwantedRecommendations.includes(extensionId)) {
1872
return;
1873
}
1874
await workspaceExtensionsConfigService.toggleUnwantedRecommendation(extensionId);
1875
}
1876
});
1877
1878
this.registerExtensionAction({
1879
id: 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations',
1880
title: localize2('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"),
1881
category: EXTENSIONS_CATEGORY,
1882
menu: {
1883
id: MenuId.CommandPalette,
1884
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)),
1885
},
1886
run: () => this.commandService.executeCommand('workbench.extensions.action.addToWorkspaceIgnoredRecommendations')
1887
});
1888
1889
this.registerExtensionAction({
1890
id: ConfigureWorkspaceRecommendedExtensionsAction.ID,
1891
title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' },
1892
category: EXTENSIONS_CATEGORY,
1893
menu: {
1894
id: MenuId.CommandPalette,
1895
when: WorkbenchStateContext.isEqualTo('workspace'),
1896
},
1897
run: () => runAction(this.instantiationService.createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL))
1898
});
1899
1900
this.registerExtensionAction({
1901
id: 'workbench.extensions.action.manageTrustedPublishers',
1902
title: localize2('workbench.extensions.action.manageTrustedPublishers', "Manage Trusted Extension Publishers"),
1903
category: EXTENSIONS_CATEGORY,
1904
f1: true,
1905
run: async (accessor: ServicesAccessor) => {
1906
const quickInputService = accessor.get(IQuickInputService);
1907
const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService);
1908
const trustedPublishers = extensionManagementService.getTrustedPublishers();
1909
const trustedPublisherItems = trustedPublishers.map(publisher => ({
1910
id: publisher.publisher,
1911
label: publisher.publisherDisplayName,
1912
description: publisher.publisher,
1913
picked: true,
1914
})).sort((a, b) => a.label.localeCompare(b.label));
1915
const result = await quickInputService.pick(trustedPublisherItems, {
1916
canPickMany: true,
1917
title: localize('trustedPublishers', "Manage Trusted Extension Publishers"),
1918
placeHolder: localize('trustedPublishersPlaceholder', "Choose which publishers to trust"),
1919
});
1920
if (result) {
1921
const untrustedPublishers = [];
1922
for (const { publisher } of trustedPublishers) {
1923
if (!result.some(r => r.id === publisher)) {
1924
untrustedPublishers.push(publisher);
1925
}
1926
}
1927
trustedPublishers.filter(publisher => !result.some(r => r.id === publisher.publisher));
1928
extensionManagementService.untrustPublishers(...untrustedPublishers);
1929
}
1930
}
1931
});
1932
1933
}
1934
1935
private registerExtensionAction(extensionActionOptions: IExtensionActionOptions): IDisposable {
1936
const menus = extensionActionOptions.menu ? Array.isArray(extensionActionOptions.menu) ? extensionActionOptions.menu : [extensionActionOptions.menu] : [];
1937
let menusWithOutTitles: ({ id: MenuId } & Omit<IMenuItem, 'command'>)[] = [];
1938
const menusWithTitles: { id: MenuId; item: IMenuItem }[] = [];
1939
if (extensionActionOptions.menuTitles) {
1940
for (let index = 0; index < menus.length; index++) {
1941
const menu = menus[index];
1942
const menuTitle = extensionActionOptions.menuTitles[menu.id.id];
1943
if (menuTitle) {
1944
menusWithTitles.push({ id: menu.id, item: { ...menu, command: { id: extensionActionOptions.id, title: menuTitle } } });
1945
} else {
1946
menusWithOutTitles.push(menu);
1947
}
1948
}
1949
} else {
1950
menusWithOutTitles = menus;
1951
}
1952
const disposables = new DisposableStore();
1953
disposables.add(registerAction2(class extends Action2 {
1954
constructor() {
1955
super({
1956
...extensionActionOptions,
1957
menu: menusWithOutTitles
1958
});
1959
}
1960
run(accessor: ServicesAccessor, ...args: unknown[]): Promise<any> {
1961
return extensionActionOptions.run(accessor, ...args);
1962
}
1963
}));
1964
if (menusWithTitles.length) {
1965
disposables.add(MenuRegistry.appendMenuItems(menusWithTitles));
1966
}
1967
return disposables;
1968
}
1969
1970
}
1971
1972
class ExtensionStorageCleaner implements IWorkbenchContribution {
1973
1974
constructor(
1975
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
1976
@IStorageService storageService: IStorageService,
1977
) {
1978
ExtensionStorageService.removeOutdatedExtensionVersions(extensionManagementService, storageService);
1979
}
1980
}
1981
1982
class TrustedPublishersInitializer implements IWorkbenchContribution {
1983
constructor(
1984
@IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService,
1985
@IUserDataProfilesService userDataProfilesService: IUserDataProfilesService,
1986
@IProductService productService: IProductService,
1987
@IStorageService storageService: IStorageService,
1988
) {
1989
const trustedPublishersInitStatusKey = 'trusted-publishers-init-migration';
1990
if (!storageService.get(trustedPublishersInitStatusKey, StorageScope.APPLICATION)) {
1991
for (const profile of userDataProfilesService.profiles) {
1992
extensionManagementService.getInstalled(ExtensionType.User, profile.extensionsResource)
1993
.then(async extensions => {
1994
const trustedPublishers = new Map<string, IPublisherInfo>();
1995
for (const extension of extensions) {
1996
if (!extension.publisherDisplayName) {
1997
continue;
1998
}
1999
const publisher = extension.manifest.publisher.toLowerCase();
2000
if (productService.trustedExtensionPublishers?.includes(publisher)
2001
|| (extension.publisherDisplayName && productService.trustedExtensionPublishers?.includes(extension.publisherDisplayName.toLowerCase()))) {
2002
continue;
2003
}
2004
trustedPublishers.set(publisher, { publisher, publisherDisplayName: extension.publisherDisplayName });
2005
}
2006
if (trustedPublishers.size) {
2007
extensionManagementService.trustPublishers(...trustedPublishers.values());
2008
}
2009
storageService.store(trustedPublishersInitStatusKey, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE);
2010
});
2011
}
2012
}
2013
}
2014
}
2015
2016
class ExtensionToolsContribution extends Disposable implements IWorkbenchContribution {
2017
2018
static readonly ID = 'extensions.chat.toolsContribution';
2019
2020
constructor(
2021
@ILanguageModelToolsService toolsService: ILanguageModelToolsService,
2022
@IInstantiationService instantiationService: IInstantiationService,
2023
) {
2024
super();
2025
const searchExtensionsTool = instantiationService.createInstance(SearchExtensionsTool);
2026
this._register(toolsService.registerTool(SearchExtensionsToolData, searchExtensionsTool));
2027
this._register(toolsService.vscodeToolSet.addTool(SearchExtensionsToolData));
2028
}
2029
}
2030
2031
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
2032
workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Restored);
2033
workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Eventually);
2034
workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, LifecyclePhase.Eventually);
2035
workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Restored);
2036
workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Restored);
2037
workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually);
2038
workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually);
2039
workbenchRegistry.registerWorkbenchContribution(ExtensionEnablementWorkspaceTrustTransitionParticipant, LifecyclePhase.Restored);
2040
workbenchRegistry.registerWorkbenchContribution(ExtensionsCompletionItemsProvider, LifecyclePhase.Restored);
2041
workbenchRegistry.registerWorkbenchContribution(UnsupportedExtensionsMigrationContrib, LifecyclePhase.Eventually);
2042
workbenchRegistry.registerWorkbenchContribution(TrustedPublishersInitializer, LifecyclePhase.Eventually);
2043
workbenchRegistry.registerWorkbenchContribution(ExtensionMarketplaceStatusUpdater, LifecyclePhase.Eventually);
2044
if (isWeb) {
2045
workbenchRegistry.registerWorkbenchContribution(ExtensionStorageCleaner, LifecyclePhase.Eventually);
2046
}
2047
2048
registerWorkbenchContribution2(ExtensionToolsContribution.ID, ExtensionToolsContribution, WorkbenchPhase.AfterRestored);
2049
2050
2051
// Running Extensions
2052
registerAction2(ShowRuntimeExtensionsAction);
2053
2054
registerAction2(class ExtensionsGallerySignInAction extends Action2 {
2055
constructor() {
2056
super({
2057
id: 'workbench.extensions.actions.gallery.signIn',
2058
title: localize2('signInToMarketplace', 'Sign in to access Extensions Marketplace'),
2059
menu: {
2060
id: MenuId.AccountsContext,
2061
when: CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.RequiresSignIn)
2062
},
2063
});
2064
}
2065
run(accessor: ServicesAccessor): Promise<void> {
2066
return accessor.get(ICommandService).executeCommand(DEFAULT_ACCOUNT_SIGN_IN_COMMAND);
2067
}
2068
});
2069
2070
Registry.as<IConfigurationMigrationRegistry>(ConfigurationMigrationExtensions.ConfigurationMigration)
2071
.registerConfigurationMigrations([{
2072
key: AutoUpdateConfigurationKey,
2073
migrateFn: (value, accessor) => {
2074
if (value === 'onlySelectedExtensions') {
2075
return { value: false };
2076
}
2077
return [];
2078
}
2079
}]);
2080
2081