Skip to content

Commit 5915d3e

Browse files
committed
feat!: remove Projector and add Modifier and Filter
1 parent 0539561 commit 5915d3e

File tree

17 files changed

+588
-488
lines changed

17 files changed

+588
-488
lines changed

builtin/filter/cwd.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as fn from "@denops/std/function";
22

3-
import { defineProjector, type Projector } from "../../projector.ts";
3+
import { defineFilter, type Filter } from "../../filter.ts";
44

55
/**
66
* Represents detailed information for each item, specifically the file path.
@@ -10,14 +10,14 @@ type Detail = {
1010
};
1111

1212
/**
13-
* Creates a Projector that filters items based on the current working directory.
13+
* Creates a Filter that filters items based on the current working directory.
1414
*
15-
* This Projector yields only those items whose `path` is within the current working directory.
15+
* This Filter yields only those items whose `path` is within the current working directory.
1616
*
17-
* @returns A Projector that filters items according to the current working directory.
17+
* @returns A Filter that filters items according to the current working directory.
1818
*/
19-
export function cwd<T extends Detail>(): Projector<T> {
20-
return defineProjector<T>(async function* (denops, { items }, { signal }) {
19+
export function cwd<T extends Detail>(): Filter<T> {
20+
return defineFilter<T>(async function* (denops, { items }, { signal }) {
2121
// Retrieve the current working directory
2222
const cwd = await fn.getcwd(denops);
2323
signal?.throwIfAborted();

builtin/filter/exists.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { exists as exists_ } from "@std/fs/exists";
22

3-
import { defineProjector, type Projector } from "../../projector.ts";
3+
import { defineFilter, type Filter } from "../../filter.ts";
44

55
/**
66
* Represents detailed information for each item, specifically the file path.
@@ -10,15 +10,15 @@ type Detail = {
1010
};
1111

1212
/**
13-
* Creates a Projector that filters items based on file existence.
13+
* Creates a Filter that filters items based on file existence.
1414
*
15-
* This Projector checks each item's `path` and yields only those items
15+
* This Filter checks each item's `path` and yields only those items
1616
* where the path exists in the filesystem.
1717
*
18-
* @returns A Projector that filters items according to file existence.
18+
* @returns A Filter that filters items according to file existence.
1919
*/
20-
export function exists(): Projector<Detail> {
21-
return defineProjector(async function* (_denops, { items }, { signal }) {
20+
export function exists(): Filter<Detail> {
21+
return defineFilter(async function* (_denops, { items }, { signal }) {
2222
// Check each item's path for existence and yield it if the file exists
2323
for await (const item of items) {
2424
if (await exists_(item.detail.path)) {

builtin/filter/noop.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { defineProjector, type Projector } from "../../projector.ts";
1+
import { defineFilter, type Filter } from "../../filter.ts";
22

33
/**
4-
* A no-operation (noop) Projector.
4+
* A no-operation (noop) Filter.
55
*
6-
* This Projector does nothing and yields no items. It can be used as a placeholder
7-
* or a default value where a Projector is required but no action is needed.
6+
* This Filter does nothing and yields no items. It can be used as a placeholder
7+
* or a default value where a Filter is required but no action is needed.
88
*
9-
* @returns A Projector that yields nothing.
9+
* @returns A Filter that yields nothing.
1010
*/
11-
export function noop<T>(): Projector<T> {
12-
return defineProjector<T>(async function* () {});
11+
export function noop<T>(): Filter<T> {
12+
return defineFilter<T>(async function* () {});
1313
}

builtin/filter/regexp.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineProjector, type Projector } from "../../projector.ts";
1+
import { defineFilter, type Filter } from "../../filter.ts";
22

33
/**
44
* Options for filtering items by regular expressions.
@@ -27,19 +27,19 @@ type Detail = {
2727
};
2828

2929
/**
30-
* Creates a Projector that filters items based on regular expression patterns.
30+
* Creates a Filter that filters items based on regular expression patterns.
3131
*
32-
* The `regexp` Projector filters items using `includes` and/or `excludes` patterns.
32+
* The `regexp` Filter filters items using `includes` and/or `excludes` patterns.
3333
* - If `includes` patterns are provided, only items that match at least one pattern are yielded.
3434
* - If `excludes` patterns are provided, any item that matches at least one pattern is excluded.
3535
*
3636
* @param options - Filtering options specifying `includes` and/or `excludes` patterns.
37-
* @returns A Projector that yields items matching the specified patterns.
37+
* @returns A Filter that yields items matching the specified patterns.
3838
*/
3939
export function regexp<T extends Detail>(
4040
{ includes, excludes }: Readonly<Options>,
41-
): Projector<T> {
42-
return defineProjector<T>(async function* (_denops, { items }, { signal }) {
41+
): Filter<T> {
42+
return defineFilter<T>(async function* (_denops, { items }, { signal }) {
4343
signal?.throwIfAborted();
4444

4545
// Process each item and yield only those matching the filter conditions

builtin/modifier/mod.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
// This file is generated by gen-mod.ts
2-
export * from "./noop.ts";
32
export * from "./relative_path.ts";

builtin/modifier/noop.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

builtin/modifier/relative_path.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as fn from "@denops/std/function";
22
import { relative } from "@std/path/relative";
33

44
import type { IdItem } from "../../item.ts";
5-
import { defineProjector, type Projector } from "../../projector.ts";
5+
import { defineModifier, type Modifier } from "../../modifier.ts";
66

77
/**
88
* Represents item details with a file path.
@@ -30,8 +30,8 @@ type DetailAfter = {
3030
export function relativePath<
3131
T extends Detail,
3232
U extends T & DetailAfter,
33-
>(): Projector<T, U> {
34-
return defineProjector(async function* (denops, { items }, { signal }) {
33+
>(): Modifier<T, U> {
34+
return defineModifier<T, U>(async function* (denops, { items }, { signal }) {
3535
// Get the current working directory
3636
const cwd = await fn.getcwd(denops);
3737
signal?.throwIfAborted();
@@ -50,7 +50,7 @@ export function relativePath<
5050
path: relpath,
5151
abspath: item.detail.path,
5252
},
53-
} as IdItem<U>;
53+
} as IdItem<T & U>;
5454
}
5555
});
5656
}

deno.jsonc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
"./builtin/matcher/regexp": "./builtin/matcher/regexp.ts",
3838
"./builtin/matcher/substring": "./builtin/matcher/substring.ts",
3939
"./builtin/modifier": "./builtin/modifier/mod.ts",
40-
"./builtin/modifier/noop": "./builtin/modifier/noop.ts",
4140
"./builtin/modifier/relative-path": "./builtin/modifier/relative_path.ts",
4241
"./builtin/previewer": "./builtin/previewer/mod.ts",
4342
"./builtin/previewer/buffer": "./builtin/previewer/buffer.ts",
@@ -70,10 +69,11 @@
7069
"./config": "./config.ts",
7170
"./coordinator": "./coordinator.ts",
7271
"./curator": "./curator.ts",
72+
"./filter": "./filter.ts",
7373
"./item": "./item.ts",
7474
"./matcher": "./matcher.ts",
75+
"./modifier": "./modifier.ts",
7576
"./previewer": "./previewer.ts",
76-
"./projector": "./projector.ts",
7777
"./renderer": "./renderer.ts",
7878
"./sorter": "./sorter.ts",
7979
"./source": "./source.ts",
@@ -123,7 +123,7 @@
123123
"@std/path": "jsr:@std/path@^1.0.8",
124124
"@std/streams": "jsr:@std/streams@^1.0.8",
125125
"@std/testing": "jsr:@std/testing@^1.0.4",
126-
"@vim-fall/core": "jsr:@vim-fall/core@^0.1.3",
126+
"@vim-fall/core": "jsr:@vim-fall/core@^0.1.4",
127127
"fzf": "npm:fzf@^0.5.2",
128128
"jsr:@vim-fall/std@^0.1.0": "./mod.ts",
129129
"jsr:@vim-fall/std@^0.1.0/builtin": "./builtin/mod.ts"

filter.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { Denops } from "@denops/std";
2+
import type { IdItem } from "@vim-fall/core/item";
3+
4+
import { defineSource, type Source } from "./source.ts";
5+
import { type Curator, defineCurator } from "./curator.ts";
6+
7+
export type FilterParams<T> = {
8+
readonly items: AsyncIterable<IdItem<T>>;
9+
};
10+
11+
export type Filter<T> = (<S extends Source<T> | Curator<T>>(source: S) => S) & {
12+
__phantom?: T;
13+
};
14+
15+
export function defineFilter<T>(
16+
modify: (
17+
denops: Denops,
18+
params: FilterParams<T>,
19+
options: { signal?: AbortSignal },
20+
) => AsyncIterableIterator<IdItem<T>>,
21+
): Filter<T> {
22+
return ((source) => {
23+
if ("collect" in source) {
24+
return defineSource(
25+
(denops, params, options) => {
26+
const items = source.collect(denops, params, options);
27+
return modify(denops, { items }, options);
28+
},
29+
);
30+
} else {
31+
return defineCurator(
32+
(denops, params, options) => {
33+
const items = source.curate(denops, params, options);
34+
return modify(denops, { items }, options);
35+
},
36+
);
37+
}
38+
}) as Filter<T>;
39+
}

filter_test.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { assertEquals } from "@std/assert";
2+
import { assertType, type IsExact } from "@std/testing/types";
3+
4+
import { defineFilter, type Filter, type FilterParams } from "./filter.ts";
5+
import type { Source } from "./source.ts";
6+
import type { Curator } from "./curator.ts";
7+
8+
Deno.test("Filter", async (t) => {
9+
await t.step("with Source", async (t) => {
10+
const filter = (() => {}) as Filter<{ a: string }>;
11+
12+
await t.step("passed type is equal to the type restriction", () => {
13+
const modified = filter({} as Source<{ a: string }>);
14+
assertType<
15+
IsExact<
16+
typeof modified,
17+
Source<{ a: string }>
18+
>
19+
>(true);
20+
});
21+
22+
await t.step("passed type establishes the type restriction", () => {
23+
const modified = filter({} as Source<{ a: string; b: string }>);
24+
assertType<
25+
IsExact<
26+
typeof modified,
27+
Source<{ a: string; b: string }>
28+
>
29+
>(true);
30+
});
31+
32+
await t.step(
33+
"passed type does not establish the type restriction",
34+
() => {
35+
// @ts-expect-error: 'a' is missing
36+
filter({} as Source<{ b: string }>);
37+
},
38+
);
39+
40+
await t.step(
41+
"check if the type constraint correctly triggers the type checking",
42+
() => {
43+
const filter1 = (() => {}) as Filter<{ a: string }>;
44+
const filter2 = (() => {}) as Filter<{ b: string }>;
45+
const filter3 = (() => {}) as Filter<{ c: string }>;
46+
function strictFunction<T extends { a: string }>(_: Filter<T>) {}
47+
strictFunction(filter1);
48+
// @ts-expect-error: 'a' is missing
49+
strictFunction(filter2);
50+
// @ts-expect-error: 'a' is missing
51+
strictFunction(filter3);
52+
},
53+
);
54+
});
55+
56+
await t.step("with Curator", async (t) => {
57+
const filter = (() => {}) as Filter<{ a: string }>;
58+
59+
await t.step("passed type is equal to the type restriction", () => {
60+
const modified = filter({} as Curator<{ a: string }>);
61+
assertType<
62+
IsExact<
63+
typeof modified,
64+
Curator<{ a: string }>
65+
>
66+
>(true);
67+
});
68+
69+
await t.step("passed type establishes the type restriction", () => {
70+
const modified = filter({} as Curator<{ a: string; b: string }>);
71+
assertType<
72+
IsExact<
73+
typeof modified,
74+
Curator<{ a: string; b: string }>
75+
>
76+
>(true);
77+
});
78+
79+
await t.step(
80+
"passed type does not establish the type restriction",
81+
() => {
82+
// @ts-expect-error: 'a' is missing
83+
filter({} as Curator<{ b: string }>);
84+
},
85+
);
86+
87+
await t.step(
88+
"check if the type constraint correctly triggers the type checking",
89+
() => {
90+
const filter1 = (() => {}) as Filter<{ a: string }>;
91+
const filter2 = (() => {}) as Filter<{ b: string }>;
92+
const filter3 = (() => {}) as Filter<{ c: string }>;
93+
function strictFunction<T extends { a: string }>(_: Filter<T>) {}
94+
strictFunction(filter1);
95+
// @ts-expect-error: 'a' is missing
96+
strictFunction(filter2);
97+
// @ts-expect-error: 'a' is missing
98+
strictFunction(filter3);
99+
},
100+
);
101+
});
102+
});
103+
104+
Deno.test("defineFilter", async (t) => {
105+
await t.step("without type constraint", () => {
106+
const filter = defineFilter(async function* (_denops, params) {
107+
// @ts-expect-error: `params` is not type restrained
108+
const _: FilterParams<{ a: string }> = params;
109+
yield* [];
110+
});
111+
assertEquals(typeof filter, "function");
112+
assertType<IsExact<typeof filter, Filter<unknown>>>(true);
113+
});
114+
115+
await t.step("without type constraint T", () => {
116+
const filter = defineFilter<{ a: string }>(
117+
async function* (_denops, params) {
118+
const _: FilterParams<{ a: string }> = params;
119+
yield* [];
120+
},
121+
);
122+
assertEquals(typeof filter, "function");
123+
assertType<IsExact<typeof filter, Filter<{ a: string }>>>(true);
124+
});
125+
});

0 commit comments

Comments
 (0)