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