Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/linkify/test/node/filePathLinkifier.spec.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { suite, test } from 'vitest';
7
import { isWindows } from '../../../../util/vs/base/common/platform';
8
import { URI } from '../../../../util/vs/base/common/uri';
9
import { PromptReference } from '../../../prompt/common/conversation';
10
import { LinkifyLocationAnchor } from '../../common/linkifiedText';
11
import { assertPartsEqual, createTestLinkifierService, linkify, workspaceFile } from './util';
12
13
14
suite('File Path Linkifier', () => {
15
16
test(`Should create file links from Markdown links`, async () => {
17
const linkifier = createTestLinkifierService(
18
'file.ts',
19
'src/file.ts'
20
);
21
22
assertPartsEqual(
23
(await linkify(linkifier,
24
'[file.ts](file.ts) [src/file.ts](src/file.ts)',
25
)).parts,
26
[
27
new LinkifyLocationAnchor(workspaceFile('file.ts')),
28
` `,
29
new LinkifyLocationAnchor(workspaceFile('src/file.ts'))
30
],
31
);
32
33
assertPartsEqual(
34
(await linkify(linkifier,
35
'[`file.ts`](file.ts) [`src/file.ts`](src/file.ts)',
36
)).parts,
37
[
38
new LinkifyLocationAnchor(workspaceFile('file.ts')),
39
` `,
40
new LinkifyLocationAnchor(workspaceFile('src/file.ts'))
41
]
42
);
43
});
44
45
test(`Should create links for directories`, async () => {
46
{
47
const linkifier = createTestLinkifierService(
48
'dir/'
49
);
50
assertPartsEqual(
51
(await linkify(linkifier,
52
'[dir](dir) [dir/](dir/)',
53
)).parts,
54
[
55
new LinkifyLocationAnchor(workspaceFile('dir')),
56
` `,
57
new LinkifyLocationAnchor(workspaceFile('dir/'))
58
]
59
);
60
}
61
{
62
const linkifier = createTestLinkifierService(
63
'dir1/dir2/'
64
);
65
assertPartsEqual(
66
(await linkify(linkifier,
67
'[dir1/dir2](dir1/dir2) [dir1/dir2/](dir1/dir2/)',
68
)).parts,
69
[
70
new LinkifyLocationAnchor(workspaceFile('dir1/dir2')),
71
` `,
72
new LinkifyLocationAnchor(workspaceFile('dir1/dir2/'))
73
]
74
);
75
}
76
});
77
78
test(`Should create file links for file paths as inline code`, async () => {
79
const linkifier = createTestLinkifierService(
80
'file.ts',
81
'src/file.ts',
82
);
83
assertPartsEqual(
84
(await linkify(linkifier,
85
'`file.ts` `src/file.ts`',
86
)).parts,
87
[
88
new LinkifyLocationAnchor(workspaceFile('file.ts')),
89
` `,
90
new LinkifyLocationAnchor(workspaceFile('src/file.ts'))
91
]
92
);
93
});
94
95
test(`Should create file paths printed as plain text `, async () => {
96
const linkifier = createTestLinkifierService(
97
'file.ts',
98
'src/file.ts',
99
);
100
assertPartsEqual(
101
(await linkify(linkifier,
102
'file.ts src/file.ts'
103
)).parts,
104
[
105
new LinkifyLocationAnchor(workspaceFile('file.ts')),
106
` `,
107
new LinkifyLocationAnchor(workspaceFile('src/file.ts'))
108
]
109
);
110
});
111
112
test(`Should de-linkify files that don't exist`, async () => {
113
const linkifier = createTestLinkifierService();
114
assertPartsEqual(
115
(await linkify(linkifier,
116
'[noSuchFile.ts](noSuchFile.ts) [src/noSuchFile.ts](src/noSuchFile.ts)',
117
)).parts,
118
[
119
'noSuchFile.ts src/noSuchFile.ts'
120
],
121
);
122
});
123
124
test(`Should de-linkify bare file links that haven't been transformed`, async () => {
125
const linkifier = createTestLinkifierService(
126
'file.ts',
127
'src/file.ts',
128
);
129
assertPartsEqual(
130
(await linkify(linkifier,
131
'[text](file.ts) [`symbol` foo](src/file.ts)'
132
)).parts,
133
[
134
'text `symbol` foo',
135
]
136
);
137
});
138
139
test(`Should not create links for https links`, async () => {
140
const linkifier = createTestLinkifierService();
141
assertPartsEqual(
142
(await linkify(linkifier,
143
'[http://example.com](http://example.com)',
144
)).parts,
145
[
146
'[http://example.com](http://example.com)',
147
],
148
);
149
});
150
151
test(`Should handle file paths with spaces in the name`, async () => {
152
const linkifier = createTestLinkifierService(
153
`space file.ts`,
154
'sub space/space file.ts',
155
);
156
157
const result = await linkify(linkifier, [
158
'[space file.ts](space%20file.ts)',
159
'[sub space/space file.ts](sub%20space/space%20file.ts)',
160
'[no such file.ts](no%20such%20file.ts)',
161
'[also not.ts](no%20such%20file.ts)',
162
].join('\n')
163
);
164
assertPartsEqual(
165
result.parts,
166
[
167
new LinkifyLocationAnchor(workspaceFile('space file.ts')),
168
`\n`,
169
new LinkifyLocationAnchor(workspaceFile('sub space/space file.ts')),
170
'\nno such file.ts\nalso not.ts',
171
]
172
);
173
});
174
175
test(`Should handle posix style absolute paths`, async () => {
176
const isFile = URI.file(isWindows ? 'c:\\foo\\isfile.ts' : '/foo/isfile.ts');
177
const noFile = URI.file(isWindows ? 'c:\\foo\\nofile.ts' : '/foo/nofile.ts');
178
const linkifier = createTestLinkifierService(
179
isFile
180
);
181
182
assertPartsEqual(
183
(await linkify(linkifier, [
184
`\`${isFile.fsPath}\``,
185
`\`${noFile.fsPath}\``,
186
].join('\n')
187
)).parts,
188
[
189
new LinkifyLocationAnchor(isFile),
190
`\n\`${noFile.fsPath}\``,
191
]
192
);
193
});
194
195
test(`Should not linkify some common ambagious short paths`, async () => {
196
const linkifier = createTestLinkifierService();
197
assertPartsEqual(
198
(await linkify(linkifier, [
199
'- `.`',
200
'- `..`',
201
'- `/.`',
202
'- `\\.`',
203
'- `/..`',
204
'- `\\..`',
205
'- `/`',
206
'- `\\`',
207
'- `/`',
208
'- `//`',
209
'- `///`',
210
].join('\n')
211
)).parts,
212
[
213
[
214
'- `.`',
215
'- `..`',
216
'- `/.`',
217
'- `\\.`',
218
'- `/..`',
219
'- `\\..`',
220
'- `/`',
221
'- `\\`',
222
'- `/`',
223
'- `//`',
224
'- `///`',
225
].join('\n')
226
]
227
);
228
});
229
230
test(`Should find file links in bold elements`, async () => {
231
const linkifier = createTestLinkifierService(
232
'file.ts',
233
'src/file.ts'
234
);
235
236
assertPartsEqual(
237
(await linkify(linkifier,
238
'**file.ts**',
239
)).parts,
240
[
241
`**`,
242
new LinkifyLocationAnchor(workspaceFile('file.ts')),
243
`**`,
244
],
245
);
246
247
assertPartsEqual(
248
(await linkify(linkifier,
249
'**`file.ts`**',
250
)).parts,
251
[
252
`**`,
253
new LinkifyLocationAnchor(workspaceFile('file.ts')),
254
`**`,
255
],
256
);
257
});
258
259
test(`Should NOT use reference fallback for paths with directory components`, async () => {
260
// When text has a path like ./node_modules/playwright/cli.js, we should NOT
261
// match it to a reference just because the basename (cli.js) matches.
262
// This prevents linking to wrong files when the model mentions paths that don't exist.
263
const linkifier = createTestLinkifierService();
264
const references = [new PromptReference(URI.file('/workspace/src/cli.js'))];
265
266
// Path with directories should NOT link to reference with matching basename
267
const result = await linkify(linkifier,
268
'./node_modules/playwright/cli.js',
269
references
270
);
271
assertPartsEqual(result.parts, [
272
'./node_modules/playwright/cli.js' // Should remain as plain text
273
]);
274
});
275
276
test(`Should use reference fallback for simple filenames`, async () => {
277
// Simple filenames without directory components CAN use reference fallback
278
const linkifier = createTestLinkifierService();
279
const refUri = URI.file('/workspace/src/cli.js');
280
const references = [new PromptReference(refUri)];
281
282
// Simple filename should link to reference with matching basename
283
const result = await linkify(linkifier,
284
'cli.js',
285
references
286
);
287
assertPartsEqual(result.parts, [
288
new LinkifyLocationAnchor(refUri)
289
]);
290
});
291
292
test(`Should NOT use reference fallback for text with code-like characters`, async () => {
293
// Text containing $, {, }, (, ) are likely code snippets, not filenames
294
const linkifier = createTestLinkifierService();
295
const refUri = URI.file('/workspace/src/config.js');
296
const references = [new PromptReference(refUri)];
297
298
// Code-like text should NOT link to reference even if basename matches
299
const result = await linkify(linkifier,
300
'config.${TerminalSettingId',
301
references
302
);
303
assertPartsEqual(result.parts, [
304
'config.${TerminalSettingId' // Should remain as plain text
305
]);
306
});
307
});
308
309