Skip to content

Commit e77d538

Browse files
authored
Merge pull request #81 from vim-denops/improve-batch
👍 Define behaviors of BatchHelper/GatherHelper instances outside of the block
2 parents 0d7da4f + 3343e1c commit e77d538

File tree

5 files changed

+150
-6
lines changed

5 files changed

+150
-6
lines changed

denops_std/batch/README.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,33 @@ export async function main(denops: Denops): Promise<void> {
6767
}
6868
```
6969

70+
The `denops` instance passed to the `batch` block is available even outside of
71+
the block. It works like a real `denops` instance, mean that you can write code
72+
like:
73+
74+
```typescript
75+
import { Denops } from "https://deno.land/x/denops_std/mod.ts";
76+
import { batch } from "https://deno.land/x/denops_std/batch/mod.ts";
77+
import * as anonymous from "https://deno.land/x/denops_std/anonymous/mod.ts";
78+
79+
export async function main(denops: Denops): Promise<void> {
80+
await batch(denops, async (denops) => {
81+
const [id] = anonymous.add(denops, () => {
82+
// This code is called outside of 'batch' block
83+
// thus the 'denops' instance works like a real one.
84+
// That's why you can write code like below
85+
if (await denops.eval("&verbose")) {
86+
await denops.cmd("echomsg 'VERBOSE'");
87+
}
88+
await denops.cmd("echomsg 'Hello world'");
89+
});
90+
await denops.cmd(
91+
`command! Test call denops#request('${denops.name}', '${id}', [])`,
92+
);
93+
});
94+
}
95+
```
96+
7097
### gather
7198

7299
Use `gather()` to call multiple denops functions sequentially without overhead
@@ -88,8 +115,8 @@ export async function main(denops: Denops): Promise<void> {
88115

89116
Not like `batch`, the function can NOT be nested.
90117

91-
Note that `denops.call()`, `denops.batch()`, or `denops.eval()` always return
92-
falsy value in `gather()`, indicating that you **cannot** write code like below:
118+
Note that `denops.call()` or `denops.eval()` always return falsy value in
119+
`gather()`, indicating that you **cannot** write code like below:
93120

94121
```typescript
95122
import { Denops } from "https://deno.land/x/denops_std/mod.ts";
@@ -106,3 +133,7 @@ export async function main(denops: Denops): Promise<void> {
106133
});
107134
}
108135
```
136+
137+
The `denops` instance passed to the `gather` block is NOT available outside of
138+
the block. An error is thrown when `denops.call()`, `denops.cmd()`, or
139+
`denops.eval()` is called.

denops_std/batch/batch.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@ import { Context, Denops, Dispatcher, Meta } from "../deps.ts";
33
class BatchHelper implements Denops {
44
#denops: Denops;
55
#calls: [string, ...unknown[]][];
6+
#closed: boolean;
67

78
constructor(denops: Denops) {
89
this.#denops = denops;
910
this.#calls = [];
11+
this.#closed = false;
1012
}
1113

1214
static getCalls(helper: BatchHelper): [string, ...unknown[]][] {
1315
return helper.#calls;
1416
}
1517

18+
static close(helper: BatchHelper): void {
19+
helper.#closed = true;
20+
}
21+
1622
get name(): string {
1723
return this.#denops.name;
1824
}
@@ -30,21 +36,33 @@ class BatchHelper implements Denops {
3036
}
3137

3238
call(fn: string, ...args: unknown[]): Promise<unknown> {
39+
if (this.#closed) {
40+
return this.#denops.call(fn, ...args);
41+
}
3342
this.#calls.push([fn, ...args]);
3443
return Promise.resolve();
3544
}
3645

3746
batch(...calls: [string, ...unknown[]][]): Promise<unknown[]> {
47+
if (this.#closed) {
48+
return this.#denops.batch(...calls);
49+
}
3850
this.#calls.push(...calls);
3951
return Promise.resolve([]);
4052
}
4153

4254
cmd(cmd: string, ctx: Context = {}): Promise<void> {
55+
if (this.#closed) {
56+
return this.#denops.cmd(cmd, ctx);
57+
}
4358
this.call("denops#api#cmd", cmd, ctx);
4459
return Promise.resolve();
4560
}
4661

4762
eval(expr: string, ctx: Context = {}): Promise<unknown> {
63+
if (this.#closed) {
64+
return this.#denops.eval(expr, ctx);
65+
}
4866
this.call("denops#api#eval", expr, ctx);
4967
return Promise.resolve();
5068
}
@@ -62,7 +80,11 @@ export async function batch(
6280
main: (helper: BatchHelper) => Promise<void>,
6381
): Promise<void> {
6482
const helper = new BatchHelper(denops);
65-
await main(helper);
83+
try {
84+
await main(helper);
85+
} finally {
86+
BatchHelper.close(helper);
87+
}
6688
const calls = BatchHelper.getCalls(helper);
6789
await denops.batch(...calls);
6890
}

denops_std/batch/batch_test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { assertEquals, test } from "../deps_test.ts";
2-
import { batch } from "./batch.ts";
2+
import { batch, BatchHelper } from "./batch.ts";
33

44
test({
55
mode: "any",
@@ -130,3 +130,33 @@ test({
130130
]);
131131
},
132132
});
133+
test({
134+
mode: "any",
135+
name:
136+
"The 'helper' instance passed in batch block is available outside of the block",
137+
fn: async (denops) => {
138+
await denops.cmd("let g:denops_batch_test = []");
139+
await denops.cmd(
140+
"command! -nargs=1 DenopsBatchTest let g:denops_batch_test += [<f-args>]",
141+
);
142+
143+
let helper: BatchHelper;
144+
await batch(denops, (denops) => {
145+
helper = denops;
146+
return Promise.resolve();
147+
});
148+
await helper!.call("execute", "DenopsBatchTest 1");
149+
await helper!.batch(["execute", "DenopsBatchTest 1"]);
150+
await helper!.cmd("DenopsBatchTest 1");
151+
assertEquals(
152+
await helper!.eval("add(g:denops_batch_test, string(1))"),
153+
["1", "1", "1", "1"],
154+
);
155+
assertEquals(await denops.eval("g:denops_batch_test") as string[], [
156+
"1",
157+
"1",
158+
"1",
159+
"1",
160+
]);
161+
},
162+
});

denops_std/batch/gather.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@ import { Context, Denops, Dispatcher, Meta } from "../deps.ts";
33
class GatherHelper implements Denops {
44
#denops: Denops;
55
#calls: [string, ...unknown[]][];
6+
#closed: boolean;
67

78
constructor(denops: Denops) {
89
this.#denops = denops;
910
this.#calls = [];
11+
this.#closed = false;
1012
}
1113

1214
static getCalls(helper: GatherHelper): [string, ...unknown[]][] {
1315
return helper.#calls;
1416
}
1517

18+
static close(helper: GatherHelper): void {
19+
helper.#closed = true;
20+
}
21+
1622
get name(): string {
1723
return this.#denops.name;
1824
}
@@ -30,6 +36,11 @@ class GatherHelper implements Denops {
3036
}
3137

3238
call(fn: string, ...args: unknown[]): Promise<unknown> {
39+
if (this.#closed) {
40+
throw new Error(
41+
"GatherHelper instance is not available outside of 'gather' block",
42+
);
43+
}
3344
this.#calls.push([fn, ...args]);
3445
return Promise.resolve();
3546
}
@@ -39,11 +50,21 @@ class GatherHelper implements Denops {
3950
}
4051

4152
cmd(cmd: string, ctx: Context = {}): Promise<void> {
53+
if (this.#closed) {
54+
throw new Error(
55+
"GatherHelper instance is not available outside of 'gather' block",
56+
);
57+
}
4258
this.call("denops#api#cmd", cmd, ctx);
4359
return Promise.resolve();
4460
}
4561

4662
eval(expr: string, ctx: Context = {}): Promise<unknown> {
63+
if (this.#closed) {
64+
throw new Error(
65+
"GatherHelper instance is not available outside of 'gather' block",
66+
);
67+
}
4768
this.call("denops#api#eval", expr, ctx);
4869
return Promise.resolve();
4970
}
@@ -61,7 +82,11 @@ export async function gather(
6182
main: (helper: GatherHelper) => Promise<void>,
6283
): Promise<unknown[]> {
6384
const helper = new GatherHelper(denops);
64-
await main(helper);
85+
try {
86+
await main(helper);
87+
} finally {
88+
GatherHelper.close(helper);
89+
}
6590
const calls = GatherHelper.getCalls(helper);
6691
return await denops.batch(...calls);
6792
}

denops_std/batch/gather_test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { assertEquals, assertThrowsAsync, test } from "../deps_test.ts";
2-
import { gather } from "./gather.ts";
2+
import { gather, GatherHelper } from "./gather.ts";
33

44
test({
55
mode: "any",
@@ -57,3 +57,39 @@ test({
5757
},
5858
prelude: ["let g:denops#enable_workaround_vim_before_8_2_3081 = 1"],
5959
});
60+
test({
61+
mode: "any",
62+
name:
63+
"The 'helper' instance passed in gather block is NOT available outside of the block",
64+
fn: async (denops) => {
65+
await denops.cmd("let g:denops_gather_test = 0");
66+
await denops.cmd("command! DenopsGatherTest let g:denops_gather_test += 1");
67+
68+
let helper: GatherHelper;
69+
await gather(denops, (denops) => {
70+
helper = denops;
71+
return Promise.resolve();
72+
});
73+
await assertThrowsAsync(
74+
async () => {
75+
await helper!.call("execute", "DenopsGatherTest");
76+
},
77+
undefined,
78+
"not available outside",
79+
);
80+
await assertThrowsAsync(
81+
async () => {
82+
await helper.cmd("DenopsGatherTest");
83+
},
84+
undefined,
85+
"not available outside",
86+
);
87+
await assertThrowsAsync(
88+
async () => {
89+
const _ = await helper.eval("v:version");
90+
},
91+
undefined,
92+
"not available outside",
93+
);
94+
},
95+
});

0 commit comments

Comments
 (0)