Skip to content

Commit c9f92a6

Browse files
committed
improvement: unknown resource type
1 parent 7e63c0c commit c9f92a6

File tree

10 files changed

+503
-131
lines changed

10 files changed

+503
-131
lines changed

biome.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,21 @@
3939
"trailingCommas": "all",
4040
"semicolons": "always"
4141
}
42-
}
42+
},
43+
"overrides": [
44+
{
45+
"include": ["src/**/*.test.ts"],
46+
"linter": {
47+
"rules": {
48+
"suspicious": {
49+
"noConsole": "warn",
50+
"noExplicitAny": "warn"
51+
},
52+
"complexity": {
53+
"noForEach": "warn"
54+
}
55+
}
56+
}
57+
}
58+
]
4359
}

src/__tests__/completion.test.ts

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
import { describe, expect, it, vi } from "vitest";
21
import { EditorState } from "@codemirror/state";
32
import { EditorView } from "@codemirror/view";
3+
import { describe, expect, it, vi } from "vitest";
44
import { resourceCompletion } from "../resources/completion.js";
5+
import type { Resource } from "../resources/resource.js";
56
import { resourcesField } from "../state.js";
6-
import { Resource } from "../resources/resource.js";
77

88
const createMockResource = (uri: string, name?: string): Resource => ({
99
uri,
10+
type: "mock",
1011
name: name || uri,
1112
description: `Test resource: ${uri}`,
1213
mimeType: "text/plain",
1314
data: undefined,
1415
});
1516

