Skip to content

Commit eb9ef6a

Browse files
authored
Merge pull request #407 from vim-denops/plugin-type-check-no-loaded
👍 `denops#plugin#check_type()` shows 'no plugins are loaded' message
2 parents 95f076b + 33ff712 commit eb9ef6a

File tree

11 files changed

+2507
-2227
lines changed

11 files changed

+2507
-2227
lines changed

autoload/denops/plugin.vim

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,15 @@ endfunction
7777

7878
function! denops#plugin#check_type(...) abort
7979
if a:0
80-
let l:scripts = [denops#_internal#plugin#get(a:1).script]
80+
const l:plugins = [denops#_internal#plugin#get(a:1)]
8181
else
82-
let l:scripts = denops#_internal#plugin#list()
83-
\->copy()->map({ _, v -> v.script })->filter({ _, v -> v !=# '' })
82+
const l:plugins = denops#_internal#plugin#list()
83+
endif
84+
const l:scripts = l:plugins
85+
\->copy()->map({ _, v -> v.script })->filter({ _, v -> v !=# '' })
86+
if empty(l:scripts)
87+
call denops#_internal#echo#info("No plugins are loaded")
88+
return
8489
endif
8590
let l:args = [g:denops#deno, 'check'] + l:scripts
8691
let l:job = denops#_internal#job#start(l:args, {

doc/denops.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ denops#plugin#reload({plugin}[, {options}])
437437

438438
*denops#plugin#check_type()*
439439
denops#plugin#check_type([{name}])
440-
Runs Deno's type check feature for {name} or all registered plugins.
440+
Runs Deno's type check feature for {name} or all loaded plugins.
441441
The result of the check will be displayed in |message-history|. It
442442
throws an error when {name} does not match the |denops-plugin-name|.
443443

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import {
2+
assertMatch,
3+
assertNotMatch,
4+
assertRejects,
5+
} from "jsr:@std/assert@^1.0.1";
6+
import { join } from "jsr:@std/path@^1.0.2/join";
7+
import { testHost } from "/denops-testutil/host.ts";
8+
import { wait } from "/denops-testutil/wait.ts";
9+
10+
const scriptValid = resolve("dummy_valid_plugin.ts");
11+
12+
testHost({
13+
name: "denops#plugin#check_type()",
14+
mode: "all",
15+
postlude: [
16+
"runtime plugin/denops.vim",
17+
],
18+
fn: async ({ host, t, stderr }) => {
19+
let outputs: string[] = [];
20+
stderr.pipeTo(
21+
new WritableStream({ write: (s) => void outputs.push(s) }),
22+
).catch(() => {});
23+
await wait(() => host.call("eval", "denops#server#status() ==# 'running'"));
24+
await host.call("execute", [
25+
"let g:__test_denops_events = []",
26+
"autocmd User DenopsPlugin* call add(g:__test_denops_events, expand('<amatch>'))",
27+
], "");
28+
29+
await t.step("if no arguments is specified", async (t) => {
30+
await t.step("if no plugins are loaded", async (t) => {
31+
outputs = [];
32+
await host.call("execute", [
33+
"let g:__test_denops_events = []",
34+
`call denops#plugin#check_type()`,
35+
], "");
36+
37+
await t.step("outputs an info message after delayed", async () => {
38+
await wait(() => outputs.join("").includes("No plugins are loaded"));
39+
});
40+
});
41+
42+
await t.step("if some plugins are loaded", async (t) => {
43+
await host.call("execute", [
44+
`call denops#plugin#load('dummyCheckNoArguments', '${scriptValid}')`,
45+
], "");
46+
outputs = [];
47+
await host.call("execute", [
48+
"let g:__test_denops_events = []",
49+
`call denops#plugin#check_type()`,
50+
], "");
51+
52+
await t.step("outputs an info message after delayed", async () => {
53+
await wait(() => outputs.join("").includes("Type check"));
54+
assertMatch(outputs.join(""), /Type check succeeded/);
55+
});
56+
});
57+
58+
await t.step("if not exists plugins are tried to load", async (t) => {
59+
await host.call("execute", [
60+
"call denops#plugin#load('notexistsplugin', 'path-to-not-exists-plugin.ts')",
61+
], "");
62+
outputs = [];
63+
await host.call("execute", [
64+
"let g:__test_denops_events = []",
65+
`call denops#plugin#check_type()`,
66+
], "");
67+
68+
await t.step("outputs an error message after delayed", async () => {
69+
await wait(() => outputs.join("").includes("Type check"));
70+
assertMatch(outputs.join(""), /Type check failed:/);
71+
});
72+
73+
await t.step("does not outputs an usage", () => {
74+
assertNotMatch(outputs.join(""), /Usage:/);
75+
});
76+
});
77+
});
78+
79+
await t.step("if the plugin name is invalid", async (t) => {
80+
await t.step("throws an error", async () => {
81+
// NOTE: '.' is not allowed in plugin name.
82+
await assertRejects(
83+
() => host.call("denops#plugin#check_type", "dummy.invalid"),
84+
Error,
85+
"Invalid plugin name: dummy.invalid",
86+
);
87+
});
88+
});
89+
90+
await t.step("if the plugin is not yet loaded", async (t) => {
91+
outputs = [];
92+
await host.call("execute", [
93+
"let g:__test_denops_events = []",
94+
`call denops#plugin#check_type('notloadedplugin')`,
95+
], "");
96+
97+
await t.step("outputs an info message after delayed", async () => {
98+
await wait(() => outputs.join("").includes("No plugins are loaded"));
99+
});
100+
101+
await t.step("does not outputs an usage", () => {
102+
assertNotMatch(outputs.join(""), /Usage:/);
103+
});
104+
});
105+
106+
await t.step("if the plugin is not exists", async (t) => {
107+
await host.call("execute", [
108+
"call denops#plugin#load('notexistsplugin', 'path-to-not-exists-plugin.ts')",
109+
], "");
110+
outputs = [];
111+
await host.call("execute", [
112+
"let g:__test_denops_events = []",
113+
`call denops#plugin#check_type('notexistsplugin')`,
114+
], "");
115+
116+
await t.step("outputs an error message after delayed", async () => {
117+
await wait(() => outputs.join("").includes("Type check"));
118+
assertMatch(outputs.join(""), /Type check failed:/);
119+
});
120+
121+
await t.step("does not outputs an usage", () => {
122+
assertNotMatch(outputs.join(""), /Usage:/);
123+
});
124+
});
125+
126+
await t.step("if the plugin is loaded", async (t) => {
127+
// Load plugin and wait.
128+
await host.call("execute", [
129+
"let g:__test_denops_events = []",
130+
`call denops#plugin#load('dummyCheckTypeLoaded', '${scriptValid}')`,
131+
], "");
132+
await wait(async () =>
133+
(await host.call("eval", "g:__test_denops_events") as string[])
134+
.includes("DenopsPluginPost:dummyCheckTypeLoaded")
135+
);
136+
137+
outputs = [];
138+
await host.call("execute", [
139+
"let g:__test_denops_events = []",
140+
`call denops#plugin#check_type('dummyCheckTypeLoaded')`,
141+
], "");
142+
143+
await t.step("outputs an info message after delayed", async () => {
144+
await wait(() => outputs.join("").includes("Type check"));
145+
assertMatch(outputs.join(""), /Type check succeeded/);
146+
});
147+
});
148+
149+
await t.step("if the plugin is unloaded", async (t) => {
150+
// Load plugin and wait.
151+
await host.call("execute", [
152+
"let g:__test_denops_events = []",
153+
`call denops#plugin#load('dummyCheckTypeUnloaded', '${scriptValid}')`,
154+
], "");
155+
await wait(async () =>
156+
(await host.call("eval", "g:__test_denops_events") as string[])
157+
.includes("DenopsPluginPost:dummyCheckTypeUnloaded")
158+
);
159+
// Unload plugin and wait.
160+
await host.call("execute", [
161+
"let g:__test_denops_events = []",
162+
`call denops#plugin#unload('dummyCheckTypeUnloaded')`,
163+
], "");
164+
await wait(async () =>
165+
(await host.call("eval", "g:__test_denops_events") as string[])
166+
.includes("DenopsPluginUnloadPost:dummyCheckTypeUnloaded")
167+
);
168+
169+
outputs = [];
170+
await host.call("execute", [
171+
"let g:__test_denops_events = []",
172+
`call denops#plugin#check_type('dummyCheckTypeUnloaded')`,
173+
], "");
174+
175+
await t.step("outputs an info message after delayed", async () => {
176+
await wait(() => outputs.join("").includes("Type check"));
177+
assertMatch(outputs.join(""), /Type check succeeded/);
178+
});
179+
});
180+
181+
await t.step("if the plugin is reloaded", async (t) => {
182+
// Load plugin and wait.
183+
await host.call("execute", [
184+
"let g:__test_denops_events = []",
185+
`call denops#plugin#load('dummyCheckTypeReloaded', '${scriptValid}')`,
186+
], "");
187+
await wait(async () =>
188+
(await host.call("eval", "g:__test_denops_events") as string[])
189+
.includes("DenopsPluginPost:dummyCheckTypeReloaded")
190+
);
191+
// Reload plugin and wait.
192+
await host.call("execute", [
193+
"let g:__test_denops_events = []",
194+
`call denops#plugin#reload('dummyCheckTypeReloaded')`,
195+
], "");
196+
await wait(async () =>
197+
(await host.call("eval", "g:__test_denops_events") as string[])
198+
.includes("DenopsPluginPost:dummyCheckTypeReloaded")
199+
);
200+
201+
outputs = [];
202+
await host.call("execute", [
203+
"let g:__test_denops_events = []",
204+
`call denops#plugin#check_type('dummyCheckTypeReloaded')`,
205+
], "");
206+
207+
await t.step("outputs an info message after delayed", async () => {
208+
await wait(() => outputs.join("").includes("Type check"));
209+
assertMatch(outputs.join(""), /Type check succeeded/);
210+
});
211+
});
212+
},
213+
});
214+
215+
/** Resolve testdata script path. */
216+
function resolve(path: string): string {
217+
return join(import.meta.dirname!, `../../../testdata/${path}`);
218+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
assertArrayIncludes,
3+
assertEquals,
4+
assertMatch,
5+
} from "jsr:@std/assert@^1.0.1";
6+
import { delay } from "jsr:@std/async@^0.224.0";
7+
import { join } from "jsr:@std/path@^1.0.2/join";
8+
import { testHost } from "/denops-testutil/host.ts";
9+
import { wait } from "/denops-testutil/wait.ts";
10+
11+
const MESSAGE_DELAY = 200; // msc
12+
13+
const runtimepathPlugin = resolve("dummy_plugins");
14+
15+
testHost({
16+
name: "denops#plugin#discover()",
17+
mode: "all",
18+
postlude: [
19+
"runtime plugin/denops.vim",
20+
],
21+
fn: async ({ host, t, stderr }) => {
22+
let outputs: string[] = [];
23+
stderr.pipeTo(
24+
new WritableStream({ write: (s) => void outputs.push(s) }),
25+
).catch(() => {});
26+
await wait(() => host.call("eval", "denops#server#status() ==# 'running'"));
27+
await host.call("execute", [
28+
"let g:__test_denops_events = []",
29+
"autocmd User DenopsPlugin* call add(g:__test_denops_events, expand('<amatch>'))",
30+
], "");
31+
32+
outputs = [];
33+
await host.call("execute", [
34+
`set runtimepath+=${await host.call("fnameescape", runtimepathPlugin)}`,
35+
`call denops#plugin#discover()`,
36+
], "");
37+
38+
await t.step("loads denops plugins", async () => {
39+
const loaded_events = [
40+
"DenopsPluginPost:",
41+
"DenopsPluginFail:",
42+
];
43+
await wait(async () =>
44+
(await host.call("eval", "g:__test_denops_events") as string[])
45+
.filter((ev) => loaded_events.some((name) => ev.startsWith(name)))
46+
.length >= 2
47+
);
48+
});
49+
50+
await t.step("fires DenopsPlugin* events", async () => {
51+
assertArrayIncludes(
52+
await host.call("eval", "g:__test_denops_events") as string[],
53+
[
54+
"DenopsPluginPre:dummy_valid",
55+
"DenopsPluginPost:dummy_valid",
56+
"DenopsPluginPre:dummy_invalid",
57+
"DenopsPluginFail:dummy_invalid",
58+
],
59+
);
60+
});
61+
62+
await t.step("does not load invaid name plugins", async () => {
63+
const valid_names = [
64+
":dummy_valid",
65+
":dummy_invalid",
66+
] as const;
67+
const actual =
68+
(await host.call("eval", "g:__test_denops_events") as string[])
69+
.filter((ev) => !valid_names.some((name) => ev.endsWith(name)));
70+
assertEquals(actual, []);
71+
});
72+
73+
await t.step("calls the plugin entrypoint", () => {
74+
assertMatch(outputs.join(""), /Hello, Denops!/);
75+
});
76+
77+
await t.step("outputs an error message after delayed", async () => {
78+
await delay(MESSAGE_DELAY);
79+
assertMatch(
80+
outputs.join(""),
81+
/Failed to load plugin 'dummy_invalid': Error: This is dummy error/,
82+
);
83+
});
84+
},
85+
});
86+
87+
/** Resolve testdata script path. */
88+
function resolve(path: string): string {
89+
return join(import.meta.dirname!, `../../../testdata/${path}`);
90+
}

0 commit comments

Comments
 (0)