Merge pull request #987 from atom/ks-fuzzaldrin

Extract string score and fuzzy filter
This commit is contained in:
Kevin Sawicki
2013-10-17 09:55:28 -07:00
5 changed files with 3 additions and 148 deletions

View File

@@ -16,7 +16,6 @@ module.exports =
Point: Point
Range: Range
Site: Site
stringscore: require '../vendor/stringscore'
# The following classes can't be used from a Task handler and should therefore
# only be exported when not running as a child node process

View File

@@ -17,6 +17,7 @@
"coffeestack": "0.6.0",
"emissary": "0.6.0",
"first-mate": "0.4.0",
"fuzzaldrin": "0.1.0",
"git-utils": "0.26.0",
"guid": "0.0.10",
"jasmine-focused": "~0.15.0",
@@ -71,7 +72,7 @@
"metrics": "0.8.0",
"package-generator": "0.12.0",
"release-notes": "0.4.0",
"settings-view": "0.28.0",
"settings-view": "0.29.0",
"snippets": "0.9.0",
"spell-check": "0.7.0",
"status-bar": "0.14.0",

View File

@@ -1,27 +0,0 @@
stringScore = require '../vendor/stringscore'
path = require 'path'
module.exports = (candidates, query, options={}) ->
if query
scoredCandidates = candidates.map (candidate) ->
string = if options.key? then candidate[options.key] else candidate
score = stringScore(string, query)
unless /\//.test(query)
# Basename matches count for more.
score += stringScore(path.basename(string), query)
# Shallow files are scored higher
depth = Math.max(1, 10 - string.split('/').length - 1)
score *= depth * 0.01
{ candidate, score }
scoredCandidates.sort (a, b) ->
if a.score > b.score then -1
else if a.score < b.score then 1
else 0
candidates = (scoredCandidate.candidate for scoredCandidate in scoredCandidates when scoredCandidate.score > 0)
candidates = candidates[0...options.maxResults] if options.maxResults?
candidates

View File

@@ -1,6 +1,6 @@
{$, View} = require './space-pen-extensions'
Editor = require './editor'
fuzzyFilter = require './fuzzy-filter'
fuzzyFilter = require('fuzzaldrin').filter
# Public: Provides a widget for users to make a selection from a list of
# choices.

118
vendor/stringscore.js vendored
View File

@@ -1,118 +0,0 @@
// MODIFIED BY NS/CJ - Don't extend the prototype of String
// MODIFIED BY CJ - Remove start_of_string_bonus
/*!
* string_score.js: String Scoring Algorithm 0.1.10
*
* http://joshaven.com/string_score
* https://github.com/joshaven/string_score
*
* Copyright (C) 2009-2011 Joshaven Potter <yourtech@gmail.com>
* Special thanks to all of the contributors listed here https://github.com/joshaven/string_score
* MIT license: http://www.opensource.org/licenses/mit-license.php
*
* Date: Tue Mar 1 2011
*/
/**
* Scores a string against another string.
* 'Hello World'.score('he'); //=> 0.5931818181818181
* 'Hello World'.score('Hello'); //=> 0.7318181818181818
*/
module.exports = function(string, abbreviation, fuzziness) {
// If the string is equal to the abbreviation, perfect match.
if (string == abbreviation) {return 1;}
// If it's not a perfect match and is empty return 0
if (abbreviation == "") {return 0;}
var total_character_score = 0,
abbreviation_length = abbreviation.length,
string_length = string.length,
start_of_string_bonus,
abbreviation_score,
fuzzies=1,
final_score;
// Walk through abbreviation and add up scores.
for (var i = 0,
character_score/* = 0*/,
index_in_string/* = 0*/,
c/* = ''*/,
index_c_lowercase/* = 0*/,
index_c_uppercase/* = 0*/,
min_index/* = 0*/;
i < abbreviation_length;
++i) {
// Find the first case-insensitive match of a character.
c = abbreviation.charAt(i);
index_c_lowercase = string.indexOf(c.toLowerCase());
index_c_uppercase = string.indexOf(c.toUpperCase());
min_index = Math.min(index_c_lowercase, index_c_uppercase);
index_in_string = (min_index > -1) ? min_index : Math.max(index_c_lowercase, index_c_uppercase);
if (index_in_string === -1) {
if (fuzziness) {
fuzzies += 1-fuzziness;
continue;
} else {
return 0;
}
} else {
character_score = 0.1;
}
// Set base score for matching 'c'.
// Same case bonus.
if (string[index_in_string] === c) {
character_score += 0.1;
}
// Consecutive letter & start-of-string Bonus
if (index_in_string === 0) {
// Increase the score when matching first character of the remainder of the string
character_score += 0.6;
if (i === 0) {
// If match is the first character of the string
// & the first character of abbreviation, add a
// start-of-string match bonus.
// start_of_string_bonus = 1 //true;
}
}
else {
// Acronym Bonus
// Weighing Logic: Typing the first character of an acronym is as if you
// preceded it with two perfect character matches.
if (string.charAt(index_in_string - 1) === ' ') {
character_score += 0.8; // * Math.min(index_in_string, 5); // Cap bonus at 0.4 * 5
}
}
// Left trim the already matched part of the string
// (forces sequential matching).
string = string.substring(index_in_string + 1, string_length);
total_character_score += character_score;
} // end of for loop
// Uncomment to weigh smaller words higher.
// return total_character_score / string_length;
abbreviation_score = total_character_score / abbreviation_length;
//percentage_of_matched_string = abbreviation_length / string_length;
//word_score = abbreviation_score * percentage_of_matched_string;
// Reduce penalty for longer strings.
//final_score = (word_score + abbreviation_score) / 2;
final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2;
final_score = final_score / fuzzies;
if (start_of_string_bonus && (final_score + 0.15 < 1)) {
final_score += 0.15;
}
return final_score;
};