16-
const createMockView = (doc: string, pos: number) => {
17+
const createMockView = (doc: string, _pos: number) => {
1718
const state = EditorState.create({
1819
doc,
1920
extensions: [resourcesField],
@@ -27,7 +28,7 @@ const createMockView = (doc: string, pos: number) => {
2728

2829
const createMockContext = (text: string, pos: number, explicit = false) => {
2930
const view = createMockView(text, pos);
30-
31+
3132
return {
3233
state: view.state,
3334
pos,
@@ -37,15 +38,15 @@ const createMockContext = (text: string, pos: number, explicit = false) => {
3738
const textBefore = text.slice(0, pos);
3839
const match = textBefore.match(regex);
3940
if (!match) return null;
40-
41+
4142
return {
4243
from: pos - match[0].length,
4344
to: pos,
4445
text: match[0],
4546
};
4647
},
4748
aborted: false,
48-
tokenBefore: (types: any) => null,
49+
tokenBefore: (_types: any) => null,
4950
prefix: (text: string, from: number) => text.slice(from, pos),
5051
};
5152
};
@@ -55,32 +56,32 @@ describe("resourceCompletion", () => {
5556
it("should return null when no @ character is present", async () => {
5657
const getResources = vi.fn().mockResolvedValue([]);
5758
const completion = resourceCompletion(getResources);
58-
59+
5960
const context = createMockContext("hello world", 11);
6061
const result = await completion(context);
61-
62+
6263
expect(result).toBeNull();
6364
expect(getResources).not.toHaveBeenCalled();
6465
});
6566

6667
it("should return null when @ is at cursor position and not explicit", async () => {
6768
const getResources = vi.fn().mockResolvedValue([]);
6869
const completion = resourceCompletion(getResources);
69-
70+
7071
const context = createMockContext("hello @", 7, false);
7172
const result = await completion(context);
72-
73+
7374
expect(result).toBeNull();
7475
});
7576

7677
it("should return completions when @ is at cursor position and explicit", async () => {
7778
const resources = [createMockResource("file.txt")];
7879
const getResources = vi.fn().mockResolvedValue(resources);
7980
const completion = resourceCompletion(getResources);
80-
81+
8182
const context = createMockContext("hello @", 7, true);
8283
const result = await completion(context);
83-
84+
8485
expect(result).not.toBeNull();
8586
expect(result?.options).toHaveLength(1);
8687
expect(result?.options[0].label).toBe("@file.txt");
@@ -90,10 +91,10 @@ describe("resourceCompletion", () => {
9091
const resources = [createMockResource("file.txt")];
9192
const getResources = vi.fn().mockResolvedValue(resources);
9293
const completion = resourceCompletion(getResources);
93-
94+
9495
const context = createMockContext("hello @fi", 9);
9596
const result = await completion(context);
96-
97+
9798
expect(result).not.toBeNull();
9899
expect(result?.options).toHaveLength(1);
99100
expect(result?.from).toBe(6); // Start of @fi
@@ -102,10 +103,10 @@ describe("resourceCompletion", () => {
102103
it("should return null when no resources are available", async () => {
103104
const getResources = vi.fn().mockResolvedValue([]);
104105
const completion = resourceCompletion(getResources);
105-
106+
106107
const context = createMockContext("hello @file", 11);
107108
const result = await completion(context);
108-
109+
109110
expect(result).toBeNull();
110111
});
111112
});
@@ -118,12 +119,12 @@ describe("resourceCompletion", () => {
118119
];
119120
const getResources = vi.fn().mockResolvedValue(resources);
120121
const completion = resourceCompletion(getResources);
121-
122+
122123
const context = createMockContext("@", 1, true);
123124
const result = await completion(context);
124-
125+
125126
expect(result?.options).toHaveLength(2);
126-
127+
127128
const option1 = result?.options[0];
128129
expect(option1?.label).toBe("@My File");
129130
expect(option1?.displayLabel).toBe("My File");
@@ -143,10 +144,10 @@ describe("resourceCompletion", () => {
143144
};
144145
const getResources = vi.fn().mockResolvedValue([resource]);
145146
const completion = resourceCompletion(getResources);
146-
147+
147148
const context = createMockContext("@", 1, true);
148149
const result = await completion(context);
149-
150+
150151
const option = result?.options[0];
151152
expect(option?.info).toBeUndefined();
152153
expect(option?.boost).toBe(0);
@@ -162,10 +163,10 @@ describe("resourceCompletion", () => {
162163
};
163164
const getResources = vi.fn().mockResolvedValue([resource]);
164165
const completion = resourceCompletion(getResources);
165-
166+
166167
const context = createMockContext("@", 1, true);
167168
const result = await completion(context);
168-
169+
169170
const option = result?.options[0];
170171
expect(option?.type).toBe("variable");
171172
});
@@ -179,10 +180,10 @@ describe("resourceCompletion", () => {
179180
boost: 999,
180181
});
181182
const completion = resourceCompletion(getResources, formatResource);
182-
183+
183184
const context = createMockContext("@", 1, true);
184185
const result = await completion(context);
185-
186+
186187
const option = result?.options[0];
187188
expect(option?.label).toBe("Custom Label");
188189
expect(option?.type).toBe("custom");
@@ -196,10 +197,10 @@ describe("resourceCompletion", () => {
196197
const resources = [createMockResource("file.txt")];
197198
const getResources = vi.fn().mockResolvedValue(resources);
198199
const completion = resourceCompletion(getResources);
199-
200+
200201
const context = createMockContext("@fi", 3);
201202
const result = await completion(context);
202-
203+
203204
const option = result?.options[0];
204205
expect(option?.apply).toBeDefined();
205206

@@ -210,7 +211,7 @@ describe("resourceCompletion", () => {
210211

211212
if (option?.apply && typeof option.apply === "function") {
212213
option.apply(mockView as any, option, 0, 3);
213-
214+
214215
expect(mockView.dispatch).toHaveBeenCalledWith({
215216
changes: { from: 0, to: 3, insert: "@file.txt " },
216217
});
@@ -223,17 +224,17 @@ describe("resourceCompletion", () => {
223224
const resources = [createMockResource("file.txt")];
224225
const getResources = vi.fn().mockResolvedValue(resources);
225226
const completion = resourceCompletion(getResources);
226-
227+
227228
const view = createMockView("@", 1);
228229
const dispatchSpy = vi.spyOn(view, "dispatch");
229-
230+
230231
const context = {
231232
...createMockContext("@", 1, true),
232233
view,
233234
};
234-
235+
235236
await completion(context);
236-
237+
237238
expect(dispatchSpy).toHaveBeenCalledWith({
238239
effects: expect.anything(),
239240
});
@@ -243,14 +244,14 @@ describe("resourceCompletion", () => {
243244
const resources = [createMockResource("file.txt")];
244245
const getResources = vi.fn().mockResolvedValue(resources);
245246
const completion = resourceCompletion(getResources);
246-
247+
247248
const context = {
248249
...createMockContext("@", 1, true),
249250
view: undefined,
250251
};
251-
252+
252253
const result = await completion(context);
253-
254+
254255
// Should still return completions even without view
255256
expect(result?.options).toHaveLength(1);
256257
});
@@ -261,10 +262,10 @@ describe("resourceCompletion", () => {
261262
const resources = [createMockResource("file.txt")];
262263
const getResources = vi.fn().mockResolvedValue(resources);
263264
const completion = resourceCompletion(getResources);
264-
265+
265266
const context = createMockContext("@file", 5);
266267
const result = await completion(context);
267-
268+
268269
expect(result).not.toBeNull();
269270
expect(result?.from).toBe(0);
270271
});
@@ -273,10 +274,10 @@ describe("resourceCompletion", () => {
273274
const resources = [createMockResource("file.txt")];
274275
const getResources = vi.fn().mockResolvedValue(resources);
275276
const completion = resourceCompletion(getResources);
276-
277+
277278
const context = createMockContext("hello @file", 11);
278279
const result = await completion(context);
279-
280+
280281
expect(result).not.toBeNull();
281282
expect(result?.from).toBe(6);
282283
});
@@ -285,20 +286,20 @@ describe("resourceCompletion", () => {
285286
const resources = [createMockResource("file123.txt")];
286287
const getResources = vi.fn().mockResolvedValue(resources);
287288
const completion = resourceCompletion(getResources);
288-
289+
289290
const context = createMockContext("@file123", 8);
290291
const result = await completion(context);
291-
292+
292293
expect(result).not.toBeNull();
293294
});
294295

295296
it("should not match @ followed by non-word characters", async () => {
296297
const getResources = vi.fn().mockResolvedValue([]);
297298
const completion = resourceCompletion(getResources);
298-
299+
299300
const context = createMockContext("@-file", 6);
300301
const result = await completion(context);
301-
302+
302303
expect(result).toBeNull();
303304
});
304305
});
@@ -307,23 +308,23 @@ describe("resourceCompletion", () => {
307308
it("should handle getResources errors gracefully", async () => {
308309
const getResources = vi.fn().mockRejectedValue(new Error("Failed to fetch"));
309310
const completion = resourceCompletion(getResources);
310-
311+
311312
const context = createMockContext("@file", 5);
312-
313+
313314
await expect(completion(context)).rejects.toThrow("Failed to fetch");
314315
});
315316

316317
it("should handle malformed resources", async () => {
317318
const malformedResource = { uri: "test" } as Resource;
318319
const getResources = vi.fn().mockResolvedValue([malformedResource]);
319320
const completion = resourceCompletion(getResources);
320-
321+
321322
const context = createMockContext("@", 1, true);
322323
const result = await completion(context);
323-
324+
324325
// Should handle gracefully and still create completion
325326
expect(result?.options).toHaveLength(1);
326327
expect(result?.options[0].label).toBe("@undefined"); // name is undefined
327328
});
328329
});
329-
});
330+
});

src/__tests__/decoration.test.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,22 @@ import { invariant } from "../utils.js";
1111
// Helper function to count decorations
1212
function countDecorations(decos: RangeSet<Decoration>): number {
1313
let count = 0;
14-
decos.between(0, Number.POSITIVE_INFINITY, () => {
15-
count++;
14+
decos.between(0, Number.POSITIVE_INFINITY, (_from, _to, decoration) => {
15+
// biome-ignore lint/suspicious/noExplicitAny: tests
16+
if ((decoration as any).widget.resource) {
17+
count++;
18+
}
19+
});
20+
return count;
21+
}
22+
23+
function countUnknownDecorations(decos: RangeSet<Decoration>): number {
24+
let count = 0;
25+
decos.between(0, Number.POSITIVE_INFINITY, (_from, _to, decoration) => {
26+
// biome-ignore lint/suspicious/noExplicitAny: tests
27+
if ((decoration as any).widget.uri) {
28+
count++;
29+
}
1630
});
1731
return count;
1832
}
@@ -124,6 +138,7 @@ describe("resourceDecorations", () => {
124138
let decorations = view.plugin(resourceDecorations)?.decorations;
125139
invariant(decorations !== undefined, "decorations should be defined");
126140
expect(countDecorations(decorations)).toBe(2); // Only 2 resources are known
141+
expect(countUnknownDecorations(decorations)).toBe(1);
127142

128143
// Add another resource
129144
const newResources = [

0 commit comments

Comments
 (0)