Limit fuzzy searching to a depth of 10.
Avoids choking when query string becomes long and there are excessively long site name entries.
This commit is contained in:
		@@ -45,7 +45,7 @@
 | 
				
			|||||||
@property(nonatomic, readonly) BOOL stored;
 | 
					@property(nonatomic, readonly) BOOL stored;
 | 
				
			||||||
@property(nonatomic, readonly) BOOL transient;
 | 
					@property(nonatomic, readonly) BOOL transient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups;
 | 
					- (instancetype)initWithEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups;
 | 
				
			||||||
- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user;
 | 
					- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user;
 | 
				
			||||||
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
 | 
					- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,12 +31,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@implementation MPSiteModel
 | 
					@implementation MPSiteModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
 | 
					- (instancetype)initWithEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!(self = [super init]))
 | 
					    if (!(self = [super init]))
 | 
				
			||||||
        return nil;
 | 
					        return nil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [self setEntity:entity fuzzyGroups:fuzzyGroups];
 | 
					    [self setEntity:entity queryGroups:queryGroups];
 | 
				
			||||||
    self.initialized = YES;
 | 
					    self.initialized = YES;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return self;
 | 
					    return self;
 | 
				
			||||||
@@ -53,23 +53,25 @@
 | 
				
			|||||||
    return self;
 | 
					    return self;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
 | 
					- (void)setEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ([self.entityOID isEqual:entity.permanentObjectID])
 | 
					    if ([self.entityOID isEqual:entity.permanentObjectID])
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    self.entityOID = entity.permanentObjectID;
 | 
					    self.entityOID = entity.permanentObjectID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    NSString *siteName = entity.name;
 | 
					    NSString *siteName = entity.name;
 | 
				
			||||||
    NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName];
 | 
					    NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
 | 
				
			||||||
    for (NSUInteger f = 0, s = (NSUInteger)-1; f < [fuzzyGroups count]; ++f) {
 | 
					    if ([attributedSiteName length])
 | 
				
			||||||
        s = [siteName rangeOfString:fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
 | 
					        for (NSUInteger f = 0, s = 0; f < [queryGroups count]; ++f, ++s) {
 | 
				
			||||||
                              range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location;
 | 
					            s = [siteName rangeOfString:queryGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
 | 
				
			||||||
        if (s == NSNotFound)
 | 
					                                  range:NSMakeRange( s, [siteName length] - s )].location;
 | 
				
			||||||
            break;
 | 
					            if (s == NSNotFound)
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            [attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
 | 
				
			||||||
 | 
					                                       range:NSMakeRange( s, [queryGroups[f] length] )];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
 | 
					 | 
				
			||||||
                                   range:NSMakeRange( s, [fuzzyGroups[f] length] )];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
 | 
					    NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
 | 
				
			||||||
    paragraphStyle.alignment = NSCenterTextAlignment;
 | 
					    paragraphStyle.alignment = NSCenterTextAlignment;
 | 
				
			||||||
    [attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];
 | 
					    [attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -611,22 +611,20 @@
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static NSRegularExpression *fuzzyRE;
 | 
					 | 
				
			||||||
    static dispatch_once_t once = 0;
 | 
					 | 
				
			||||||
    dispatch_once( &once, ^{
 | 
					 | 
				
			||||||
        fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
 | 
					 | 
				
			||||||
    } );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    prof_new( @"updateSites" );
 | 
					    prof_new( @"updateSites" );
 | 
				
			||||||
    NSString *queryString = self.siteField.stringValue;
 | 
					    NSString *queryString = self.siteField.stringValue;
 | 
				
			||||||
    NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"] stringByAppendingString:@"*"];
 | 
					    NSMutableArray *queryGroups = [NSMutableArray new];
 | 
				
			||||||
 | 
					    NSMutableString *queryPattern = [NSMutableString new];
 | 
				
			||||||
 | 
					    [queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
 | 
				
			||||||
 | 
					                                 usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
 | 
				
			||||||
 | 
					                                     if (substringRange.location < 10) {
 | 
				
			||||||
 | 
					                                         [queryGroups addObject:substring];
 | 
				
			||||||
 | 
					                                         [queryPattern appendString:@"*"];
 | 
				
			||||||
 | 
					                                     }
 | 
				
			||||||
 | 
					                                     [queryPattern appendString:substring];
 | 
				
			||||||
 | 
					                                 }];
 | 
				
			||||||
 | 
					    [queryPattern appendString:@"*"];
 | 
				
			||||||
    prof_rewind( @"queryPattern" );
 | 
					    prof_rewind( @"queryPattern" );
 | 
				
			||||||
    NSMutableArray *fuzzyGroups = [NSMutableArray new];
 | 
					 | 
				
			||||||
    [fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
 | 
					 | 
				
			||||||
                           usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
 | 
					 | 
				
			||||||
                               [fuzzyGroups addObject:[queryString substringWithRange:result.range]];
 | 
					 | 
				
			||||||
                           }];
 | 
					 | 
				
			||||||
    prof_rewind( @"fuzzyRE" );
 | 
					 | 
				
			||||||
    [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
 | 
					    [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
 | 
				
			||||||
        prof_rewind( @"moc" );
 | 
					        prof_rewind( @"moc" );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -648,7 +646,7 @@
 | 
				
			|||||||
        BOOL exact = NO;
 | 
					        BOOL exact = NO;
 | 
				
			||||||
        NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
 | 
					        NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
 | 
				
			||||||
        for (MPSiteEntity *site in siteResults) {
 | 
					        for (MPSiteEntity *site in siteResults) {
 | 
				
			||||||
            [newSites addObject:[[MPSiteModel alloc] initWithEntity:site fuzzyGroups:fuzzyGroups]];
 | 
					            [newSites addObject:[[MPSiteModel alloc] initWithEntity:site queryGroups:queryGroups]];
 | 
				
			||||||
            exact |= [site.name isEqualToString:queryString];
 | 
					            exact |= [site.name isEqualToString:queryString];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        prof_rewind( @"newSites: %u, exact: %d", (uint)[siteResults count], exact );
 | 
					        prof_rewind( @"newSites: %u, exact: %d", (uint)[siteResults count], exact );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ typedef NS_ENUM ( NSUInteger, MPSiteCellMode ) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@interface MPSiteCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate>
 | 
					@interface MPSiteCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@property(nonatomic) NSArray *fuzzyGroups;
 | 
					@property(nonatomic) NSArray *queryGroups;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
 | 
					- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
 | 
				
			||||||
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
 | 
					- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,7 +135,7 @@
 | 
				
			|||||||
    [super prepareForReuse];
 | 
					    [super prepareForReuse];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self.siteOID = nil;
 | 
					    self.siteOID = nil;
 | 
				
			||||||
    self.fuzzyGroups = nil;
 | 
					    self.queryGroups = nil;
 | 
				
			||||||
    self.transientSite = nil;
 | 
					    self.transientSite = nil;
 | 
				
			||||||
    self.mode = MPPasswordCellModePassword;
 | 
					    self.mode = MPPasswordCellModePassword;
 | 
				
			||||||
    [self updateAnimated:NO];
 | 
					    [self updateAnimated:NO];
 | 
				
			||||||
@@ -150,11 +150,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma mark - State
 | 
					#pragma mark - State
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- (void)setFuzzyGroups:(NSArray *)fuzzyGroups {
 | 
					- (void)setQueryGroups:(NSArray *)queryGroups {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (self.fuzzyGroups == fuzzyGroups)
 | 
					    if (self.queryGroups == queryGroups)
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    _fuzzyGroups = fuzzyGroups;
 | 
					    _queryGroups = queryGroups;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]];
 | 
					    [self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -656,14 +656,14 @@
 | 
				
			|||||||
    NSString *siteName = self.transientSite?: site.name;
 | 
					    NSString *siteName = self.transientSite?: site.name;
 | 
				
			||||||
    NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
 | 
					    NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
 | 
				
			||||||
    if ([attributedSiteName length])
 | 
					    if ([attributedSiteName length])
 | 
				
			||||||
        for (NSUInteger f = 0, s = (NSUInteger)-1; f < [self.fuzzyGroups count]; ++f) {
 | 
					        for (NSUInteger f = 0, s = 0; f < [self.queryGroups count]; ++f, ++s) {
 | 
				
			||||||
            s = [siteName rangeOfString:self.fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
 | 
					            s = [siteName rangeOfString:self.queryGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
 | 
				
			||||||
                                  range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location;
 | 
					                                  range:NSMakeRange( s, [siteName length] - s )].location;
 | 
				
			||||||
            if (s == NSNotFound)
 | 
					            if (s == NSNotFound)
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            [attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[UIColor redColor]
 | 
					            [attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[UIColor redColor]
 | 
				
			||||||
                                       range:NSMakeRange( s, [self.fuzzyGroups[f] length] )];
 | 
					                                       range:NSMakeRange( s, [self.queryGroups[f] length] )];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (self.transientSite)
 | 
					    if (self.transientSite)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
				
			|||||||
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate>
 | 
					@interface MPSitesViewController()<NSFetchedResultsControllerDelegate>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
 | 
					@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
 | 
				
			||||||
@property(nonatomic, strong) NSArray *fuzzyGroups;
 | 
					@property(nonatomic, strong) NSArray *queryGroups;
 | 
				
			||||||
@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
 | 
					@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
 | 
				
			||||||
@property(nonatomic, strong) NSMutableArray<NSMutableArray *> *dataSource;
 | 
					@property(nonatomic, strong) NSMutableArray<NSMutableArray *> *dataSource;
 | 
				
			||||||
@property(nonatomic, weak) UIViewController *popdownVC;
 | 
					@property(nonatomic, weak) UIViewController *popdownVC;
 | 
				
			||||||
@@ -162,7 +162,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
				
			|||||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
 | 
					- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    MPSiteCell *cell = [MPSiteCell dequeueFromCollectionView:collectionView indexPath:indexPath];
 | 
					    MPSiteCell *cell = [MPSiteCell dequeueFromCollectionView:collectionView indexPath:indexPath];
 | 
				
			||||||
    [cell setFuzzyGroups:self.fuzzyGroups];
 | 
					    [cell setQueryGroups:self.queryGroups];
 | 
				
			||||||
    id item = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item];
 | 
					    id item = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item];
 | 
				
			||||||
    if ([item isKindOfClass:[MPSiteEntity class]])
 | 
					    if ([item isKindOfClass:[MPSiteEntity class]])
 | 
				
			||||||
        [cell setSite:item animated:NO];
 | 
					        [cell setSite:item animated:NO];
 | 
				
			||||||
@@ -355,21 +355,19 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
				
			|||||||
- (void)reloadSites {
 | 
					- (void)reloadSites {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [self.fetchedResultsController.managedObjectContext performBlock:^{
 | 
					    [self.fetchedResultsController.managedObjectContext performBlock:^{
 | 
				
			||||||
        static NSRegularExpression *fuzzyRE;
 | 
					 | 
				
			||||||
        static dispatch_once_t once = 0;
 | 
					 | 
				
			||||||
        dispatch_once( &once, ^{
 | 
					 | 
				
			||||||
            fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
 | 
					 | 
				
			||||||
        } );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        NSString *queryString = self.query;
 | 
					        NSString *queryString = self.query;
 | 
				
			||||||
        NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"]
 | 
					        NSMutableArray *queryGroups = [NSMutableArray new];
 | 
				
			||||||
                stringByAppendingString:@"*"];
 | 
					        NSMutableString *queryPattern = [NSMutableString new];
 | 
				
			||||||
        NSMutableArray *fuzzyGroups = [NSMutableArray new];
 | 
					        [queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
 | 
				
			||||||
        [fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
 | 
					                              usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
 | 
				
			||||||
                               usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
 | 
					            if (substringRange.location < 10) {
 | 
				
			||||||
                                   [fuzzyGroups addObject:[queryString substringWithRange:result.range]];
 | 
					                [queryGroups addObject:substring];
 | 
				
			||||||
                               }];
 | 
					                [queryPattern appendString:@"*"];
 | 
				
			||||||
        self.fuzzyGroups = fuzzyGroups;
 | 
					            }
 | 
				
			||||||
 | 
					                                  [queryPattern appendString:substring];
 | 
				
			||||||
 | 
					                              }];
 | 
				
			||||||
 | 
					        [queryPattern appendString:@"*"];
 | 
				
			||||||
 | 
					        self.queryGroups = queryGroups;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        NSError *error = nil;
 | 
					        NSError *error = nil;
 | 
				
			||||||
        self.fetchedResultsController.fetchRequest.predicate =
 | 
					        self.fetchedResultsController.fetchRequest.predicate =
 | 
				
			||||||
@@ -382,7 +380,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
				
			|||||||
                                       toSections:[self createDataSource]
 | 
					                                       toSections:[self createDataSource]
 | 
				
			||||||
                                      reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
 | 
					                                      reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
 | 
				
			||||||
                        for (MPSiteCell *cell in self.collectionView.visibleCells)
 | 
					                        for (MPSiteCell *cell in self.collectionView.visibleCells)
 | 
				
			||||||
                            [cell setFuzzyGroups:self.fuzzyGroups];
 | 
					                            [cell setQueryGroups:self.queryGroups];
 | 
				
			||||||
                    }];
 | 
					                    }];
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
    }];
 | 
					    }];
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user