Skip to content

Commit ced9fdf

Browse files
committed
scm: folder context actions
1 parent bcd7bce commit ced9fdf

5 files changed

Lines changed: 140 additions & 118 deletions

File tree

extensions/git/package.nls.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"command.syncRebase": "Sync (Rebase)",
5656
"command.publish": "Publish Branch",
5757
"command.showOutput": "Show Git Output",
58-
"command.ignore": "Add File to .gitignore",
58+
"command.ignore": "Add to .gitignore",
5959
"command.stashIncludeUntracked": "Stash (Include Untracked)",
6060
"command.stash": "Stash",
6161
"command.stashPop": "Pop Stash...",

src/vs/base/common/resourceTree.ts

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,57 +6,61 @@
66
import { memoize } from 'vs/base/common/decorators';
77
import * as paths from 'vs/base/common/path';
88
import { Iterator } from 'vs/base/common/iterator';
9-
import { relativePath } from 'vs/base/common/resources';
9+
import { relativePath, joinPath } from 'vs/base/common/resources';
1010
import { URI } from 'vs/base/common/uri';
1111

12-
export interface ILeafNode<T> {
13-
readonly path: string;
12+
export interface ILeafNode<T, C = void> {
13+
readonly uri: URI;
14+
readonly relativePath: string;
1415
readonly name: string;
1516
readonly element: T;
17+
readonly context: C;
1618
}
1719

18-
export interface IBranchNode<T> {
19-
readonly path: string;
20+
export interface IBranchNode<T, C = void> {
21+
readonly uri: URI;
22+
readonly relativePath: string;
2023
readonly name: string;
2124
readonly size: number;
22-
readonly children: Iterator<INode<T>>;
23-
readonly parent: IBranchNode<T> | undefined;
24-
get(childName: string): INode<T> | undefined;
25+
readonly children: Iterator<INode<T, C>>;
26+
readonly parent: IBranchNode<T, C> | undefined;
27+
readonly context: C;
28+
get(childName: string): INode<T, C> | undefined;
2529
}
2630

27-
export type INode<T> = IBranchNode<T> | ILeafNode<T>;
31+
export type INode<T, C> = IBranchNode<T, C> | ILeafNode<T, C>;
2832

2933
// Internals
3034

31-
class Node {
35+
class Node<C> {
3236

3337
@memoize
34-
get name(): string { return paths.posix.basename(this.path); }
38+
get name(): string { return paths.posix.basename(this.relativePath); }
3539

36-
constructor(readonly path: string) { }
40+
constructor(readonly uri: URI, readonly relativePath: string, readonly context: C) { }
3741
}
3842

39-
class BranchNode<T> extends Node implements IBranchNode<T> {
43+
class BranchNode<T, C> extends Node<C> implements IBranchNode<T, C> {
4044

41-
private _children = new Map<string, BranchNode<T> | LeafNode<T>>();
45+
private _children = new Map<string, BranchNode<T, C> | LeafNode<T, C>>();
4246

4347
get size(): number {
4448
return this._children.size;
4549
}
4650

47-
get children(): Iterator<BranchNode<T> | LeafNode<T>> {
51+
get children(): Iterator<BranchNode<T, C> | LeafNode<T, C>> {
4852
return Iterator.fromIterableIterator(this._children.values());
4953
}
5054

51-
constructor(path: string, readonly parent: IBranchNode<T> | undefined = undefined) {
52-
super(path);
55+
constructor(uri: URI, relativePath: string, context: C, readonly parent: IBranchNode<T, C> | undefined = undefined) {
56+
super(uri, relativePath, context);
5357
}
5458

55-
get(path: string): BranchNode<T> | LeafNode<T> | undefined {
59+
get(path: string): BranchNode<T, C> | LeafNode<T, C> | undefined {
5660
return this._children.get(path);
5761
}
5862

59-
set(path: string, child: BranchNode<T> | LeafNode<T>): void {
63+
set(path: string, child: BranchNode<T, C> | LeafNode<T, C>): void {
6064
this._children.set(path, child);
6165
}
6266

@@ -65,36 +69,52 @@ class BranchNode<T> extends Node implements IBranchNode<T> {
6569
}
6670
}
6771

68-
class LeafNode<T> extends Node implements ILeafNode<T> {
72+
class LeafNode<T, C> extends Node<C> implements ILeafNode<T, C> {
6973

70-
constructor(path: string, readonly element: T) {
71-
super(path);
74+
constructor(uri: URI, path: string, context: C, readonly element: T) {
75+
super(uri, path, context);
7276
}
7377
}
7478

75-
export class ResourceTree<T extends NonNullable<any>> {
79+
function collect<T, C>(node: INode<T, C>, result: T[]): T[] {
80+
if (ResourceTree.isBranchNode(node)) {
81+
Iterator.forEach(node.children, child => collect(child, result));
82+
} else {
83+
result.push(node.element);
84+
}
85+
86+
return result;
87+
}
88+
89+
export class ResourceTree<T extends NonNullable<any>, C> {
7690

77-
readonly root = new BranchNode<T>('');
91+
readonly root: BranchNode<T, C>;
7892

79-
static isBranchNode<T>(obj: any): obj is IBranchNode<T> {
93+
static isBranchNode<T, C>(obj: any): obj is IBranchNode<T, C> {
8094
return obj instanceof BranchNode;
8195
}
8296

83-
static getRoot<T>(node: IBranchNode<T>): IBranchNode<T> {
97+
static getRoot<T, C>(node: IBranchNode<T, C>): IBranchNode<T, C> {
8498
while (node.parent) {
8599
node = node.parent;
86100
}
87101

88102
return node;
89103
}
90104

91-
constructor(private rootURI: URI = URI.file('/')) { }
105+
static collect<T, C>(node: INode<T, C>): T[] {
106+
return collect(node, []);
107+
}
108+
109+
constructor(context: C, rootURI: URI = URI.file('/')) {
110+
this.root = new BranchNode(rootURI, '', context);
111+
}
92112

93113
add(uri: URI, element: T): void {
94-
const key = relativePath(this.rootURI, uri) || uri.fsPath;
114+
const key = relativePath(this.root.uri, uri) || uri.fsPath;
95115
const parts = key.split(/[\\\/]/).filter(p => !!p);
96116
let node = this.root;
97-
let path = this.root.path;
117+
let path = '';
98118

99119
for (let i = 0; i < parts.length; i++) {
100120
const name = parts[i];
@@ -104,10 +124,10 @@ export class ResourceTree<T extends NonNullable<any>> {
104124

105125
if (!child) {
106126
if (i < parts.length - 1) {
107-
child = new BranchNode(path, node);
127+
child = new BranchNode(joinPath(this.root.uri, path), path, this.root.context, node);
108128
node.set(name, child);
109129
} else {
110-
child = new LeafNode(path, element);
130+
child = new LeafNode(uri, path, this.root.context, element);
111131
node.set(name, child);
112132
return;
113133
}
@@ -119,7 +139,7 @@ export class ResourceTree<T extends NonNullable<any>> {
119139
}
120140

121141
// replace
122-
node.set(name, new LeafNode(path, element));
142+
node.set(name, new LeafNode(uri, path, this.root.context, element));
123143
return;
124144
} else if (i === parts.length - 1) {
125145
throw new Error('Inconsistent tree: can\'t override branch with leaf.');
@@ -130,12 +150,12 @@ export class ResourceTree<T extends NonNullable<any>> {
130150
}
131151

132152
delete(uri: URI): T | undefined {
133-
const key = relativePath(this.rootURI, uri) || uri.fsPath;
153+
const key = relativePath(this.root.uri, uri) || uri.fsPath;
134154
const parts = key.split(/[\\\/]/).filter(p => !!p);
135155
return this._delete(this.root, parts, 0);
136156
}
137157

138-
private _delete(node: BranchNode<T>, parts: string[], index: number): T | undefined {
158+
private _delete(node: BranchNode<T, C>, parts: string[], index: number): T | undefined {
139159
const name = parts[index];
140160
const child = node.get(name);
141161

src/vs/base/test/common/resourceTree.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,39 @@ import { URI } from 'vs/base/common/uri';
99

1010
suite('ResourceTree', function () {
1111
test('ctor', function () {
12-
const tree = new ResourceTree<string>();
12+
const tree = new ResourceTree<string, null>(null);
1313
assert(ResourceTree.isBranchNode(tree.root));
1414
assert.equal(tree.root.size, 0);
1515
});
1616

1717
test('simple', function () {
18-
const tree = new ResourceTree<string>();
18+
const tree = new ResourceTree<string, null>(null);
1919

2020
tree.add(URI.file('/foo/bar.txt'), 'bar contents');
2121
assert(ResourceTree.isBranchNode(tree.root));
2222
assert.equal(tree.root.size, 1);
2323

24-
let foo = tree.root.get('foo') as IBranchNode<string>;
24+
let foo = tree.root.get('foo') as IBranchNode<string, null>;
2525
assert(foo);
2626
assert(ResourceTree.isBranchNode(foo));
2727
assert.equal(foo.size, 1);
2828

29-
let bar = foo.get('bar.txt') as ILeafNode<string>;
29+
let bar = foo.get('bar.txt') as ILeafNode<string, null>;
3030
assert(bar);
3131
assert(!ResourceTree.isBranchNode(bar));
3232
assert.equal(bar.element, 'bar contents');
3333

3434
tree.add(URI.file('/hello.txt'), 'hello contents');
3535
assert.equal(tree.root.size, 2);
3636

37-
let hello = tree.root.get('hello.txt') as ILeafNode<string>;
37+
let hello = tree.root.get('hello.txt') as ILeafNode<string, null>;
3838
assert(hello);
3939
assert(!ResourceTree.isBranchNode(hello));
4040
assert.equal(hello.element, 'hello contents');
4141

4242
tree.delete(URI.file('/foo/bar.txt'));
4343
assert.equal(tree.root.size, 1);
44-
hello = tree.root.get('hello.txt') as ILeafNode<string>;
44+
hello = tree.root.get('hello.txt') as ILeafNode<string, null>;
4545
assert(hello);
4646
assert(!ResourceTree.isBranchNode(hello));
4747
assert.equal(hello.element, 'hello contents');

src/vs/workbench/contrib/scm/browser/media/scmViewlet.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
.scm-viewlet .monaco-list-row .resource-group {
9292
display: flex;
9393
height: 100%;
94+
align-items: center;
9495
}
9596

9697
.scm-viewlet .monaco-list-row .resource-group > .name {
@@ -99,6 +100,7 @@
99100
font-weight: bold;
100101
overflow: hidden;
101102
text-overflow: ellipsis;
103+
text-decoration: underline;
102104
}
103105

104106
.scm-viewlet .monaco-list-row .resource {
@@ -125,6 +127,7 @@
125127

126128
.scm-viewlet .monaco-list-row .resource-group > .count {
127129
padding: 0 8px;
130+
display: flex;
128131
}
129132

130133
.scm-viewlet .monaco-list-row .resource > .decoration-icon {

0 commit comments

Comments
 (0)