Skip to content

Commit 27ee06c

Browse files
committed
feat: Add more compose function and tests
1 parent c9ab1a4 commit 27ee06c

16 files changed

+676
-0
lines changed

action.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Denops } from "@denops/std";
22
import type { Action, InvokeParams } from "@vim-fall/core/action";
33

44
import type { Promish } from "./util/_typeutil.ts";
5+
import { type DerivableArray, deriveArray } from "./util/derivable.ts";
56

67
/**
78
* Define an action.
@@ -21,4 +22,25 @@ export function defineAction<T>(
2122
};
2223
}
2324

25+
/**
26+
* Compose multiple actions.
27+
*
28+
* The actions are invoked in the order they are passed.
29+
*
30+
* @param actions The actions to compose.
31+
* @returns The composed action.
32+
*/
33+
export function composeActions<
34+
T,
35+
A extends DerivableArray<[Action<T>, ...Action<T>[]]>,
36+
>(...actions: A): Action<T> {
37+
return {
38+
invoke: async (denops, params, options) => {
39+
for (const action of deriveArray(actions)) {
40+
await action.invoke(denops, params, options);
41+
}
42+
},
43+
};
44+
}
45+
2446
export type * from "@vim-fall/core/action";

action_test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { assertEquals } from "@std/assert";
2+
import { assertType, type IsExact } from "@std/testing/types";
3+
import { DenopsStub } from "@denops/test/stub";
4+
import { type Action, composeActions, defineAction } from "./action.ts";
5+
6+
Deno.test("defineAction", () => {
7+
const action = defineAction(async () => {});
8+
assertEquals(typeof action.invoke, "function");
9+
assertType<IsExact<typeof action, Action<unknown>>>(true);
10+
});
11+
12+
Deno.test("composeActions", async () => {
13+
const results: string[] = [];
14+
const action1 = defineAction(() => {
15+
results.push("action1");
16+
});
17+
const action2 = defineAction(() => {
18+
results.push("action2");
19+
});
20+
const action3 = defineAction(() => {
21+
results.push("action3");
22+
});
23+
const action = composeActions(action2, action1, action3);
24+
const denops = new DenopsStub();
25+
const params = {
26+
item: undefined,
27+
selectedItems: [],
28+
filteredItems: [],
29+
};
30+
await action.invoke(denops, params, {});
31+
assertEquals(results, ["action2", "action1", "action3"]);
32+
});

curator_test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { assertEquals } from "@std/assert";
2+
import { assertType, type IsExact } from "@std/testing/types";
3+
import { type Curator, defineCurator } from "./curator.ts";
4+
5+
Deno.test("defineCurator", () => {
6+
const curator = defineCurator(async function* () {});
7+
assertEquals(typeof curator.curate, "function");
8+
assertType<IsExact<typeof curator, Curator<unknown>>>(true);
9+
});

deno.jsonc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,14 @@
115115
"@core/errorutil": "jsr:@core/errorutil@^1.2.0",
116116
"@core/iterutil": "jsr:@core/iterutil@^0.9.0",
117117
"@denops/std": "jsr:@denops/std@^7.3.0",
118+
"@denops/test": "jsr:@denops/test@^3.0.4",
118119
"@lambdalisue/systemopen": "jsr:@lambdalisue/systemopen@^1.0.0",
119120
"@std/assert": "jsr:@std/assert@^1.0.7",
120121
"@std/fs": "jsr:@std/fs@^1.0.5",
121122
"@std/jsonc": "jsr:@std/jsonc@^1.0.1",
122123
"@std/path": "jsr:@std/path@^1.0.8",
123124
"@std/streams": "jsr:@std/streams@^1.0.8",
125+
"@std/testing": "jsr:@std/testing@^1.0.4",
124126
"@vim-fall/core": "jsr:@vim-fall/core@^0.1.0-pre.0",
125127
"fzf": "npm:fzf@^0.5.2"
126128
}

matcher.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type { Denops } from "@denops/std";
22
import type { IdItem } from "@vim-fall/core/item";
33
import type { Matcher, MatchParams } from "@vim-fall/core/matcher";
44

5+
import { type DerivableArray, deriveArray } from "./util/derivable.ts";
6+
57
/**
68
* Define a matcher.
79
*
@@ -18,4 +20,28 @@ export function defineMatcher<T>(
1820
return { match };
1921
}
2022

23+
/**
24+
* Compose multiple matchers.
25+
*
26+
* The matchers are applied in the order they are passed.
27+
*
28+
* @param matchers The matchers to compose.
29+
* @returns The composed matcher.
30+
*/
31+
export function composeMatchers<
32+
T,
33+
M extends DerivableArray<[Matcher<T>, ...Matcher<T>[]]>,
34+
>(...matchers: M): Matcher<T> {
35+
return {
36+
match: async function* (denops, { items, query }, options) {
37+
for (const matcher of deriveArray(matchers)) {
38+
items = await Array.fromAsync(
39+
matcher.match(denops, { items, query }, options),
40+
);
41+
}
42+
yield* items;
43+
},
44+
};
45+
}
46+
2147
export type * from "@vim-fall/core/matcher";

