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:
parent
7091e2ee1b
commit
8582c934c2
@ -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];
|
||||||
}];
|
}];
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
|
Loading…
Reference in New Issue
Block a user