mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-12 17:48:19 +01:00
Add explorer context menu to install an extension from a VSIX (#108787)
This commit is contained in:
@@ -14,7 +14,7 @@ import { IExtensionRecommendationsService } from 'vs/workbench/services/extensio
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import {
|
||||
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
|
||||
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction,
|
||||
@@ -57,6 +57,10 @@ import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
|
||||
import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService';
|
||||
import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
|
||||
// Singletons
|
||||
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
|
||||
@@ -74,6 +78,16 @@ Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessPro
|
||||
helpEntries: [{ description: localize('manageExtensionsHelp', "Manage Extensions"), needsEditor: false }]
|
||||
});
|
||||
|
||||
// Explorer
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
group: '4_extensions',
|
||||
command: {
|
||||
id: INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID,
|
||||
title: localize('installVSIX', "Install VSIX"),
|
||||
},
|
||||
when: ResourceContextKey.Extension.isEqualTo('.vsix')
|
||||
});
|
||||
|
||||
// Editor
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
EditorDescriptor.create(
|
||||
@@ -206,6 +220,45 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID,
|
||||
handler: async (accessor: ServicesAccessor, resources: URI[] | URI) => {
|
||||
const extensionService = accessor.get(IExtensionService);
|
||||
const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const hostService = accessor.get(IHostService);
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
const viewlet = await viewletService.openViewlet(VIEWLET_ID, true);
|
||||
|
||||
if (!viewlet) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extensions = Array.isArray(resources) ? resources : [resources];
|
||||
await Promise.all(extensions.map(async (vsix) => await extensionsWorkbenchService.install(vsix)))
|
||||
.then(async (extensions) => {
|
||||
for (const extension of extensions) {
|
||||
const requireReload = !(extension.local && extensionService.canAddExtension(toExtensionDescription(extension.local)));
|
||||
const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.displayName || extension.name)
|
||||
: localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.displayName || extension.name);
|
||||
const actions = requireReload ? [{
|
||||
label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
|
||||
run: () => hostService.reload()
|
||||
}] : [];
|
||||
notificationService.prompt(
|
||||
Severity.Info,
|
||||
message,
|
||||
actions,
|
||||
{ sticky: true }
|
||||
);
|
||||
}
|
||||
await instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'workbench.extensions.uninstallExtension',
|
||||
description: {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import * as json from 'vs/base/common/json';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
|
||||
import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
@@ -3007,50 +3007,26 @@ export class InstallVSIXAction extends Action {
|
||||
constructor(
|
||||
id = InstallVSIXAction.ID,
|
||||
label = InstallVSIXAction.LABEL,
|
||||
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@IFileDialogService private readonly fileDialogService: IFileDialogService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, 'extension-action install-vsix', true);
|
||||
}
|
||||
|
||||
async run(vsixPaths?: URI[]): Promise<void> {
|
||||
if (!vsixPaths) {
|
||||
vsixPaths = await this.fileDialogService.showOpenDialog({
|
||||
title: localize('installFromVSIX', "Install from VSIX"),
|
||||
filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }],
|
||||
canSelectFiles: true,
|
||||
openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install"))
|
||||
});
|
||||
async run(): Promise<void> {
|
||||
const vsixPaths = await this.fileDialogService.showOpenDialog({
|
||||
title: localize('installFromVSIX', "Install from VSIX"),
|
||||
filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }],
|
||||
canSelectFiles: true,
|
||||
openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install"))
|
||||
});
|
||||
|
||||
if (!vsixPaths) {
|
||||
return;
|
||||
}
|
||||
if (!vsixPaths) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Install extension(s), display notification(s), display @installed extensions
|
||||
await Promise.all(vsixPaths.map(async (vsix) => await this.extensionsWorkbenchService.install(vsix)))
|
||||
.then(async (extensions) => {
|
||||
for (const extension of extensions) {
|
||||
const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
|
||||
const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.displayName || extension.name)
|
||||
: localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.displayName || extension.name);
|
||||
const actions = requireReload ? [{
|
||||
label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
|
||||
run: () => this.hostService.reload()
|
||||
}] : [];
|
||||
this.notificationService.prompt(
|
||||
Severity.Info,
|
||||
message,
|
||||
actions,
|
||||
{ sticky: true }
|
||||
);
|
||||
}
|
||||
await this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run(true);
|
||||
});
|
||||
await this.commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { append, $, Dimension, hide, show } from 'vs/base/browser/dom';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, CloseExtensionDetailsOnViewChangeKey } from '../common/extensions';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, CloseExtensionDetailsOnViewChangeKey, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from '../common/extensions';
|
||||
import {
|
||||
ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
|
||||
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, SearchCategoryAction,
|
||||
@@ -60,6 +60,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
|
||||
const DefaultViewsContext = new RawContextKey<boolean>('defaultExtensionViews', true);
|
||||
const SearchMarketplaceExtensionsContext = new RawContextKey<boolean>('searchMarketplaceExtensions', false);
|
||||
@@ -364,6 +365,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
|
||||
@IPreferencesService private readonly preferencesService: IPreferencesService,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
|
||||
|
||||
@@ -475,7 +477,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
|
||||
|
||||
try {
|
||||
// Attempt to install the extension(s)
|
||||
await this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL).run(vsixPaths);
|
||||
await this.commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths);
|
||||
}
|
||||
catch (err) {
|
||||
this.notificationService.error(err);
|
||||
|
||||
@@ -145,3 +145,4 @@ export class ExtensionContainers extends Disposable {
|
||||
}
|
||||
|
||||
export const TOGGLE_IGNORE_EXTENSION_ACTION_ID = 'workbench.extensions.action.toggleIgnoreExtension';
|
||||
export const INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID = 'workbench.extensions.command.installFromVSIX';
|
||||
|
||||
Reference in New Issue
Block a user