From da053d7282781379808806a0b30a7588d554e901 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 6 Jul 2022 22:39:47 -0700 Subject: [PATCH] [File Tree] Significantly improve the watcher - Switch to fine grained folder and file watching responses - Navigate the PathNode tree using a fast dictionary of path components - Quickly refresh the file tree by locating parent nodes to refresh Signed-off-by: Christopher Snowhill --- FileTree/FileTreeDataSource.m | 40 ++++++++++++++++++++--------------- FileTree/PathNode.h | 10 +++++++++ FileTree/PathNode.m | 23 ++++++++++++++++++++ FileTree/PathWatcher.h | 2 +- FileTree/PathWatcher.m | 4 ++-- FileTree/SmartFolderNode.m | 5 +++++ 6 files changed, 64 insertions(+), 20 deletions(-) diff --git a/FileTree/FileTreeDataSource.m b/FileTree/FileTreeDataSource.m index 9335ffe4c..593cf08c4 100644 --- a/FileTree/FileTreeDataSource.m +++ b/FileTree/FileTreeDataSource.m @@ -130,34 +130,40 @@ static NSURL *defaultMusicDirectory(void) { withString:@"" options:NSAnchoredSearch range:NSMakeRange(0, [path length])] stringByStandardizingPath]; + if([relativePath isEqualToString:[[self rootURL] path]]) + relativePath = @""; PathNode *node = rootNode; DLog(@"Root | Relative | Path: %@ | %@ | %@", [[self rootURL] path], relativePath, path); for(NSString *c in [relativePath pathComponents]) { DLog(@"COMPONENT: %@", c); - BOOL found = NO; - for(PathNode *subnode in [node subpaths]) { - if([[[[subnode URL] path] lastPathComponent] isEqualToString:c]) { - node = subnode; - found = YES; - } - } - - if(!found) { - DLog(@"Not found!"); - return nil; - } + PathNode *subnode = [[node subpathsLookup] objectForKey:c]; + if(!subnode) return nil; + node = subnode; } return node; } -- (void)pathDidChange:(NSString *)path { +- (void)pathDidChange:(NSString *)path flags:(FSEventStreamEventFlags)flags { DLog(@"PATH DID CHANGE: %@", path); // Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node] - PathNode *node = [self nodeForPath:path]; - DLog(@"NODE IS: %@", node); - [node updatePath]; - [self reloadPathNode:node]; + PathNode *node; + do { + node = [self nodeForPath:path]; + path = [path stringByDeletingLastPathComponent]; + if(!path || [path length] < 2) return; + } while(!node); + + if(flags & kFSEventStreamEventFlagItemRemoved) { + DLog(@"Removing node: %@", node); + PathNode *parentNode = [self nodeForPath:path]; + [parentNode updatePath]; + [self reloadPathNode:parentNode]; + } else { + DLog(@"NODE IS: %@", node); + [node updatePath]; + [self reloadPathNode:node]; + } } - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { diff --git a/FileTree/PathNode.h b/FileTree/PathNode.h index 7efe4a09f..07b6b8755 100644 --- a/FileTree/PathNode.h +++ b/FileTree/PathNode.h @@ -16,9 +16,13 @@ NSURL *url; NSString *display; // The pretty path to display. + NSString *lastPathComponent; + NSImage *icon; NSArray *subpaths; + + NSMutableDictionary *subpathsLookup; } - (id)initWithDataSource:(FileTreeDataSource *)ds url:(NSURL *)u; @@ -34,6 +38,12 @@ - (NSString *)display; - (void)setDisplay:(NSString *)s; +- (NSString *)lastPathComponent; +- (void)setLastPathComponent:(NSString *)s; + +- (NSDictionary *)subpathsLookup; +- (void)setSubpathsLookup:(NSMutableDictionary *)d; + - (NSImage *)icon; - (BOOL)isLeaf; diff --git a/FileTree/PathNode.m b/FileTree/PathNode.m index abfba3b0d..820e09775 100644 --- a/FileTree/PathNode.m +++ b/FileTree/PathNode.m @@ -60,6 +60,8 @@ NSURL *resolveAliases(NSURL *url) { display = [[NSFileManager defaultManager] displayNameAtPath:[u path]]; + lastPathComponent = [[u path] lastPathComponent]; + icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]]; [icon setSize:NSMakeSize(16.0, 16.0)]; @@ -134,6 +136,19 @@ NSURL *resolveAliases(NSURL *url) { - (void)setSubpaths:(NSArray *)s { subpaths = s; + + subpathsLookup = [[NSMutableDictionary alloc] init]; + for(PathNode *node in s) { + [subpathsLookup setObject:node forKey:node.lastPathComponent]; + } +} + +- (NSDictionary *)subpathsLookup { + return subpathsLookup; +} + +- (void)setSubpathsLookup:(NSMutableDictionary *)d { + subpathsLookup = d; } - (BOOL)isLeaf { @@ -152,4 +167,12 @@ NSURL *resolveAliases(NSURL *url) { return icon; } +- (NSString *)lastPathComponent { + return lastPathComponent; +} + +- (void)setLastPathComponent:(NSString *)s { + lastPathComponent = s; +} + @end diff --git a/FileTree/PathWatcher.h b/FileTree/PathWatcher.h index e73012575..9cf4ee3da 100644 --- a/FileTree/PathWatcher.h +++ b/FileTree/PathWatcher.h @@ -25,6 +25,6 @@ @protocol PathWatcherDelegate -- (void)pathDidChange:(NSString *)path; +- (void)pathDidChange:(NSString *)path flags:(FSEventStreamEventFlags)flags; @end diff --git a/FileTree/PathWatcher.m b/FileTree/PathWatcher.m index ee02d30e3..6dcee5153 100644 --- a/FileTree/PathWatcher.m +++ b/FileTree/PathWatcher.m @@ -22,7 +22,7 @@ const FSEventStreamEventId eventIds[]) { printf("Callback called\n"); for(i = 0; i < numEvents; i++) { NSString *pathString = [[NSString alloc] initWithUTF8String:paths[i]]; - [[pathWatcher delegate] pathDidChange:pathString]; + [[pathWatcher delegate] pathDidChange:pathString flags:eventFlags[i]]; } } @@ -62,7 +62,7 @@ const FSEventStreamEventId eventIds[]) { (__bridge CFArrayRef)pathsToWatch, kFSEventStreamEventIdSinceNow, // Or a previous event ID 1.0, // latency in seconds - kFSEventStreamCreateFlagNone // Watch this and all its subdirectories + kFSEventStreamCreateFlagFileEvents // Watch this and all its subdirectories ); FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); diff --git a/FileTree/SmartFolderNode.m b/FileTree/SmartFolderNode.m index 6cb6f748c..7bc58aaec 100644 --- a/FileTree/SmartFolderNode.m +++ b/FileTree/SmartFolderNode.m @@ -41,6 +41,11 @@ - (void)setSubpaths:(id)s { subpaths = s; + + subpathsLookup = [[NSMutableDictionary alloc] init]; + for(PathNode *node in s) { + [subpathsLookup setObject:node forKey:node.lastPathComponent]; + } } - (unsigned int)countOfSubpaths {