Skip to content

Commit 31d5419

Browse files
authored
Merge pull request #219 from uga-rosa/keymap
👍 Add send keys helper
2 parents b773bfa + db60bbe commit 31d5419

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

denops_std/helper/keymap.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { deferred } from "https://deno.land/std@0.208.0/async/deferred.ts#=";
2+
import { type Denops } from "https://deno.land/x/denops_core@v5.0.0/mod.ts";
3+
import {
4+
exprQuote as q,
5+
ExprString,
6+
isExprString,
7+
useExprString,
8+
} from "./expr_string.ts";
9+
import { batch } from "../batch/mod.ts";
10+
import { register } from "../lambda/mod.ts";
11+
import { feedkeys } from "../function/mod.ts";
12+
import is from "https://deno.land/x/unknownutil@v3.10.0/is.ts";
13+
14+
export type Keys = {
15+
keys: string | ExprString;
16+
remap: boolean;
17+
};
18+
19+
export type KeysSpecifier = Keys | Keys["keys"];
20+
21+
function toArray<T>(x: T | T[]): T[] {
22+
return is.Array(x) ? x : [x];
23+
}
24+
25+
function toKeys(keys: KeysSpecifier): Keys {
26+
if (is.String(keys) || isExprString(keys)) {
27+
return { keys, remap: false };
28+
}
29+
return keys;
30+
}
31+
32+
/**
33+
* Send key sequences synchronously.
34+
* `denops#request` blocks, so note that it can only be used within `denops#notify`.
35+
*
36+
* ```typescript
37+
* import { Denops } from "../mod.ts";
38+
* import * as fn from "../function/mod.ts";
39+
* import { send } from "./keymap.ts";
40+
* import { exprQuote as q } from "./expr_string.ts";
41+
*
42+
* export async function main(denops: Denops): Promise<void> {
43+
* denops.dispatcher = {
44+
* send: async (): Promise<void> => {
45+
* // Let's say the current buffer is "foo".
46+
* await send(denops, "bar");
47+
* // The buffer has been changed!
48+
* // "foobar"
49+
* console.log(await fn.getline(denops, "."));
50+
*
51+
* // Send special keys.
52+
* await send(denops, [
53+
* q`${"\\<BS>".repeat(6)}`,
54+
* "baz",
55+
* ]);
56+
* // "baz"
57+
* console.log(await fn.getline(denops, "."));
58+
*
59+
* // Send remaped keys.
60+
* await send(denops, { keys: q`\<C-l>`, remap: true });
61+
* // "bazsend"
62+
* console.log(await fn.getline(denops, "."));
63+
* },
64+
* };
65+
* await denops.cmd(`inoremap <C-k> <Cmd>call denops#notify('${denops.name}', 'send', [])<CR>`);
66+
* await denops.cmd(`inoremap <C-l> send`);
67+
* }
68+
* ```
69+
*/
70+
export async function send(
71+
denops: Denops,
72+
keys: KeysSpecifier | KeysSpecifier[],
73+
): Promise<void> {
74+
const waiter = deferred<void>();
75+
const id = register(denops, () => waiter.resolve(), { once: true });
76+
await Promise.all([
77+
useExprString(denops, async (denops) => {
78+
await batch(denops, async (denops) => {
79+
const normKeys = toArray(keys).map(toKeys);
80+
for (const key of normKeys) {
81+
await feedkeys(denops, key.keys, key.remap ? "m" : "n");
82+
}
83+
await feedkeys(
84+
denops,
85+
q`\<Cmd>call denops#notify('${denops.name}', '${id}', [])\<CR>`,
86+
"n",
87+
);
88+
});
89+
}),
90+
waiter,
91+
]);
92+
}

denops_std/helper/keymap_test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { assertEquals } from "https://deno.land/std@0.205.0/assert/mod.ts";
2+
import { test } from "https://deno.land/x/denops_test@v1.4.0/mod.ts";
3+
import { is } from "https://deno.land/x/unknownutil@v3.10.0/mod.ts";
4+
import * as fn from "../function/mod.ts";
5+
import { exprQuote as q } from "./expr_string.ts";
6+
import { KeysSpecifier, send } from "./keymap.ts";
7+
8+
function toArray<T>(x: T | T[]): T[] {
9+
return is.Array(x) ? x : [x];
10+
}
11+
12+
type Spec = {
13+
name: string;
14+
keys: KeysSpecifier | KeysSpecifier[];
15+
expect: string;
16+
};
17+
18+
test({
19+
mode: "nvim",
20+
name: "send()",
21+
fn: async (denops, t) => {
22+
const specs: Spec[] = [
23+
{
24+
name: "normal key",
25+
keys: "foo",
26+
expect: "foo",
27+
},
28+
{
29+
name: "special key",
30+
keys: q`foo\<BS>bar\<Left>\<Del>`,
31+
expect: "foba",
32+
},
33+
{
34+
name: "remapped key",
35+
keys: { keys: q`\<C-l>`, remap: true },
36+
expect: "foo",
37+
},
38+
];
39+
await denops.cmd("inoremap <C-l> foo");
40+
41+
for (const spec of specs) {
42+
await t.step({
43+
name: spec.name,
44+
fn: async () => {
45+
await fn.deletebufline(denops, "%", 1, "$");
46+
assertEquals(await fn.getline(denops, 1), "");
47+
await send(denops, [
48+
q`\<Esc>`,
49+
"i",
50+
...toArray(spec.keys),
51+
]);
52+
assertEquals(await fn.getline(denops, 1), spec.expect);
53+
},
54+
});
55+
}
56+
},
57+
});

0 commit comments

Comments
 (0)