Skip to content

Commit bcd7bce

Browse files
committed
wip: scm folder menus
1 parent 701b16a commit bcd7bce

6 files changed

Lines changed: 149 additions & 59 deletions

File tree

extensions/git/package.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,53 @@
850850
"group": "inline"
851851
}
852852
],
853+
"scm/resourceFolder/context": [
854+
{
855+
"command": "git.stage",
856+
"when": "scmProvider == git && scmResourceGroup == merge",
857+
"group": "1_modification"
858+
},
859+
{
860+
"command": "git.stage",
861+
"when": "scmProvider == git && scmResourceGroup == merge",
862+
"group": "inline"
863+
},
864+
{
865+
"command": "git.unstage",
866+
"when": "scmProvider == git && scmResourceGroup == index",
867+
"group": "1_modification"
868+
},
869+
{
870+
"command": "git.unstage",
871+
"when": "scmProvider == git && scmResourceGroup == index",
872+
"group": "inline"
873+
},
874+
{
875+
"command": "git.stage",
876+
"when": "scmProvider == git && scmResourceGroup == workingTree",
877+
"group": "1_modification"
878+
},
879+
{
880+
"command": "git.clean",
881+
"when": "scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository",
882+
"group": "1_modification"
883+
},
884+
{
885+
"command": "git.clean",
886+
"when": "scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository",
887+
"group": "inline"
888+
},
889+
{
890+
"command": "git.stage",
891+
"when": "scmProvider == git && scmResourceGroup == workingTree",
892+
"group": "inline"
893+
},
894+
{
895+
"command": "git.ignore",
896+
"when": "scmProvider == git && scmResourceGroup == workingTree",
897+
"group": "1_modification@3"
898+
}
899+
],
853900
"scm/resourceState/context": [
854901
{
855902
"command": "git.stage",

src/vs/base/common/resourceTree.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface IBranchNode<T> {
2020
readonly name: string;
2121
readonly size: number;
2222
readonly children: Iterator<INode<T>>;
23+
readonly parent: IBranchNode<T> | undefined;
2324
get(childName: string): INode<T> | undefined;
2425
}
2526

@@ -47,6 +48,10 @@ class BranchNode<T> extends Node implements IBranchNode<T> {
4748
return Iterator.fromIterableIterator(this._children.values());
4849
}
4950

51+
constructor(path: string, readonly parent: IBranchNode<T> | undefined = undefined) {
52+
super(path);
53+
}
54+
5055
get(path: string): BranchNode<T> | LeafNode<T> | undefined {
5156
return this._children.get(path);
5257
}
@@ -75,6 +80,14 @@ export class ResourceTree<T extends NonNullable<any>> {
7580
return obj instanceof BranchNode;
7681
}
7782

83+
static getRoot<T>(node: IBranchNode<T>): IBranchNode<T> {
84+
while (node.parent) {
85+
node = node.parent;
86+
}
87+
88+
return node;
89+
}
90+
7891
constructor(private rootURI: URI = URI.file('/')) { }
7992

8093
add(uri: URI, element: T): void {
@@ -91,7 +104,7 @@ export class ResourceTree<T extends NonNullable<any>> {
91104

92105
if (!child) {
93106
if (i < parts.length - 1) {
94-
child = new BranchNode(path);
107+
child = new BranchNode(path, node);
95108
node.set(name, child);
96109
} else {
97110
child = new LeafNode(path, element);

src/vs/platform/actions/common/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const enum MenuId {
8787
ProblemsPanelContext,
8888
SCMChangeContext,
8989
SCMResourceContext,
90+
SCMResourceFolderContext,
9091
SCMResourceGroupContext,
9192
SCMSourceControl,
9293
SCMTitle,

src/vs/workbench/api/common/menusExtensionPoint.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ namespace schema {
3939
case 'menuBar/file': return MenuId.MenubarFileMenu;
4040
case 'scm/title': return MenuId.SCMTitle;
4141
case 'scm/sourceControl': return MenuId.SCMSourceControl;
42-
case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext;
4342
case 'scm/resourceState/context': return MenuId.SCMResourceContext;
43+
case 'scm/resourceFolder/context': return MenuId.SCMResourceFolderContext;
44+
case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext;
4445
case 'scm/change/title': return MenuId.SCMChangeContext;
4546
case 'statusBar/windowIndicator': return MenuId.StatusBarWindowIndicatorMenu;
4647
case 'view/title': return MenuId.ViewTitle;

src/vs/workbench/contrib/scm/browser/menus.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import 'vs/css!./media/scmViewlet';
77
import { Event, Emitter } from 'vs/base/common/event';
8-
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
8+
import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle';
99
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
1010
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
1111
import { IAction } from 'vs/base/common/actions';
@@ -20,13 +20,15 @@ function actionEquals(a: IAction, b: IAction): boolean {
2020
return a.id === b.id;
2121
}
2222

23-
interface ISCMResourceGroupMenuEntry extends IDisposable {
23+
interface ISCMResourceGroupMenuEntry {
2424
readonly group: ISCMResourceGroup;
25+
readonly disposable: IDisposable;
2526
}
2627

2728
interface ISCMMenus {
2829
readonly resourceGroupMenu: IMenu;
2930
readonly resourceMenu: IMenu;
31+
readonly resourceFolderMenu: IMenu;
3032
}
3133

3234
export function getSCMResourceContextKey(resource: ISCMResourceGroup | ISCMResource): string {
@@ -48,7 +50,7 @@ export class SCMMenus implements IDisposable {
4850
private readonly resourceGroupMenuEntries: ISCMResourceGroupMenuEntry[] = [];
4951
private readonly resourceGroupMenus = new Map<ISCMResourceGroup, ISCMMenus>();
5052

51-
private readonly disposables: IDisposable[] = [];
53+
private readonly disposables = new DisposableStore();
5254

5355
constructor(
5456
provider: ISCMProvider | undefined,
@@ -68,7 +70,7 @@ export class SCMMenus implements IDisposable {
6870
}
6971

7072
this.titleMenu = this.menuService.createMenu(MenuId.SCMTitle, this.contextKeyService);
71-
this.disposables.push(this.titleMenu);
73+
this.disposables.add(this.titleMenu);
7274

7375
this.titleMenu.onDidChange(this.updateTitleActions, this, this.disposables);
7476
this.updateTitleActions();
@@ -109,6 +111,10 @@ export class SCMMenus implements IDisposable {
109111
return this.getActions(MenuId.SCMResourceContext, resource).secondary;
110112
}
111113

114+
getResourceFolderContextActions(group: ISCMResourceGroup): IAction[] {
115+
return this.getActions(MenuId.SCMResourceFolderContext, group).secondary;
116+
}
117+
112118
private getActions(menuId: MenuId, resource: ISCMResourceGroup | ISCMResource): { primary: IAction[]; secondary: IAction[]; } {
113119
const contextKeyService = this.contextKeyService.createScoped();
114120
contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(resource));
@@ -141,6 +147,14 @@ export class SCMMenus implements IDisposable {
141147
return this.resourceGroupMenus.get(group)!.resourceMenu;
142148
}
143149

150+
getResourceFolderMenu(group: ISCMResourceGroup): IMenu {
151+
if (!this.resourceGroupMenus.has(group)) {
152+
throw new Error('SCM Resource Group menu not found');
153+
}
154+
155+
return this.resourceGroupMenus.get(group)!.resourceFolderMenu;
156+
}
157+
144158
private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice<ISCMResourceGroup>): void {
145159
const menuEntriesToInsert = toInsert.map<ISCMResourceGroupMenuEntry>(group => {
146160
const contextKeyService = this.contextKeyService.createScoped();
@@ -149,30 +163,23 @@ export class SCMMenus implements IDisposable {
149163

150164
const resourceGroupMenu = this.menuService.createMenu(MenuId.SCMResourceGroupContext, contextKeyService);
151165
const resourceMenu = this.menuService.createMenu(MenuId.SCMResourceContext, contextKeyService);
166+
const resourceFolderMenu = this.menuService.createMenu(MenuId.SCMResourceFolderContext, contextKeyService);
167+
const disposable = combinedDisposable(contextKeyService, resourceGroupMenu, resourceMenu, resourceFolderMenu);
152168

153-
this.resourceGroupMenus.set(group, { resourceGroupMenu, resourceMenu });
154-
155-
return {
156-
group,
157-
dispose() {
158-
contextKeyService.dispose();
159-
resourceGroupMenu.dispose();
160-
resourceMenu.dispose();
161-
}
162-
};
169+
this.resourceGroupMenus.set(group, { resourceGroupMenu, resourceMenu, resourceFolderMenu });
170+
return { group, disposable };
163171
});
164172

165173
const deleted = this.resourceGroupMenuEntries.splice(start, deleteCount, ...menuEntriesToInsert);
166174

167175
for (const entry of deleted) {
168176
this.resourceGroupMenus.delete(entry.group);
169-
entry.dispose();
177+
entry.disposable.dispose();
170178
}
171179
}
172180

173181
dispose(): void {
174-
dispose(this.disposables);
175-
dispose(this.resourceGroupMenuEntries);
176-
this.resourceGroupMenus.clear();
182+
this.disposables.dispose();
183+
this.resourceGroupMenuEntries.forEach(e => e.disposable.dispose());
177184
}
178185
}

0 commit comments

Comments
 (0)