Skip to content

Commit ad05e96

Browse files
authored
Merge pull request #136 from vim-denops/fix-echo
🐛 Fix behavior of `helper.echo/echoerr` and add a way to silence messages
2 parents 9700f97 + eda0e5d commit ad05e96

File tree

6 files changed

+168
-24
lines changed

6 files changed

+168
-24
lines changed

denops_std/autocmd/group.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ export type GroupRemoveOptions = Omit<RemoveOptions, "group">;
1414
export async function group(
1515
denops: Denops,
1616
name: string,
17-
main: (helper: GroupHelper) => void,
17+
executor: (helper: GroupHelper) => void,
1818
): Promise<void> {
1919
const commands: string[] = [];
2020
const helper = new GroupHelper(commands);
21-
main(helper);
21+
executor(helper);
2222
await execute(denops, [
2323
`aug ${name}`,
2424
...commands,

denops_std/batch/batch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ class BatchHelper implements Denops {
8686
*/
8787
export async function batch(
8888
denops: Denops,
89-
main: (helper: BatchHelper) => Promise<void>,
89+
executor: (helper: BatchHelper) => Promise<void>,
9090
): Promise<void> {
9191
const helper = new BatchHelper(denops);
9292
try {
93-
await main(helper);
93+
await executor(helper);
9494
} finally {
9595
BatchHelper.close(helper);
9696
}

denops_std/batch/gather.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ class GatherHelper implements Denops {
8888
*/
8989
export async function gather(
9090
denops: Denops,
91-
main: (helper: GatherHelper) => Promise<void>,
91+
executor: (helper: GatherHelper) => Promise<void>,
9292
): Promise<unknown[]> {
9393
const helper = new GatherHelper(denops);
9494
try {
95-
await main(helper);
95+
await executor(helper);
9696
} finally {
9797
GatherHelper.close(helper);
9898
}

denops_std/buffer/buffer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,10 @@ export async function concrete(
256256
/**
257257
* Ensure the executor is executed under the specified buffer
258258
*/
259-
export async function ensure<T = void>(
259+
export async function ensure<T>(
260260
denops: Denops,
261261
bufnr: number,
262-
executor: () => Promise<T>,
262+
executor: () => T,
263263
): Promise<T> {
264264
const [bufnrCur, winidCur, winidNext] = await batch.gather(
265265
denops,
@@ -295,10 +295,10 @@ export async function ensure<T = void>(
295295
/**
296296
* Ensure the executor is executed under a modifiable buffer
297297
*/
298-
export async function modifiable<T = void>(
298+
export async function modifiable<T>(
299299
denops: Denops,
300300
bufnr: number,
301-
executor: () => Promise<T>,
301+
executor: () => T,
302302
): Promise<T> {
303303
const [modified, modifiable, foldmethod] = await batch.gather(
304304
denops,

denops_std/helper/README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,75 @@ export async function main(denops: Denops): Promise<void> {
2222
}
2323
```
2424

25+
##### Warning
26+
27+
In order to make the behavior of Vim and Neovim consistent, `timer_start()` is
28+
used internally not only in Vim but also in Neovim. Note that this means that
29+
you cannot control the message by prepending `silent` when calling it from the
30+
Vim script. If you want to control the message, use the `setSilent` function to
31+
change the silent state to `'silent'` or `'silent!'` in advance, or use the
32+
`ensureSilent` function to fix the silent state to `'silent'` or `'silent!'`
33+
during execution of any function.
34+
35+
### setSilent / getSilent
36+
37+
By setting the silent state with `setSilent`, you can control `silent` and
38+
`silent!` messages as follows.
39+
40+
```typescript
41+
import { Denops } from "../mod.ts";
42+
import { echo, echoerr, getSilent, setSilent } from "../helper/mod.ts";
43+
44+
export async function main(denops: Denops): Promise<void> {
45+
// Because silent is "silent!", `echo` and `echoerr` doesn't show messages.
46+
setSilent(denops, "silent!");
47+
await echo(denops, "Hello\nWorld!");
48+
await echoerr(denops, "This is error message");
49+
50+
// Because silent is "silent", `echo` doesn't show messages.
51+
setSilent(denops, "silent");
52+
await echo(denops, "Hello\nWorld!");
53+
await echoerr(denops, "This is error message");
54+
55+
// Because silent is "", both show messages.
56+
setSilent(denops, "");
57+
await echo(denops, "Hello\nWorld!");
58+
await echoerr(denops, "This is error message");
59+
}
60+
```
61+
62+
Use the `getSilent()` function to get the current silent state.
63+
64+
### ensureSilent
65+
66+
To control `silent` and `silent!` messages while executing a particular
67+
function, use `ensureSilent` as follows
68+
69+
```typescript
70+
import { Denops } from "../mod.ts";
71+
import { echo, echoerr, ensureSilent } from "../helper/mod.ts";
72+
73+
export async function main(denops: Denops): Promise<void> {
74+
// Because silent is "silent!", `echo` and `echoerr` doesn't show messages.
75+
await ensureSilent(denops, "silent!", async () => {
76+
await echo(denops, "Hello\nWorld!");
77+
await echoerr(denops, "This is error message");
78+
});
79+
80+
// Because silent is "silent", `echo` doesn't show messages.
81+
await ensureSilent(denops, "silent", async () => {
82+
await echo(denops, "Hello\nWorld!");
83+
await echoerr(denops, "This is error message");
84+
});
85+
86+
// Because silent is "", both shows messages.
87+
await ensureSilent(denops, "", async () => {
88+
await echo(denops, "Hello\nWorld!");
89+
await echoerr(denops, "This is error message");
90+
});
91+
}
92+
```
93+
2594
### friendlyCall
2695

2796
Use `friendlyCall()` to call given function and print a friendly error message

denops_std/helper/echo.ts

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { batch } from "../batch/mod.ts";
44
import { generateUniqueString } from "../util.ts";
55

66
const cacheKey = Symbol("denops_std/helper/echo");
7+
const cacheKeySilent = Symbol("denops_std/helper/echo/silent");
78
const suffix = generateUniqueString();
89

910
async function ensurePrerequisites(denops: Denops): Promise<string> {
@@ -13,9 +14,11 @@ async function ensurePrerequisites(denops: Denops): Promise<string> {
1314
denops.context[cacheKey] = true;
1415
const script = `
1516
let g:loaded_denops_std_helper_echo_${suffix} = 1
17+
let s:denops_std_helper_echo_timer = 0
1618
1719
function! DenopsStdHelperEcho_${suffix}(message) abort
18-
call timer_start(0, { -> s:DenopsStdHelperEchoInternal_${suffix}(a:message) })
20+
call timer_stop(s:denops_std_helper_echo_timer)
21+
let s:denops_std_helper_echo_timer = timer_start(0, { -> s:DenopsStdHelperEchoInternal_${suffix}(a:message) })
1922
endfunction
2023
2124
function! s:DenopsStdHelperEchoInternal_${suffix}(message) abort
@@ -26,6 +29,39 @@ async function ensurePrerequisites(denops: Denops): Promise<string> {
2629
return suffix;
2730
}
2831

32+
export type Silent = "" | "silent" | "silent!";
33+
34+
/**
35+
* Get global silent status
36+
*/
37+
export function getSilent(denops: Denops): Silent {
38+
return (denops.context[cacheKeySilent] ?? "") as Silent;
39+
}
40+
41+
/**
42+
* Set global silent status
43+
*/
44+
export function setSilent(denops: Denops, silent: Silent): void {
45+
denops.context[cacheKeySilent] = silent;
46+
}
47+
48+
/**
49+
* Ensure global silent status during given function
50+
*/
51+
export async function ensureSilent<T>(
52+
denops: Denops,
53+
silent: Silent,
54+
executor: () => T,
55+
): Promise<T> {
56+
const saved = denops.context[cacheKeySilent];
57+
denops.context[cacheKeySilent] = silent;
58+
try {
59+
return await executor();
60+
} finally {
61+
denops.context[cacheKeySilent] = saved;
62+
}
63+
}
64+
2965
/**
3066
* Echo message as like `echo` on Vim script.
3167
*
@@ -36,29 +72,68 @@ async function ensurePrerequisites(denops: Denops): Promise<string> {
3672
*
3773
* Note that it does nothing and return immediately when denops is
3874
* running as 'test' mode to avoid unwilling test failures.
75+
*
76+
* WARNING:
77+
* In order to make the behavior of Vim and Neovim consistent,
78+
* `timer_start()` is used internally not only in Vim but also in
79+
* Neovim. Note that this means that you cannot control the message
80+
* by prepending `silent` when calling it from the Vim script.
81+
* If you want to control the message, use the `setSilent` function
82+
* to change the silent state to `'silent'` or `'silent!'` in
83+
* advance, or use the `ensureSilent` function to fix the silent state
84+
* to `'silent'` or `'silent!'` during execution of any function.
3985
*/
40-
export function echo(denops: Denops, message: string): Promise<void> {
86+
export function echo(
87+
denops: Denops,
88+
message: string,
89+
): Promise<void> {
90+
const silent = getSilent(denops);
91+
switch (silent) {
92+
case "silent":
93+
case "silent!":
94+
return Promise.resolve();
95+
}
4196
if (denops.meta.mode === "test") {
4297
return Promise.resolve();
43-
} else if (denops.meta.host === "vim") {
44-
return echoVim(denops, message);
4598
} else {
46-
return denops.cmd("redraw | echo message", { message });
99+
return echoInternal(denops, message);
47100
}
48101
}
49102

50103
/**
51104
* Echo message as an error message.
52105
*
53-
* Note that this function just use ErrorMsg highlight and is not equivalent
54-
* to `echoerr` command in Vim/Neovim.
106+
* Note that this function just use ErrorMsg highlight and is not
107+
* equivalent to `echoerr` command in Vim/Neovim.
108+
*
109+
* WARNING:
110+
* In order to make the behavior of Vim and Neovim consistent,
111+
* `timer_start()` is used internally not only in Vim but also in
112+
* Neovim. Note that this means that you cannot control the message
113+
* by prepending `silent` when calling it from the Vim script.
114+
* If you want to control the message, use the `setSilent` function
115+
* to change the silent state to `'silent!'` in advance, or use the
116+
* `ensureSilent` function to fix the silent state to `'silent!'`
117+
* during execution of any function.
55118
*/
56-
export async function echoerr(denops: Denops, message: string): Promise<void> {
57-
await batch(denops, async (denops) => {
58-
await denops.cmd("echohl ErrorMsg");
59-
await echo(denops, message);
60-
await denops.cmd("echohl None");
61-
});
119+
export async function echoerr(
120+
denops: Denops,
121+
message: string,
122+
): Promise<void> {
123+
const silent = getSilent(denops);
124+
switch (silent) {
125+
case "silent!":
126+
return Promise.resolve();
127+
}
128+
if (denops.meta.mode === "test") {
129+
return Promise.resolve();
130+
} else {
131+
await batch(denops, async (denops) => {
132+
await denops.cmd("echohl ErrorMsg");
133+
await echoInternal(denops, message);
134+
await denops.cmd("echohl None");
135+
});
136+
}
62137
}
63138

64139
/**
@@ -85,7 +160,7 @@ export async function friendlyCall(
85160
}
86161
}
87162

88-
async function echoVim(denops: Denops, message: string): Promise<void> {
163+
async function echoInternal(denops: Denops, message: string): Promise<void> {
89164
const suffix = await ensurePrerequisites(denops);
90165
await denops.call(`DenopsStdHelperEcho_${suffix}`, message);
91166
}

0 commit comments

Comments
 (0)