matcher_test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { assertEquals } from "@std/assert";
2+
import { assertType, type IsExact } from "@std/testing/types";
3+
import { DenopsStub } from "@denops/test/stub";
4+
import { composeMatchers, defineMatcher, type Matcher } from "./matcher.ts";
5+
6+
Deno.test("defineMatcher", () => {
7+
const matcher = defineMatcher(async function* () {});
8+
assertEquals(typeof matcher.match, "function");
9+
assertType<IsExact<typeof matcher, Matcher<unknown>>>(true);
10+
});
11+
12+
Deno.test("composeMatchers", async () => {
13+
const results: string[] = [];
14+
const matcher1 = defineMatcher(async function* (_denops, { items }) {
15+
results.push("matcher1");
16+
yield* items.filter((item) => item.value.includes("1"));
17+
});
18+
const matcher2 = defineMatcher(async function* (_denops, { items }) {
19+
results.push("matcher2");
20+
yield* items.filter((item) => item.value.includes("2"));
21+
});
22+
const matcher3 = defineMatcher(async function* (_denops, { items }) {
23+
results.push("matcher3");
24+
yield* items.filter((item) => item.value.includes("3"));
25+
});
26+
const matcher = composeMatchers(matcher2, matcher1, matcher3);
27+
const denops = new DenopsStub();
28+
const params = {
29+
query: "",
30+
items: Array.from({ length: 1000 }).map((_, id) => ({
31+
id,
32+
value: id.toString(),
33+
detail: undefined,
34+
})),
35+
};
36+
const items = await Array.fromAsync(matcher.match(denops, params, {}));
37+
assertEquals(results, ["matcher2", "matcher1", "matcher3"]);
38+
assertEquals(items.map((item) => item.value), [
39+
"123",
40+
"132",
41+
"213",
42+
"231",
43+
"312",
44+
"321",
45+
]);
46+
});

previewer.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { PreviewItem } from "@vim-fall/core/item";
33
import type { Previewer, PreviewParams } from "@vim-fall/core/previewer";
44

55
import type { Promish } from "./util/_typeutil.ts";
6+
import { type DerivableArray, deriveArray } from "./util/derivable.ts";
67

78
/**
89
* Define a previewer.
@@ -20,4 +21,30 @@ export function definePreviewer<T>(
2021
return { preview };
2122
}
2223

24+
/**
25+
* Compose multiple previewers.
26+
*
27+
* The previewers are tried in the order they are passed.
28+
* Once a previewer returns a non-`undefined` value, the previewing process is
29+
* stopped.
30+
*
31+
* @param previewers The previewers to compose.
32+
* @returns The composed previewer.
33+
*/
34+
export function composePreviewers<
35+
T,
36+
P extends DerivableArray<[Previewer<T>, ...Previewer<T>[]]>,
37+
>(...previewers: P): Previewer<T> {
38+
return {
39+
preview: async (denops, params, options) => {
40+
for (const previewer of deriveArray(previewers)) {
41+
const item = await previewer.preview(denops, params, options);
42+
if (item) {
43+
return item;
44+
}
45+
}
46+
},
47+
};
48+
}
49+
2350
export type * from "@vim-fall/core/previewer";

previewer_test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { assertEquals } from "@std/assert";
2+
import { assertType, type IsExact } from "@std/testing/types";
3+
import { DenopsStub } from "@denops/test/stub";
4+
import {
5+
composePreviewers,
6+
definePreviewer,
7+
type Previewer,
8+
} from "./previewer.ts";
9+
10+
Deno.test("definePreviewer", () => {
11+
const previewer = definePreviewer(async () => {});
12+
assertEquals(typeof previewer.preview, "function");
13+
assertType<IsExact<typeof previewer, Previewer<unknown>>>(true);
14+
});
15+
16+
Deno.test("composePreviewers", async () => {
17+
const results: string[] = [];
18+
const previewer1 = definePreviewer(() => {
19+
results.push("previewer1");
20+
return { content: ["Hello world"] };
21+
});
22+
const previewer2 = definePreviewer(() => {
23+
results.push("previewer2");
24+
});
25+
const previewer3 = definePreviewer(() => {
26+
results.push("previewer3");
27+
return { content: ["Goodbye world"] };
28+
});
29+
const previewer = composePreviewers(previewer2, previewer1, previewer3);
30+
const denops = new DenopsStub();
31+
const params = {
32+
item: {
33+
id: 0,
34+
value: "123",
35+
detail: undefined,
36+
},
37+
};
38+
const item = await previewer.preview(denops, params, {});
39+
assertEquals(results, ["previewer2", "previewer1"]);
40+
assertEquals(item, {
41+
content: ["Hello world"],
42+
});
43+
});

projector.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export function defineProjector<T, U = T>(
3030

3131
/**
3232
* Compose multiple projectors.
33+
*
34+
* The projectors are applied in the order they are passed.
3335
*/
3436
export function composeProjectors<
3537
T extends FirstType<P> extends Derivable<Projector<infer T, unknown>> ? T
@@ -59,6 +61,8 @@ export function composeProjectors<
5961
/**
6062
* Pipe projectors to a source or a curator.
6163
*
64+
* The projectors are applied in the order they are passed.
65+
*
6266
* @param source The source or curator.
6367
* @param projectors The projectors.
6468
* @returns The source or curator.

0 commit comments

Comments
 (0)