diff options
-rw-r--r-- | Source/SPStringAdditions.h | 2 | ||||
-rw-r--r-- | Source/SPStringAdditions.m | 104 | ||||
-rw-r--r-- | Source/SPTextView.h | 2 | ||||
-rw-r--r-- | Source/SPTextView.m | 101 |
4 files changed, 118 insertions, 91 deletions
diff --git a/Source/SPStringAdditions.h b/Source/SPStringAdditions.h index a0b39504..15a3d9a9 100644 --- a/Source/SPStringAdditions.h +++ b/Source/SPStringAdditions.h @@ -75,4 +75,6 @@ static inline id NSMutableAttributedStringAttributeAtIndex (NSMutableAttributedS - (CGFloat)levenshteinDistanceWithWord:(NSString *)stringB; +- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path error:(NSError**)theError; + @end diff --git a/Source/SPStringAdditions.m b/Source/SPStringAdditions.m index 847b4dbc..9d9d1779 100644 --- a/Source/SPStringAdditions.m +++ b/Source/SPStringAdditions.m @@ -402,6 +402,110 @@ } /** + * Run self as BASH command(s) and return the result. + * This task can be interrupted by pressing ⌘. + * + * @param shellEnvironment A dictionary of environment variable values whose keys are the variable names. + * + * @param path The current directory for the bash command. If path is nil, the current directory is inherited from the process that created the receiver (normally /). + * + * @param theError If not nil and the bash command failed it contains the returned error message as NSLocalizedDescriptionKey + * + */ +- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path error:(NSError**)theError +{ + BOOL userTerminated = NO; + + NSTask *bashTask = [[NSTask alloc] init]; + [bashTask setLaunchPath: @"/bin/bash"]; + + if(shellEnvironment != nil && [shellEnvironment isKindOfClass:[NSDictionary class]] && [shellEnvironment count]) + [bashTask setEnvironment:shellEnvironment]; + + if(path != nil) + [bashTask setCurrentDirectoryPath:path]; + + [bashTask setArguments:[NSArray arrayWithObjects: @"-c", self, nil]]; + + NSPipe *stdout_pipe = [NSPipe pipe]; + [bashTask setStandardOutput:stdout_pipe]; + NSFileHandle *stdout_file = [stdout_pipe fileHandleForReading]; + + NSPipe *stderr_pipe = [NSPipe pipe]; + [bashTask setStandardError:stderr_pipe]; + NSFileHandle *stderr_file = [stderr_pipe fileHandleForReading]; + [bashTask launch]; + + // Listen to ⌘. to terminate + while(1) { + if(![bashTask isRunning] || [bashTask processIdentifier] == 0) break; + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + usleep(10000); + if(!event) continue; + if ([event type] == NSKeyDown) { + unichar key = [[event characters] length] == 1 ? [[event characters] characterAtIndex:0] : 0; + if (([event modifierFlags] & NSCommandKeyMask) && key == '.') { + [bashTask terminate]; + userTerminated = YES; + break; + } + } else { + [NSApp sendEvent:event]; + } + } + + [bashTask waitUntilExit]; + + if(userTerminated) { + if(bashTask) [bashTask release]; + NSBeep(); + NSLog(@"“%@” was terminated by user.", self); + return @""; + } + + // If return from bash re-activate Sequel Pro + [NSApp activateIgnoringOtherApps:YES]; + + NSInteger status = [bashTask terminationStatus]; + NSData *outdata = [stdout_file readDataToEndOfFile]; + NSData *errdata = [stderr_file readDataToEndOfFile]; + + if(outdata != nil) { + NSString *stdout = [[[NSString alloc] initWithData:outdata encoding:NSUTF8StringEncoding] autorelease]; + if(bashTask) [bashTask release]; + if(stdout != nil) { + if (status == 0) { + return [stdout description]; + } else { + if(theError != NULL) { + *theError = [[[NSError alloc] initWithDomain:NSPOSIXErrorDomain + code:status + userInfo:[NSDictionary dictionaryWithObjectsAndKeys: + [[[NSString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] autorelease], + NSLocalizedDescriptionKey, + nil]] autorelease]; + } else { + NSBeep(); + } + return @""; + } + } else { + NSLog(@"Couldn't read return string from “%@” by using UTF-8 encoding.", self); + NSBeep(); + } + } else { + if(bashTask) [bashTask release]; + NSLog(@"Couldn't read data from command “%@”.", self); + NSBeep(); + return @""; + } + +} + +/** * Returns the minimum of a, b and c. */ - (NSInteger)smallestOf:(NSInteger)a andOf:(NSInteger)b andOf:(NSInteger)c diff --git a/Source/SPTextView.h b/Source/SPTextView.h index 9730151c..69be86d3 100644 --- a/Source/SPTextView.h +++ b/Source/SPTextView.h @@ -143,6 +143,4 @@ - (BOOL)isSnippetMode; -- (NSString *)runBashCommand:(NSString *)command; - @end diff --git a/Source/SPTextView.m b/Source/SPTextView.m index 08feffe4..0686fbb2 100644 --- a/Source/SPTextView.m +++ b/Source/SPTextView.m @@ -1711,10 +1711,19 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) if(tagRange.length) { [theHintString flushCachedRegexData]; NSRange cmdRange = [theHintString rangeOfRegex:@"(?s)(?<!\\\\)\\$\\(\\s*(.*)\\s*\\)" capture:1L]; - if(cmdRange.length) - [theHintString replaceCharactersInRange:tagRange withString:[self runBashCommand:[theHintString substringWithRange:cmdRange]]]; - else + if(cmdRange.length) { + NSError *err = nil; + NSString *cmdResult = [[theHintString substringWithRange:cmdRange] runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&err]; + if(err == nil) { + [theHintString replaceCharactersInRange:tagRange withString:cmdResult]; + } else { + NSString *errorMessage = [err localizedDescription]; + SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, + [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), [theHintString substringWithRange:cmdRange], errorMessage]); + } + } else { [theHintString replaceCharactersInRange:tagRange withString:@""]; + } } [theHintString flushCachedRegexData]; @@ -1848,92 +1857,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) } /** - * Run 'command' as BASH command(s) and return the result. - * This task can be interrupted by pressing ⌘. - */ -- (NSString *)runBashCommand:(NSString *)command -{ - BOOL userTerminated = NO; - - NSTask *bashTask = [[NSTask alloc] init]; - [bashTask setLaunchPath: @"/bin/bash"]; - [bashTask setArguments:[NSArray arrayWithObjects: @"-c", command, nil]]; - - NSPipe *stdout_pipe = [NSPipe pipe]; - [bashTask setStandardOutput:stdout_pipe]; - NSFileHandle *stdout_file = [stdout_pipe fileHandleForReading]; - - NSPipe *stderr_pipe = [NSPipe pipe]; - [bashTask setStandardError:stderr_pipe]; - NSFileHandle *stderr_file = [stderr_pipe fileHandleForReading]; - [bashTask launch]; - - // Listen to ⌘. to terminate - while(1) { - if(![bashTask isRunning] || [bashTask processIdentifier] == 0) break; - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - usleep(10000); - if(!event) continue; - if ([event type] == NSKeyDown) { - unichar key = [[event characters] length] == 1 ? [[event characters] characterAtIndex:0] : 0; - if (([event modifierFlags] & NSCommandKeyMask) && key == '.') { - [bashTask terminate]; - userTerminated = YES; - break; - } - } else { - [NSApp sendEvent:event]; - } - } - - [bashTask waitUntilExit]; - - if(userTerminated) { - if(bashTask) [bashTask release]; - NSBeep(); - NSLog(@"“%@” was terminated by user.", command); - return @""; - } - - // If return from bash re-activate Sequel Pro - [NSApp activateIgnoringOtherApps:YES]; - - NSInteger status = [bashTask terminationStatus]; - NSData *outdata = [stdout_file readDataToEndOfFile]; - NSData *errdata = [stderr_file readDataToEndOfFile]; - - if(outdata != nil) { - NSString *stdout = [[NSString alloc] initWithData:outdata encoding:NSUTF8StringEncoding]; - NSString *error = [[[NSString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] autorelease]; - if(bashTask) [bashTask release]; - if(stdout != nil) { - if (status == 0) { - return [stdout autorelease]; - } else { - NSString *error = [[[NSString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] autorelease]; - SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, - [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), command, [error description]]); - [stdout release]; - NSBeep(); - return @""; - } - } else { - NSLog(@"Couldn't read return string from “%@” by using UTF-8 encoding.", command); - NSBeep(); - } - } else { - if(bashTask) [bashTask release]; - NSLog(@"Couldn't read data from command “%@”.", command); - NSBeep(); - return @""; - } - -} - -/** * Checks whether the current caret position in inside of a defined snippet range */ - (BOOL)checkForCaretInsideSnippet |