Path: blob/main/src/vs/base/test/common/fuzzyScorer.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { compareItemsByFuzzyScore, FuzzyScore, FuzzyScore2, FuzzyScorerCache, IItemAccessor, IItemScore, pieceToQuery, prepareQuery, scoreFuzzy, scoreFuzzy2, scoreItemFuzzy } from '../../common/fuzzyScorer.js';7import { Schemas } from '../../common/network.js';8import { basename, dirname, posix, sep, win32 } from '../../common/path.js';9import { isWindows } from '../../common/platform.js';10import { URI } from '../../common/uri.js';11import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js';1213class ResourceAccessorClass implements IItemAccessor<URI> {1415getItemLabel(resource: URI): string {16return basename(resource.fsPath);17}1819getItemDescription(resource: URI): string {20return dirname(resource.fsPath);21}2223getItemPath(resource: URI): string {24return resource.fsPath;25}26}2728const ResourceAccessor = new ResourceAccessorClass();2930class ResourceWithSlashAccessorClass implements IItemAccessor<URI> {3132getItemLabel(resource: URI): string {33return basename(resource.fsPath);34}3536getItemDescription(resource: URI): string {37return posix.normalize(dirname(resource.path));38}3940getItemPath(resource: URI): string {41return posix.normalize(resource.path);42}43}4445const ResourceWithSlashAccessor = new ResourceWithSlashAccessorClass();4647class ResourceWithBackslashAccessorClass implements IItemAccessor<URI> {4849getItemLabel(resource: URI): string {50return basename(resource.fsPath);51}5253getItemDescription(resource: URI): string {54return win32.normalize(dirname(resource.path));55}5657getItemPath(resource: URI): string {58return win32.normalize(resource.path);59}60}6162const ResourceWithBackslashAccessor = new ResourceWithBackslashAccessorClass();6364class NullAccessorClass implements IItemAccessor<URI> {6566getItemLabel(resource: URI): string {67return undefined!;68}6970getItemDescription(resource: URI): string {71return undefined!;72}7374getItemPath(resource: URI): string {75return undefined!;76}77}7879function _doScore(target: string, query: string, allowNonContiguousMatches?: boolean): FuzzyScore {80const preparedQuery = prepareQuery(query);8182return scoreFuzzy(target, preparedQuery.normalized, preparedQuery.normalizedLowercase, allowNonContiguousMatches ?? !preparedQuery.expectContiguousMatch);83}8485function _doScore2(target: string, query: string, matchOffset: number = 0): FuzzyScore2 {86const preparedQuery = prepareQuery(query);8788return scoreFuzzy2(target, preparedQuery, 0, matchOffset);89}9091function scoreItem<T>(item: T, query: string, allowNonContiguousMatches: boolean, accessor: IItemAccessor<T>, cache: FuzzyScorerCache = Object.create(null)): IItemScore {92return scoreItemFuzzy(item, prepareQuery(query), allowNonContiguousMatches, accessor, cache);93}9495function compareItemsByScore<T>(itemA: T, itemB: T, query: string, allowNonContiguousMatches: boolean, accessor: IItemAccessor<T>): number {96return compareItemsByFuzzyScore(itemA, itemB, prepareQuery(query), allowNonContiguousMatches, accessor, Object.create(null));97}9899const NullAccessor = new NullAccessorClass();100101suite('Fuzzy Scorer', () => {102103test('score (fuzzy)', function () {104const target = 'HelLo-World';105106const scores: FuzzyScore[] = [];107scores.push(_doScore(target, 'HelLo-World', true)); // direct case match108scores.push(_doScore(target, 'hello-world', true)); // direct mix-case match109scores.push(_doScore(target, 'HW', true)); // direct case prefix (multiple)110scores.push(_doScore(target, 'hw', true)); // direct mix-case prefix (multiple)111scores.push(_doScore(target, 'H', true)); // direct case prefix112scores.push(_doScore(target, 'h', true)); // direct mix-case prefix113scores.push(_doScore(target, 'W', true)); // direct case word prefix114scores.push(_doScore(target, 'Ld', true)); // in-string case match (multiple)115scores.push(_doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit)116scores.push(_doScore(target, 'w', true)); // direct mix-case word prefix117scores.push(_doScore(target, 'L', true)); // in-string case match118scores.push(_doScore(target, 'l', true)); // in-string mix-case match119scores.push(_doScore(target, '4', true)); // no match120121// Assert scoring order122const sortedScores = scores.concat().sort((a, b) => b[0] - a[0]);123assert.deepStrictEqual(scores, sortedScores);124125// Assert scoring positions126// let positions = scores[0][1];127// assert.strictEqual(positions.length, 'HelLo-World'.length);128129// positions = scores[2][1];130// assert.strictEqual(positions.length, 'HW'.length);131// assert.strictEqual(positions[0], 0);132// assert.strictEqual(positions[1], 6);133});134135test('score (non fuzzy)', function () {136const target = 'HelLo-World';137138assert.ok(_doScore(target, 'HelLo-World', false)[0] > 0);139assert.strictEqual(_doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length);140141assert.ok(_doScore(target, 'hello-world', false)[0] > 0);142assert.strictEqual(_doScore(target, 'HW', false)[0], 0);143assert.ok(_doScore(target, 'h', false)[0] > 0);144assert.ok(_doScore(target, 'ello', false)[0] > 0);145assert.ok(_doScore(target, 'ld', false)[0] > 0);146assert.strictEqual(_doScore(target, 'eo', false)[0], 0);147});148149test('scoreItem - matches are proper', function () {150let res = scoreItem(null, 'something', true, ResourceAccessor);151assert.ok(!res.score);152153const resource = URI.file('/xyz/some/path/someFile123.txt');154155res = scoreItem(resource, 'something', true, NullAccessor);156assert.ok(!res.score);157158// Path Identity159const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor);160assert.ok(identityRes.score);161assert.strictEqual(identityRes.descriptionMatch!.length, 1);162assert.strictEqual(identityRes.labelMatch!.length, 1);163assert.strictEqual(identityRes.descriptionMatch![0].start, 0);164assert.strictEqual(identityRes.descriptionMatch![0].end, ResourceAccessor.getItemDescription(resource).length);165assert.strictEqual(identityRes.labelMatch![0].start, 0);166assert.strictEqual(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length);167168// Basename Prefix169const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor);170assert.ok(basenamePrefixRes.score);171assert.ok(!basenamePrefixRes.descriptionMatch);172assert.strictEqual(basenamePrefixRes.labelMatch!.length, 1);173assert.strictEqual(basenamePrefixRes.labelMatch![0].start, 0);174assert.strictEqual(basenamePrefixRes.labelMatch![0].end, 'som'.length);175176// Basename Camelcase177const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor);178assert.ok(basenameCamelcaseRes.score);179assert.ok(!basenameCamelcaseRes.descriptionMatch);180assert.strictEqual(basenameCamelcaseRes.labelMatch!.length, 2);181assert.strictEqual(basenameCamelcaseRes.labelMatch![0].start, 0);182assert.strictEqual(basenameCamelcaseRes.labelMatch![0].end, 1);183assert.strictEqual(basenameCamelcaseRes.labelMatch![1].start, 4);184assert.strictEqual(basenameCamelcaseRes.labelMatch![1].end, 5);185186// Basename Match187const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor);188assert.ok(basenameRes.score);189assert.ok(!basenameRes.descriptionMatch);190assert.strictEqual(basenameRes.labelMatch!.length, 2);191assert.strictEqual(basenameRes.labelMatch![0].start, 1);192assert.strictEqual(basenameRes.labelMatch![0].end, 2);193assert.strictEqual(basenameRes.labelMatch![1].start, 4);194assert.strictEqual(basenameRes.labelMatch![1].end, 5);195196// Path Match197const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor);198assert.ok(pathRes.score);199assert.ok(pathRes.descriptionMatch);200assert.ok(pathRes.labelMatch);201assert.strictEqual(pathRes.labelMatch.length, 1);202assert.strictEqual(pathRes.labelMatch[0].start, 8);203assert.strictEqual(pathRes.labelMatch[0].end, 11);204assert.strictEqual(pathRes.descriptionMatch.length, 1);205assert.strictEqual(pathRes.descriptionMatch[0].start, 1);206assert.strictEqual(pathRes.descriptionMatch[0].end, 4);207208// No Match209const noRes = scoreItem(resource, '987', true, ResourceAccessor);210assert.ok(!noRes.score);211assert.ok(!noRes.labelMatch);212assert.ok(!noRes.descriptionMatch);213214// No Exact Match215const noExactRes = scoreItem(resource, '"sF"', true, ResourceAccessor);216assert.ok(!noExactRes.score);217assert.ok(!noExactRes.labelMatch);218assert.ok(!noExactRes.descriptionMatch);219assert.strictEqual(noRes.score, noExactRes.score);220221// Verify Scores222assert.ok(identityRes.score > basenamePrefixRes.score);223assert.ok(basenamePrefixRes.score > basenameRes.score);224assert.ok(basenameRes.score > pathRes.score);225assert.ok(pathRes.score > noRes.score);226});227228test('scoreItem - multiple', function () {229const resource = URI.file('/xyz/some/path/someFile123.txt');230231const res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor);232assert.ok(res1.score);233assert.strictEqual(res1.labelMatch?.length, 1);234assert.strictEqual(res1.labelMatch[0].start, 0);235assert.strictEqual(res1.labelMatch[0].end, 4);236assert.strictEqual(res1.descriptionMatch?.length, 1);237assert.strictEqual(res1.descriptionMatch[0].start, 1);238assert.strictEqual(res1.descriptionMatch[0].end, 4);239240const res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor);241assert.ok(res2.score);242assert.strictEqual(res1.score, res2.score);243assert.strictEqual(res2.labelMatch?.length, 1);244assert.strictEqual(res2.labelMatch[0].start, 0);245assert.strictEqual(res2.labelMatch[0].end, 4);246assert.strictEqual(res2.descriptionMatch?.length, 1);247assert.strictEqual(res2.descriptionMatch[0].start, 1);248assert.strictEqual(res2.descriptionMatch[0].end, 4);249250const res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor);251assert.ok(res3.score);252assert.ok(res3.score > res2.score);253assert.strictEqual(res3.labelMatch?.length, 1);254assert.strictEqual(res3.labelMatch[0].start, 0);255assert.strictEqual(res3.labelMatch[0].end, 11);256assert.strictEqual(res3.descriptionMatch?.length, 1);257assert.strictEqual(res3.descriptionMatch[0].start, 1);258assert.strictEqual(res3.descriptionMatch[0].end, 4);259260const res4 = scoreItem(resource, 'path z y', true, ResourceAccessor);261assert.ok(res4.score);262assert.ok(res4.score < res2.score);263assert.strictEqual(res4.labelMatch?.length, 0);264assert.strictEqual(res4.descriptionMatch?.length, 2);265assert.strictEqual(res4.descriptionMatch[0].start, 2);266assert.strictEqual(res4.descriptionMatch[0].end, 4);267assert.strictEqual(res4.descriptionMatch[1].start, 10);268assert.strictEqual(res4.descriptionMatch[1].end, 14);269});270271test('scoreItem - multiple with cache yields different results', function () {272const resource = URI.file('/xyz/some/path/someFile123.txt');273const cache = {};274const res1 = scoreItem(resource, 'xyz sm', true, ResourceAccessor, cache);275assert.ok(res1.score);276277// from the cache's perspective this should be a totally different query278const res2 = scoreItem(resource, 'xyz "sm"', true, ResourceAccessor, cache);279assert.ok(!res2.score);280});281282test('scoreItem - invalid input', function () {283284let res = scoreItem(null, null!, true, ResourceAccessor);285assert.strictEqual(res.score, 0);286287res = scoreItem(null, 'null', true, ResourceAccessor);288assert.strictEqual(res.score, 0);289});290291test('scoreItem - optimize for file paths', function () {292const resource = URI.file('/xyz/others/spath/some/xsp/file123.txt');293294// xsp is more relevant to the end of the file path even though it matches295// fuzzy also in the beginning. we verify the more relevant match at the296// end gets returned.297const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor);298assert.ok(pathRes.score);299assert.ok(pathRes.descriptionMatch);300assert.ok(pathRes.labelMatch);301assert.strictEqual(pathRes.labelMatch.length, 1);302assert.strictEqual(pathRes.labelMatch[0].start, 0);303assert.strictEqual(pathRes.labelMatch[0].end, 7);304assert.strictEqual(pathRes.descriptionMatch.length, 1);305assert.strictEqual(pathRes.descriptionMatch[0].start, 23);306assert.strictEqual(pathRes.descriptionMatch[0].end, 26);307});308309test('scoreItem - avoid match scattering (bug #36119)', function () {310const resource = URI.file('projects/ui/cula/ats/target.mk');311312const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor);313assert.ok(pathRes.score);314assert.ok(pathRes.descriptionMatch);315assert.ok(pathRes.labelMatch);316assert.strictEqual(pathRes.labelMatch.length, 1);317assert.strictEqual(pathRes.labelMatch[0].start, 0);318assert.strictEqual(pathRes.labelMatch[0].end, 9);319});320321test('scoreItem - prefers more compact matches', function () {322const resource = URI.file('/1a111d1/11a1d1/something.txt');323324// expect "ad" to be matched towards the end of the file because the325// match is more compact326const res = scoreItem(resource, 'ad', true, ResourceAccessor);327assert.ok(res.score);328assert.ok(res.descriptionMatch);329assert.ok(!res.labelMatch!.length);330assert.strictEqual(res.descriptionMatch.length, 2);331assert.strictEqual(res.descriptionMatch[0].start, 11);332assert.strictEqual(res.descriptionMatch[0].end, 12);333assert.strictEqual(res.descriptionMatch[1].start, 13);334assert.strictEqual(res.descriptionMatch[1].end, 14);335});336337test('scoreItem - proper target offset', function () {338const resource = URI.file('etem');339340const res = scoreItem(resource, 'teem', true, ResourceAccessor);341assert.ok(!res.score);342});343344test('scoreItem - proper target offset #2', function () {345const resource = URI.file('ede');346347const res = scoreItem(resource, 'de', true, ResourceAccessor);348349assert.strictEqual(res.labelMatch!.length, 1);350assert.strictEqual(res.labelMatch![0].start, 1);351assert.strictEqual(res.labelMatch![0].end, 3);352});353354test('scoreItem - proper target offset #3', function () {355const resource = URI.file('/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg');356357const res = scoreItem(resource, 'debug', true, ResourceAccessor);358359assert.strictEqual(res.descriptionMatch!.length, 3);360assert.strictEqual(res.descriptionMatch![0].start, 9);361assert.strictEqual(res.descriptionMatch![0].end, 10);362assert.strictEqual(res.descriptionMatch![1].start, 36);363assert.strictEqual(res.descriptionMatch![1].end, 37);364assert.strictEqual(res.descriptionMatch![2].start, 40);365assert.strictEqual(res.descriptionMatch![2].end, 41);366367assert.strictEqual(res.labelMatch!.length, 2);368assert.strictEqual(res.labelMatch![0].start, 9);369assert.strictEqual(res.labelMatch![0].end, 10);370assert.strictEqual(res.labelMatch![1].start, 20);371assert.strictEqual(res.labelMatch![1].end, 21);372});373374test('scoreItem - no match unless query contained in sequence', function () {375const resource = URI.file('abcde');376377const res = scoreItem(resource, 'edcda', true, ResourceAccessor);378assert.ok(!res.score);379});380381test('scoreItem - match if using slash or backslash (local, remote resource)', function () {382const localResource = URI.file('abcde/super/duper');383const remoteResource = URI.from({ scheme: Schemas.vscodeRemote, path: 'abcde/super/duper' });384385for (const resource of [localResource, remoteResource]) {386let res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceAccessor);387assert.ok(res.score);388389res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithSlashAccessor);390assert.ok(res.score);391392res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithBackslashAccessor);393assert.ok(res.score);394395res = scoreItem(resource, 'abcde/super/duper', true, ResourceAccessor);396assert.ok(res.score);397398res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithSlashAccessor);399assert.ok(res.score);400401res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithBackslashAccessor);402assert.ok(res.score);403}404});405406test('scoreItem - ensure upper case bonus only applies on non-consecutive matches (bug #134723)', function () {407const resourceWithUpper = URI.file('ASDFasdfasdf');408const resourceAllLower = URI.file('asdfasdfasdf');409410assert.ok(scoreItem(resourceAllLower, 'asdf', true, ResourceAccessor).score > scoreItem(resourceWithUpper, 'asdf', true, ResourceAccessor).score);411});412413test('compareItemsByScore - identity', function () {414const resourceA = URI.file('/some/path/fileA.txt');415const resourceB = URI.file('/some/path/other/fileB.txt');416const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');417418// Full resource A path419let query = ResourceAccessor.getItemPath(resourceA);420421let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));422assert.strictEqual(res[0], resourceA);423assert.strictEqual(res[1], resourceB);424assert.strictEqual(res[2], resourceC);425426res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));427assert.strictEqual(res[0], resourceA);428assert.strictEqual(res[1], resourceB);429assert.strictEqual(res[2], resourceC);430431// Full resource B path432query = ResourceAccessor.getItemPath(resourceB);433434res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));435assert.strictEqual(res[0], resourceB);436assert.strictEqual(res[1], resourceA);437assert.strictEqual(res[2], resourceC);438439res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));440assert.strictEqual(res[0], resourceB);441assert.strictEqual(res[1], resourceA);442assert.strictEqual(res[2], resourceC);443});444445test('compareFilesByScore - basename prefix', function () {446const resourceA = URI.file('/some/path/fileA.txt');447const resourceB = URI.file('/some/path/other/fileB.txt');448const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');449450// Full resource A basename451let query = ResourceAccessor.getItemLabel(resourceA);452453let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));454assert.strictEqual(res[0], resourceA);455assert.strictEqual(res[1], resourceB);456assert.strictEqual(res[2], resourceC);457458res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));459assert.strictEqual(res[0], resourceA);460assert.strictEqual(res[1], resourceB);461assert.strictEqual(res[2], resourceC);462463// Full resource B basename464query = ResourceAccessor.getItemLabel(resourceB);465466res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));467assert.strictEqual(res[0], resourceB);468assert.strictEqual(res[1], resourceA);469assert.strictEqual(res[2], resourceC);470471res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));472assert.strictEqual(res[0], resourceB);473assert.strictEqual(res[1], resourceA);474assert.strictEqual(res[2], resourceC);475});476477test('compareFilesByScore - basename camelcase', function () {478const resourceA = URI.file('/some/path/fileA.txt');479const resourceB = URI.file('/some/path/other/fileB.txt');480const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');481482// resource A camelcase483let query = 'fA';484485let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));486assert.strictEqual(res[0], resourceA);487assert.strictEqual(res[1], resourceB);488assert.strictEqual(res[2], resourceC);489490res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));491assert.strictEqual(res[0], resourceA);492assert.strictEqual(res[1], resourceB);493assert.strictEqual(res[2], resourceC);494495// resource B camelcase496query = 'fB';497498res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));499assert.strictEqual(res[0], resourceB);500assert.strictEqual(res[1], resourceA);501assert.strictEqual(res[2], resourceC);502503res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));504assert.strictEqual(res[0], resourceB);505assert.strictEqual(res[1], resourceA);506assert.strictEqual(res[2], resourceC);507});508509test('compareFilesByScore - basename scores', function () {510const resourceA = URI.file('/some/path/fileA.txt');511const resourceB = URI.file('/some/path/other/fileB.txt');512const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');513514// Resource A part of basename515let query = 'fileA';516517let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));518assert.strictEqual(res[0], resourceA);519assert.strictEqual(res[1], resourceB);520assert.strictEqual(res[2], resourceC);521522res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));523assert.strictEqual(res[0], resourceA);524assert.strictEqual(res[1], resourceB);525assert.strictEqual(res[2], resourceC);526527// Resource B part of basename528query = 'fileB';529530res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));531assert.strictEqual(res[0], resourceB);532assert.strictEqual(res[1], resourceA);533assert.strictEqual(res[2], resourceC);534535res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));536assert.strictEqual(res[0], resourceB);537assert.strictEqual(res[1], resourceA);538assert.strictEqual(res[2], resourceC);539});540541test('compareFilesByScore - path scores', function () {542const resourceA = URI.file('/some/path/fileA.txt');543const resourceB = URI.file('/some/path/other/fileB.txt');544const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');545546// Resource A part of path547let query = 'pathfileA';548549let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));550assert.strictEqual(res[0], resourceA);551assert.strictEqual(res[1], resourceB);552assert.strictEqual(res[2], resourceC);553554res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));555assert.strictEqual(res[0], resourceA);556assert.strictEqual(res[1], resourceB);557assert.strictEqual(res[2], resourceC);558559// Resource B part of path560query = 'pathfileB';561562res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));563assert.strictEqual(res[0], resourceB);564assert.strictEqual(res[1], resourceA);565assert.strictEqual(res[2], resourceC);566567res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));568assert.strictEqual(res[0], resourceB);569assert.strictEqual(res[1], resourceA);570assert.strictEqual(res[2], resourceC);571});572573test('compareFilesByScore - prefer shorter basenames', function () {574const resourceA = URI.file('/some/path/fileA.txt');575const resourceB = URI.file('/some/path/other/fileBLonger.txt');576const resourceC = URI.file('/unrelated/the/path/other/fileC.txt');577578// Resource A part of path579const query = 'somepath';580581let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));582assert.strictEqual(res[0], resourceA);583assert.strictEqual(res[1], resourceB);584assert.strictEqual(res[2], resourceC);585586res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));587assert.strictEqual(res[0], resourceA);588assert.strictEqual(res[1], resourceB);589assert.strictEqual(res[2], resourceC);590});591592test('compareFilesByScore - prefer shorter basenames (match on basename)', function () {593const resourceA = URI.file('/some/path/fileA.txt');594const resourceB = URI.file('/some/path/other/fileBLonger.txt');595const resourceC = URI.file('/unrelated/the/path/other/fileC.txt');596597// Resource A part of path598const query = 'file';599600let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));601assert.strictEqual(res[0], resourceA);602assert.strictEqual(res[1], resourceC);603assert.strictEqual(res[2], resourceB);604605res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));606assert.strictEqual(res[0], resourceA);607assert.strictEqual(res[1], resourceC);608assert.strictEqual(res[2], resourceB);609});610611test('compareFilesByScore - prefer shorter paths', function () {612const resourceA = URI.file('/some/path/fileA.txt');613const resourceB = URI.file('/some/path/other/fileB.txt');614const resourceC = URI.file('/unrelated/some/path/other/fileC.txt');615616// Resource A part of path617const query = 'somepath';618619let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));620assert.strictEqual(res[0], resourceA);621assert.strictEqual(res[1], resourceB);622assert.strictEqual(res[2], resourceC);623624res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));625assert.strictEqual(res[0], resourceA);626assert.strictEqual(res[1], resourceB);627assert.strictEqual(res[2], resourceC);628});629630test('compareFilesByScore - prefer shorter paths (bug #17443)', function () {631const resourceA = URI.file('config/test/t1.js');632const resourceB = URI.file('config/test.js');633const resourceC = URI.file('config/test/t2.js');634635const query = 'co/te';636637const res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));638assert.strictEqual(res[0], resourceB);639assert.strictEqual(res[1], resourceA);640assert.strictEqual(res[2], resourceC);641});642643test('compareFilesByScore - prefer matches in label over description if scores are otherwise equal', function () {644const resourceA = URI.file('parts/quick/arrow-left-dark.svg');645const resourceB = URI.file('parts/quickopen/quickopen.ts');646647const query = 'partsquick';648649const res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));650assert.strictEqual(res[0], resourceB);651assert.strictEqual(res[1], resourceA);652});653654test('compareFilesByScore - prefer camel case matches', function () {655const resourceA = URI.file('config/test/NullPointerException.java');656const resourceB = URI.file('config/test/nopointerexception.java');657658for (const query of ['npe', 'NPE']) {659let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));660assert.strictEqual(res[0], resourceA);661assert.strictEqual(res[1], resourceB);662663res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));664assert.strictEqual(res[0], resourceA);665assert.strictEqual(res[1], resourceB);666}667});668669test('compareFilesByScore - prefer more compact camel case matches', function () {670const resourceA = URI.file('config/test/openthisAnythingHandler.js');671const resourceB = URI.file('config/test/openthisisnotsorelevantforthequeryAnyHand.js');672673const query = 'AH';674675let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));676assert.strictEqual(res[0], resourceB);677assert.strictEqual(res[1], resourceA);678679res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));680assert.strictEqual(res[0], resourceB);681assert.strictEqual(res[1], resourceA);682});683684test('compareFilesByScore - prefer more compact matches (label)', function () {685const resourceA = URI.file('config/test/examasdaple.js');686const resourceB = URI.file('config/test/exampleasdaasd.ts');687688const query = 'xp';689690let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));691assert.strictEqual(res[0], resourceB);692assert.strictEqual(res[1], resourceA);693694res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));695assert.strictEqual(res[0], resourceB);696assert.strictEqual(res[1], resourceA);697});698699test('compareFilesByScore - prefer more compact matches (path)', function () {700const resourceA = URI.file('config/test/examasdaple/file.js');701const resourceB = URI.file('config/test/exampleasdaasd/file.ts');702703const query = 'xp';704705let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));706assert.strictEqual(res[0], resourceB);707assert.strictEqual(res[1], resourceA);708709res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));710assert.strictEqual(res[0], resourceB);711assert.strictEqual(res[1], resourceA);712});713714test('compareFilesByScore - prefer more compact matches (label and path)', function () {715const resourceA = URI.file('config/example/thisfile.ts');716const resourceB = URI.file('config/24234243244/example/file.js');717718const query = 'exfile';719720let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));721assert.strictEqual(res[0], resourceB);722assert.strictEqual(res[1], resourceA);723724res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));725assert.strictEqual(res[0], resourceB);726assert.strictEqual(res[1], resourceA);727});728729test('compareFilesByScore - avoid match scattering (bug #34210)', function () {730const resourceA = URI.file('node_modules1/bundle/lib/model/modules/ot1/index.js');731const resourceB = URI.file('node_modules1/bundle/lib/model/modules/un1/index.js');732const resourceC = URI.file('node_modules1/bundle/lib/model/modules/modu1/index.js');733const resourceD = URI.file('node_modules1/bundle/lib/model/modules/oddl1/index.js');734735let query = isWindows ? 'modu1\\index.js' : 'modu1/index.js';736737let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));738assert.strictEqual(res[0], resourceC);739740res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));741assert.strictEqual(res[0], resourceC);742743query = isWindows ? 'un1\\index.js' : 'un1/index.js';744745res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));746assert.strictEqual(res[0], resourceB);747748res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));749assert.strictEqual(res[0], resourceB);750});751752test('compareFilesByScore - avoid match scattering (bug #21019 1.)', function () {753const resourceA = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceLoad/index.js');754const resourceB = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceDistribution/index.js');755const resourceC = URI.file('app/containers/Services/NetworkData/ServiceDetailTabs/ServiceTabs/StatVideo/index.js');756757const query = 'StatVideoindex';758759let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));760assert.strictEqual(res[0], resourceC);761762res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));763assert.strictEqual(res[0], resourceC);764});765766test('compareFilesByScore - avoid match scattering (bug #21019 2.)', function () {767const resourceA = URI.file('src/build-helper/store/redux.ts');768const resourceB = URI.file('src/repository/store/redux.ts');769770const query = 'reproreduxts';771772let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));773assert.strictEqual(res[0], resourceB);774775res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));776assert.strictEqual(res[0], resourceB);777});778779test('compareFilesByScore - avoid match scattering (bug #26649)', function () {780const resourceA = URI.file('photobook/src/components/AddPagesButton/index.js');781const resourceB = URI.file('photobook/src/components/ApprovalPageHeader/index.js');782const resourceC = URI.file('photobook/src/canvasComponents/BookPage/index.js');783784const query = 'bookpageIndex';785786let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));787assert.strictEqual(res[0], resourceC);788789res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));790assert.strictEqual(res[0], resourceC);791});792793test('compareFilesByScore - avoid match scattering (bug #33247)', function () {794const resourceA = URI.file('ui/src/utils/constants.js');795const resourceB = URI.file('ui/src/ui/Icons/index.js');796797const query = isWindows ? 'ui\\icons' : 'ui/icons';798799let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));800assert.strictEqual(res[0], resourceB);801802res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));803assert.strictEqual(res[0], resourceB);804});805806test('compareFilesByScore - avoid match scattering (bug #33247 comment)', function () {807const resourceA = URI.file('ui/src/components/IDInput/index.js');808const resourceB = URI.file('ui/src/ui/Input/index.js');809810const query = isWindows ? 'ui\\input\\index' : 'ui/input/index';811812let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));813assert.strictEqual(res[0], resourceB);814815res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));816assert.strictEqual(res[0], resourceB);817});818819test('compareFilesByScore - avoid match scattering (bug #36166)', function () {820const resourceA = URI.file('django/contrib/sites/locale/ga/LC_MESSAGES/django.mo');821const resourceB = URI.file('django/core/signals.py');822823const query = 'djancosig';824825let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));826assert.strictEqual(res[0], resourceB);827828res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));829assert.strictEqual(res[0], resourceB);830});831832test('compareFilesByScore - avoid match scattering (bug #32918)', function () {833const resourceA = URI.file('adsys/protected/config.php');834const resourceB = URI.file('adsys/protected/framework/smarty/sysplugins/smarty_internal_config.php');835const resourceC = URI.file('duowanVideo/wap/protected/config.php');836837const query = 'protectedconfig.php';838839let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));840assert.strictEqual(res[0], resourceA);841assert.strictEqual(res[1], resourceC);842assert.strictEqual(res[2], resourceB);843844res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));845assert.strictEqual(res[0], resourceA);846assert.strictEqual(res[1], resourceC);847assert.strictEqual(res[2], resourceB);848});849850test('compareFilesByScore - avoid match scattering (bug #14879)', function () {851const resourceA = URI.file('pkg/search/gradient/testdata/constraint_attrMatchString.yml');852const resourceB = URI.file('cmd/gradient/main.go');853854const query = 'gradientmain';855856let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));857assert.strictEqual(res[0], resourceB);858859res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));860assert.strictEqual(res[0], resourceB);861});862863test('compareFilesByScore - avoid match scattering (bug #14727 1)', function () {864const resourceA = URI.file('alpha-beta-cappa.txt');865const resourceB = URI.file('abc.txt');866867const query = 'abc';868869let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));870assert.strictEqual(res[0], resourceB);871872res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));873assert.strictEqual(res[0], resourceB);874});875876test('compareFilesByScore - avoid match scattering (bug #14727 2)', function () {877const resourceA = URI.file('xerxes-yak-zubba/index.js');878const resourceB = URI.file('xyz/index.js');879880const query = 'xyz';881882let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));883assert.strictEqual(res[0], resourceB);884885res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));886assert.strictEqual(res[0], resourceB);887});888889test('compareFilesByScore - avoid match scattering (bug #18381)', function () {890const resourceA = URI.file('AssymblyInfo.cs');891const resourceB = URI.file('IAsynchronousTask.java');892893const query = 'async';894895let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));896assert.strictEqual(res[0], resourceB);897898res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));899assert.strictEqual(res[0], resourceB);900});901902test('compareFilesByScore - avoid match scattering (bug #35572)', function () {903const resourceA = URI.file('static/app/source/angluar/-admin/-organization/-settings/layout/layout.js');904const resourceB = URI.file('static/app/source/angular/-admin/-project/-settings/_settings/settings.js');905906const query = 'partisettings';907908let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));909assert.strictEqual(res[0], resourceB);910911res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));912assert.strictEqual(res[0], resourceB);913});914915test('compareFilesByScore - avoid match scattering (bug #36810)', function () {916const resourceA = URI.file('Trilby.TrilbyTV.Web.Portal/Views/Systems/Index.cshtml');917const resourceB = URI.file('Trilby.TrilbyTV.Web.Portal/Areas/Admins/Views/Tips/Index.cshtml');918919const query = 'tipsindex.cshtml';920921let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));922assert.strictEqual(res[0], resourceB);923924res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));925assert.strictEqual(res[0], resourceB);926});927928test('compareFilesByScore - prefer shorter hit (bug #20546)', function () {929const resourceA = URI.file('editor/core/components/tests/list-view-spec.js');930const resourceB = URI.file('editor/core/components/list-view.js');931932const query = 'listview';933934let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));935assert.strictEqual(res[0], resourceB);936937res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));938assert.strictEqual(res[0], resourceB);939});940941test('compareFilesByScore - avoid match scattering (bug #12095)', function () {942const resourceA = URI.file('src/vs/workbench/contrib/files/common/explorerViewModel.ts');943const resourceB = URI.file('src/vs/workbench/contrib/files/browser/views/explorerView.ts');944const resourceC = URI.file('src/vs/workbench/contrib/files/browser/views/explorerViewer.ts');945946const query = 'filesexplorerview.ts';947948let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));949assert.strictEqual(res[0], resourceB);950951res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));952assert.strictEqual(res[0], resourceB);953});954955test('compareFilesByScore - prefer case match (bug #96122)', function () {956const resourceA = URI.file('lists.php');957const resourceB = URI.file('lib/Lists.php');958959const query = 'Lists.php';960961let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));962assert.strictEqual(res[0], resourceB);963964res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));965assert.strictEqual(res[0], resourceB);966});967968test('compareFilesByScore - prefer shorter match (bug #103052) - foo bar', function () {969const resourceA = URI.file('app/emails/foo.bar.js');970const resourceB = URI.file('app/emails/other-footer.other-bar.js');971972for (const query of ['foo bar', 'foobar']) {973let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));974assert.strictEqual(res[0], resourceA);975assert.strictEqual(res[1], resourceB);976977res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));978assert.strictEqual(res[0], resourceA);979assert.strictEqual(res[1], resourceB);980}981});982983test('compareFilesByScore - prefer shorter match (bug #103052) - payment model', function () {984const resourceA = URI.file('app/components/payment/payment.model.js');985const resourceB = URI.file('app/components/online-payments-history/online-payments-history.model.js');986987for (const query of ['payment model', 'paymentmodel']) {988let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));989assert.strictEqual(res[0], resourceA);990assert.strictEqual(res[1], resourceB);991992res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));993assert.strictEqual(res[0], resourceA);994assert.strictEqual(res[1], resourceB);995}996});997998test('compareFilesByScore - prefer shorter match (bug #103052) - color', function () {999const resourceA = URI.file('app/constants/color.js');1000const resourceB = URI.file('app/components/model/input/pick-avatar-color.js');10011002for (const query of ['color js', 'colorjs']) {1003let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1004assert.strictEqual(res[0], resourceA);1005assert.strictEqual(res[1], resourceB);10061007res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1008assert.strictEqual(res[0], resourceA);1009assert.strictEqual(res[1], resourceB);1010}1011});10121013test('compareFilesByScore - prefer strict case prefix', function () {1014const resourceA = URI.file('app/constants/color.js');1015const resourceB = URI.file('app/components/model/input/Color.js');10161017let query = 'Color';10181019let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1020assert.strictEqual(res[0], resourceB);1021assert.strictEqual(res[1], resourceA);10221023res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1024assert.strictEqual(res[0], resourceB);1025assert.strictEqual(res[1], resourceA);10261027query = 'color';10281029res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1030assert.strictEqual(res[0], resourceA);1031assert.strictEqual(res[1], resourceB);10321033res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1034assert.strictEqual(res[0], resourceA);1035assert.strictEqual(res[1], resourceB);1036});10371038test('compareFilesByScore - prefer prefix (bug #103052)', function () {1039const resourceA = URI.file('test/smoke/src/main.ts');1040const resourceB = URI.file('src/vs/editor/common/services/semantikTokensProviderStyling.ts');10411042const query = 'smoke main.ts';10431044let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1045assert.strictEqual(res[0], resourceA);1046assert.strictEqual(res[1], resourceB);10471048res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1049assert.strictEqual(res[0], resourceA);1050assert.strictEqual(res[1], resourceB);1051});10521053test('compareFilesByScore - boost better prefix match if multiple queries are used', function () {1054const resourceA = URI.file('src/vs/workbench/services/host/browser/browserHostService.ts');1055const resourceB = URI.file('src/vs/workbench/browser/workbench.ts');10561057for (const query of ['workbench.ts browser', 'browser workbench.ts', 'browser workbench', 'workbench browser']) {1058let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1059assert.strictEqual(res[0], resourceB);1060assert.strictEqual(res[1], resourceA);10611062res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1063assert.strictEqual(res[0], resourceB);1064assert.strictEqual(res[1], resourceA);1065}1066});10671068test('compareFilesByScore - boost shorter prefix match if multiple queries are used', function () {1069const resourceA = URI.file('src/vs/workbench/node/actions/windowActions.ts');1070const resourceB = URI.file('src/vs/workbench/electron-node/window.ts');10711072for (const query of ['window node', 'window.ts node']) {1073let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1074assert.strictEqual(res[0], resourceB);1075assert.strictEqual(res[1], resourceA);10761077res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1078assert.strictEqual(res[0], resourceB);1079assert.strictEqual(res[1], resourceA);1080}1081});10821083test('compareFilesByScore - skip preference on label match when using path sep', function () {1084const resourceA = URI.file('djangosite/ufrela/def.py');1085const resourceB = URI.file('djangosite/urls/default.py');10861087const query = 'url/def';10881089let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1090assert.strictEqual(res[0], resourceB);1091assert.strictEqual(res[1], resourceA);10921093res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1094assert.strictEqual(res[0], resourceB);1095assert.strictEqual(res[1], resourceA);1096});10971098test('compareFilesByScore - boost shorter prefix match if multiple queries are used (#99171)', function () {1099const resourceA = URI.file('mesh_editor_lifetime_job.h');1100const resourceB = URI.file('lifetime_job.h');11011102const query = 'm life, life m';11031104let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1105assert.strictEqual(res[0], resourceB);1106assert.strictEqual(res[1], resourceA);11071108res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1109assert.strictEqual(res[0], resourceB);1110assert.strictEqual(res[1], resourceA);1111});11121113test('compareFilesByScore - boost consecutive matches in the beginning over end', function () {1114const resourceA = URI.file('src/vs/server/node/extensionHostStatusService.ts');1115const resourceB = URI.file('src/vs/workbench/browser/parts/notifications/notificationsStatus.ts');11161117const query = 'notStatus';11181119let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1120assert.strictEqual(res[0], resourceB);1121assert.strictEqual(res[1], resourceA);11221123res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));1124assert.strictEqual(res[0], resourceB);1125assert.strictEqual(res[1], resourceA);1126});11271128test('prepareQuery', () => {1129assert.strictEqual(prepareQuery(' f*a ').normalized, 'fa');1130assert.strictEqual(prepareQuery('model Tester.ts').original, 'model Tester.ts');1131assert.strictEqual(prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase());1132assert.strictEqual(prepareQuery('model Tester.ts').normalized, 'modelTester.ts');1133assert.strictEqual(prepareQuery('model Tester.ts').expectContiguousMatch, false); // doesn't have quotes in it1134assert.strictEqual(prepareQuery('Model Tester.ts').normalizedLowercase, 'modeltester.ts');1135assert.strictEqual(prepareQuery('ModelTester.ts').containsPathSeparator, false);1136assert.strictEqual(prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);1137assert.strictEqual(prepareQuery('"hello"').expectContiguousMatch, true);1138assert.strictEqual(prepareQuery('"hello"').normalized, 'hello');11391140// with spaces1141let query = prepareQuery('He*llo World');1142assert.strictEqual(query.original, 'He*llo World');1143assert.strictEqual(query.normalized, 'HelloWorld');1144assert.strictEqual(query.normalizedLowercase, 'HelloWorld'.toLowerCase());1145assert.strictEqual(query.values?.length, 2);1146assert.strictEqual(query.values?.[0].original, 'He*llo');1147assert.strictEqual(query.values?.[0].normalized, 'Hello');1148assert.strictEqual(query.values?.[0].normalizedLowercase, 'Hello'.toLowerCase());1149assert.strictEqual(query.values?.[1].original, 'World');1150assert.strictEqual(query.values?.[1].normalized, 'World');1151assert.strictEqual(query.values?.[1].normalizedLowercase, 'World'.toLowerCase());11521153const restoredQuery = pieceToQuery(query.values);1154assert.strictEqual(restoredQuery.original, query.original);1155assert.strictEqual(restoredQuery.values?.length, query.values?.length);1156assert.strictEqual(restoredQuery.containsPathSeparator, query.containsPathSeparator);11571158// with spaces that are empty1159query = prepareQuery(' Hello World ');1160assert.strictEqual(query.original, ' Hello World ');1161assert.strictEqual(query.originalLowercase, ' Hello World '.toLowerCase());1162assert.strictEqual(query.normalized, 'HelloWorld');1163assert.strictEqual(query.normalizedLowercase, 'HelloWorld'.toLowerCase());1164assert.strictEqual(query.values?.length, 2);1165assert.strictEqual(query.values?.[0].original, 'Hello');1166assert.strictEqual(query.values?.[0].originalLowercase, 'Hello'.toLowerCase());1167assert.strictEqual(query.values?.[0].normalized, 'Hello');1168assert.strictEqual(query.values?.[0].normalizedLowercase, 'Hello'.toLowerCase());1169assert.strictEqual(query.values?.[1].original, 'World');1170assert.strictEqual(query.values?.[1].originalLowercase, 'World'.toLowerCase());1171assert.strictEqual(query.values?.[1].normalized, 'World');1172assert.strictEqual(query.values?.[1].normalizedLowercase, 'World'.toLowerCase());11731174// Path related1175if (isWindows) {1176assert.strictEqual(prepareQuery('C:\\some\\path').pathNormalized, 'C:\\some\\path');1177assert.strictEqual(prepareQuery('C:\\some\\path').normalized, 'C:\\some\\path');1178assert.strictEqual(prepareQuery('C:\\some\\path').containsPathSeparator, true);1179assert.strictEqual(prepareQuery('C:/some/path').pathNormalized, 'C:\\some\\path');1180assert.strictEqual(prepareQuery('C:/some/path').normalized, 'C:\\some\\path');1181assert.strictEqual(prepareQuery('C:/some/path').containsPathSeparator, true);1182} else {1183assert.strictEqual(prepareQuery('/some/path').pathNormalized, '/some/path');1184assert.strictEqual(prepareQuery('/some/path').normalized, '/some/path');1185assert.strictEqual(prepareQuery('/some/path').containsPathSeparator, true);1186assert.strictEqual(prepareQuery('\\some\\path').pathNormalized, '/some/path');1187assert.strictEqual(prepareQuery('\\some\\path').normalized, '/some/path');1188assert.strictEqual(prepareQuery('\\some\\path').containsPathSeparator, true);1189}1190});11911192test('fuzzyScore2 (matching)', function () {1193const target = 'HelLo-World';11941195for (const offset of [0, 3]) {1196let [score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HelLo-World', offset);11971198assert.ok(score);1199assert.strictEqual(matches.length, 1);1200assert.strictEqual(matches[0].start, 0 + offset);1201assert.strictEqual(matches[0].end, target.length + offset);12021203[score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HW', offset);12041205assert.ok(score);1206assert.strictEqual(matches.length, 2);1207assert.strictEqual(matches[0].start, 0 + offset);1208assert.strictEqual(matches[0].end, 1 + offset);1209assert.strictEqual(matches[1].start, 6 + offset);1210assert.strictEqual(matches[1].end, 7 + offset);1211}1212});12131214test('fuzzyScore2 (multiple queries)', function () {1215const target = 'HelLo-World';12161217const [firstSingleScore, firstSingleMatches] = _doScore2(target, 'HelLo');1218const [secondSingleScore, secondSingleMatches] = _doScore2(target, 'World');1219const firstAndSecondSingleMatches = [...firstSingleMatches || [], ...secondSingleMatches || []];12201221let [multiScore, multiMatches] = _doScore2(target, 'HelLo World');12221223function assertScore() {1224assert.ok(multiScore ?? 0 >= ((firstSingleScore ?? 0) + (secondSingleScore ?? 0)));1225for (let i = 0; multiMatches && i < multiMatches.length; i++) {1226const multiMatch = multiMatches[i];1227const firstAndSecondSingleMatch = firstAndSecondSingleMatches[i];12281229if (multiMatch && firstAndSecondSingleMatch) {1230assert.strictEqual(multiMatch.start, firstAndSecondSingleMatch.start);1231assert.strictEqual(multiMatch.end, firstAndSecondSingleMatch.end);1232} else {1233assert.fail();1234}1235}1236}12371238function assertNoScore() {1239assert.strictEqual(multiScore, undefined);1240assert.strictEqual(multiMatches.length, 0);1241}12421243assertScore();12441245[multiScore, multiMatches] = _doScore2(target, 'World HelLo');1246assertScore();12471248[multiScore, multiMatches] = _doScore2(target, 'World HelLo World');1249assertScore();12501251[multiScore, multiMatches] = _doScore2(target, 'World HelLo Nothing');1252assertNoScore();12531254[multiScore, multiMatches] = _doScore2(target, 'More Nothing');1255assertNoScore();1256});12571258test('fuzzyScore2 (#95716)', function () {1259const target = '# ❌ Wow';12601261const score = _doScore2(target, '❌');1262assert.ok(score);1263assert.ok(typeof score[0] === 'number');1264assert.ok(score[1].length > 0);1265});12661267test('Using quotes should expect contiguous matches match', function () {1268// missing the "i" in the query1269assert.strictEqual(_doScore('contiguous', '"contguous"')[0], 0);12701271const score = _doScore('contiguous', '"contiguous"');1272assert.ok(score[0] > 0);1273});12741275test('Using quotes should highlight contiguous indexes', function () {1276const score = _doScore('2021-7-26.md', '"26"');1277assert.strictEqual(score[0], 14);12781279// The indexes of the 2 and 6 of "26"1280assert.strictEqual(score[1][0], 7);1281assert.strictEqual(score[1][1], 8);1282});12831284ensureNoDisposablesAreLeakedInTestSuite();1285});128612871288