diff --git a/Quicksilver/Code-QuickStepCore/QSCore.h b/Quicksilver/Code-QuickStepCore/QSCore.h index f2879bd13..1be605ab3 100644 --- a/Quicksilver/Code-QuickStepCore/QSCore.h +++ b/Quicksilver/Code-QuickStepCore/QSCore.h @@ -19,7 +19,6 @@ #import "QSComputerSource.h" #import "QSDebug.h" #import "QSDefines.h" -#import "QSense.h" #import "QSExecutor.h" #import "QSGlobalSelectionProvider.h" #import "QSHandledObjectHandler.h" diff --git a/Quicksilver/Code-QuickStepCore/QSStringRanker.m b/Quicksilver/Code-QuickStepCore/QSStringRanker.m index e30268ee8..74439cea9 100644 --- a/Quicksilver/Code-QuickStepCore/QSStringRanker.m +++ b/Quicksilver/Code-QuickStepCore/QSStringRanker.m @@ -7,7 +7,7 @@ // #import "QSStringRanker.h" -#import "QSense.h" +#import "QSSense.h" #import "NSString_Purification.h" @implementation QSDefaultStringRanker diff --git a/Quicksilver/Code-QuickStepFoundation/NSString_BLTRExtensions.h b/Quicksilver/Code-QuickStepFoundation/NSString_BLTRExtensions.h index fd67a4e50..2edfdaa69 100644 --- a/Quicksilver/Code-QuickStepFoundation/NSString_BLTRExtensions.h +++ b/Quicksilver/Code-QuickStepFoundation/NSString_BLTRExtensions.h @@ -13,16 +13,9 @@ NSAttributedString * highlightString(NSString *string, NSString *keyString); NSComparisonResult prefixCompare(NSString *aString, NSString *bString); @interface NSString (Abbreviation) -- (CGFloat) scoreForString:(NSString *)string; - -- (NSArray *)hitsForString:(NSString *)testString; - - (CGFloat) scoreForAbbreviation:(NSString *)abbreviation; -//- (float) oldScoreForAbbreviation:(NSString *)abbreviation; - (CGFloat) scoreForAbbreviation:(NSString *)abbreviation hitMask:(NSMutableIndexSet *)mask; -//- (float) oldScoreForAbbreviation:(NSString *)abbreviation hitMask:(NSMutableIndexSet *)mask; - (CGFloat) scoreForAbbreviation:(NSString *)abbreviation inRange:(NSRange)searchRange fromRange:(NSRange)abbreviationRange hitMask:(NSMutableIndexSet *)mask; - @end @interface NSAttributedString (Sizing) diff --git a/Quicksilver/Code-QuickStepFoundation/NSString_BLTRExtensions.m b/Quicksilver/Code-QuickStepFoundation/NSString_BLTRExtensions.m index 44865159d..d1ef48136 100644 --- a/Quicksilver/Code-QuickStepFoundation/NSString_BLTRExtensions.m +++ b/Quicksilver/Code-QuickStepFoundation/NSString_BLTRExtensions.m @@ -16,6 +16,7 @@ NSComparisonResult prefixCompare(NSString *aString, NSString *bString) { } @implementation NSString (Abbreviation) +/* FIXME: This can be removed sometimes */ - (CGFloat) scoreForString:(NSString *)testString { CGFloat score = 1; NSInteger i; @@ -34,15 +35,6 @@ - (CGFloat) scoreForString:(NSString *)testString { return score; } -#if 0 -- (CGFloat) oldScoreForAbbreviation:(NSString *)abbreviation hitMask:(NSMutableIndexSet *)mask { - return [self scoreForAbbreviation:abbreviation inRange:NSMakeRange(0, [self length]) fromRange:NSMakeRange(0, [abbreviation length]) hitMask:mask]; -} -- (CGFloat) oldScoreForAbbreviation:(NSString *)abbreviation { - return [self oldScoreForAbbreviation:abbreviation hitMask:nil]; -} -#endif - - (CGFloat) scoreForAbbreviation:(NSString *)abbreviation { return [self scoreForAbbreviation:abbreviation hitMask:nil]; } @@ -51,51 +43,13 @@ - (CGFloat) scoreForAbbreviation:(NSString *)abbreviation hitMask:(NSMutableInde } - (CGFloat) scoreForAbbreviation:(NSString *)abbreviation inRange:(NSRange)searchRange fromRange:(NSRange)abbreviationRange hitMask:(NSMutableIndexSet *)mask { - CGFloat score, remainingScore; - NSInteger 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; - 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 (matchedRange.location == NSNotFound || 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 - if ([[NSCharacterSet whitespaceCharacterSet] characterIsMember:[self characterAtIndex:matchedRange.location-1]]) { - for (j = matchedRange.location-2; j >= (NSInteger) 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 >= (NSInteger) 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; + CFRange strRange = CFRangeMake(searchRange.location, searchRange.length); + CFRange abbrRange = CFRangeMake(abbreviationRange.location, abbreviationRange.length); + + return QSScoreForAbbreviationWithRanges((__bridge CFStringRef)self, (__bridge CFStringRef)abbreviation, mask, strRange, abbrRange); } +/* FIXME: This can be removed sometimes */ - (NSArray *)hitsForString:(NSString *)testString { NSMutableArray *hitsArray = [NSMutableArray arrayWithCapacity:[self length]]; NSUInteger i; diff --git a/Quicksilver/Code-QuickStepFoundation/QSFoundation.h b/Quicksilver/Code-QuickStepFoundation/QSFoundation.h index f7012dfd2..84bea9c06 100644 --- a/Quicksilver/Code-QuickStepFoundation/QSFoundation.h +++ b/Quicksilver/Code-QuickStepFoundation/QSFoundation.h @@ -32,6 +32,7 @@ #import "QSLSTools.h" #import "QSLog.h" #import "QSHotKeyEvent.h" +#import "QSSense.h" #import "NSWorkspace_BLTRExtensions.h" #import "NSWindow_BLTRExtensions.h" #import "NSView_BLTRExtensions.h" diff --git a/Quicksilver/Code-QuickStepCore/QSense.h b/Quicksilver/Code-QuickStepFoundation/QSSense.h similarity index 65% rename from Quicksilver/Code-QuickStepCore/QSense.h rename to Quicksilver/Code-QuickStepFoundation/QSSense.h index efe76eaba..76cf93112 100644 --- a/Quicksilver/Code-QuickStepCore/QSense.h +++ b/Quicksilver/Code-QuickStepFoundation/QSSense.h @@ -10,3 +10,4 @@ CGFloat QSScoreForAbbreviation(CFStringRef string, CFStringRef abbr, id hitMask); +CGFloat QSScoreForAbbreviationWithRanges(CFStringRef str, CFStringRef abbr, id mask, CFRange strRange, CFRange abbrRange); diff --git a/Quicksilver/Code-QuickStepFoundation/QSSense.m b/Quicksilver/Code-QuickStepFoundation/QSSense.m new file mode 100644 index 000000000..fd587966c --- /dev/null +++ b/Quicksilver/Code-QuickStepFoundation/QSSense.m @@ -0,0 +1,110 @@ +// +// QSense.m +// QSqSense +// +// Created by Alcor on 11/22/04. +// Copyright 2004 Blacktree. All rights reserved. +// + +#import "QSSense.h" + +#define MIN_ABBR_OPTIMIZE 0 +#define IGNORED_SCORE 0.9 +#define SKIPPED_SCORE 0.15 + + + +CGFloat QSScoreForAbbreviationWithRanges(CFStringRef str, CFStringRef abbr, id mask, CFRange strRange, CFRange abbrRange); + +CGFloat QSScoreForAbbreviation(CFStringRef str, CFStringRef abbr, id mask) { + return QSScoreForAbbreviationWithRanges(str, abbr, mask, CFRangeMake(0, CFStringGetLength(str)), CFRangeMake(0, CFStringGetLength(abbr))); +} + +CGFloat QSScoreForAbbreviationWithRanges(CFStringRef str, CFStringRef abbr, id mask, CFRange strRange, CFRange abbrRange) { + if (!abbrRange.length) + return IGNORED_SCORE; //deduct some points for all remaining letters + + if (abbrRange.length > strRange.length) + return 0.0; + + static CFCharacterSetRef wordSeparator = NULL; + static CFCharacterSetRef uppercase = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + wordSeparator = CFCharacterSetCreateMutableCopy(NULL, CFCharacterSetGetPredefined(kCFCharacterSetWhitespace)); + CFCharacterSetAddCharactersInString((CFMutableCharacterSetRef)wordSeparator, (CFStringRef)@"."); + + uppercase = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter); + }); + + // Create an inline buffer version of str. Will be used in loop below + // for faster lookups. + CFStringInlineBuffer inlineBuffer; + CFStringInitInlineBuffer(str, &inlineBuffer, strRange); + CFLocaleRef userLoc = CFLocaleCopyCurrent(); + + CGFloat score = 0.0, remainingScore = 0.0; + CFIndex i, j; + CFRange matchedRange, remainingStrRange, adjustedStrRange = strRange; + + // Search for steadily smaller portions of the abbreviation + for (i = abbrRange.length; i > 0; i--) { + CFStringRef curAbbr = CFStringCreateWithSubstring (NULL, abbr, CFRangeMake(abbrRange.location, i) ); + // terminality + // axeen + + BOOL found = CFStringFindWithOptionsAndLocale(str, curAbbr, + CFRangeMake(adjustedStrRange.location, adjustedStrRange.length - abbrRange.length + i), + kCFCompareCaseInsensitive | kCFCompareDiacriticInsensitive | kCFCompareLocalized, + userLoc, &matchedRange); + CFRelease(curAbbr); + + if (!found) { + continue; + } + + if (mask) { + [mask addIndexesInRange:NSMakeRange(matchedRange.location, matchedRange.length)]; + } + + remainingStrRange.location = matchedRange.location + matchedRange.length; + remainingStrRange.length = strRange.location + strRange.length - remainingStrRange.location; + + // Search what is left of the string with the rest of the abbreviation + remainingScore = QSScoreForAbbreviationWithRanges(str, abbr, mask, remainingStrRange, CFRangeMake(abbrRange.location + i, abbrRange.length - i)); + + if (remainingScore) { + score = remainingStrRange.location-strRange.location; + // ignore skipped characters if is first letter of a word + if (matchedRange.location > strRange.location) { + // if some letters were skipped + UniChar previousChar = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, matchedRange.location - 1); + UniChar character = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, matchedRange.location); + if (CFCharacterSetIsCharacterMember(wordSeparator, previousChar)) { + // We're on the first letter of a word + for (j = matchedRange.location - 2; j >= strRange.location; j--) { + if (CFCharacterSetIsCharacterMember(wordSeparator, CFStringGetCharacterFromInlineBuffer(&inlineBuffer, j))) + score--; + else + score -= SKIPPED_SCORE; + } + } else if (CFCharacterSetIsCharacterMember(uppercase, character)) { + for (j = matchedRange.location - 1; j >= strRange.location; j--) { + if (CFCharacterSetIsCharacterMember(uppercase, CFStringGetCharacterFromInlineBuffer(&inlineBuffer, j))) + score--; + else + score -= SKIPPED_SCORE; + } + } else { + score -= (matchedRange.location-strRange.location)/2; + } + } + score += remainingScore * remainingStrRange.length; + score /= strRange.length; + CFRelease(userLoc); + return score; + } + } + CFRelease(userLoc); + return 0; +} diff --git a/Quicksilver/Quicksilver.xcodeproj/project.pbxproj b/Quicksilver/Quicksilver.xcodeproj/project.pbxproj index 396ad43b6..c0ce230e5 100644 --- a/Quicksilver/Quicksilver.xcodeproj/project.pbxproj +++ b/Quicksilver/Quicksilver.xcodeproj/project.pbxproj @@ -158,6 +158,9 @@ 4DD89F290EBDDBA9005A15AE /* QSNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DD89F200EBDDBA9005A15AE /* QSNotifications.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4DD89F2A0EBDDBA9005A15AE /* QSPreferenceKeys.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DD89F210EBDDBA9005A15AE /* QSPreferenceKeys.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4DD89F2B0EBDDBA9005A15AE /* QSTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DD89F220EBDDBA9005A15AE /* QSTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4DF329D61EA2C1C9003CD3CE /* QSSense.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E5FB7907B20DD10044D6EF /* QSSense.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4DF329D71EA2C1D5003CD3CE /* QSSense.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E5FB7A07B20DD10044D6EF /* QSSense.m */; }; + 4DF329D91EA2C5F0003CD3CE /* TestQSSense.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DF329D81EA2C5F0003CD3CE /* TestQSSense.m */; }; 4DF441B50ED9C66700656AD1 /* QSHandledObjectHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FB8AD4D08E6328D000A65C4 /* QSHandledObjectHandler.m */; }; 4DFE7DA20E081A3B000B9AA3 /* NSImage+QuickLook.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFE7DA00E081A30000B9AA3 /* NSImage+QuickLook.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 4DFE7DA30E081A3C000B9AA3 /* NSImage+QuickLook.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DFE7D9F0E081A30000B9AA3 /* NSImage+QuickLook.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -633,8 +636,6 @@ E1E5FBE207B20DD20044D6EF /* QSComputerSource.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E5FB7507B20DD10044D6EF /* QSComputerSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; E1E5FBE307B20DD20044D6EF /* QSComputerSource.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E5FB7607B20DD10044D6EF /* QSComputerSource.m */; }; E1E5FBE407B20DD20044D6EF /* QSCore.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E5FB7707B20DD10044D6EF /* QSCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E1E5FBE607B20DD20044D6EF /* QSense.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E5FB7907B20DD10044D6EF /* QSense.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E1E5FBE707B20DD20044D6EF /* QSense.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E5FB7A07B20DD10044D6EF /* QSense.m */; }; E1E5FBE807B20DD20044D6EF /* QSExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E5FB7B07B20DD10044D6EF /* QSExecutor.h */; settings = {ATTRIBUTES = (Public, ); }; }; E1E5FBE907B20DD20044D6EF /* QSExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E5FB7C07B20DD10044D6EF /* QSExecutor.m */; }; E1E5FBEA07B20DD20044D6EF /* QSGlobalSelectionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E5FB7D07B20DD10044D6EF /* QSGlobalSelectionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1177,6 +1178,7 @@ 4DD89F200EBDDBA9005A15AE /* QSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QSNotifications.h; sourceTree = ""; usesTabs = 1; }; 4DD89F210EBDDBA9005A15AE /* QSPreferenceKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QSPreferenceKeys.h; sourceTree = ""; usesTabs = 1; }; 4DD89F220EBDDBA9005A15AE /* QSTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QSTypes.h; sourceTree = ""; usesTabs = 1; }; + 4DF329D81EA2C5F0003CD3CE /* TestQSSense.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestQSSense.m; path = "Tests/Tests-QSFoundation/TestQSSense.m"; sourceTree = ""; }; 4DFE7D9F0E081A30000B9AA3 /* NSImage+QuickLook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+QuickLook.h"; sourceTree = ""; }; 4DFE7DA00E081A30000B9AA3 /* NSImage+QuickLook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage+QuickLook.m"; sourceTree = ""; }; 4DFE7DA10E081A30000B9AA3 /* README.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = README.rtf; sourceTree = ""; }; @@ -2185,8 +2187,8 @@ E1E5FB7507B20DD10044D6EF /* QSComputerSource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSComputerSource.h; sourceTree = ""; usesTabs = 1; }; E1E5FB7607B20DD10044D6EF /* QSComputerSource.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = QSComputerSource.m; sourceTree = ""; usesTabs = 1; }; E1E5FB7707B20DD10044D6EF /* QSCore.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSCore.h; sourceTree = ""; usesTabs = 1; }; - E1E5FB7907B20DD10044D6EF /* QSense.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSense.h; sourceTree = ""; usesTabs = 1; }; - E1E5FB7A07B20DD10044D6EF /* QSense.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = QSense.m; sourceTree = ""; usesTabs = 1; }; + E1E5FB7907B20DD10044D6EF /* QSSense.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSSense.h; sourceTree = ""; usesTabs = 1; }; + E1E5FB7A07B20DD10044D6EF /* QSSense.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = QSSense.m; sourceTree = ""; usesTabs = 1; }; E1E5FB7B07B20DD10044D6EF /* QSExecutor.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSExecutor.h; sourceTree = ""; usesTabs = 1; }; E1E5FB7C07B20DD10044D6EF /* QSExecutor.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = QSExecutor.m; sourceTree = ""; usesTabs = 1; wrapsLines = 1; }; E1E5FB7D07B20DD10044D6EF /* QSGlobalSelectionProvider.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSGlobalSelectionProvider.h; sourceTree = ""; usesTabs = 1; }; @@ -2642,6 +2644,7 @@ children = ( CD661C5319DBD2F3000F3695 /* TestNSApplicationMethods.m */, 66448E1214F42A4E000FA2E2 /* TestPathWildcards.m */, + 4DF329D81EA2C5F0003CD3CE /* TestQSSense.m */, ); name = QSFoundationTests; sourceTree = ""; @@ -3194,6 +3197,8 @@ CDCC200E10A4C14B009C4EED /* QSMDPredicate.h */, CDCC200F10A4C14B009C4EED /* QSMDPredicate.m */, 4DD89F1F0EBDDBA9005A15AE /* QSMacros.h */, + E1E5FB7907B20DD10044D6EF /* QSSense.h */, + E1E5FB7A07B20DD10044D6EF /* QSSense.m */, E1E5FA6E07B204BE0044D6EF /* NDAlias+QSMods.h */, E1E5FA6F07B204BE0044D6EF /* NDAlias+QSMods.m */, E1E5FA7007B204BE0044D6EF /* NDProcess+QSMods.h */, @@ -3306,8 +3311,6 @@ 4DD89F1C0EBDDBA9005A15AE /* QSDefines.h */, D49399091350078E00B908C6 /* QSDownloads.h */, D493990A1350078E00B908C6 /* QSDownloads.m */, - E1E5FB7907B20DD10044D6EF /* QSense.h */, - E1E5FB7A07B20DD10044D6EF /* QSense.m */, E1E5FB7B07B20DD10044D6EF /* QSExecutor.h */, E1E5FB7C07B20DD10044D6EF /* QSExecutor.m */, E1E5FB7D07B20DD10044D6EF /* QSGlobalSelectionProvider.h */, @@ -3600,7 +3603,6 @@ E1E5FBE407B20DD20044D6EF /* QSCore.h in Headers */, 4DD89F240EBDDBA9005A15AE /* QSDebug.h in Headers */, 4DD89F250EBDDBA9005A15AE /* QSDefines.h in Headers */, - E1E5FBE607B20DD20044D6EF /* QSense.h in Headers */, E1E5FBE807B20DD20044D6EF /* QSExecutor.h in Headers */, E1E5FBEA07B20DD20044D6EF /* QSGlobalSelectionProvider.h in Headers */, 7FB8AD4E08E6328D000A65C4 /* QSHandledObjectHandler.h in Headers */, @@ -3772,6 +3774,7 @@ CD803A381C5C3D2D00CF90F3 /* SFLListItem.h in Headers */, 4D66BC25148701EA00351C42 /* NSPathControl+NDAlias.h in Headers */, 4D66BC27148701EA00351C42 /* NSSavePanel+NDAlias.h in Headers */, + 4DF329D61EA2C1C9003CD3CE /* QSSense.h in Headers */, 4D66BC29148701EA00351C42 /* NSString+NDCarbonUtilities.h in Headers */, D4BDCC1F19E7F8F50080710C /* SUExport.h in Headers */, D4BDCC1C19E7F78F0080710C /* SUStandardVersionComparator.h in Headers */, @@ -4603,6 +4606,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4DF329D91EA2C5F0003CD3CE /* TestQSSense.m in Sources */, CD661C5419DBD2F3000F3695 /* TestNSApplicationMethods.m in Sources */, 66448E1314F42A4E000FA2E2 /* TestPathWildcards.m in Sources */, ); @@ -4691,7 +4695,6 @@ E1E5FBDD07B20DD20044D6EF /* QSCollection.m in Sources */, E1E5FBDF07B20DD20044D6EF /* QSCommand.m in Sources */, E1E5FBE307B20DD20044D6EF /* QSComputerSource.m in Sources */, - E1E5FBE707B20DD20044D6EF /* QSense.m in Sources */, E1E5FBE907B20DD20044D6EF /* QSExecutor.m in Sources */, E1E5FBEB07B20DD20044D6EF /* QSGlobalSelectionProvider.m in Sources */, 4DF441B50ED9C66700656AD1 /* QSHandledObjectHandler.m in Sources */, @@ -4809,6 +4812,7 @@ 7FDF341907F7D59E00594789 /* NSDictionary+BLTRExtensions.m in Sources */, 7F3DB4BB0926D1620062EDFD /* NSEvent+BLTRExtensions.m in Sources */, E1E5FABC07B204BF0044D6EF /* NSException_TraceExtensions.m in Sources */, + 4DF329D71EA2C1D5003CD3CE /* QSSense.m in Sources */, E1E5FABE07B204BF0044D6EF /* NSFileManager_BLTRExtensions.m in Sources */, E1E5FAC007B204BF0044D6EF /* NSGeometry_BLTRExtensions.m in Sources */, 4DFE7DA20E081A3B000B9AA3 /* NSImage+QuickLook.m in Sources */, diff --git a/Quicksilver/Tests/Tests-QSFoundation/TestQSSense.m b/Quicksilver/Tests/Tests-QSFoundation/TestQSSense.m new file mode 100644 index 000000000..462aab50f --- /dev/null +++ b/Quicksilver/Tests/Tests-QSFoundation/TestQSSense.m @@ -0,0 +1,153 @@ +// +// TestQSSense.m +// Quicksilver +// +// Created by Etienne on 15/04/2017. +// +// + +#import + +@interface TestQSSense : XCTestCase + +@end + +@implementation TestQSSense + +const float ACC = 0.00001; + +- (void)testScoreSimple { + CFStringRef str = CFSTR("Test string"); + CGFloat score = 0; + + score = QSScoreForAbbreviation(str, CFSTR("t"), nil); + XCTAssertEqualWithAccuracy(score, 0.90909, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("ts"), nil); + XCTAssertEqualWithAccuracy(score, 0.92727, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("te"), nil); + XCTAssertEqualWithAccuracy(score, 0.91818, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("tet"), nil); + XCTAssertEqualWithAccuracy(score, 0.93636, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("str"), nil); + XCTAssertEqualWithAccuracy(score, 0.91818, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("tstr"), nil); + XCTAssertEqualWithAccuracy(score, 0.79090, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("ng"), nil); + XCTAssertEqualWithAccuracy(score, 0.63636, ACC); +} + +- (void)testScoreLongString { + CFStringRef str = CFSTR("This is a really long test string for testing"); + CGFloat score = 0; + + score = QSScoreForAbbreviation(str, CFSTR("t"), nil); + XCTAssertEqualWithAccuracy(score, 0.90222, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("ts"), nil); + XCTAssertEqualWithAccuracy(score, 0.88666, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("te"), nil); + XCTAssertEqualWithAccuracy(score, 0.80777, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("tet"), nil); + XCTAssertEqualWithAccuracy(score, 0.81222, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("str"), nil); + XCTAssertEqualWithAccuracy(score, 0.78555, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("tstr"), nil); + XCTAssertEqualWithAccuracy(score, 0.67777, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("testi"), nil); + XCTAssertEqualWithAccuracy(score, 0.74000, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("for"), nil); + XCTAssertEqualWithAccuracy(score, 0.75888, ACC); + + score = QSScoreForAbbreviation(str, CFSTR("ng"), nil); + XCTAssertEqualWithAccuracy(score, 0.74666, ACC); +} + +- (void)testLongString { + CFStringRef str = CFSTR("This excellent string tells us an interesting story"); + CFRange strRange = CFRangeMake(0, 27); // tells^ + CFStringRef abbr = CFSTR("test"); + CFRange abbrRange = CFRangeMake(0, CFStringGetLength(abbr)); + CGFloat score = 0; + const int STEP = 4; + NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init]; + NSIndexSet *results = nil; + + score = QSScoreForAbbreviationWithRanges(str, CFSTR("test"), indexes, strRange, abbrRange); + XCTAssertEqualWithAccuracy(score, 0.74074, ACC); + results = [NSIndexSet indexSetFromArray:@[@(0), @(5), @(15), @(16), @(22), @(23)]]; + XCTAssertEqualObjects(indexes, results); + + strRange.length += STEP; + score = QSScoreForAbbreviationWithRanges(str, CFSTR("test"), indexes, strRange, abbrRange); + XCTAssertEqualWithAccuracy(score, 0.76129, ACC); + results = [NSIndexSet indexSetFromArray:@[@(0), @(5), @(15), @(16), @(22), @(23), @(26)]]; + XCTAssertEqualObjects(indexes, results); + + strRange.length += STEP; + score = QSScoreForAbbreviationWithRanges(str, CFSTR("test"), indexes, strRange, abbrRange); + XCTAssertEqualWithAccuracy(score, 0.77714, ACC); + results = [NSIndexSet indexSetFromArray:@[@(0), @(5), @(15), @(16), @(22), @(23), @(26)]]; + XCTAssertEqualObjects(indexes, results); + + strRange.length += STEP; + score = QSScoreForAbbreviationWithRanges(str, CFSTR("test"), indexes, strRange, abbrRange); + XCTAssertEqualWithAccuracy(score, 0.74230, ACC); + results = [NSIndexSet indexSetFromArray:@[@(0), @(5), @(15), @(16), @(22), @(23), @(26), @(36)]]; + XCTAssertEqualObjects(indexes, results); + + strRange.length += STEP; + score = QSScoreForAbbreviationWithRanges(str, CFSTR("test"), indexes, strRange, abbrRange); + XCTAssertEqualWithAccuracy(score, 0.69883, ACC); + results = [NSIndexSet indexSetFromArray:@[@(0), @(5), @(15), @(16), @(22), @(23), @(26), @(36), @(40), @(41)]]; + XCTAssertEqualObjects(indexes, results); + + strRange.length += STEP; + score = QSScoreForAbbreviationWithRanges(str, CFSTR("test"), indexes, strRange, abbrRange); + XCTAssertEqualWithAccuracy(score, 0.71595, ACC); + results = [NSIndexSet indexSetFromArray:@[@(0), @(5), @(15), @(16), @(22), @(23), @(26), @(36), @(40), @(41)]]; + XCTAssertEqualObjects(indexes, results); + + strRange.length += STEP; + score = QSScoreForAbbreviationWithRanges(str, CFSTR("test"), indexes, strRange, abbrRange); + XCTAssertEqualWithAccuracy(score, 0.73039, ACC); + results = [NSIndexSet indexSetFromArray:@[@(0), @(5), @(15), @(16), @(22), @(23), @(26), @(36), @(40), @(41)]]; + XCTAssertEqualObjects(indexes, results); +} + +- (void)testPerformance { + [self measureBlock:^{ + CFStringRef str = CFSTR("Test string"); + CFStringRef abbr = CFSTR("tsg"); + + QSScoreForAbbreviationWithRanges(str, abbr, nil, + CFRangeMake(0, CFStringGetLength(str)), + CFRangeMake(0, CFStringGetLength(abbr))); + }]; +} + +- (void)testPerformanceMicro { + [self measureBlock:^{ + CFStringRef str = CFSTR("This is a really long test string for testing"); + CFStringRef abbr = CFSTR("tsg"); + + for (int i = 0; i <= 100000; i++) { + QSScoreForAbbreviationWithRanges(str, abbr, nil, + CFRangeMake(0, CFStringGetLength(str)), + CFRangeMake(0, CFStringGetLength(abbr))); + } + }]; +} + +@end