Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/common/fuzzyScorer.test.ts
5251 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 assert from 'assert';
7
import { compareItemsByFuzzyScore, FuzzyScore, FuzzyScore2, FuzzyScorerCache, IItemAccessor, IItemScore, pieceToQuery, prepareQuery, scoreFuzzy, scoreFuzzy2, scoreItemFuzzy } from '../../common/fuzzyScorer.js';
8
import { Schemas } from '../../common/network.js';
9
import { basename, dirname, posix, sep, win32 } from '../../common/path.js';
10
import { isWindows } from '../../common/platform.js';
11
import { URI } from '../../common/uri.js';
12
import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js';
13
14
class ResourceAccessorClass implements IItemAccessor<URI> {
15
16
getItemLabel(resource: URI): string {
17
return basename(resource.fsPath);
18
}
19
20
getItemDescription(resource: URI): string {
21
return dirname(resource.fsPath);
22
}
23
24
getItemPath(resource: URI): string {
25
return resource.fsPath;
26
}
27
}
28
29
const ResourceAccessor = new ResourceAccessorClass();
30
31
class ResourceWithSlashAccessorClass implements IItemAccessor<URI> {
32
33
getItemLabel(resource: URI): string {
34
return basename(resource.fsPath);
35
}
36
37
getItemDescription(resource: URI): string {
38
return posix.normalize(dirname(resource.path));
39
}
40
41
getItemPath(resource: URI): string {
42
return posix.normalize(resource.path);
43
}
44
}
45
46
const ResourceWithSlashAccessor = new ResourceWithSlashAccessorClass();
47
48
class ResourceWithBackslashAccessorClass implements IItemAccessor<URI> {
49
50
getItemLabel(resource: URI): string {
51
return basename(resource.fsPath);
52
}
53
54
getItemDescription(resource: URI): string {
55
return win32.normalize(dirname(resource.path));
56
}
57
58
getItemPath(resource: URI): string {
59
return win32.normalize(resource.path);
60
}
61
}
62
63
const ResourceWithBackslashAccessor = new ResourceWithBackslashAccessorClass();
64
65
class NullAccessorClass implements IItemAccessor<URI> {
66
67
getItemLabel(resource: URI): string {
68
return undefined!;
69
}
70
71
getItemDescription(resource: URI): string {
72
return undefined!;
73
}
74
75
getItemPath(resource: URI): string {
76
return undefined!;
77
}
78
}
79
80
function _doScore(target: string, query: string, allowNonContiguousMatches?: boolean): FuzzyScore {
81
const preparedQuery = prepareQuery(query);
82
83
return scoreFuzzy(target, preparedQuery.normalized, preparedQuery.normalizedLowercase, allowNonContiguousMatches ?? !preparedQuery.expectContiguousMatch);
84
}
85
86
function _doScore2(target: string, query: string, matchOffset: number = 0): FuzzyScore2 {
87
const preparedQuery = prepareQuery(query);
88
89
return scoreFuzzy2(target, preparedQuery, 0, matchOffset);
90
}
91
92
function scoreItem<T>(item: T, query: string, allowNonContiguousMatches: boolean, accessor: IItemAccessor<T>, cache: FuzzyScorerCache = Object.create(null)): IItemScore {
93
return scoreItemFuzzy(item, prepareQuery(query), allowNonContiguousMatches, accessor, cache);
94
}
95
96
function compareItemsByScore<T>(itemA: T, itemB: T, query: string, allowNonContiguousMatches: boolean, accessor: IItemAccessor<T>): number {
97
return compareItemsByFuzzyScore(itemA, itemB, prepareQuery(query), allowNonContiguousMatches, accessor, Object.create(null));
98
}
99
100
const NullAccessor = new NullAccessorClass();
101
102
suite('Fuzzy Scorer', () => {
103
104
test('score (fuzzy)', function () {
105
const target = 'HelLo-World';
106
107
const scores: FuzzyScore[] = [];
108
scores.push(_doScore(target, 'HelLo-World', true)); // direct case match
109
scores.push(_doScore(target, 'hello-world', true)); // direct mix-case match
110
scores.push(_doScore(target, 'HW', true)); // direct case prefix (multiple)
111
scores.push(_doScore(target, 'hw', true)); // direct mix-case prefix (multiple)
112
scores.push(_doScore(target, 'H', true)); // direct case prefix
113
scores.push(_doScore(target, 'h', true)); // direct mix-case prefix
114
scores.push(_doScore(target, 'W', true)); // direct case word prefix
115
scores.push(_doScore(target, 'Ld', true)); // in-string case match (multiple)
116
scores.push(_doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit)
117
scores.push(_doScore(target, 'w', true)); // direct mix-case word prefix
118
scores.push(_doScore(target, 'L', true)); // in-string case match
119
scores.push(_doScore(target, 'l', true)); // in-string mix-case match
120
scores.push(_doScore(target, '4', true)); // no match
121
122
// Assert scoring order
123
const sortedScores = scores.concat().sort((a, b) => b[0] - a[0]);
124
assert.deepStrictEqual(scores, sortedScores);
125
126
// Assert scoring positions
127
// let positions = scores[0][1];
128
// assert.strictEqual(positions.length, 'HelLo-World'.length);
129
130
// positions = scores[2][1];
131
// assert.strictEqual(positions.length, 'HW'.length);
132
// assert.strictEqual(positions[0], 0);
133
// assert.strictEqual(positions[1], 6);
134
});
135
136
test('score (non fuzzy)', function () {
137
const target = 'HelLo-World';
138
139
assert.ok(_doScore(target, 'HelLo-World', false)[0] > 0);
140
assert.strictEqual(_doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length);
141
142
assert.ok(_doScore(target, 'hello-world', false)[0] > 0);
143
assert.strictEqual(_doScore(target, 'HW', false)[0], 0);
144
assert.ok(_doScore(target, 'h', false)[0] > 0);
145
assert.ok(_doScore(target, 'ello', false)[0] > 0);
146
assert.ok(_doScore(target, 'ld', false)[0] > 0);
147
assert.strictEqual(_doScore(target, 'eo', false)[0], 0);
148
});
149
150
test('scoreItem - matches are proper', function () {
151
let res = scoreItem(null, 'something', true, ResourceAccessor);
152
assert.ok(!res.score);
153
154
const resource = URI.file('/xyz/some/path/someFile123.txt');
155
156
res = scoreItem(resource, 'something', true, NullAccessor);
157
assert.ok(!res.score);
158
159
// Path Identity
160
const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor);
161
assert.ok(identityRes.score);
162
assert.strictEqual(identityRes.descriptionMatch!.length, 1);
163
assert.strictEqual(identityRes.labelMatch!.length, 1);
164
assert.strictEqual(identityRes.descriptionMatch![0].start, 0);
165
assert.strictEqual(identityRes.descriptionMatch![0].end, ResourceAccessor.getItemDescription(resource).length);
166
assert.strictEqual(identityRes.labelMatch![0].start, 0);
167
assert.strictEqual(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length);
168
169
// Basename Prefix
170
const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor);
171
assert.ok(basenamePrefixRes.score);
172
assert.ok(!basenamePrefixRes.descriptionMatch);
173
assert.strictEqual(basenamePrefixRes.labelMatch!.length, 1);
174
assert.strictEqual(basenamePrefixRes.labelMatch![0].start, 0);
175
assert.strictEqual(basenamePrefixRes.labelMatch![0].end, 'som'.length);
176
177
// Basename Camelcase
178
const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor);
179
assert.ok(basenameCamelcaseRes.score);
180
assert.ok(!basenameCamelcaseRes.descriptionMatch);
181
assert.strictEqual(basenameCamelcaseRes.labelMatch!.length, 2);
182
assert.strictEqual(basenameCamelcaseRes.labelMatch![0].start, 0);
183
assert.strictEqual(basenameCamelcaseRes.labelMatch![0].end, 1);
184
assert.strictEqual(basenameCamelcaseRes.labelMatch![1].start, 4);
185
assert.strictEqual(basenameCamelcaseRes.labelMatch![1].end, 5);
186
187
// Basename Match
188
const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor);
189
assert.ok(basenameRes.score);
190
assert.ok(!basenameRes.descriptionMatch);
191
assert.strictEqual(basenameRes.labelMatch!.length, 2);
192
assert.strictEqual(basenameRes.labelMatch![0].start, 1);
193
assert.strictEqual(basenameRes.labelMatch![0].end, 2);
194
assert.strictEqual(basenameRes.labelMatch![1].start, 4);
195
assert.strictEqual(basenameRes.labelMatch![1].end, 5);
196
197
// Path Match
198
const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor);
199
assert.ok(pathRes.score);
200
assert.ok(pathRes.descriptionMatch);
201
assert.ok(pathRes.labelMatch);
202
assert.strictEqual(pathRes.labelMatch.length, 1);
203
assert.strictEqual(pathRes.labelMatch[0].start, 8);
204
assert.strictEqual(pathRes.labelMatch[0].end, 11);
205
assert.strictEqual(pathRes.descriptionMatch.length, 1);
206
assert.strictEqual(pathRes.descriptionMatch[0].start, 1);
207
assert.strictEqual(pathRes.descriptionMatch[0].end, 4);
208
209
// Ellipsis Match
210
const ellipsisRes = scoreItem(resource, '…me/path/someFile123.txt', true, ResourceAccessor);
211
assert.ok(ellipsisRes.score);
212
assert.ok(pathRes.descriptionMatch);
213
assert.ok(pathRes.labelMatch);
214
assert.strictEqual(pathRes.labelMatch.length, 1);
215
assert.strictEqual(pathRes.labelMatch[0].start, 8);
216
assert.strictEqual(pathRes.labelMatch[0].end, 11);
217
assert.strictEqual(pathRes.descriptionMatch.length, 1);
218
assert.strictEqual(pathRes.descriptionMatch[0].start, 1);
219
assert.strictEqual(pathRes.descriptionMatch[0].end, 4);
220
221
// No Match
222
const noRes = scoreItem(resource, '987', true, ResourceAccessor);
223
assert.ok(!noRes.score);
224
assert.ok(!noRes.labelMatch);
225
assert.ok(!noRes.descriptionMatch);
226
227
// No Exact Match
228
const noExactRes = scoreItem(resource, '"sF"', true, ResourceAccessor);
229
assert.ok(!noExactRes.score);
230
assert.ok(!noExactRes.labelMatch);
231
assert.ok(!noExactRes.descriptionMatch);
232
assert.strictEqual(noRes.score, noExactRes.score);
233
234
// Verify Scores
235
assert.ok(identityRes.score > basenamePrefixRes.score);
236
assert.ok(basenamePrefixRes.score > basenameRes.score);
237
assert.ok(basenameRes.score > pathRes.score);
238
assert.ok(pathRes.score > noRes.score);
239
});
240
241
test('scoreItem - multiple', function () {
242
const resource = URI.file('/xyz/some/path/someFile123.txt');
243
244
const res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor);
245
assert.ok(res1.score);
246
assert.strictEqual(res1.labelMatch?.length, 1);
247
assert.strictEqual(res1.labelMatch[0].start, 0);
248
assert.strictEqual(res1.labelMatch[0].end, 4);
249
assert.strictEqual(res1.descriptionMatch?.length, 1);
250
assert.strictEqual(res1.descriptionMatch[0].start, 1);
251
assert.strictEqual(res1.descriptionMatch[0].end, 4);
252
253
const res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor);
254
assert.ok(res2.score);
255
assert.strictEqual(res1.score, res2.score);
256
assert.strictEqual(res2.labelMatch?.length, 1);
257
assert.strictEqual(res2.labelMatch[0].start, 0);
258
assert.strictEqual(res2.labelMatch[0].end, 4);
259
assert.strictEqual(res2.descriptionMatch?.length, 1);
260
assert.strictEqual(res2.descriptionMatch[0].start, 1);
261
assert.strictEqual(res2.descriptionMatch[0].end, 4);
262
263
const res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor);
264
assert.ok(res3.score);
265
assert.ok(res3.score > res2.score);
266
assert.strictEqual(res3.labelMatch?.length, 1);
267
assert.strictEqual(res3.labelMatch[0].start, 0);
268
assert.strictEqual(res3.labelMatch[0].end, 11);
269
assert.strictEqual(res3.descriptionMatch?.length, 1);
270
assert.strictEqual(res3.descriptionMatch[0].start, 1);
271
assert.strictEqual(res3.descriptionMatch[0].end, 4);
272
273
const res4 = scoreItem(resource, 'path z y', true, ResourceAccessor);
274
assert.ok(res4.score);
275
assert.ok(res4.score < res2.score);
276
assert.strictEqual(res4.labelMatch?.length, 0);
277
assert.strictEqual(res4.descriptionMatch?.length, 2);
278
assert.strictEqual(res4.descriptionMatch[0].start, 2);
279
assert.strictEqual(res4.descriptionMatch[0].end, 4);
280
assert.strictEqual(res4.descriptionMatch[1].start, 10);
281
assert.strictEqual(res4.descriptionMatch[1].end, 14);
282
});
283
284
test('scoreItem - multiple with cache yields different results', function () {
285
const resource = URI.file('/xyz/some/path/someFile123.txt');
286
const cache = {};
287
const res1 = scoreItem(resource, 'xyz sm', true, ResourceAccessor, cache);
288
assert.ok(res1.score);
289
290
// from the cache's perspective this should be a totally different query
291
const res2 = scoreItem(resource, 'xyz "sm"', true, ResourceAccessor, cache);
292
assert.ok(!res2.score);
293
});
294
295
test('scoreItem - invalid input', function () {
296
297
let res = scoreItem(null, null!, true, ResourceAccessor);
298
assert.strictEqual(res.score, 0);
299
300
res = scoreItem(null, 'null', true, ResourceAccessor);
301
assert.strictEqual(res.score, 0);
302
});
303
304
test('scoreItem - optimize for file paths', function () {
305
const resource = URI.file('/xyz/others/spath/some/xsp/file123.txt');
306
307
// xsp is more relevant to the end of the file path even though it matches
308
// fuzzy also in the beginning. we verify the more relevant match at the
309
// end gets returned.
310
const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor);
311
assert.ok(pathRes.score);
312
assert.ok(pathRes.descriptionMatch);
313
assert.ok(pathRes.labelMatch);
314
assert.strictEqual(pathRes.labelMatch.length, 1);
315
assert.strictEqual(pathRes.labelMatch[0].start, 0);
316
assert.strictEqual(pathRes.labelMatch[0].end, 7);
317
assert.strictEqual(pathRes.descriptionMatch.length, 1);
318
assert.strictEqual(pathRes.descriptionMatch[0].start, 23);
319
assert.strictEqual(pathRes.descriptionMatch[0].end, 26);
320
});
321
322
test('scoreItem - avoid match scattering (bug #36119)', function () {
323
const resource = URI.file('projects/ui/cula/ats/target.mk');
324
325
const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor);
326
assert.ok(pathRes.score);
327
assert.ok(pathRes.descriptionMatch);
328
assert.ok(pathRes.labelMatch);
329
assert.strictEqual(pathRes.labelMatch.length, 1);
330
assert.strictEqual(pathRes.labelMatch[0].start, 0);
331
assert.strictEqual(pathRes.labelMatch[0].end, 9);
332
});
333
334
test('scoreItem - prefers more compact matches', function () {
335
const resource = URI.file('/1a111d1/11a1d1/something.txt');
336
337
// expect "ad" to be matched towards the end of the file because the
338
// match is more compact
339
const res = scoreItem(resource, 'ad', true, ResourceAccessor);
340
assert.ok(res.score);
341
assert.ok(res.descriptionMatch);
342
assert.ok(!res.labelMatch!.length);
343
assert.strictEqual(res.descriptionMatch.length, 2);
344
assert.strictEqual(res.descriptionMatch[0].start, 11);
345
assert.strictEqual(res.descriptionMatch[0].end, 12);
346
assert.strictEqual(res.descriptionMatch[1].start, 13);
347
assert.strictEqual(res.descriptionMatch[1].end, 14);
348
});
349
350
test('scoreItem - proper target offset', function () {
351
const resource = URI.file('etem');
352
353
const res = scoreItem(resource, 'teem', true, ResourceAccessor);
354
assert.ok(!res.score);
355
});
356
357
test('scoreItem - proper target offset #2', function () {
358
const resource = URI.file('ede');
359
360
const res = scoreItem(resource, 'de', true, ResourceAccessor);
361
362
assert.strictEqual(res.labelMatch!.length, 1);
363
assert.strictEqual(res.labelMatch![0].start, 1);
364
assert.strictEqual(res.labelMatch![0].end, 3);
365
});
366
367
test('scoreItem - proper target offset #3', function () {
368
const resource = URI.file('/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg');
369
370
const res = scoreItem(resource, 'debug', true, ResourceAccessor);
371
372
assert.strictEqual(res.descriptionMatch!.length, 3);
373
assert.strictEqual(res.descriptionMatch![0].start, 9);
374
assert.strictEqual(res.descriptionMatch![0].end, 10);
375
assert.strictEqual(res.descriptionMatch![1].start, 36);
376
assert.strictEqual(res.descriptionMatch![1].end, 37);
377
assert.strictEqual(res.descriptionMatch![2].start, 40);
378
assert.strictEqual(res.descriptionMatch![2].end, 41);
379
380
assert.strictEqual(res.labelMatch!.length, 2);
381
assert.strictEqual(res.labelMatch![0].start, 9);
382
assert.strictEqual(res.labelMatch![0].end, 10);
383
assert.strictEqual(res.labelMatch![1].start, 20);
384
assert.strictEqual(res.labelMatch![1].end, 21);
385
});
386
387
test('scoreItem - no match unless query contained in sequence', function () {
388
const resource = URI.file('abcde');
389
390
const res = scoreItem(resource, 'edcda', true, ResourceAccessor);
391
assert.ok(!res.score);
392
});
393
394
test('scoreItem - match if using slash or backslash (local, remote resource)', function () {
395
const localResource = URI.file('abcde/super/duper');
396
const remoteResource = URI.from({ scheme: Schemas.vscodeRemote, path: 'abcde/super/duper' });
397
398
for (const resource of [localResource, remoteResource]) {
399
let res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceAccessor);
400
assert.ok(res.score);
401
402
res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithSlashAccessor);
403
assert.ok(res.score);
404
405
res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithBackslashAccessor);
406
assert.ok(res.score);
407
408
res = scoreItem(resource, 'abcde/super/duper', true, ResourceAccessor);
409
assert.ok(res.score);
410
411
res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithSlashAccessor);
412
assert.ok(res.score);
413
414
res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithBackslashAccessor);
415
assert.ok(res.score);
416
}
417
});
418
419
test('scoreItem - ensure upper case bonus only applies on non-consecutive matches (bug #134723)', function () {
420
const resourceWithUpper = URI.file('ASDFasdfasdf');
421
const resourceAllLower = URI.file('asdfasdfasdf');
422
423
assert.ok(scoreItem(resourceAllLower, 'asdf', true, ResourceAccessor).score > scoreItem(resourceWithUpper, 'asdf', true, ResourceAccessor).score);
424
});
425
426
test('compareItemsByScore - identity', function () {
427
const resourceA = URI.file('/some/path/fileA.txt');
428
const resourceB = URI.file('/some/path/other/fileB.txt');
429
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
430
431
// Full resource A path
432
let query = ResourceAccessor.getItemPath(resourceA);
433
434
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
435
assert.strictEqual(res[0], resourceA);
436
assert.strictEqual(res[1], resourceB);
437
assert.strictEqual(res[2], resourceC);
438
439
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
440
assert.strictEqual(res[0], resourceA);
441
assert.strictEqual(res[1], resourceB);
442
assert.strictEqual(res[2], resourceC);
443
444
// Full resource B path
445
query = ResourceAccessor.getItemPath(resourceB);
446
447
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
448
assert.strictEqual(res[0], resourceB);
449
assert.strictEqual(res[1], resourceA);
450
assert.strictEqual(res[2], resourceC);
451
452
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
453
assert.strictEqual(res[0], resourceB);
454
assert.strictEqual(res[1], resourceA);
455
assert.strictEqual(res[2], resourceC);
456
});
457
458
test('compareFilesByScore - basename prefix', function () {
459
const resourceA = URI.file('/some/path/fileA.txt');
460
const resourceB = URI.file('/some/path/other/fileB.txt');
461
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
462
463
// Full resource A basename
464
let query = ResourceAccessor.getItemLabel(resourceA);
465
466
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
467
assert.strictEqual(res[0], resourceA);
468
assert.strictEqual(res[1], resourceB);
469
assert.strictEqual(res[2], resourceC);
470
471
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
472
assert.strictEqual(res[0], resourceA);
473
assert.strictEqual(res[1], resourceB);
474
assert.strictEqual(res[2], resourceC);
475
476
// Full resource B basename
477
query = ResourceAccessor.getItemLabel(resourceB);
478
479
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
480
assert.strictEqual(res[0], resourceB);
481
assert.strictEqual(res[1], resourceA);
482
assert.strictEqual(res[2], resourceC);
483
484
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
485
assert.strictEqual(res[0], resourceB);
486
assert.strictEqual(res[1], resourceA);
487
assert.strictEqual(res[2], resourceC);
488
});
489
490
test('compareFilesByScore - basename camelcase', function () {
491
const resourceA = URI.file('/some/path/fileA.txt');
492
const resourceB = URI.file('/some/path/other/fileB.txt');
493
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
494
495
// resource A camelcase
496
let query = 'fA';
497
498
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
499
assert.strictEqual(res[0], resourceA);
500
assert.strictEqual(res[1], resourceB);
501
assert.strictEqual(res[2], resourceC);
502
503
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
504
assert.strictEqual(res[0], resourceA);
505
assert.strictEqual(res[1], resourceB);
506
assert.strictEqual(res[2], resourceC);
507
508
// resource B camelcase
509
query = 'fB';
510
511
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
512
assert.strictEqual(res[0], resourceB);
513
assert.strictEqual(res[1], resourceA);
514
assert.strictEqual(res[2], resourceC);
515
516
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
517
assert.strictEqual(res[0], resourceB);
518
assert.strictEqual(res[1], resourceA);
519
assert.strictEqual(res[2], resourceC);
520
});
521
522
test('compareFilesByScore - basename scores', function () {
523
const resourceA = URI.file('/some/path/fileA.txt');
524
const resourceB = URI.file('/some/path/other/fileB.txt');
525
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
526
527
// Resource A part of basename
528
let query = 'fileA';
529
530
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
531
assert.strictEqual(res[0], resourceA);
532
assert.strictEqual(res[1], resourceB);
533
assert.strictEqual(res[2], resourceC);
534
535
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
536
assert.strictEqual(res[0], resourceA);
537
assert.strictEqual(res[1], resourceB);
538
assert.strictEqual(res[2], resourceC);
539
540
// Resource B part of basename
541
query = 'fileB';
542
543
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
544
assert.strictEqual(res[0], resourceB);
545
assert.strictEqual(res[1], resourceA);
546
assert.strictEqual(res[2], resourceC);
547
548
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
549
assert.strictEqual(res[0], resourceB);
550
assert.strictEqual(res[1], resourceA);
551
assert.strictEqual(res[2], resourceC);
552
});
553
554
test('compareFilesByScore - path scores', function () {
555
const resourceA = URI.file('/some/path/fileA.txt');
556
const resourceB = URI.file('/some/path/other/fileB.txt');
557
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
558
559
// Resource A part of path
560
let query = 'pathfileA';
561
562
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
563
assert.strictEqual(res[0], resourceA);
564
assert.strictEqual(res[1], resourceB);
565
assert.strictEqual(res[2], resourceC);
566
567
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
568
assert.strictEqual(res[0], resourceA);
569
assert.strictEqual(res[1], resourceB);
570
assert.strictEqual(res[2], resourceC);
571
572
// Resource B part of path
573
query = 'pathfileB';
574
575
res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
576
assert.strictEqual(res[0], resourceB);
577
assert.strictEqual(res[1], resourceA);
578
assert.strictEqual(res[2], resourceC);
579
580
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
581
assert.strictEqual(res[0], resourceB);
582
assert.strictEqual(res[1], resourceA);
583
assert.strictEqual(res[2], resourceC);
584
});
585
586
test('compareFilesByScore - prefer shorter basenames', function () {
587
const resourceA = URI.file('/some/path/fileA.txt');
588
const resourceB = URI.file('/some/path/other/fileBLonger.txt');
589
const resourceC = URI.file('/unrelated/the/path/other/fileC.txt');
590
591
// Resource A part of path
592
const query = 'somepath';
593
594
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
595
assert.strictEqual(res[0], resourceA);
596
assert.strictEqual(res[1], resourceB);
597
assert.strictEqual(res[2], resourceC);
598
599
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
600
assert.strictEqual(res[0], resourceA);
601
assert.strictEqual(res[1], resourceB);
602
assert.strictEqual(res[2], resourceC);
603
});
604
605
test('compareFilesByScore - prefer shorter basenames (match on basename)', function () {
606
const resourceA = URI.file('/some/path/fileA.txt');
607
const resourceB = URI.file('/some/path/other/fileBLonger.txt');
608
const resourceC = URI.file('/unrelated/the/path/other/fileC.txt');
609
610
// Resource A part of path
611
const query = 'file';
612
613
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
614
assert.strictEqual(res[0], resourceA);
615
assert.strictEqual(res[1], resourceC);
616
assert.strictEqual(res[2], resourceB);
617
618
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
619
assert.strictEqual(res[0], resourceA);
620
assert.strictEqual(res[1], resourceC);
621
assert.strictEqual(res[2], resourceB);
622
});
623
624
test('compareFilesByScore - prefer shorter paths', function () {
625
const resourceA = URI.file('/some/path/fileA.txt');
626
const resourceB = URI.file('/some/path/other/fileB.txt');
627
const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');
628
629
// Resource A part of path
630
const query = 'somepath';
631
632
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
633
assert.strictEqual(res[0], resourceA);
634
assert.strictEqual(res[1], resourceB);
635
assert.strictEqual(res[2], resourceC);
636
637
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
638
assert.strictEqual(res[0], resourceA);
639
assert.strictEqual(res[1], resourceB);
640
assert.strictEqual(res[2], resourceC);
641
});
642
643
test('compareFilesByScore - prefer shorter paths (bug #17443)', function () {
644
const resourceA = URI.file('config/test/t1.js');
645
const resourceB = URI.file('config/test.js');
646
const resourceC = URI.file('config/test/t2.js');
647
648
const query = 'co/te';
649
650
const res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
651
assert.strictEqual(res[0], resourceB);
652
assert.strictEqual(res[1], resourceA);
653
assert.strictEqual(res[2], resourceC);
654
});
655
656
test('compareFilesByScore - prefer matches in label over description if scores are otherwise equal', function () {
657
const resourceA = URI.file('parts/quick/arrow-left-dark.svg');
658
const resourceB = URI.file('parts/quickopen/quickopen.ts');
659
660
const query = 'partsquick';
661
662
const res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
663
assert.strictEqual(res[0], resourceB);
664
assert.strictEqual(res[1], resourceA);
665
});
666
667
test('compareFilesByScore - prefer camel case matches', function () {
668
const resourceA = URI.file('config/test/NullPointerException.java');
669
const resourceB = URI.file('config/test/nopointerexception.java');
670
671
for (const query of ['npe', 'NPE']) {
672
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
673
assert.strictEqual(res[0], resourceA);
674
assert.strictEqual(res[1], resourceB);
675
676
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
677
assert.strictEqual(res[0], resourceA);
678
assert.strictEqual(res[1], resourceB);
679
}
680
});
681
682
test('compareFilesByScore - prefer more compact camel case matches', function () {
683
const resourceA = URI.file('config/test/openthisAnythingHandler.js');
684
const resourceB = URI.file('config/test/openthisisnotsorelevantforthequeryAnyHand.js');
685
686
const query = 'AH';
687
688
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
689
assert.strictEqual(res[0], resourceB);
690
assert.strictEqual(res[1], resourceA);
691
692
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
693
assert.strictEqual(res[0], resourceB);
694
assert.strictEqual(res[1], resourceA);
695
});
696
697
test('compareFilesByScore - prefer more compact matches (label)', function () {
698
const resourceA = URI.file('config/test/examasdaple.js');
699
const resourceB = URI.file('config/test/exampleasdaasd.ts');
700
701
const query = 'xp';
702
703
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
704
assert.strictEqual(res[0], resourceB);
705
assert.strictEqual(res[1], resourceA);
706
707
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
708
assert.strictEqual(res[0], resourceB);
709
assert.strictEqual(res[1], resourceA);
710
});
711
712
test('compareFilesByScore - prefer more compact matches (path)', function () {
713
const resourceA = URI.file('config/test/examasdaple/file.js');
714
const resourceB = URI.file('config/test/exampleasdaasd/file.ts');
715
716
const query = 'xp';
717
718
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
719
assert.strictEqual(res[0], resourceB);
720
assert.strictEqual(res[1], resourceA);
721
722
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
723
assert.strictEqual(res[0], resourceB);
724
assert.strictEqual(res[1], resourceA);
725
});
726
727
test('compareFilesByScore - prefer more compact matches (label and path)', function () {
728
const resourceA = URI.file('config/example/thisfile.ts');
729
const resourceB = URI.file('config/24234243244/example/file.js');
730
731
const query = 'exfile';
732
733
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
734
assert.strictEqual(res[0], resourceB);
735
assert.strictEqual(res[1], resourceA);
736
737
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
738
assert.strictEqual(res[0], resourceB);
739
assert.strictEqual(res[1], resourceA);
740
});
741
742
test('compareFilesByScore - avoid match scattering (bug #34210)', function () {
743
const resourceA = URI.file('node_modules1/bundle/lib/model/modules/ot1/index.js');
744
const resourceB = URI.file('node_modules1/bundle/lib/model/modules/un1/index.js');
745
const resourceC = URI.file('node_modules1/bundle/lib/model/modules/modu1/index.js');
746
const resourceD = URI.file('node_modules1/bundle/lib/model/modules/oddl1/index.js');
747
748
let query = isWindows ? 'modu1\\index.js' : 'modu1/index.js';
749
750
let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
751
assert.strictEqual(res[0], resourceC);
752
753
res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
754
assert.strictEqual(res[0], resourceC);
755
756
query = isWindows ? 'un1\\index.js' : 'un1/index.js';
757
758
res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
759
assert.strictEqual(res[0], resourceB);
760
761
res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
762
assert.strictEqual(res[0], resourceB);
763
});
764
765
test('compareFilesByScore - avoid match scattering (bug #21019 1.)', function () {
766
const resourceA = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceLoad/index.js');
767
const resourceB = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceDistribution/index.js');
768
const resourceC = URI.file('app/containers/Services/NetworkData/ServiceDetailTabs/ServiceTabs/StatVideo/index.js');
769
770
const query = 'StatVideoindex';
771
772
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
773
assert.strictEqual(res[0], resourceC);
774
775
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
776
assert.strictEqual(res[0], resourceC);
777
});
778
779
test('compareFilesByScore - avoid match scattering (bug #21019 2.)', function () {
780
const resourceA = URI.file('src/build-helper/store/redux.ts');
781
const resourceB = URI.file('src/repository/store/redux.ts');
782
783
const query = 'reproreduxts';
784
785
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
786
assert.strictEqual(res[0], resourceB);
787
788
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
789
assert.strictEqual(res[0], resourceB);
790
});
791
792
test('compareFilesByScore - avoid match scattering (bug #26649)', function () {
793
const resourceA = URI.file('photobook/src/components/AddPagesButton/index.js');
794
const resourceB = URI.file('photobook/src/components/ApprovalPageHeader/index.js');
795
const resourceC = URI.file('photobook/src/canvasComponents/BookPage/index.js');
796
797
const query = 'bookpageIndex';
798
799
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
800
assert.strictEqual(res[0], resourceC);
801
802
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
803
assert.strictEqual(res[0], resourceC);
804
});
805
806
test('compareFilesByScore - avoid match scattering (bug #33247)', function () {
807
const resourceA = URI.file('ui/src/utils/constants.js');
808
const resourceB = URI.file('ui/src/ui/Icons/index.js');
809
810
const query = isWindows ? 'ui\\icons' : 'ui/icons';
811
812
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
813
assert.strictEqual(res[0], resourceB);
814
815
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
816
assert.strictEqual(res[0], resourceB);
817
});
818
819
test('compareFilesByScore - avoid match scattering (bug #33247 comment)', function () {
820
const resourceA = URI.file('ui/src/components/IDInput/index.js');
821
const resourceB = URI.file('ui/src/ui/Input/index.js');
822
823
const query = isWindows ? 'ui\\input\\index' : 'ui/input/index';
824
825
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
826
assert.strictEqual(res[0], resourceB);
827
828
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
829
assert.strictEqual(res[0], resourceB);
830
});
831
832
test('compareFilesByScore - avoid match scattering (bug #36166)', function () {
833
const resourceA = URI.file('django/contrib/sites/locale/ga/LC_MESSAGES/django.mo');
834
const resourceB = URI.file('django/core/signals.py');
835
836
const query = 'djancosig';
837
838
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
839
assert.strictEqual(res[0], resourceB);
840
841
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
842
assert.strictEqual(res[0], resourceB);
843
});
844
845
test('compareFilesByScore - avoid match scattering (bug #32918)', function () {
846
const resourceA = URI.file('adsys/protected/config.php');
847
const resourceB = URI.file('adsys/protected/framework/smarty/sysplugins/smarty_internal_config.php');
848
const resourceC = URI.file('duowanVideo/wap/protected/config.php');
849
850
const query = 'protectedconfig.php';
851
852
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
853
assert.strictEqual(res[0], resourceA);
854
assert.strictEqual(res[1], resourceC);
855
assert.strictEqual(res[2], resourceB);
856
857
res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
858
assert.strictEqual(res[0], resourceA);
859
assert.strictEqual(res[1], resourceC);
860
assert.strictEqual(res[2], resourceB);
861
});
862
863
test('compareFilesByScore - avoid match scattering (bug #14879)', function () {
864
const resourceA = URI.file('pkg/search/gradient/testdata/constraint_attrMatchString.yml');
865
const resourceB = URI.file('cmd/gradient/main.go');
866
867
const query = 'gradientmain';
868
869
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
870
assert.strictEqual(res[0], resourceB);
871
872
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
873
assert.strictEqual(res[0], resourceB);
874
});
875
876
test('compareFilesByScore - avoid match scattering (bug #14727 1)', function () {
877
const resourceA = URI.file('alpha-beta-cappa.txt');
878
const resourceB = URI.file('abc.txt');
879
880
const query = 'abc';
881
882
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
883
assert.strictEqual(res[0], resourceB);
884
885
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
886
assert.strictEqual(res[0], resourceB);
887
});
888
889
test('compareFilesByScore - avoid match scattering (bug #14727 2)', function () {
890
const resourceA = URI.file('xerxes-yak-zubba/index.js');
891
const resourceB = URI.file('xyz/index.js');
892
893
const query = 'xyz';
894
895
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
896
assert.strictEqual(res[0], resourceB);
897
898
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
899
assert.strictEqual(res[0], resourceB);
900
});
901
902
test('compareFilesByScore - avoid match scattering (bug #18381)', function () {
903
const resourceA = URI.file('AssymblyInfo.cs');
904
const resourceB = URI.file('IAsynchronousTask.java');
905
906
const query = 'async';
907
908
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
909
assert.strictEqual(res[0], resourceB);
910
911
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
912
assert.strictEqual(res[0], resourceB);
913
});
914
915
test('compareFilesByScore - avoid match scattering (bug #35572)', function () {
916
const resourceA = URI.file('static/app/source/angluar/-admin/-organization/-settings/layout/layout.js');
917
const resourceB = URI.file('static/app/source/angular/-admin/-project/-settings/_settings/settings.js');
918
919
const query = 'partisettings';
920
921
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
922
assert.strictEqual(res[0], resourceB);
923
924
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
925
assert.strictEqual(res[0], resourceB);
926
});
927
928
test('compareFilesByScore - avoid match scattering (bug #36810)', function () {
929
const resourceA = URI.file('Trilby.TrilbyTV.Web.Portal/Views/Systems/Index.cshtml');
930
const resourceB = URI.file('Trilby.TrilbyTV.Web.Portal/Areas/Admins/Views/Tips/Index.cshtml');
931
932
const query = 'tipsindex.cshtml';
933
934
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
935
assert.strictEqual(res[0], resourceB);
936
937
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
938
assert.strictEqual(res[0], resourceB);
939
});
940
941
test('compareFilesByScore - prefer shorter hit (bug #20546)', function () {
942
const resourceA = URI.file('editor/core/components/tests/list-view-spec.js');
943
const resourceB = URI.file('editor/core/components/list-view.js');
944
945
const query = 'listview';
946
947
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
948
assert.strictEqual(res[0], resourceB);
949
950
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
951
assert.strictEqual(res[0], resourceB);
952
});
953
954
test('compareFilesByScore - avoid match scattering (bug #12095)', function () {
955
const resourceA = URI.file('src/vs/workbench/contrib/files/common/explorerViewModel.ts');
956
const resourceB = URI.file('src/vs/workbench/contrib/files/browser/views/explorerView.ts');
957
const resourceC = URI.file('src/vs/workbench/contrib/files/browser/views/explorerViewer.ts');
958
959
const query = 'filesexplorerview.ts';
960
961
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
962
assert.strictEqual(res[0], resourceB);
963
964
res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
965
assert.strictEqual(res[0], resourceB);
966
});
967
968
test('compareFilesByScore - prefer case match (bug #96122)', function () {
969
const resourceA = URI.file('lists.php');
970
const resourceB = URI.file('lib/Lists.php');
971
972
const query = 'Lists.php';
973
974
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
975
assert.strictEqual(res[0], resourceB);
976
977
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
978
assert.strictEqual(res[0], resourceB);
979
});
980
981
test('compareFilesByScore - prefer shorter match (bug #103052) - foo bar', function () {
982
const resourceA = URI.file('app/emails/foo.bar.js');
983
const resourceB = URI.file('app/emails/other-footer.other-bar.js');
984
985
for (const query of ['foo bar', 'foobar']) {
986
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
987
assert.strictEqual(res[0], resourceA);
988
assert.strictEqual(res[1], resourceB);
989
990
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
991
assert.strictEqual(res[0], resourceA);
992
assert.strictEqual(res[1], resourceB);
993
}
994
});
995
996
test('compareFilesByScore - prefer shorter match (bug #103052) - payment model', function () {
997
const resourceA = URI.file('app/components/payment/payment.model.js');
998
const resourceB = URI.file('app/components/online-payments-history/online-payments-history.model.js');
999
1000
for (const query of ['payment model', 'paymentmodel']) {
1001
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1002
assert.strictEqual(res[0], resourceA);
1003
assert.strictEqual(res[1], resourceB);
1004
1005
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1006
assert.strictEqual(res[0], resourceA);
1007
assert.strictEqual(res[1], resourceB);
1008
}
1009
});
1010
1011
test('compareFilesByScore - prefer shorter match (bug #103052) - color', function () {
1012
const resourceA = URI.file('app/constants/color.js');
1013
const resourceB = URI.file('app/components/model/input/pick-avatar-color.js');
1014
1015
for (const query of ['color js', 'colorjs']) {
1016
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1017
assert.strictEqual(res[0], resourceA);
1018
assert.strictEqual(res[1], resourceB);
1019
1020
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1021
assert.strictEqual(res[0], resourceA);
1022
assert.strictEqual(res[1], resourceB);
1023
}
1024
});
1025
1026
test('compareFilesByScore - prefer strict case prefix', function () {
1027
const resourceA = URI.file('app/constants/color.js');
1028
const resourceB = URI.file('app/components/model/input/Color.js');
1029
1030
let query = 'Color';
1031
1032
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1033
assert.strictEqual(res[0], resourceB);
1034
assert.strictEqual(res[1], resourceA);
1035
1036
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1037
assert.strictEqual(res[0], resourceB);
1038
assert.strictEqual(res[1], resourceA);
1039
1040
query = 'color';
1041
1042
res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1043
assert.strictEqual(res[0], resourceA);
1044
assert.strictEqual(res[1], resourceB);
1045
1046
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1047
assert.strictEqual(res[0], resourceA);
1048
assert.strictEqual(res[1], resourceB);
1049
});
1050
1051
test('compareFilesByScore - prefer prefix (bug #103052)', function () {
1052
const resourceA = URI.file('test/smoke/src/main.ts');
1053
const resourceB = URI.file('src/vs/editor/common/services/semantikTokensProviderStyling.ts');
1054
1055
const query = 'smoke main.ts';
1056
1057
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1058
assert.strictEqual(res[0], resourceA);
1059
assert.strictEqual(res[1], resourceB);
1060
1061
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1062
assert.strictEqual(res[0], resourceA);
1063
assert.strictEqual(res[1], resourceB);
1064
});
1065
1066
test('compareFilesByScore - boost better prefix match if multiple queries are used', function () {
1067
const resourceA = URI.file('src/vs/workbench/services/host/browser/browserHostService.ts');
1068
const resourceB = URI.file('src/vs/workbench/browser/workbench.ts');
1069
1070
for (const query of ['workbench.ts browser', 'browser workbench.ts', 'browser workbench', 'workbench browser']) {
1071
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1072
assert.strictEqual(res[0], resourceB);
1073
assert.strictEqual(res[1], resourceA);
1074
1075
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1076
assert.strictEqual(res[0], resourceB);
1077
assert.strictEqual(res[1], resourceA);
1078
}
1079
});
1080
1081
test('compareFilesByScore - boost shorter prefix match if multiple queries are used', function () {
1082
const resourceA = URI.file('src/vs/workbench/node/actions/windowActions.ts');
1083
const resourceB = URI.file('src/vs/workbench/electron-node/window.ts');
1084
1085
for (const query of ['window node', 'window.ts node']) {
1086
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1087
assert.strictEqual(res[0], resourceB);
1088
assert.strictEqual(res[1], resourceA);
1089
1090
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1091
assert.strictEqual(res[0], resourceB);
1092
assert.strictEqual(res[1], resourceA);
1093
}
1094
});
1095
1096
test('compareFilesByScore - skip preference on label match when using path sep', function () {
1097
const resourceA = URI.file('djangosite/ufrela/def.py');
1098
const resourceB = URI.file('djangosite/urls/default.py');
1099
1100
const query = 'url/def';
1101
1102
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1103
assert.strictEqual(res[0], resourceB);
1104
assert.strictEqual(res[1], resourceA);
1105
1106
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1107
assert.strictEqual(res[0], resourceB);
1108
assert.strictEqual(res[1], resourceA);
1109
});
1110
1111
test('compareFilesByScore - boost shorter prefix match if multiple queries are used (#99171)', function () {
1112
const resourceA = URI.file('mesh_editor_lifetime_job.h');
1113
const resourceB = URI.file('lifetime_job.h');
1114
1115
const query = 'm life, life m';
1116
1117
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1118
assert.strictEqual(res[0], resourceB);
1119
assert.strictEqual(res[1], resourceA);
1120
1121
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1122
assert.strictEqual(res[0], resourceB);
1123
assert.strictEqual(res[1], resourceA);
1124
});
1125
1126
test('compareFilesByScore - boost consecutive matches in the beginning over end', function () {
1127
const resourceA = URI.file('src/vs/server/node/extensionHostStatusService.ts');
1128
const resourceB = URI.file('src/vs/workbench/browser/parts/notifications/notificationsStatus.ts');
1129
1130
const query = 'notStatus';
1131
1132
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1133
assert.strictEqual(res[0], resourceB);
1134
assert.strictEqual(res[1], resourceA);
1135
1136
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
1137
assert.strictEqual(res[0], resourceB);
1138
assert.strictEqual(res[1], resourceA);
1139
});
1140
1141
test('prepareQuery', () => {
1142
assert.strictEqual(prepareQuery(' f*a ').normalized, 'fa');
1143
assert.strictEqual(prepareQuery(' f…a ').normalized, 'fa');
1144
assert.strictEqual(prepareQuery('main#').normalized, 'main');
1145
assert.strictEqual(prepareQuery('main#').original, 'main#');
1146
assert.strictEqual(prepareQuery('foo*').normalized, 'foo');
1147
assert.strictEqual(prepareQuery('foo*').original, 'foo*');
1148
assert.strictEqual(prepareQuery('model Tester.ts').original, 'model Tester.ts');
1149
assert.strictEqual(prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase());
1150
assert.strictEqual(prepareQuery('model Tester.ts').normalized, 'modelTester.ts');
1151
assert.strictEqual(prepareQuery('model Tester.ts').expectContiguousMatch, false); // doesn't have quotes in it
1152
assert.strictEqual(prepareQuery('Model Tester.ts').normalizedLowercase, 'modeltester.ts');
1153
assert.strictEqual(prepareQuery('ModelTester.ts').containsPathSeparator, false);
1154
assert.strictEqual(prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);
1155
assert.strictEqual(prepareQuery('"hello"').expectContiguousMatch, true);
1156
assert.strictEqual(prepareQuery('"hello"').normalized, 'hello');
1157
1158
// with spaces
1159
let query = prepareQuery('He*llo World');
1160
assert.strictEqual(query.original, 'He*llo World');
1161
assert.strictEqual(query.normalized, 'HelloWorld');
1162
assert.strictEqual(query.normalizedLowercase, 'HelloWorld'.toLowerCase());
1163
assert.strictEqual(query.values?.length, 2);
1164
assert.strictEqual(query.values?.[0].original, 'He*llo');
1165
assert.strictEqual(query.values?.[0].normalized, 'Hello');
1166
assert.strictEqual(query.values?.[0].normalizedLowercase, 'Hello'.toLowerCase());
1167
assert.strictEqual(query.values?.[1].original, 'World');
1168
assert.strictEqual(query.values?.[1].normalized, 'World');
1169
assert.strictEqual(query.values?.[1].normalizedLowercase, 'World'.toLowerCase());
1170
1171
const restoredQuery = pieceToQuery(query.values);
1172
assert.strictEqual(restoredQuery.original, query.original);
1173
assert.strictEqual(restoredQuery.values?.length, query.values?.length);
1174
assert.strictEqual(restoredQuery.containsPathSeparator, query.containsPathSeparator);
1175
1176
// with spaces that are empty
1177
query = prepareQuery(' Hello World ');
1178
assert.strictEqual(query.original, ' Hello World ');
1179
assert.strictEqual(query.originalLowercase, ' Hello World '.toLowerCase());
1180
assert.strictEqual(query.normalized, 'HelloWorld');
1181
assert.strictEqual(query.normalizedLowercase, 'HelloWorld'.toLowerCase());
1182
assert.strictEqual(query.values?.length, 2);
1183
assert.strictEqual(query.values?.[0].original, 'Hello');
1184
assert.strictEqual(query.values?.[0].originalLowercase, 'Hello'.toLowerCase());
1185
assert.strictEqual(query.values?.[0].normalized, 'Hello');
1186
assert.strictEqual(query.values?.[0].normalizedLowercase, 'Hello'.toLowerCase());
1187
assert.strictEqual(query.values?.[1].original, 'World');
1188
assert.strictEqual(query.values?.[1].originalLowercase, 'World'.toLowerCase());
1189
assert.strictEqual(query.values?.[1].normalized, 'World');
1190
assert.strictEqual(query.values?.[1].normalizedLowercase, 'World'.toLowerCase());
1191
1192
// Path related
1193
if (isWindows) {
1194
assert.strictEqual(prepareQuery('C:\\some\\path').pathNormalized, 'C:\\some\\path');
1195
assert.strictEqual(prepareQuery('C:\\some\\path').normalized, 'C:\\some\\path');
1196
assert.strictEqual(prepareQuery('C:\\some\\path').containsPathSeparator, true);
1197
assert.strictEqual(prepareQuery('C:/some/path').pathNormalized, 'C:\\some\\path');
1198
assert.strictEqual(prepareQuery('C:/some/path').normalized, 'C:\\some\\path');
1199
assert.strictEqual(prepareQuery('C:/some/path').containsPathSeparator, true);
1200
} else {
1201
assert.strictEqual(prepareQuery('/some/path').pathNormalized, '/some/path');
1202
assert.strictEqual(prepareQuery('/some/path').normalized, '/some/path');
1203
assert.strictEqual(prepareQuery('/some/path').containsPathSeparator, true);
1204
assert.strictEqual(prepareQuery('\\some\\path').pathNormalized, '/some/path');
1205
assert.strictEqual(prepareQuery('\\some\\path').normalized, '/some/path');
1206
assert.strictEqual(prepareQuery('\\some\\path').containsPathSeparator, true);
1207
}
1208
});
1209
1210
test('fuzzyScore2 (matching)', function () {
1211
const target = 'HelLo-World';
1212
1213
for (const offset of [0, 3]) {
1214
let [score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HelLo-World', offset);
1215
1216
assert.ok(score);
1217
assert.strictEqual(matches.length, 1);
1218
assert.strictEqual(matches[0].start, 0 + offset);
1219
assert.strictEqual(matches[0].end, target.length + offset);
1220
1221
[score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HW', offset);
1222
1223
assert.ok(score);
1224
assert.strictEqual(matches.length, 2);
1225
assert.strictEqual(matches[0].start, 0 + offset);
1226
assert.strictEqual(matches[0].end, 1 + offset);
1227
assert.strictEqual(matches[1].start, 6 + offset);
1228
assert.strictEqual(matches[1].end, 7 + offset);
1229
}
1230
});
1231
1232
test('fuzzyScore2 (multiple queries)', function () {
1233
const target = 'HelLo-World';
1234
1235
const [firstSingleScore, firstSingleMatches] = _doScore2(target, 'HelLo');
1236
const [secondSingleScore, secondSingleMatches] = _doScore2(target, 'World');
1237
const firstAndSecondSingleMatches = [...firstSingleMatches || [], ...secondSingleMatches || []];
1238
1239
let [multiScore, multiMatches] = _doScore2(target, 'HelLo World');
1240
1241
function assertScore() {
1242
assert.ok(multiScore ?? 0 >= ((firstSingleScore ?? 0) + (secondSingleScore ?? 0)));
1243
for (let i = 0; multiMatches && i < multiMatches.length; i++) {
1244
const multiMatch = multiMatches[i];
1245
const firstAndSecondSingleMatch = firstAndSecondSingleMatches[i];
1246
1247
if (multiMatch && firstAndSecondSingleMatch) {
1248
assert.strictEqual(multiMatch.start, firstAndSecondSingleMatch.start);
1249
assert.strictEqual(multiMatch.end, firstAndSecondSingleMatch.end);
1250
} else {
1251
assert.fail();
1252
}
1253
}
1254
}
1255
1256
function assertNoScore() {
1257
assert.strictEqual(multiScore, undefined);
1258
assert.strictEqual(multiMatches.length, 0);
1259
}
1260
1261
assertScore();
1262
1263
[multiScore, multiMatches] = _doScore2(target, 'World HelLo');
1264
assertScore();
1265
1266
[multiScore, multiMatches] = _doScore2(target, 'World HelLo World');
1267
assertScore();
1268
1269
[multiScore, multiMatches] = _doScore2(target, 'World HelLo Nothing');
1270
assertNoScore();
1271
1272
[multiScore, multiMatches] = _doScore2(target, 'More Nothing');
1273
assertNoScore();
1274
});
1275
1276
test('fuzzyScore2 (#95716)', function () {
1277
const target = '# ❌ Wow';
1278
1279
const score = _doScore2(target, '❌');
1280
assert.ok(score);
1281
assert.ok(typeof score[0] === 'number');
1282
assert.ok(score[1].length > 0);
1283
});
1284
1285
test('Using quotes should expect contiguous matches match', function () {
1286
// missing the "i" in the query
1287
assert.strictEqual(_doScore('contiguous', '"contguous"')[0], 0);
1288
1289
const score = _doScore('contiguous', '"contiguous"');
1290
assert.ok(score[0] > 0);
1291
});
1292
1293
test('Using quotes should highlight contiguous indexes', function () {
1294
const score = _doScore('2021-7-26.md', '"26"');
1295
assert.strictEqual(score[0], 14);
1296
1297
// The indexes of the 2 and 6 of "26"
1298
assert.strictEqual(score[1][0], 7);
1299
assert.strictEqual(score[1][1], 8);
1300
});
1301
1302
test('Workspace symbol search with special characters (#, *)', function () {
1303
// Simulates the scenario from the issue where rust-analyzer uses # and * as query modifiers
1304
// The original query (with special chars) should reach the language server
1305
// but normalized query (without special chars) should be used for fuzzy matching
1306
1307
// Test #: User types "main#", language server returns "main" symbol
1308
let query = prepareQuery('main#');
1309
assert.strictEqual(query.original, 'main#'); // Sent to language server
1310
assert.strictEqual(query.normalized, 'main'); // Used for fuzzy matching
1311
let [score, matches] = _doScore2('main', 'main#');
1312
assert.ok(typeof score === 'number' && score > 0, 'Should match "main" symbol when query is "main#"');
1313
assert.ok(matches.length > 0);
1314
1315
// Test *: User types "foo*", language server returns "foo" symbol
1316
query = prepareQuery('foo*');
1317
assert.strictEqual(query.original, 'foo*'); // Sent to language server
1318
assert.strictEqual(query.normalized, 'foo'); // Used for fuzzy matching
1319
[score, matches] = _doScore2('foo', 'foo*');
1320
assert.ok(typeof score === 'number' && score > 0, 'Should match "foo" symbol when query is "foo*"');
1321
assert.ok(matches.length > 0);
1322
1323
// Test both: User types "MyClass#*", should match "MyClass"
1324
query = prepareQuery('MyClass#*');
1325
assert.strictEqual(query.original, 'MyClass#*');
1326
assert.strictEqual(query.normalized, 'MyClass');
1327
[score, matches] = _doScore2('MyClass', 'MyClass#*');
1328
assert.ok(typeof score === 'number' && score > 0, 'Should match "MyClass" symbol when query is "MyClass#*"');
1329
assert.ok(matches.length > 0);
1330
1331
// Test fuzzy matching still works: User types "MC#", should match "MyClass"
1332
query = prepareQuery('MC#');
1333
assert.strictEqual(query.original, 'MC#');
1334
assert.strictEqual(query.normalized, 'MC');
1335
[score, matches] = _doScore2('MyClass', 'MC#');
1336
assert.ok(typeof score === 'number' && score > 0, 'Should fuzzy match "MyClass" symbol when query is "MC#"');
1337
assert.ok(matches.length > 0);
1338
1339
// Make sure leading # or # in the middle are not removed.
1340
query = prepareQuery('#SpecialFunction');
1341
assert.strictEqual(query.original, '#SpecialFunction');
1342
assert.strictEqual(query.normalized, '#SpecialFunction');
1343
[score, matches] = _doScore2('#SpecialFunction', '#SpecialFunction');
1344
assert.ok(typeof score === 'number' && score > 0, 'Should match "#SpecialFunction" symbol when query is "#SpecialFunction"');
1345
assert.ok(matches.length > 0);
1346
1347
// Make sure standalone # is not removed
1348
query = prepareQuery('#');
1349
assert.strictEqual(query.original, '#');
1350
assert.strictEqual(query.normalized, '#', 'Standalone # should not be removed');
1351
[score, matches] = _doScore2('#', '#');
1352
assert.ok(typeof score === 'number' && score > 0, 'Should match "#" symbol when query is "#"');
1353
assert.ok(matches.length > 0);
1354
});
1355
1356
ensureNoDisposablesAreLeakedInTestSuite();
1357
});
1358
1359