diff --git a/extensions/git/package.json b/extensions/git/package.json index 7365f7c1b86..3d00bb23494 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -73,46 +73,51 @@ } ], "menus": { - "scm/git/title": [ + "scm/title": [ { "command": "git.refresh", - "group": "navigation" + "group": "navigation", + "when": "scmProvider == git" } ], - "scm/git/index/title/context": [ + "scm/resourceGroup/context": [ { - "command": "git.unstage-all" + "command": "git.unstage-all", + "when": "scmProvider == git && scmResourceGroup == index" }, { "command": "git.unstage-all", - "group": "inline" - } - ], - "scm/git/index/context": [ - { - "command": "git.unstage" - }, - { - "command": "git.unstage", - "group": "inline" - } - ], - "scm/git/workingTree/title/context": [ - { - "command": "git.stage-all" + "group": "inline", + "when": "scmProvider == git && scmResourceGroup == index" }, { "command": "git.stage-all", - "group": "inline" + "when": "scmProvider == git && scmResourceGroup == workingTree" + }, + { + "command": "git.stage-all", + "group": "inline", + "when": "scmProvider == git && scmResourceGroup == workingTree" } ], - "scm/git/workingTree/context": [ + "scm/resource/context": [ { - "command": "git.stage" + "command": "git.unstage", + "when": "scmProvider == git && scmResourceGroup == index" + }, + { + "command": "git.unstage", + "group": "inline", + "when": "scmProvider == git && scmResourceGroup == index" }, { "command": "git.stage", - "group": "inline" + "when": "scmProvider == git && scmResourceGroup == workingTree" + }, + { + "command": "git.stage", + "group": "inline", + "when": "scmProvider == git && scmResourceGroup == workingTree" } ] }, diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index f71c084fe7e..489c7bfc08a 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -306,6 +306,7 @@ export function any(...events: Event[]): Event { } export function debounceEvent(event: Event, merger: (last: T, event: T) => T, delay?: number): Event; +export function debounceEvent(event: Event, merger: (last: O, event: I) => O, delay?: number): Event; export function debounceEvent(event: Event, merger: (last: O, event: I) => O, delay: number = 100): Event { let subscription: IDisposable; diff --git a/src/vs/platform/actions/browser/menusExtensionPoint.ts b/src/vs/platform/actions/browser/menusExtensionPoint.ts index 932bcd6af47..497a437071f 100644 --- a/src/vs/platform/actions/browser/menusExtensionPoint.ts +++ b/src/vs/platform/actions/browser/menusExtensionPoint.ts @@ -13,7 +13,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { forEach } from 'vs/base/common/collections'; import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { MenuId, SCMTitleMenuId, SCMResourceMenuID, SCMResourceGroupMenuID, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; namespace schema { @@ -33,12 +33,12 @@ namespace schema { case 'explorer/context': return MenuId.ExplorerContext; case 'editor/title/context': return MenuId.EditorTitleContext; case 'debug/callstack/context': return MenuId.DebugCallStackContext; + case 'scm/title': return MenuId.SCMTitle; + case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext; + case 'scm/resource/context': return MenuId.SCMResourceContext; } - return SCMTitleMenuId.parse(value) - || SCMResourceGroupMenuID.parse(value) - || SCMResourceMenuID.parse(value) - || void 0; + return void 0; } export function isValidMenuItems(menu: IUserFriendlyMenuItem[], collector: ExtensionMessageCollector): boolean { diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index cca12444620..8de6e209e80 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -46,6 +46,9 @@ export class MenuId { static readonly DebugCallStackContext = new MenuId('8'); static readonly DebugBreakpointsContext = new MenuId('9'); static readonly DebugConsoleContext = new MenuId('10'); + static readonly SCMTitle = new MenuId('11'); + static readonly SCMResourceGroupContext = new MenuId('12'); + static readonly SCMResourceContext = new MenuId('13'); constructor(private _id: string) { @@ -56,50 +59,6 @@ export class MenuId { } } -export class SCMTitleMenuId extends MenuId { - - static parse(value: string): MenuId | null { - const match = /^scm\/([^/]+)\/title$/.exec(value); - return match ? new SCMTitleMenuId(match[1]) : null; - } - - constructor(private _providerId: string) { - super(`scm/${_providerId}/title`); - } -} - -export class SCMResourceGroupMenuID extends MenuId { - - static parse(value: string): MenuId | null { - const match = /^scm\/([^/]+)\/([^/]+)\/title\/context$/.exec(value); - - if (match) { - const [, providerId, resourceGroupId] = match; - return new SCMResourceGroupMenuID(providerId, resourceGroupId); - } - } - - constructor(private providerId: string, private resourceGroupId: string) { - super(`scm/${providerId}/${resourceGroupId}/title/context`); - } -} - -export class SCMResourceMenuID extends MenuId { - - static parse(value: string): MenuId | null { - const match = /^scm\/([^/]+)\/([^/]+)\/context$/.exec(value); - - if (match) { - const [, providerId, resourceGroupId] = match; - return new SCMResourceMenuID(providerId, resourceGroupId); - } - } - - constructor(private providerId: string, private resourceGroupId: string) { - super(`scm/${providerId}/${resourceGroupId}/context`); - } -} - export const IMenuService = createDecorator('menuService'); export interface IMenuService { diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index b0c11d52039..5d91cc08d57 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -258,17 +258,22 @@ class ScopedContextKeyService extends AbstractContextKeyService { private _parent: AbstractContextKeyService; private _domNode: IContextKeyServiceTarget; - constructor(parent: AbstractContextKeyService, emitter: Emitter, domNode: IContextKeyServiceTarget) { + constructor(parent: AbstractContextKeyService, emitter: Emitter, domNode?: IContextKeyServiceTarget) { super(parent.createChildContext()); this._parent = parent; this._onDidChangeContextKey = emitter; - this._domNode = domNode; - this._domNode.setAttribute(KEYBINDING_CONTEXT_ATTR, String(this._myContextId)); + + if (domNode) { + this._domNode = domNode; + this._domNode.setAttribute(KEYBINDING_CONTEXT_ATTR, String(this._myContextId)); + } } public dispose(): void { this._parent.disposeContext(this._myContextId); - this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR); + if (this._domNode) { + this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR); + } } public get onDidChangeContext(): Event { diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 71ba74893a5..b67adb12f29 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -465,7 +465,7 @@ export interface IContextKeyService { contextMatchesRules(rules: ContextKeyExpr): boolean; getContextKeyValue(key: string): T; - createScoped(target: IContextKeyServiceTarget): IContextKeyService; + createScoped(target?: IContextKeyServiceTarget): IContextKeyService; getContextValue(target: IContextKeyServiceTarget): any; } diff --git a/src/vs/workbench/parts/scm/browser/scmMenus.ts b/src/vs/workbench/parts/scm/browser/scmMenus.ts index ddc0e52b6bf..1c893a5dd50 100644 --- a/src/vs/workbench/parts/scm/browser/scmMenus.ts +++ b/src/vs/workbench/parts/scm/browser/scmMenus.ts @@ -7,8 +7,8 @@ import 'vs/css!./media/scmViewlet'; import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IMenuService, SCMTitleMenuId, SCMResourceMenuID, SCMResourceGroupMenuID, MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { ISCMService, ISCMProvider } from 'vs/workbench/services/scm/common/scm'; @@ -21,12 +21,15 @@ export class SCMMenus implements IDisposable { private titleDisposable: IDisposable = EmptyDisposable; private titleActions: IAction[] = []; private titleSecondaryActions: IAction[] = []; + private activeProviderContextKey: IContextKey; constructor( @IContextKeyService private contextKeyService: IContextKeyService, @ISCMService private scmService: ISCMService, @IMenuService private menuService: IMenuService ) { + this.activeProviderContextKey = contextKeyService.createKey('scmProvider', ''); + this.setActiveProvider(this.scmService.activeProvider); this.scmService.onDidChangeProvider(this.setActiveProvider, this, this.disposables); } @@ -37,12 +40,13 @@ export class SCMMenus implements IDisposable { this.titleDisposable = EmptyDisposable; } + this.activeProviderContextKey.set(activeProvider ? activeProvider.id : ''); + if (!activeProvider) { return; } - const titleMenuId = new SCMTitleMenuId(activeProvider.id); - const titleMenu = this.menuService.createMenu(titleMenuId, this.contextKeyService); + const titleMenu = this.menuService.createMenu(MenuId.SCMTitle, this.contextKeyService); const updateActions = () => fillInActions(titleMenu, null, { primary: this.titleActions, secondary: this.titleSecondaryActions }); const listener = titleMenu.onDidChange(updateActions); updateActions(); @@ -64,48 +68,40 @@ export class SCMMenus implements IDisposable { } getResourceGroupActions(resourceGroupId: string): IAction[] { - if (!this.scmService.activeProvider) { - return []; - } - - const menuId = new SCMResourceGroupMenuID(this.scmService.activeProvider.id, resourceGroupId); - return this.getActions(menuId).primary; + return this.getActions(MenuId.SCMResourceGroupContext, resourceGroupId).primary; } getResourceGroupContextActions(resourceGroupId: string): IAction[] { - if (!this.scmService.activeProvider) { - return []; - } - - const menuId = new SCMResourceGroupMenuID(this.scmService.activeProvider.id, resourceGroupId); - return this.getActions(menuId).secondary; + return this.getActions(MenuId.SCMResourceGroupContext, resourceGroupId).secondary; } getResourceActions(resourceGroupId: string): IAction[] { - if (!this.scmService.activeProvider) { - return []; - } - - const menuId = new SCMResourceMenuID(this.scmService.activeProvider.id, resourceGroupId); - return this.getActions(menuId).primary; + return this.getActions(MenuId.SCMResourceContext, resourceGroupId).primary; } getResourceContextActions(resourceGroupId: string): IAction[] { - if (!this.scmService.activeProvider) { - return []; - } - - const menuId = new SCMResourceMenuID(this.scmService.activeProvider.id, resourceGroupId); - return this.getActions(menuId).secondary; + return this.getActions(MenuId.SCMResourceContext, resourceGroupId).secondary; } - private getActions(menuId: MenuId): { primary: IAction[]; secondary: IAction[]; } { - const menu = this.menuService.createMenu(menuId, this.contextKeyService); + private static readonly NoActions = { primary: [], secondary: [] }; + + private getActions(menuId: MenuId, resourceGroupId: string): { primary: IAction[]; secondary: IAction[]; } { + if (!this.scmService.activeProvider) { + return SCMMenus.NoActions; + } + + const contextKeyService = this.contextKeyService.createScoped(); + contextKeyService.createKey('scmResourceGroup', resourceGroupId); + + const menu = this.menuService.createMenu(menuId, contextKeyService); const primary = []; const secondary = []; const result = { primary, secondary }; fillInActions(menu, null, result, g => g === 'inline'); + menu.dispose(); + contextKeyService.dispose(); + return result; }