quicksilver/development/string_ranking.txt · Last modified: 2010/07/14 03:19 (external edit)


Below is the core string ranking code used by Quicksilver. Any suggestions would be welcome.

This code is called by the Object Ranking Code

Usage:

[@"My Great Test String" scoreForAbbreviation:@"mgstr"];


@implementation NSString (Abbreviation)

- (float) scoreForAbbreviation:(NSString *)abbreviation{
	return [[self | scoreForAbbreviation:abbreviation hitMask:nil ]];
}
- (float) scoreForAbbreviation:(NSString *)abbreviation hitMask:(NSMutableIndexSet *)mask{
	return [[self | scoreForAbbreviation:abbreviation inRange:NSMakeRange(0,[self length ]]) fromRange:NSMakeRange(0,[[abbreviation | length ]]) hitMask:mask];
}
- (float) scoreForAbbreviation:(NSString *)abbreviation inRange:(NSRange)searchRange fromRange:(NSRange)abbreviationRange hitMask:(NSMutableIndexSet *)mask{
    float score,remainingScore;
	int i,j;
	NSRange matchedRange,remainingSearchRange;
	if (!abbreviationRange.length) return 0.9; //deduct some points for all remaining letters
	if (abbreviationRange.length>searchRange.length)return 0.0;
	/*
	 {
		 matchedRange=[[self | rangeOfString:[abbreviation substringWithRange:NSMakeRange(abbreviationRange.location,1) ]]
								  options:NSCaseInsensitiveSearch
									range:searchRange];
		 
		 if (matchedRange.location=NSNotFound) return 0.9;
		 searchRange.length-=matchedRange.location-searchRange.location;
		 searchRange.location=matchedRange.location;
	 }
	 */
	for (i=abbreviationRange.length; i>0;i--){ //Search for steadily smaller portions of the abbreviation
		matchedRange=[self rangeOfString:[abbreviation substringWithRange:NSMakeRange(abbreviationRange.location,i)] options:NSCaseInsensitiveSearch range:searchRange];
        
		if (NSNotFound == matchedRange.location) continue;
		if (matchedRange.location+abbreviationRange.length>NSMaxRange(searchRange)) continue;
        
		if (mask) [mask addIndexesInRange:matchedRange];
		
		remainingSearchRange.location=NSMaxRange(matchedRange);
		remainingSearchRange.length=NSMaxRange(searchRange)-remainingSearchRange.location;
		
		// Search what is left of the string with the rest of the abbreviation
		remainingScore = [self scoreForAbbreviation:abbreviation inRange:remainingSearchRange fromRange:NSMakeRange(abbreviationRange.location+i,abbreviationRange.length-i) hitMask:mask];
        
		if (remainingScore) {
			score = remainingSearchRange.location - searchRange.location;
			// ignore skipped characters if is first letter of a word
			if (matchedRange.location>searchRange.location){//if some letters were skipped
				j=0;
				if ([[NSCharacterSet whitespaceCharacterSet] characterIsMember: [self characterAtIndex: matchedRange.location-1]]) {
					for (j=matchedRange.location-2; j >= (int)searchRange.location; j--) {
						if ([[NSCharacterSet whitespaceCharacterSet] characterIsMember: [self characterAtIndex:j]]) score--;
						else score-=0.15;
					}
					
				} else if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember: [self characterAtIndex: matchedRange.location]]) {
					for (j=matchedRange.location-1; j >= (int)searchRange.location; j--) {
						if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember: [self characterAtIndex:j]])
							score--;
						else
							score-=0.15;
					}
				} else {
					score -= matchedRange.location - searchRange.location;
				}
				
			}
			
			score += remainingScore * remainingSearchRange.length;
			score /= searchRange.length;
			return score;
		}
	}
	return 0;
}

@end