// // GLFileView.m // GitX // // Created by German Laullon on 14/09/10. // Copyright 2010 __MyCompanyName__. All rights reserved. // #import "GLFileView.h" #import "PBGitGradientBarView.h" #define GROUP_LABEL @"Label" // string #define GROUP_SEPARATOR @"HasSeparator" // BOOL as NSNumber #define GROUP_SELECTION_MODE @"SelectionMode" // MGScopeBarGroupSelectionMode (int) as NSNumber #define GROUP_ITEMS @"Items" // array of dictionaries, each containing the following keys: #define ITEM_IDENTIFIER @"Identifier" // string #define ITEM_NAME @"Name" // string @interface GLFileView () - (void)saveSplitViewPosition; + (NSString *)parseDiffBlock:(NSString *)txt; + (NSString *)parseDiffHeader:(NSString *)txt; + (NSString *)parseDiffChunk:(NSString *)txt; + (NSString *)parseBinaryDiff:(NSString *)txt; @end @implementation GLFileView - (void) awakeFromNib { NSString *formatFile = [[NSBundle mainBundle] pathForResource:@"format" ofType:@"html" inDirectory:@"html/views/log"]; if(formatFile!=nil) logFormat=[NSString stringWithContentsOfURL:[NSURL fileURLWithPath:formatFile] encoding:NSUTF8StringEncoding error:nil]; startFile = @"fileview"; //repository = historyController.repository; [super awakeFromNib]; [historyController.treeController addObserver:self forKeyPath:@"selection" options:0 context:@"treeController"]; self.groups = [NSMutableArray arrayWithCapacity:0]; NSArray *items = [NSArray arrayWithObjects: [NSDictionary dictionaryWithObjectsAndKeys: startFile, ITEM_IDENTIFIER, @"Source", ITEM_NAME, nil], [NSDictionary dictionaryWithObjectsAndKeys: @"blame", ITEM_IDENTIFIER, @"Blame", ITEM_NAME, nil], [NSDictionary dictionaryWithObjectsAndKeys: @"log", ITEM_IDENTIFIER, @"History", ITEM_NAME, nil], [NSDictionary dictionaryWithObjectsAndKeys: @"diff", ITEM_IDENTIFIER, @"Diff", ITEM_NAME, nil], nil]; [self.groups addObject:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], GROUP_SEPARATOR, [NSNumber numberWithInt:MGRadioSelectionMode], GROUP_SELECTION_MODE, // single selection group. items, GROUP_ITEMS, nil]]; NSArray *difft = [NSArray arrayWithObjects: [NSDictionary dictionaryWithObjectsAndKeys: @"l", ITEM_IDENTIFIER, @"Local", ITEM_NAME, nil], [NSDictionary dictionaryWithObjectsAndKeys: @"h", ITEM_IDENTIFIER, @"HEAD", ITEM_NAME, nil], nil]; [self.groups addObject:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], GROUP_SEPARATOR, [NSNumber numberWithInt:MGRadioSelectionMode], GROUP_SELECTION_MODE, // single selection group. difft, GROUP_ITEMS, @"Diff with:",GROUP_LABEL, nil]]; [typeBar reloadData]; [fileListSplitView setHidden:YES]; [self performSelector:@selector(restoreSplitViewPositiion) withObject:nil afterDelay:0]; } - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { [self showFile]; } - (void) showFile { NSError *theError = nil; NSArray *files=[historyController.treeController selectedObjects]; if ([files count]>0) { PBGitTree *file=[files objectAtIndex:0]; DLog(@"file=%@ == %@ => %d",file,lastFile,[file isEqualTo:lastFile]); if(![file isEqualTo:lastFile]){ lastFile=file; NSString *fileTxt = @""; if(startFile==@"fileview"){ fileTxt=[file textContents:&theError]; if(!theError) fileTxt=[GLFileView escapeHTML:fileTxt]; }else if(startFile==@"blame"){ fileTxt=[file blame:&theError]; if(!theError) fileTxt=[self parseBlame:fileTxt]; }else if(startFile==@"log"){ fileTxt=[file log:logFormat error:&theError]; }else if(startFile==@"diff"){ fileTxt=[file diff:diffType error:&theError]; if(!theError) fileTxt=[GLFileView parseDiff:fileTxt]; } id script = [view windowScriptObject]; if(!theError){ NSString *filePath = [file fullPath]; fileTxt=[fileTxt stringByReplacingOccurrencesOfString:@"\t" withString:@" "]; DLog(@"file.sha='%@'",file.sha); fileTxt=[fileTxt stringByReplacingOccurrencesOfString:@"{SHA_PREV}" withString:file.sha]; if(diffType==@"h") { fileTxt=[fileTxt stringByReplacingOccurrencesOfString:@"{SHA}" withString:@"HEAD"]; }else { fileTxt=[fileTxt stringByReplacingOccurrencesOfString:@"{SHA}" withString:@"--"]; } [script callWebScriptMethod:@"showFile" withArguments:[NSArray arrayWithObjects:fileTxt, filePath, nil]]; }else{ [script callWebScriptMethod:@"setMessage" withArguments:[NSArray arrayWithObjects:[theError localizedDescription], nil]]; } [self updateSearch:searchField]; } } #ifdef DEBUG_BUILD DOMHTMLElement *dom=(DOMHTMLElement *)[[[view mainFrame] DOMDocument] documentElement]; NSString *domH=[dom outerHTML]; NSString *tmpFile=@"~/tmp/test.html"; [domH writeToFile:[tmpFile stringByExpandingTildeInPath] atomically:true encoding:NSUTF8StringEncoding error:nil]; #endif } #pragma mark JavaScript log.js methods - (void) selectCommit:(NSString*)c { [historyController selectCommit:[PBGitSHA shaWithString:c]]; } // TODO: need to be refactoring - (void) openFileMerge:(NSString*)file sha:(NSString *)sha sha2:(NSString *)sha2; { NSArray *args=[NSArray arrayWithObjects:@"difftool",@"--no-prompt",@"--tool=opendiff",sha,sha2,file,nil]; [historyController.repository handleInWorkDirForArguments:args]; } #pragma mark MGScopeBarDelegate methods - (int)numberOfGroupsInScopeBar:(MGScopeBar *)theScopeBar { return [self.groups count]; } - (NSArray *)scopeBar:(MGScopeBar *)theScopeBar itemIdentifiersForGroup:(int)groupNumber { return [[self.groups objectAtIndex:groupNumber] valueForKeyPath:[NSString stringWithFormat:@"%@.%@", GROUP_ITEMS, ITEM_IDENTIFIER]]; } - (NSString *)scopeBar:(MGScopeBar *)theScopeBar labelForGroup:(int)groupNumber { return [[self.groups objectAtIndex:groupNumber] objectForKey:GROUP_LABEL]; // might be nil, which is fine (nil means no label). } - (NSString *)scopeBar:(MGScopeBar *)theScopeBar titleOfItem:(NSString *)identifier inGroup:(int)groupNumber { NSArray *items = [[self.groups objectAtIndex:groupNumber] objectForKey:GROUP_ITEMS]; if (items) { for (NSDictionary *item in items) { if ([[item objectForKey:ITEM_IDENTIFIER] isEqualToString:identifier]) { return [item objectForKey:ITEM_NAME]; break; } } } return nil; } - (MGScopeBarGroupSelectionMode)scopeBar:(MGScopeBar *)theScopeBar selectionModeForGroup:(int)groupNumber { return [[[self.groups objectAtIndex:groupNumber] objectForKey:GROUP_SELECTION_MODE] intValue]; } - (void)scopeBar:(MGScopeBar *)theScopeBar selectedStateChanged:(BOOL)selected forItem:(NSString *)identifier inGroup:(int)groupNumber { if((groupNumber==0) && (startFile!=identifier)){ NSString *path = [NSString stringWithFormat:@"html/views/%@", identifier]; NSString *html = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:path]; NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:html]]; [[view mainFrame] loadRequest:request]; startFile=identifier; }else if(groupNumber==1){ diffType=identifier; if(startFile==@"diff"){ [[view mainFrame] reload]; } } lastFile=nil; } - (NSView *)accessoryViewForScopeBar:(MGScopeBar *)scopeBar { return accessoryView; } - (void) didLoad { [self showFile]; } - (void)closeView { [historyController.treeController removeObserver:self forKeyPath:@"selection"]; [self saveSplitViewPosition]; [super closeView]; } + (NSString *) escapeHTML:(NSString *)txt { if (txt == nil) return txt; NSMutableString *newTxt = [NSMutableString stringWithString:txt]; [newTxt replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [newTxt length])]; [newTxt replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [newTxt length])]; [newTxt replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [newTxt length])]; [newTxt replaceOccurrencesOfString:@"\"" withString:@""" options:NSLiteralSearch range:NSMakeRange(0, [newTxt length])]; [newTxt replaceOccurrencesOfString:@"'" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [newTxt length])]; return newTxt; } + (NSString *)parseDiffTree:(NSString *)txt withStats:(NSMutableDictionary *)stats { NSInteger granTotal=1; for(NSArray *stat in [stats allValues]){ NSInteger add=[[stat objectAtIndex:0] integerValue]; NSInteger rem=[[stat objectAtIndex:1] integerValue]; NSInteger tot=add+rem; if(tot>granTotal) granTotal=tot; [stats setObject:[NSArray arrayWithObjects:[NSNumber numberWithInteger:add],[NSNumber numberWithInteger:rem],[NSNumber numberWithInteger:tot],nil] forKey:[stat objectAtIndex:2]]; } NSArray *lines = [txt componentsSeparatedByString:@"\n"]; NSMutableString *res=[NSMutableString string]; [res appendString:@"
| "]; [res appendString:[NSString stringWithFormat:@"%@",status,file,fileName,txt]]; [res appendString:@" | "];
[res appendString:@" "];
[res appendString:[NSString stringWithFormat:@"",((add*100)/granTotal)]];
[res appendString:[NSString stringWithFormat:@"",((rem*100)/granTotal)]];
[res appendString:@" "];
[res appendString:[NSString stringWithFormat:@" | + %d | - %d |
%@
",line]]; }while((line=[lines nextObject])); [res appendString:@"%@ | \n\n",commitID,truncate_c,truncate_a,truncate_s];
[headers setObject:block forKey:[header objectAtIndex:0]];
}
[res appendString:[headers objectForKey:[header objectAtIndex:0]]];
NSMutableString *code=[NSMutableString string];
do{
line=[lines objectAtIndex:i++];
}while([line characterAtIndex:0]!='\t');
line=[line substringFromIndex:1];
[code appendString:line];
[code appendString:@"\n"];
int n;
for(n=1;n | \n"];
}else{
break;
}
[res appendString:@"