提交 4daf254d 编写于 作者: S Sandeep Somavarapu

#87285 Contribute copy and configure action using menu

上级 2395bc07
......@@ -70,6 +70,8 @@ export const enum MenuId {
EditorTitleContext,
EmptyEditorGroupContext,
ExplorerContext,
ExtensionContext,
GlobalActivity,
MenubarAppearanceMenu,
MenubarDebugMenu,
MenubarEditMenu,
......@@ -106,7 +108,6 @@ export const enum MenuId {
CommentThreadActions,
CommentTitle,
CommentActions,
GlobalActivity
}
export interface IMenuActionOptions {
......
......@@ -6,7 +6,7 @@
import { localize } from 'vs/nls';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { SyncActionDescriptor, MenuRegistry, MenuId, registerAction } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
......@@ -14,7 +14,7 @@ import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } fro
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 } from 'vs/workbench/contrib/extensions/common/extensions';
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionMenuActionContext } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import {
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
......@@ -44,6 +44,10 @@ import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { RemoteExtensionsInstaller } from 'vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IProductService } from 'vs/platform/product/common/productService';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
// Singletons
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
......@@ -336,6 +340,54 @@ MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
order: 3
});
// Extension Context Menu
registerAction({
id: 'workbench.extensions.action.copyExtension',
title: { value: localize('workbench.extensions.action.copyExtension', "Copy"), original: 'Copy' },
async handler(accessor, context: IExtensionMenuActionContext) {
const productService = accessor.get(IProductService);
const name = localize('extensionInfoName', 'Name: {0}', context.packageJSON.displayName);
const id = localize('extensionInfoId', 'Id: {0}', context.id);
const description = localize('extensionInfoDescription', 'Description: {0}', context.packageJSON.description);
const verision = localize('extensionInfoVersion', 'Version: {0}', context.packageJSON.version);
const publisher = localize('extensionInfoPublisher', 'Publisher: {0}', context.packageJSON.publisher);
const link = productService.extensionsGallery ? localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link: {0}', `${productService.extensionsGallery!.itemUrl}?itemName=${context.id}`) : null;
const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`;
await accessor.get(IClipboardService).writeText(clipboardStr);
},
menu: {
menuId: MenuId.ExtensionContext,
group: '1_copy'
},
});
registerAction({
id: 'workbench.extensions.action.copyExtensionId',
title: { value: localize('workbench.extensions.action.copyExtensionId', "Copy Extension Id"), original: 'Copy Extension Id' },
async handler(accessor, context: IExtensionMenuActionContext) {
await accessor.get(IClipboardService).writeText(context.id);
},
menu: {
menuId: MenuId.ExtensionContext,
group: '1_copy'
},
});
registerAction({
id: 'workbench.extensions.action.configure',
title: { value: localize('workbench.extensions.action.configure', "Configure..."), original: 'Configure...' },
async handler(accessor, context: IExtensionMenuActionContext) {
await accessor.get(IPreferencesService).openSettings(false, `@ext:${context.id}`);
},
menu: {
menuId: MenuId.ExtensionContext,
group: '2_configure',
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration'))
},
});
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
class ExtensionsContributions implements IWorkbenchContribution {
......
......@@ -13,7 +13,7 @@ import * as json from 'vs/base/common/json';
import { ActionViewItem, Separator, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionbar';
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, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG, IExtensionMenuAction } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
import { ExtensionsLabel, IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService, IExtensionRecommendation, IExtensionsConfigContent, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
......@@ -39,7 +39,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { PagedModel } from 'vs/base/common/paging';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { MenuRegistry, MenuId, IMenuService } from 'vs/platform/actions/common/actions';
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
......@@ -55,10 +55,8 @@ import { coalesce } from 'vs/base/common/arrays';
import { IWorkbenchThemeService, COLOR_THEME_SETTING, ICON_THEME_SETTING, IFileIconTheme, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { ILabelService } from 'vs/platform/label/common/label';
import { prefersExecuteOnUI, prefersExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IProductService } from 'vs/platform/product/common/productService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
......@@ -671,7 +669,9 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@IExtensionService private readonly extensionService: IExtensionService,
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
@IMenuService private readonly menuService: IMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super(ManageExtensionAction.ID, '', '', true, true, instantiationService);
......@@ -708,11 +708,10 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
groups.push([this.instantiationService.createInstance(UninstallAction)]);
groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]);
const extensionActions: ExtensionAction[] = [this.instantiationService.createInstance(CopyExtensionInfoAction), this.instantiationService.createInstance(CopyExtensionIdAction)];
if (this.extension && this.extension.local && this.extension.local.manifest.contributes && this.extension.local.manifest.contributes.configuration) {
extensionActions.push(this.instantiationService.createInstance(ExtensionSettingsAction));
}
groups.push(extensionActions);
const contextKeyService = this.contextKeyService.createScoped();
contextKeyService.createKey('extensionStatus', 'installed');
contextKeyService.createKey<boolean>('extensionHasConfiguration', !!this.extension && !!this.extension.local && !!this.extension.local.manifest.contributes && !!this.extension.local.manifest.contributes.configuration);
MenuItemExtensionAction.getContextMenuActions(this.menuService, contextKeyService).forEach(actions => groups.push(actions));
groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = this.extension));
......@@ -738,6 +737,32 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
}
}
export class MenuItemExtensionAction extends ExtensionAction {
constructor(private readonly action: IExtensionMenuAction) {
super(action.id, action.label);
}
static getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService): ExtensionAction[][] {
const groups: ExtensionAction[][] = [];
const menu = menuService.createMenu(MenuId.ExtensionContext, contextKeyService);
menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => new MenuItemExtensionAction(action))));
menu.dispose();
return groups;
}
update() { }
async run(): Promise<void> {
if (this.extension) {
const packageJSON = await this.extension.getManifest(CancellationToken.None);
if (packageJSON) {
return this.action.run({ id: this.extension.identifier.id, packageJSON });
}
}
}
}
export class InstallAnotherVersionAction extends ExtensionAction {
static readonly ID = 'workbench.extensions.action.install.anotherVersion';
......@@ -790,89 +815,6 @@ export class InstallAnotherVersionAction extends ExtensionAction {
}
}
export class CopyExtensionInfoAction extends ExtensionAction {
static readonly ID = 'workbench.extensions.action.copyExtension';
static readonly LABEL = localize('workbench.extensions.action.copyExtension', "Copy");
constructor(
@IClipboardService private readonly clipboardService: IClipboardService
) {
super(CopyExtensionInfoAction.ID, CopyExtensionInfoAction.LABEL);
this.update();
}
update(): void {
this.enabled = !!this.extension;
}
async run(): Promise<any> {
if (!this.extension) {
return;
}
const name = localize('extensionInfoName', 'Name: {0}', this.extension.displayName);
const id = localize('extensionInfoId', 'Id: {0}', this.extension.identifier.id);
const description = localize('extensionInfoDescription', 'Description: {0}', this.extension.description);
const verision = localize('extensionInfoVersion', 'Version: {0}', this.extension.version);
const publisher = localize('extensionInfoPublisher', 'Publisher: {0}', this.extension.publisherDisplayName);
const link = this.extension.url ? localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link: {0}', this.extension.url.toString()) : null;
const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`;
return this.clipboardService.writeText(clipboardStr);
}
}
export class CopyExtensionIdAction extends ExtensionAction {
static readonly ID = 'workbench.extensions.action.copyExtensionId';
static readonly LABEL = localize('workbench.extensions.action.copyExtensionId', "Copy Extension Id");
constructor(
@IClipboardService private readonly clipboardService: IClipboardService
) {
super(CopyExtensionIdAction.ID, CopyExtensionIdAction.LABEL);
this.update();
}
update(): void {
this.enabled = !!this.extension;
}
async run(): Promise<any> {
if (!this.extension) {
return;
}
return this.clipboardService.writeText(this.extension.identifier.id);
}
}
export class ExtensionSettingsAction extends ExtensionAction {
static readonly ID = 'extensions.extensionSettings';
static readonly LABEL = localize('extensionSettingsAction', "Configure Extension Settings");
constructor(
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(ExtensionSettingsAction.ID, ExtensionSettingsAction.LABEL);
this.update();
}
update(): void {
this.enabled = !!this.extension;
}
async run(): Promise<any> {
if (!this.extension) {
return;
}
this.preferencesService.openSettings(false, `@ext:${this.extension.identifier.id}`);
return Promise.resolve();
}
}
export class EnableForWorkspaceAction extends ExtensionAction {
static readonly ID = 'extensions.enableForWorkspace';
......
......@@ -28,7 +28,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction, InstallLocalExtensionsInRemoteAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction, InstallLocalExtensionsInRemoteAction, MenuItemExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { WorkbenchPagedList } from 'vs/platform/list/browser/listService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
......@@ -48,6 +48,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IMenuService } from 'vs/platform/actions/common/actions';
// Extensions that are automatically classified as Programming Language extensions, but should be Feature extensions
const FORCE_FEATURE_EXTENSIONS = ['vscode.git', 'vscode.search-result'];
......@@ -106,7 +107,8 @@ export class ExtensionsListView extends ViewPane {
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,
@IProductService protected readonly productService: IProductService,
@IContextKeyService contextKeyService: IContextKeyService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IMenuService private readonly menuService: IMenuService,
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService);
this.server = options.server;
......@@ -228,16 +230,27 @@ export class ExtensionsListView extends ViewPane {
const fileIconThemes = await this.workbenchThemeService.getFileIconThemes();
const manageExtensionAction = this.instantiationService.createInstance(ManageExtensionAction);
manageExtensionAction.extension = e.element;
const groups = manageExtensionAction.getActionGroups(runningExtensions, colorThemes, fileIconThemes);
let actions: IAction[] = [];
for (const menuActions of groups) {
actions = [...actions, ...menuActions, new Separator()];
}
if (manageExtensionAction.enabled) {
const groups = manageExtensionAction.getActionGroups(runningExtensions, colorThemes, fileIconThemes);
let actions: IAction[] = [];
for (const menuActions of groups) {
actions = [...actions, ...menuActions, new Separator()];
}
this.contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => actions.slice(0, actions.length - 1)
});
} else if (e.element) {
const groups = MenuItemExtensionAction.getContextMenuActions(this.menuService, this.contextKeyService.createScoped());
groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = e.element!));
let actions: IAction[] = [];
for (const menuActions of groups) {
actions = [...actions, ...menuActions, new Separator()];
}
this.contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => actions
});
}
}
}
......@@ -866,10 +879,11 @@ export class ServerExtensionsView extends ExtensionsListView {
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
@IProductService productService: IProductService,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@IMenuService menuService: IMenuService,
) {
options.server = server;
super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService, productService, contextKeyService);
super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService, productService, contextKeyService, menuService);
this._register(onDidChangeTitle(title => this.updateTitle(title)));
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册