diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 637dff30817..426c2f1ed1e 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1734,4 +1734,30 @@ declare module 'vscode' { } //#endregion + + //#region Support `scmResourceState` in `when` clauses #86180 https://github.com/microsoft/vscode/issues/86180 + + export interface SourceControlResourceState { + /** + * Context value of the resource state. This can be used to contribute resource specific actions. + * For example, if a resource is given a context value as `diffable`. When contributing actions to `scm/resourceState/context` + * using `menus` extension point, you can specify context value for key `scmResourceState` in `when` expressions, like `scmResourceState == diffable`. + * ``` + * "contributes": { + * "menus": { + * "scm/resourceState/context": [ + * { + * "command": "extension.diff", + * "when": "scmResourceState == diffable" + * } + * ] + * } + * } + * ``` + * This will show action `extension.diff` only for resources with `contextValue` is `diffable`. + */ + readonly contextValue?: string; + } + + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index e75ac0b4da3..68b6e9cbaea 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -68,7 +68,8 @@ class MainThreadSCMResource implements ISCMResource { private readonly handle: number, public sourceUri: URI, public resourceGroup: ISCMResourceGroup, - public decorations: ISCMResourceDecorations + public decorations: ISCMResourceDecorations, + public contextValue: string ) { } open(): Promise { @@ -198,7 +199,7 @@ class MainThreadSCMProvider implements ISCMProvider { for (const [start, deleteCount, rawResources] of groupSlices) { const resources = rawResources.map(rawResource => { - const [handle, sourceUri, icons, tooltip, strikeThrough, faded] = rawResource; + const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] = rawResource; const icon = icons[0]; const iconDark = icons[1] || icon; const decorations = { @@ -216,7 +217,8 @@ class MainThreadSCMProvider implements ISCMProvider { handle, URI.revive(sourceUri), group, - decorations + decorations, + contextValue ); }); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ee4368dbe33..1a141cd9bf2 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -715,7 +715,8 @@ export type SCMRawResource = [ UriComponents[] /*icons: light, dark*/, string /*tooltip*/, boolean /*strike through*/, - boolean /*faded*/ + boolean /*faded*/, + string /*context value*/ ]; export type SCMRawResourceSplice = [ diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 34a3138ad32..0fdfbd44923 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -306,8 +306,9 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG const tooltip = (r.decorations && r.decorations.tooltip) || ''; const strikeThrough = r.decorations && !!r.decorations.strikeThrough; const faded = r.decorations && !!r.decorations.faded; + const contextValue = r.contextValue || ''; - const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded] as SCMRawResource; + const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] as SCMRawResource; return { rawResource, handle }; }); diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index 327801803c2..cd1f114db4f 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -27,10 +27,13 @@ interface ISCMResourceGroupMenuEntry { interface ISCMMenus { readonly resourceGroupMenu: IMenu; - readonly resourceMenu: IMenu; readonly resourceFolderMenu: IMenu; } +interface ISCMResourceMenu extends IDisposable { + readonly menu: IMenu; +} + export function getSCMResourceContextKey(resource: ISCMResourceGroup | ISCMResource): string { return isSCMResource(resource) ? resource.resourceGroup.id : resource.id; } @@ -111,6 +114,10 @@ export class SCMMenus implements IDisposable { return this.getActions(MenuId.SCMResourceContext, resource).secondary; } + getResourceInlineActions(resource: ISCMResource): IAction[] { + return this.getActions(MenuId.SCMResourceContext, resource).primary; + } + getResourceFolderContextActions(group: ISCMResourceGroup): IAction[] { return this.getActions(MenuId.SCMResourceFolderContext, group).secondary; } @@ -118,6 +125,9 @@ export class SCMMenus implements IDisposable { private getActions(menuId: MenuId, resource: ISCMResourceGroup | ISCMResource): { primary: IAction[]; secondary: IAction[]; } { const contextKeyService = this.contextKeyService.createScoped(); contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(resource)); + if (isSCMResource(resource)) { + contextKeyService.createKey('scmResourceState', resource.contextValue); + } const menu = this.menuService.createMenu(menuId, contextKeyService); const primary: IAction[] = []; @@ -131,6 +141,20 @@ export class SCMMenus implements IDisposable { return result; } + createResourceMenu(group: ISCMResourceGroup, resource: ISCMResource): ISCMResourceMenu { + const contextKeyService = this.contextKeyService.createScoped(); + contextKeyService.createKey('scmProvider', group.provider.contextValue); + contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(resource)); + contextKeyService.createKey('scmResourceState', resource.contextValue); + + const menu = this.menuService.createMenu(MenuId.SCMResourceContext, contextKeyService); + + const disposable = combinedDisposable(menu, contextKeyService); + const dispose = () => disposable.dispose(); + + return { menu, dispose }; + } + getResourceGroupMenu(group: ISCMResourceGroup): IMenu { if (!this.resourceGroupMenus.has(group)) { throw new Error('SCM Resource Group menu not found'); @@ -139,14 +163,6 @@ export class SCMMenus implements IDisposable { return this.resourceGroupMenus.get(group)!.resourceGroupMenu; } - getResourceMenu(group: ISCMResourceGroup): IMenu { - if (!this.resourceGroupMenus.has(group)) { - throw new Error('SCM Resource Group menu not found'); - } - - return this.resourceGroupMenus.get(group)!.resourceMenu; - } - getResourceFolderMenu(group: ISCMResourceGroup): IMenu { if (!this.resourceGroupMenus.has(group)) { throw new Error('SCM Resource Group menu not found'); @@ -162,11 +178,10 @@ export class SCMMenus implements IDisposable { contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(group)); const resourceGroupMenu = this.menuService.createMenu(MenuId.SCMResourceGroupContext, contextKeyService); - const resourceMenu = this.menuService.createMenu(MenuId.SCMResourceContext, contextKeyService); const resourceFolderMenu = this.menuService.createMenu(MenuId.SCMResourceFolderContext, contextKeyService); - const disposable = combinedDisposable(contextKeyService, resourceGroupMenu, resourceMenu, resourceFolderMenu); + const disposable = combinedDisposable(contextKeyService, resourceGroupMenu, resourceFolderMenu); - this.resourceGroupMenus.set(group, { resourceGroupMenu, resourceMenu, resourceFolderMenu }); + this.resourceGroupMenus.set(group, { resourceGroupMenu, resourceFolderMenu }); return { group, disposable }; }); diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index ab9c041d125..e015b62a8d9 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -220,7 +220,9 @@ class ResourceRenderer implements ICompressibleTreeRenderer; }