diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index dcc07b01e51..205ca37d121 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -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(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(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: { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 0f54df948fc..8ef92310fcb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -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 { - 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 { + 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); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 13d923aefba..68731f3aed6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -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('defaultExtensionViews', true); const SearchMarketplaceExtensionsContext = new RawContextKey('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); diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 83067be82bc..110c26969b5 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -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';