Skip to content

Commit 51b94e3

Browse files
Add FilePathSet
1 parent b044195 commit 51b94e3

2 files changed

Lines changed: 100 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { containsPath } from "../pure/files";
2+
3+
/**
4+
* A set of file paths.
5+
*
6+
* All paths in the set will not overlap. If a path is added to the set
7+
* that is a parent or child of an existing path in the set, only the
8+
* parent path will be kept.
9+
*/
10+
export class FilePathSet {
11+
private paths: string[] = [];
12+
13+
/** Is the set currently empty */
14+
public isEmpty(): boolean {
15+
return this.paths.length === 0;
16+
}
17+
18+
/**
19+
* Adds the path to the set.
20+
*
21+
* The set will not contain overlapping paths. This means that if the
22+
* new path is a child of an existing path in the set then it will not
23+
* be added. And if the new path is a parent of any existing paths, then
24+
* those existing paths will be removed. This can cause the "size" of
25+
* the set of decrease, but it won't go from non-zero to zero.
26+
*/
27+
public addPath(path: string): void {
28+
if (this.paths.some((p) => containsPath(p, path))) {
29+
// The new path is a child of an existing path, so don't add it.
30+
return;
31+
} else {
32+
// Remove any existing paths that are children of the new path.
33+
this.paths = this.paths.filter((p) => !containsPath(path, p));
34+
this.paths.push(path);
35+
}
36+
}
37+
38+
/** Removes and returns a path from the set, if the set of non-empty. */
39+
public popPath(): string | undefined {
40+
return this.paths.shift();
41+
}
42+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { FilePathSet } from "../../../src/common/file-path-set";
2+
3+
describe("FilePathSet", () => {
4+
describe("isEmpty", () => {
5+
it("should return true only when set is empty", () => {
6+
const v = new FilePathSet();
7+
expect(v.isEmpty()).toBe(true);
8+
9+
v.addPath("/foo");
10+
expect(v.isEmpty()).toBe(false);
11+
12+
v.popPath();
13+
expect(v.isEmpty()).toBe(true);
14+
});
15+
});
16+
17+
describe("addPath / popPath", () => {
18+
it("should keep all paths when they don't overlap", () => {
19+
const v = new FilePathSet();
20+
v.addPath("/foo");
21+
v.addPath("/bar");
22+
v.addPath("/baz");
23+
expect(v.popPath()).toBe("/foo");
24+
expect(v.popPath()).toBe("/bar");
25+
expect(v.popPath()).toBe("/baz");
26+
expect(v.popPath()).toBe(undefined);
27+
});
28+
29+
it("should only keep one copy of repeated paths", () => {
30+
const v = new FilePathSet();
31+
v.addPath("/foo");
32+
v.addPath("/foo");
33+
v.addPath("/foo");
34+
expect(v.popPath()).toBe("/foo");
35+
expect(v.popPath()).toBe(undefined);
36+
});
37+
38+
it("should adding adding paths that are children of existing paths", () => {
39+
const v = new FilePathSet();
40+
v.addPath("/foo");
41+
v.addPath("/foo/bar");
42+
v.addPath("/foo/baz");
43+
expect(v.popPath()).toBe("/foo");
44+
expect(v.popPath()).toBe(undefined);
45+
});
46+
47+
it("should remove existing paths that are children of new paths", () => {
48+
const v = new FilePathSet();
49+
v.addPath("/foo");
50+
v.addPath("/bar/baz");
51+
v.addPath("/bar/qux");
52+
v.addPath("/bar");
53+
expect(v.popPath()).toBe("/foo");
54+
expect(v.popPath()).toBe("/bar");
55+
expect(v.popPath()).toBe(undefined);
56+
});
57+
});
58+
});

0 commit comments

Comments
 (0)