in patched-vscode/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts [246:724]
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."),
}
}
}
}
},
'extensions.experimental.deferredStartupFinishedActivation': {
type: 'boolean',
description: localize('extensionsDeferredStartupFinishedActivation', "When enabled, extensions which declare the `onStartupFinished` activation event will be activated after a timeout."),
default: false
},
'extensions.experimental.issueQuickAccess': {
type: 'boolean',
description: localize('extensionsInQuickAccess', "When enabled, extensions can be searched for via Quick Access and report issues from there."),
default: true
},
'extensions.openNotebookData': {
type: 'object',
scope: ConfigurationScope.APPLICATION,
default: {},
},
}
});
const jsonRegistry = <jsonContributionRegistry.IJSONContributionRegistry>Registry.as(jsonContributionRegistry.Extensions.JSONContribution);
jsonRegistry.registerSchema(ExtensionsConfigurationSchemaId, ExtensionsConfigurationSchema);
// Register Commands
CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean, feature?: string) => {
const extensionService = accessor.get(IExtensionsWorkbenchService);
const extension = extensionService.local.find(e => areSameExtensions(e.identifier, { id: extensionId }));
if (extension) {
extensionService.open(extension, { tab, preserveFocus, feature });
} else {
throw new Error(localize('notFound', "Extension '{0}' not found.", extensionId));
}
});
CommandsRegistry.registerCommand('extension.open', async (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean, feature?: string, sideByside?: boolean) => {
const extensionService = accessor.get(IExtensionsWorkbenchService);
const commandService = accessor.get(ICommandService);
const [extension] = await extensionService.getExtensions([{ id: extensionId }], CancellationToken.None);
if (extension) {
return extensionService.open(extension, { tab, preserveFocus, feature, sideByside });
}
return commandService.executeCommand('_extensions.manage', extensionId, tab, preserveFocus, feature);
});
CommandsRegistry.registerCommand({
id: 'workbench.extensions.installExtension',
metadata: {
description: localize('workbench.extensions.installExtension.description', "Install the given extension"),
args: [
{
name: 'extensionIdOrVSIXUri',
description: localize('workbench.extensions.installExtension.arg.decription', "Extension id or VSIX resource uri"),
constraint: (value: any) => typeof value === 'string' || value instanceof URI,
},
{
name: 'options',
description: '(optional) Options for installing the extension. Object with the following properties: ' +
'`installOnlyNewlyAddedFromExtensionPackVSIX`: When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only when installing VSIX. ',
isOptional: true,
schema: {
'type': 'object',
'properties': {
'installOnlyNewlyAddedFromExtensionPackVSIX': {
'type': 'boolean',
'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."),
default: false
},
'installPreReleaseVersion': {
'type': 'boolean',
'description': localize('workbench.extensions.installExtension.option.installPreReleaseVersion', "When enabled, VS Code installs the pre-release version of the extension if available."),
default: false
},
'donotSync': {
'type': 'boolean',
'description': localize('workbench.extensions.installExtension.option.donotSync', "When enabled, VS Code do not sync this extension when Settings Sync is on."),
default: false
},
'justification': {
'type': ['string', 'object'],
'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."),
},
'enable': {
'type': 'boolean',
'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."),
default: false
},
'context': {
'type': 'object',
'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."),
}
}
}
}
]
},
handler: async (
accessor,
arg: string | UriComponents,
options?: {
installOnlyNewlyAddedFromExtensionPackVSIX?: boolean;
installPreReleaseVersion?: boolean;
donotSync?: boolean;
justification?: string | { reason: string; action: string };
enable?: boolean;
context?: IStringDictionary<any>;
}) => {
const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);
const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService);
const extensionGalleryService = accessor.get(IExtensionGalleryService);
try {
if (typeof arg === 'string') {
const [id, version] = getIdAndVersion(arg);
const extension = extensionsWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id, uuid: version }));
if (extension?.enablementState === EnablementState.DisabledByExtensionKind) {
const [gallery] = await extensionGalleryService.getExtensions([{ id, preRelease: options?.installPreReleaseVersion }], CancellationToken.None);
if (!gallery) {
throw new Error(localize('notFound', "Extension '{0}' not found.", arg));
}
await extensionManagementService.installFromGallery(gallery, {
isMachineScoped: options?.donotSync ? true : undefined, /* do not allow syncing extensions automatically while installing through the command */
installPreReleaseVersion: options?.installPreReleaseVersion,
installGivenVersion: !!version,
context: { ...options?.context, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.COMMAND },
});
} else {
await extensionsWorkbenchService.install(arg, {
version,
installPreReleaseVersion: options?.installPreReleaseVersion,
context: { ...options?.context, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.COMMAND },
justification: options?.justification,
enable: options?.enable,
isMachineScoped: options?.donotSync ? true : undefined, /* do not allow syncing extensions automatically while installing through the command */
}, ProgressLocation.Notification);
}
} else {
const vsix = URI.revive(arg);
await extensionsWorkbenchService.install(vsix, { installOnlyNewlyAddedFromExtensionPack: options?.installOnlyNewlyAddedFromExtensionPackVSIX });
}
} catch (e) {
onUnexpectedError(e);
throw e;
}
}
});
CommandsRegistry.registerCommand({
id: 'workbench.extensions.uninstallExtension',
metadata: {
description: localize('workbench.extensions.uninstallExtension.description', "Uninstall the given extension"),
args: [
{
name: localize('workbench.extensions.uninstallExtension.arg.name', "Id of the extension to uninstall"),
schema: {
'type': 'string'
}
}
]
},
handler: async (accessor, id: string) => {
if (!id) {
throw new Error(localize('id required', "Extension id required."));
}
const extensionManagementService = accessor.get(IExtensionManagementService);
const installed = await extensionManagementService.getInstalled();
const [extensionToUninstall] = installed.filter(e => areSameExtensions(e.identifier, { id }));
if (!extensionToUninstall) {
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));
}
if (extensionToUninstall.isBuiltin) {
throw new Error(localize('builtin', "Extension '{0}' is a Built-in extension and cannot be installed", id));
}
try {
await extensionManagementService.uninstall(extensionToUninstall);
} catch (e) {
onUnexpectedError(e);
throw e;
}
}
});
CommandsRegistry.registerCommand({
id: 'workbench.extensions.search',
metadata: {
description: localize('workbench.extensions.search.description', "Search for a specific extension"),
args: [
{
name: localize('workbench.extensions.search.arg.name', "Query to use in search"),
schema: { 'type': 'string' }
}
]
},
handler: async (accessor, query: string = '') => {
const paneCompositeService = accessor.get(IPaneCompositePartService);
const viewlet = await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true);
if (!viewlet) {
return;
}
(viewlet.getViewPaneContainer() as IExtensionsViewPaneContainer).search(query);
viewlet.focus();
}
});
function overrideActionForActiveExtensionEditorWebview(command: MultiCommand | undefined, f: (webview: IWebview) => void) {
command?.addImplementation(105, 'extensions-editor', (accessor) => {
const editorService = accessor.get(IEditorService);
const editor = editorService.activeEditorPane;
if (editor instanceof ExtensionEditor) {
if (editor.activeWebview?.isFocused) {
f(editor.activeWebview);
return true;
}
}
return false;
});
}
overrideActionForActiveExtensionEditorWebview(CopyAction, webview => webview.copy());
overrideActionForActiveExtensionEditorWebview(CutAction, webview => webview.cut());
overrideActionForActiveExtensionEditorWebview(PasteAction, webview => webview.paste());
// Contexts
export const CONTEXT_HAS_LOCAL_SERVER = new RawContextKey<boolean>('hasLocalServer', false);
export const CONTEXT_HAS_REMOTE_SERVER = new RawContextKey<boolean>('hasRemoteServer', false);
export const CONTEXT_HAS_WEB_SERVER = new RawContextKey<boolean>('hasWebServer', false);
async function runAction(action: IAction): Promise<void> {
try {
await action.run();
} finally {
if (isDisposable(action)) {
action.dispose();
}
}
}
type IExtensionActionOptions = IAction2Options & {
menuTitles?: { [id: string]: string };
run(accessor: ServicesAccessor, ...args: any[]): Promise<any>;
};
class ExtensionsContributions extends Disposable implements IWorkbenchContribution {
constructor(
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
@IContextKeyService contextKeyService: IContextKeyService,
@IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IDialogService private readonly dialogService: IDialogService,
@ICommandService private readonly commandService: ICommandService,
) {
super();
const hasGalleryContext = CONTEXT_HAS_GALLERY.bindTo(contextKeyService);
if (extensionGalleryService.isEnabled()) {
hasGalleryContext.set(true);
}
const hasLocalServerContext = CONTEXT_HAS_LOCAL_SERVER.bindTo(contextKeyService);
if (this.extensionManagementServerService.localExtensionManagementServer) {
hasLocalServerContext.set(true);
}
const hasRemoteServerContext = CONTEXT_HAS_REMOTE_SERVER.bindTo(contextKeyService);
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
hasRemoteServerContext.set(true);
}
const hasWebServerContext = CONTEXT_HAS_WEB_SERVER.bindTo(contextKeyService);
if (this.extensionManagementServerService.webExtensionManagementServer) {
hasWebServerContext.set(true);
}
this.registerGlobalActions();
this.registerContextMenuActions();
this.registerQuickAccessProvider();
}
private registerQuickAccessProvider(): void {
if (this.extensionManagementServerService.localExtensionManagementServer
|| this.extensionManagementServerService.remoteExtensionManagementServer
|| this.extensionManagementServerService.webExtensionManagementServer
) {
Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessProvider({
ctor: InstallExtensionQuickAccessProvider,
prefix: InstallExtensionQuickAccessProvider.PREFIX,
placeholder: localize('installExtensionQuickAccessPlaceholder', "Type the name of an extension to install or search."),
helpEntries: [{ description: localize('installExtensionQuickAccessHelp', "Install or Search Extensions") }]
});
}
}
// Global actions
private registerGlobalActions(): void {
this._register(MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
command: {
id: VIEWLET_ID,
title: localize({ key: 'miPreferencesExtensions', comment: ['&& denotes a mnemonic'] }, "&&Extensions")
},
group: '2_configuration',
order: 3
}));
this._register(MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
command: {
id: VIEWLET_ID,
title: localize('showExtensions', "Extensions")
},
group: '2_configuration',
order: 3
}));
this.registerExtensionAction({
id: 'workbench.extensions.action.focusExtensionsView',
title: localize2('focusExtensions', 'Focus on Extensions View'),
category: ExtensionsLocalizedLabel,
f1: true,
run: async (accessor: ServicesAccessor) => {
await accessor.get(IPaneCompositePartService).openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true);
}
});
this.registerExtensionAction({
id: 'workbench.extensions.action.installExtensions',
title: localize2('installExtensions', 'Install Extensions'),
category: ExtensionsLocalizedLabel,
menu: {
id: MenuId.CommandPalette,
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
},
run: async (accessor: ServicesAccessor) => {
accessor.get(IViewsService).openViewContainer(VIEWLET_ID, true);
}
});
this.registerExtensionAction({
id: 'workbench.extensions.action.showRecommendedKeymapExtensions',
title: localize2('showRecommendedKeymapExtensionsShort', 'Keymaps'),
category: PreferencesLocalizedLabel,
menu: [{
id: MenuId.CommandPalette,
when: CONTEXT_HAS_GALLERY
}, {
id: MenuId.EditorTitle,
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_HAS_GALLERY),
group: '2_keyboard_discover_actions'
}],
menuTitles: {
[MenuId.EditorTitle.id]: localize('importKeyboardShortcutsFroms', "Migrate Keyboard Shortcuts from...")
},
run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recommended:keymaps '))
});
this.registerExtensionAction({
id: 'workbench.extensions.action.showLanguageExtensions',
title: localize2('showLanguageExtensionsShort', 'Language Extensions'),
category: PreferencesLocalizedLabel,
menu: {
id: MenuId.CommandPalette,
when: CONTEXT_HAS_GALLERY
},
run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recommended:languages '))
});
this.registerExtensionAction({
id: 'workbench.extensions.action.checkForUpdates',
title: localize2('checkForUpdates', 'Check for Extension Updates'),
category: ExtensionsLocalizedLabel,
menu: [{
id: MenuId.CommandPalette,
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
}, {
id: MenuId.ViewContainerTitle,
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY),
group: '1_updates',
order: 1
}],
run: async () => {
await this.extensionsWorkbenchService.checkForUpdates();
const outdated = this.extensionsWorkbenchService.outdated;
if (outdated.length) {
return runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@outdated '));
} else {
return this.dialogService.info(localize('noUpdatesAvailable', "All extensions are up to date."));
}
}
});
const autoUpdateExtensionsSubMenu = new MenuId('autoUpdateExtensionsSubMenu');
MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, {
submenu: autoUpdateExtensionsSubMenu,
title: localize('configure auto updating extensions', "Auto Update Extensions"),
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY),
group: '1_updates',
order: 5,
});
this.registerExtensionAction({
id: 'configureExtensionsAutoUpdate.all',
title: localize('configureExtensionsAutoUpdate.all', "All Extensions"),
toggled: ContextKeyExpr.and(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions')),
menu: [{
id: autoUpdateExtensionsSubMenu,
order: 1,
}],
run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, true)
});
this.registerExtensionAction({
id: 'configureExtensionsAutoUpdate.enabled',
title: localize('configureExtensionsAutoUpdate.enabled', "Enabled Extensions"),
toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'),
menu: [{
id: autoUpdateExtensionsSubMenu,
order: 2,
}],
run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlyEnabledExtensions')
});
this.registerExtensionAction({
id: 'configureExtensionsAutoUpdate.selected',
title: localize('configureExtensionsAutoUpdate.selected', "Selected Extensions"),
toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'),
menu: [{
id: autoUpdateExtensionsSubMenu,
order: 2,
}],
run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlySelectedExtensions')
});
this.registerExtensionAction({
id: 'configureExtensionsAutoUpdate.none',
title: localize('configureExtensionsAutoUpdate.none', "None"),
toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false),
menu: [{
id: autoUpdateExtensionsSubMenu,
order: 3,
}],
run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, false)
});
this.registerExtensionAction({
id: 'workbench.extensions.action.updateAllExtensions',
title: localize2('updateAll', 'Update All Extensions'),
category: ExtensionsLocalizedLabel,
precondition: HasOutdatedExtensionsContext,
menu: [
{
id: MenuId.CommandPalette,
when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER))
}, {
id: MenuId.ViewContainerTitle,
when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`).negate(), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'))),
group: '1_updates',
order: 2
}, {
id: MenuId.ViewTitle,
when: ContextKeyExpr.equals('view', OUTDATED_EXTENSIONS_VIEW_ID),
group: 'navigation',
order: 1
}
],
icon: installWorkspaceRecommendedIcon,
run: async () => {
const outdated = this.extensionsWorkbenchService.outdated;
const results = await this.extensionsWorkbenchService.updateAll();
results.forEach((result) => {
if (result.error) {
const extension: IExtension | undefined = outdated.find((extension) => areSameExtensions(extension.identifier, result.identifier));
if (extension) {