From 71cdb0dbe05da0a37e0719241a3137f7c0126051 Mon Sep 17 00:00:00 2001 From: Nathan Kinsinger Date: Mon, 6 Sep 2010 15:22:51 -0600 Subject: [PATCH] Add search modes for git pickaxe Add two new search modes to search the commits for strings that were added or removed in a commit. - match by string or an extended POSIX regex (see man git-log -S and --pickaxe-regex) - include the modes in the search menu - store the current mode in user defaults - show progress indicator when search takes time --- PBGitDefaults.h | 2 + PBGitDefaults.m | 15 ++++ PBGitHistoryView.xib | 70 +++++++++++++-- PBHistorySearchController.h | 17 ++++ PBHistorySearchController.m | 172 +++++++++++++++++++++++++++++++++++- 5 files changed, 266 insertions(+), 10 deletions(-) diff --git a/PBGitDefaults.h b/PBGitDefaults.h index c7c494a..46b28b5 100644 --- a/PBGitDefaults.h +++ b/PBGitDefaults.h @@ -34,5 +34,7 @@ + (void) removePreviousDocumentPaths; + (NSInteger) branchFilter; + (void) setBranchFilter:(NSInteger)state; ++ (NSInteger)historySearchMode; ++ (void)setHistorySearchMode:(NSInteger)mode; @end diff --git a/PBGitDefaults.m b/PBGitDefaults.m index 0e042a5..ccc2162 100644 --- a/PBGitDefaults.m +++ b/PBGitDefaults.m @@ -7,6 +7,7 @@ // #import "PBGitDefaults.h" +#import "PBHistorySearchController.h" #define kDefaultVerticalLineLength 50 #define kCommitMessageViewVerticalLineLength @"PBCommitMessageViewVerticalLineLength" @@ -25,6 +26,7 @@ #define kOpenPreviousDocumentsOnLaunch @"PBOpenPreviousDocumentsOnLaunch" #define kPreviousDocumentPaths @"PBPreviousDocumentPaths" #define kBranchFilterState @"PBBranchFilter" +#define kHistorySearchMode @"PBHistorySearchMode" @implementation PBGitDefaults @@ -53,6 +55,8 @@ forKey:kShouldCheckoutBranch]; [defaultValues setObject:[NSNumber numberWithBool:NO] forKey:kOpenPreviousDocumentsOnLaunch]; + [defaultValues setObject:[NSNumber numberWithInteger:kGitXBasicSeachMode] + forKey:kHistorySearchMode]; [[NSUserDefaults standardUserDefaults] registerDefaults:defaultValues]; } @@ -170,4 +174,15 @@ [[NSUserDefaults standardUserDefaults] setInteger:state forKey:kBranchFilterState]; } ++ (NSInteger)historySearchMode +{ + return [[NSUserDefaults standardUserDefaults] integerForKey:kHistorySearchMode]; +} + ++ (void)setHistorySearchMode:(NSInteger)mode +{ + [[NSUserDefaults standardUserDefaults] setInteger:mode forKey:kHistorySearchMode]; +} + + @end diff --git a/PBGitHistoryView.xib b/PBGitHistoryView.xib index b6bf807..f9e2774 100644 --- a/PBGitHistoryView.xib +++ b/PBGitHistoryView.xib @@ -702,6 +702,15 @@ 266 YES + + + -2147482359 + + {{637, 3}, {16, 16}} + + 20746 + 100 + 265 @@ -2096,6 +2105,14 @@ 437 + + + progressIndicator + + + + 448 + @@ -2439,6 +2456,7 @@ + Commits Scope Bar @@ -2695,6 +2713,11 @@ + + 447 + + + @@ -2803,6 +2826,7 @@ 428.IBSegmentedControlInspectorSelectedSegmentMetadataKey 430.IBPluginDependency 431.IBPluginDependency + 447.IBPluginDependency 46.IBEditorWindowLastContentRect 46.IBPluginDependency 48.IBPluginDependency @@ -2987,6 +3011,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin {{9, 486}, {852, 432}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3023,7 +3048,7 @@ - 437 + 448 @@ -3480,6 +3505,9 @@ YES YES + selectBasicSearch: + selectPickaxeSearch: + selectRegexSearch: stepperPressed: updateSearch: @@ -3487,17 +3515,35 @@ YES id id + id + id + id YES YES + selectBasicSearch: + selectPickaxeSearch: + selectRegexSearch: stepperPressed: updateSearch: YES + + selectBasicSearch: + id + + + selectPickaxeSearch: + id + + + selectRegexSearch: + id + stepperPressed: id @@ -3515,6 +3561,7 @@ commitController historyController numberOfMatchesField + progressIndicator searchField stepper @@ -3523,6 +3570,7 @@ NSArrayController PBGitHistoryController NSTextField + NSProgressIndicator NSSearchField NSSegmentedControl @@ -3534,6 +3582,7 @@ commitController historyController numberOfMatchesField + progressIndicator searchField stepper @@ -3551,6 +3600,10 @@ numberOfMatchesField NSTextField + + progressIndicator + NSProgressIndicator + searchField NSSearchField @@ -4360,13 +4413,6 @@ QuartzCore.framework/Headers/CIImageProvider.h - - NSObject - - IBFrameworkSource - ScriptingBridge.framework/Headers/SBApplication.h - - NSObject @@ -4472,6 +4518,14 @@ AppKit.framework/Headers/NSPopUpButton.h + + NSProgressIndicator + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSProgressIndicator.h + + NSResponder diff --git a/PBHistorySearchController.h b/PBHistorySearchController.h index 75bd250..e51ba47 100644 --- a/PBHistorySearchController.h +++ b/PBHistorySearchController.h @@ -9,6 +9,13 @@ #import +typedef enum historySearchModes { + kGitXBasicSeachMode = 1, + kGitXPickaxeSearchMode, + kGitXRegexSearchMode, + kGitXMaxSearchMode // always keep this item last +} PBHistorySearchMode; + @class PBGitHistoryController; @@ -16,11 +23,16 @@ PBGitHistoryController *historyController; NSArrayController *commitController; + PBHistorySearchMode searchMode; NSIndexSet *results; NSSearchField *searchField; NSSegmentedControl *stepper; NSTextField *numberOfMatchesField; + NSProgressIndicator *progressIndicator; + NSTimer *searchTimer; + + NSTask *backgroundSearchTask; NSPanel *rewindPanel; } @@ -31,11 +43,16 @@ @property (assign) IBOutlet NSSearchField *searchField; @property (assign) IBOutlet NSSegmentedControl *stepper; @property (assign) IBOutlet NSTextField *numberOfMatchesField; +@property (assign) IBOutlet NSProgressIndicator *progressIndicator; + +@property (assign) PBHistorySearchMode searchMode; - (BOOL)isRowInSearchResults:(NSInteger)rowIndex; - (BOOL)hasSearchResults; +- (void)selectSearchMode:(id)sender; + - (void)selectNextResult; - (void)selectPreviousResult; - (IBAction)stepperPressed:(id)sender; diff --git a/PBHistorySearchController.m b/PBHistorySearchController.m index 1aa3313..8a48320 100644 --- a/PBHistorySearchController.m +++ b/PBHistorySearchController.m @@ -9,6 +9,7 @@ #import "PBHistorySearchController.h" #import "PBGitHistoryController.h" #import "PBGitRepository.h" +#import "PBGitDefaults.h" #import @@ -20,6 +21,8 @@ - (void)setupSearchMenuTemplate; - (void)startBasicSearch; +- (void)startBackgroundSearch; +- (void)clearProgressIndicator; - (void)showSearchRewindPanelReverse:(BOOL)isReversed; @@ -30,6 +33,8 @@ #define kGitXSearchDirectionPrevious -1 #define kGitXBasicSearchLabel @"Subject, Author, SHA" +#define kGitXPickaxeSearchLabel @"Commit (pickaxe)" +#define kGitXRegexSearchLabel @"Commit (pickaxe regex)" #define kGitXSearchArrangedObjectsContext @"GitXSearchArrangedObjectsContext" @@ -42,6 +47,9 @@ @synthesize searchField; @synthesize stepper; @synthesize numberOfMatchesField; +@synthesize progressIndicator; + +@synthesize searchMode; @@ -58,6 +66,12 @@ return ([results count] > 0); } +- (void)selectSearchMode:(id)sender +{ + self.searchMode = [sender tag]; + [self updateSearch:self]; +} + - (void)selectNextResult { [self selectNextResultInDirection:kGitXSearchDirectionNext]; @@ -90,13 +104,16 @@ - (IBAction)updateSearch:(id)sender { - [self startBasicSearch]; + if (self.searchMode == kGitXBasicSeachMode) + [self startBasicSearch]; + else + [self startBackgroundSearch]; } - (void)awakeFromNib { [self setupSearchMenuTemplate]; - [[searchField cell] setPlaceholderString:@"Subject, Author, SHA"]; + self.searchMode = [PBGitDefaults historySearchMode]; [self updateUI]; @@ -181,6 +198,7 @@ [stepper setHidden:NO]; [historyController.commitList reloadData]; } + [self clearProgressIndicator]; } // changes the selection to the next match after the current selected row unless the current row is already a match @@ -203,6 +221,24 @@ NSMenu *searchMenu = [[NSMenu alloc] initWithTitle:@"Search Menu"]; NSMenuItem *item; + item = [[NSMenuItem alloc] initWithTitle:kGitXBasicSearchLabel action:@selector(selectSearchMode:) keyEquivalent:@""]; + [item setTarget:self]; + [item setTag:kGitXBasicSeachMode]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:kGitXPickaxeSearchLabel action:@selector(selectSearchMode:) keyEquivalent:@""]; + [item setTarget:self]; + [item setTag:kGitXPickaxeSearchMode]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:kGitXRegexSearchLabel action:@selector(selectSearchMode:) keyEquivalent:@""]; + [item setTarget:self]; + [item setTag:kGitXRegexSearchMode]; + [searchMenu addItem:item]; + + item = [NSMenuItem separatorItem]; + [searchMenu addItem:item]; + item = [[NSMenuItem alloc] initWithTitle:@"Recent Searches" action:NULL keyEquivalent:@""]; [item setTag:NSSearchFieldRecentsTitleMenuItemTag]; [searchMenu addItem:item]; @@ -226,6 +262,77 @@ [[searchField cell] setSearchMenuTemplate:searchMenu]; } +- (void)updateSearchMenuState +{ + NSMenu *searchMenu = [[searchField cell] searchMenuTemplate]; + if (!searchMenu) + return; + + NSMenuItem *item; + + item = [searchMenu itemWithTag:kGitXBasicSeachMode]; + [item setState:(searchMode == kGitXBasicSeachMode) ? NSOnState : NSOffState]; + + item = [searchMenu itemWithTag:kGitXPickaxeSearchMode]; + [item setState:(searchMode == kGitXPickaxeSearchMode) ? NSOnState : NSOffState]; + + item = [searchMenu itemWithTag:kGitXRegexSearchMode]; + [item setState:(searchMode == kGitXRegexSearchMode) ? NSOnState : NSOffState]; + + [[searchField cell] setSearchMenuTemplate:searchMenu]; + + [PBGitDefaults setHistorySearchMode:searchMode]; +} + +- (void)updateSearchPlaceholderString +{ + switch (self.searchMode) { + case kGitXPickaxeSearchMode: + [[searchField cell] setPlaceholderString:kGitXPickaxeSearchLabel]; + break; + case kGitXRegexSearchMode: + [[searchField cell] setPlaceholderString:kGitXRegexSearchLabel]; + break; + default: + [[searchField cell] setPlaceholderString:kGitXBasicSearchLabel]; + break; + } +} + +- (void)setSearchMode:(PBHistorySearchMode)mode +{ + if ((mode < kGitXBasicSeachMode) || (mode >= kGitXMaxSearchMode)) + mode = kGitXBasicSeachMode; + + searchMode = mode; + [PBGitDefaults setHistorySearchMode:searchMode]; + + [self updateSearchMenuState]; + [self updateSearchPlaceholderString]; +} + +- (void)searchTimerFired:(NSTimer*)theTimer +{ + [self.progressIndicator setHidden:NO]; + [self.progressIndicator startAnimation:self]; +} + +- (void)clearProgressIndicator +{ + [searchTimer invalidate]; + searchTimer = nil; + [self.progressIndicator setHidden:YES]; + [self.progressIndicator stopAnimation:self]; +} + +- (void)startProgressIndicator +{ + [self clearProgressIndicator]; + [numberOfMatchesField setHidden:YES]; + [stepper setHidden:YES]; + searchTimer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(searchTimerFired:) userInfo:nil repeats:NO]; +} + #pragma mark Basic Search @@ -255,6 +362,67 @@ +#pragma mark Pickaxe/Regex Search + +- (void)startBackgroundSearch +{ + if (backgroundSearchTask) { + NSFileHandle *handle = [[backgroundSearchTask standardOutput] fileHandleForReading]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; + [backgroundSearchTask terminate]; + } + + NSString *searchString = [[searchField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([searchString isEqualToString:@""]) { + [self clearSearch]; + return; + } + + results = nil; + + NSMutableArray *searchArguments = [NSMutableArray arrayWithObjects:@"log", @"--pretty=format:%H", [NSString stringWithFormat:@"-S%@", searchString], nil]; + if (self.searchMode == kGitXRegexSearchMode) + [searchArguments insertObject:@"--pickaxe-regex" atIndex:1]; + + backgroundSearchTask = [PBEasyPipe taskForCommand:[PBGitBinary path] withArgs:searchArguments inDir:[[historyController.repository fileURL] path]]; + [backgroundSearchTask launch]; + + NSFileHandle *handle = [[backgroundSearchTask standardOutput] fileHandleForReading]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(parseBackgroundSearchResults:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; + [handle readToEndOfFileInBackgroundAndNotify]; + + [self startProgressIndicator]; +} + +- (void)parseBackgroundSearchResults:(NSNotification *)notification +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadToEndOfFileCompletionNotification object:[notification object]]; + backgroundSearchTask = nil; + + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; + NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem]; + + NSString *resultsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSArray *resultsArray = [resultsString componentsSeparatedByString:@"\n"]; + + for (NSString *resultSHA in resultsArray) { + NSUInteger index = 0; + for (PBGitCommit *commit in [commitController arrangedObjects]) { + if ([resultSHA isEqualToString:commit.sha.string]) { + [indexes addIndex:index]; + break; + } + index++; + } + } + + results = indexes; + [self clearProgressIndicator]; + [self updateSelectedResult]; +} + + + #pragma mark - #pragma mark Rewind Panel