From 7eff5f7f146735ae05485561eb5153de7d1e4c9c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 10 Dec 2015 08:06:03 +0100 Subject: [PATCH] extract scorer.ts and give OSS attribution --- src/vs/base/common/OSSREADME.json | 8 ++ src/vs/base/common/scorer.ts | 85 +++++++++++++++++++ src/vs/base/common/strings.ts | 66 -------------- src/vs/base/test/common/scorer.test.ts | 35 ++++++++ src/vs/base/test/common/strings.test.ts | 23 ----- .../search/browser/openAnythingHandler.ts | 9 +- 6 files changed, 133 insertions(+), 93 deletions(-) create mode 100644 src/vs/base/common/OSSREADME.json create mode 100644 src/vs/base/common/scorer.ts create mode 100644 src/vs/base/test/common/scorer.test.ts diff --git a/src/vs/base/common/OSSREADME.json b/src/vs/base/common/OSSREADME.json new file mode 100644 index 00000000000..8d5e021bf26 --- /dev/null +++ b/src/vs/base/common/OSSREADME.json @@ -0,0 +1,8 @@ +// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: +[{ + "name": "string_scorer", + "version": "10 March 2015", + "license": "MIT License", + "repositoryURL": "https://github.com/joshaven/string_score", + "description": "The file scorer.ts was inspired by the string_score algorithm from Joshaven Potter." +}] diff --git a/src/vs/base/common/scorer.ts b/src/vs/base/common/scorer.ts new file mode 100644 index 00000000000..1c6435fddbd --- /dev/null +++ b/src/vs/base/common/scorer.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +// Based on material from: + +/*! +* string_score.js: String Scoring Algorithm 0.1.22 +* +* http://joshaven.com/string_score +* https://github.com/joshaven/string_score +* +* Copyright (C) 2009-2014 Joshaven Potter +* Special thanks to all of the contributors listed here https://github.com/joshaven/string_score +* MIT License: http://opensource.org/licenses/MIT +* +* Date: Tue Mar 1 2011 +* Updated: Tue Mar 10 2015 +*/ + +/** + * Compute a score for the given string and the given query. + * + * Rules: + * Character score: 1 + * Same case bonus: 1 + * Upper case bonus: 1 + * Start of word/path bonus: 7 + * Start of string bonus: 8 + */ +export function score(target: string, query: string): number { + let score = 0; + + if (!target || !query) { + return score; // return early if target or query are undefined + } + + const queryLen = query.length; + const targetLower = target.toLowerCase(); + const queryLower = query.toLowerCase(); + const wordPathBoundary = ['-', '_', ' ', '/', '\\']; + + let index = 0; + while (index < queryLen) { + var indexOf = targetLower.indexOf(queryLower[index]); + if (indexOf < 0) { + index++; + continue; // no match + } + + // Character Match Bonus + score += 1; + + // Same Case Bonous + if (target[indexOf] === query[indexOf]) { + score += 1; + } + + // Upper Case Bonus + if (isUpper(target.charCodeAt(indexOf))) { + score += 1; + } + + // Prefix Bonus + if (indexOf === 0) { + score += 8; + } + + // Start of Word/Path Bonous + if (wordPathBoundary.some(w => w === target[indexOf - 1])) { + score += 7; + } + + index++; + } + + return score; +} + +function isUpper(code: number): boolean { + return 65 <= code && code <= 90; +} \ No newline at end of file diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 5d19aaee29b..c1f672641d9 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -604,70 +604,4 @@ export var UTF8_BOM_CHARACTER = String.fromCharCode(__utf8_bom); export function startsWithUTF8BOM(str: string): boolean { return (str && str.length > 0 && str.charCodeAt(0) === __utf8_bom); -} - - -/** - * Compute a score for the given string and the given query. Inspired by String Scoring Algorithm: - * http://joshaven.com/string_score - * https://github.com/joshaven/string_score - * - * Rules: - * Character score: 1 - * Same case bonus: 1 - * Upper case bonus: 1 - * Start of word/path bonus: 7 - * Start of string bonus: 8 - */ -export function score(target: string, query: string): number { - let score = 0; - - if (!target || !query) { - return score; // return early if target or query are undefined - } - - const queryLen = query.length; - const targetLower = target.toLowerCase(); - const queryLower = query.toLowerCase(); - const wordPathBoundary = ['-', '_', ' ', '/', '\\']; - - let index = 0; - while (index < queryLen) { - var indexOf = targetLower.indexOf(queryLower[index]); - if (indexOf < 0) { - index++; - continue; // no match - } - - // Character Match Bonus - score += 1; - - // Same Case Bonous - if (target[indexOf] === query[indexOf]) { - score += 1; - } - - // Upper Case Bonus - if (isUpper(target.charCodeAt(indexOf))) { - score += 1; - } - - // Prefix Bonus - if (indexOf === 0) { - score += 8; - } - - // Start of Word/Path Bonous - if (wordPathBoundary.some(w => w === target[indexOf - 1])) { - score += 7; - } - - index++; - } - - return score; -} - -function isUpper(code: number): boolean { - return 65 <= code && code <= 90; } \ No newline at end of file diff --git a/src/vs/base/test/common/scorer.test.ts b/src/vs/base/test/common/scorer.test.ts new file mode 100644 index 00000000000..6de190abcba --- /dev/null +++ b/src/vs/base/test/common/scorer.test.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import scorer = require('vs/base/common/scorer'); + +suite('Scorer', () => { + + test("score", function() { + const target = 'HelLo-World'; + + const scores = []; + scores.push(scorer.score(target, 'HelLo-World')); // direct case match + scores.push(scorer.score(target, 'hello-world')); // direct mix-case match + scores.push(scorer.score(target, 'HW')); // direct case prefix (multiple) + scores.push(scorer.score(target, 'H')); // direct case prefix + scores.push(scorer.score(target, 'hw')); // direct mix-case prefix (multiple) + scores.push(scorer.score(target, 'h')); // direct mix-case prefix + scores.push(scorer.score(target, 'W')); // direct case word prefix + scores.push(scorer.score(target, 'w')); // direct mix-case word prefix + scores.push(scorer.score(target, 'Ld')); // in-string case match (multiple) + scores.push(scorer.score(target, 'L')); // in-string case match + scores.push(scorer.score(target, 'ld')); // in-string mix-case match + scores.push(scorer.score(target, 'l')); // in-string mix-case match + scores.push(scorer.score(target, '4')); // no match + + // Assert scoring order + let sortedScores = scores.sort(); + assert.deepEqual(scores.reverse(), sortedScores); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 5f721c8d15f..0a3576931f0 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -180,27 +180,4 @@ suite('Strings', () => { assert.strictEqual(strings.localeCompare('A', 'a'), 'A'.localeCompare('a')); assert.strictEqual(strings.localeCompare('a', 'A'), 'a'.localeCompare('A')); }); - - test("scorer", function() { - const target = 'HelLo-World'; - - const scores = []; - scores.push(strings.score(target, 'HelLo-World')); // direct case match - scores.push(strings.score(target, 'hello-world')); // direct mix-case match - scores.push(strings.score(target, 'HW')); // direct case prefix (multiple) - scores.push(strings.score(target, 'H')); // direct case prefix - scores.push(strings.score(target, 'hw')); // direct mix-case prefix (multiple) - scores.push(strings.score(target, 'h')); // direct mix-case prefix - scores.push(strings.score(target, 'W')); // direct case word prefix - scores.push(strings.score(target, 'w')); // direct mix-case word prefix - scores.push(strings.score(target, 'Ld')); // in-string case match (multiple) - scores.push(strings.score(target, 'L')); // in-string case match - scores.push(strings.score(target, 'ld')); // in-string mix-case match - scores.push(strings.score(target, 'l')); // in-string mix-case match - scores.push(strings.score(target, '4')); // no match - - // Assert scoring order - let sortedScores = scores.sort(); - assert.deepEqual(scores.reverse(), sortedScores); - }); }); \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 68528979939..51916d63169 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -10,6 +10,7 @@ import nls = require('vs/nls'); import {ThrottledDelayer} from 'vs/base/common/async'; import types = require('vs/base/common/types'); import strings = require('vs/base/common/strings'); +import scorer = require('vs/base/common/scorer'); import paths = require('vs/base/common/paths'); import filters = require('vs/base/common/filters'); import labels = require('vs/base/common/labels'); @@ -292,15 +293,15 @@ export class OpenAnythingHandler extends QuickOpenHandler { // Fuzzy scoring is special if (enableFuzzyScoring) { - const labelAScore = strings.score(elementA.getLabel(), lookFor); - const labelBScore = strings.score(elementB.getLabel(), lookFor); + const labelAScore = scorer.score(elementA.getLabel(), lookFor); + const labelBScore = scorer.score(elementB.getLabel(), lookFor); if (labelAScore !== labelBScore) { return labelAScore > labelBScore ? -1 : 1; } - const descriptionAScore = strings.score(elementA.getDescription(), lookFor); - const descriptionBScore = strings.score(elementB.getDescription(), lookFor); + const descriptionAScore = scorer.score(elementA.getDescription(), lookFor); + const descriptionBScore = scorer.score(elementB.getDescription(), lookFor); if (descriptionAScore !== descriptionBScore) { return descriptionAScore > descriptionBScore ? -1 : 1; -- GitLab