aboutsummaryrefslogtreecommitdiffstats
path: root/Source/CMTextView.m
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CMTextView.m')
-rw-r--r--Source/CMTextView.m112
1 files changed, 72 insertions, 40 deletions
diff --git a/Source/CMTextView.m b/Source/CMTextView.m
index 474b0af9..0caa12c0 100644
--- a/Source/CMTextView.m
+++ b/Source/CMTextView.m
@@ -31,6 +31,7 @@
#import "SPNarrowDownCompletion.h"
#import "SPConstants.h"
#import "SPQueryController.h"
+#import "SPTooltip.h"
#pragma mark -
#pragma mark lex init
@@ -115,10 +116,10 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[[self textStorage] setDelegate:self];
// Set defaults for general usage
- autoindentEnabled = YES;
+ autoindentEnabled = NO;
autopairEnabled = YES;
autoindentIgnoresEnter = NO;
- autouppercaseKeywordsEnabled = YES;
+ autouppercaseKeywordsEnabled = NO;
autohelpEnabled = NO;
delBackwardsWasPressed = NO;
startListeningToBoundChanges = NO;
@@ -1112,6 +1113,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
- (void)insertFavoriteAsSnippet:(NSString*)theSnippet atRange:(NSRange)targetRange
{
+ // Do not allow the insertion of a query favorite if snippets are active
if(snippetControlCounter > -1) {
NSBeep();
return;
@@ -1130,7 +1132,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSMutableString *snip = [[NSMutableString alloc] initWithCapacity:[theSnippet length]];
@try{
- NSString *re = @"(?<!\\\\)\\$\\{(1?\\d):([^\\{\\}]*)\\}";
+ NSString *re = @"(?s)(?<!\\\\)\\$\\{(1?\\d):(.{0}|.*?[^\\\\])\\}";
if(targetRange.length)
targetRange = NSIntersectionRange(NSMakeRange(0,[[self string] length]), targetRange);
@@ -1139,16 +1141,20 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if(snip == nil || ![snip length]) return;
// Replace `${x:…}` by ${x:`…`} for convience
- [snip replaceOccurrencesOfRegex:@"`\\$\\{(1?\\d):([^\\{\\}]*)\\}`" withString:@"${$1:`$2`}"];
+ [snip replaceOccurrencesOfRegex:@"`(?s)(?<!\\\\)\\$\\{(1?\\d):(.{0}|.*?[^\\\\])\\}`" withString:@"${$1:`$2`}"];
[snip flushCachedRegexData];
snippetControlCounter = -1;
snippetControlMax = -1;
currentSnippetIndex = -1;
+ // Suppress snippet range calculation in [self textStorageDidProcessEditing] while initial insertion
+ snippetWasJustInserted = YES;
+
while([snip isMatchedByRegex:re]) {
[snip flushCachedRegexData];
snippetControlCounter++;
+
NSRange snipRange = [snip rangeOfRegex:re capture:0L];
NSInteger snipCnt = [[snip substringWithRange:[snip rangeOfRegex:re capture:1L]] intValue];
NSRange hintRange = [snip rangeOfRegex:re capture:2L];
@@ -1199,16 +1205,23 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
}
+ // Handle escaped characters
+ [theHintString replaceOccurrencesOfRegex:@"\\\\(\\$\\(|\\}|\\$SP_)" withString:@"$1"];
+ [theHintString flushCachedRegexData];
+
// If inside the snippet hint $(…) is defined run … as BASH command
- // and replace the snippet hint by the return string of that command
- if([theHintString isMatchedByRegex:@"(?s)(?<!\\\\)\\s*\\$\\(.*\\)\\s*"]) {
- NSRange r = [theHintString rangeOfRegex:@"(?s)(?<!\\\\)\\s*\\$\\((.*)\\)\\s*" capture:1L];
- if(r.length)
- [theHintString setString:[self runBashCommand:[theHintString substringWithRange:r]]];
- else
- [theHintString setString:@""];
+ // and replace $(…) by the return string of that command. Please note
+ // only one $(…) statement is allowed within one ${…} snippet environment.
+ NSRange tagRange = [theHintString rangeOfRegex:@"(?s)(?<!\\\\)\\$\\((.*)\\)"];
+ 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
+ [theHintString replaceCharactersInRange:tagRange withString:@""];
}
+ [theHintString flushCachedRegexData];
[snip replaceCharactersInRange:snipRange withString:theHintString];
[snip flushCachedRegexData];
@@ -1235,16 +1248,22 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
snippetControlArray[snippetControlMax][2] = 0;
}
- // Registering for undo
- [self breakUndoCoalescing];
-
- // Insert favorite query as snippet if any
- // if(targetRange.length)
- [self setSelectedRange:targetRange];
+ // unescape escaped snippets and re-adjust successive snippet locations : \${1:a} → ${1:a}
+ NSString *ure = @"(?s)\\\\\\$\\{(1?\\d):(.{0}|.*?[^\\\\])\\}";
+ while([snip isMatchedByRegex:ure]) {
+ NSRange escapeRange = [snip rangeOfRegex:ure capture:0L];
+ [snip replaceCharactersInRange:escapeRange withString:[snip substringWithRange:NSMakeRange(escapeRange.location+1,escapeRange.length-1)]];
+ NSUInteger loc = escapeRange.location + targetRange.location;
+ [snip flushCachedRegexData];
+ for(i=0; i<=snippetControlMax; i++)
+ if(snippetControlArray[i][0] > -1 && snippetControlArray[i][0] > loc)
+ snippetControlArray[i][0]--;
+ }
- // Suppress snippet range calculation in [self textStorageDidProcessEditing] while initial insertion
- snippetWasJustInserted = YES;
+ // Insert favorite query by selecting the tab trigger if any
+ [self setSelectedRange:targetRange];
+ // Registering for undo
[self breakUndoCoalescing];
[self insertText:snip];
@@ -1271,32 +1290,34 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
/*
- * Run 'command' as BASH script and return the result.
+ * 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/sh"];
+ [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]) break;
- NSEvent* event = [NSApp nextEventMatchingMask:NSKeyDownMask
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
+ 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;
@@ -1305,9 +1326,13 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
userTerminated = YES;
break;
}
+ } else {
+ [NSApp sendEvent:event];
}
}
+ [bashTask waitUntilExit];
+
if(userTerminated) {
if(bashTask) [bashTask release];
NSBeep();
@@ -1315,25 +1340,33 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
return @"";
}
- [bashTask waitUntilExit];
+ // If return from bash re-activate Sequel Pro
+ [NSApp activateIgnoringOtherApps:YES];
+
NSInteger status = [bashTask terminationStatus];
- NSData *data = [stdout_file readDataToEndOfFile];
+ NSData *outdata = [stdout_file readDataToEndOfFile];
+ NSData *errdata = [stderr_file readDataToEndOfFile];
- if(data != nil) {
- NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ if(outdata != nil) {
+ NSString *stdout = [[[NSString alloc] initWithData:outdata encoding:NSUTF8StringEncoding] description];
+ NSString *error = [[[NSString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] description];
if(bashTask) [bashTask release];
- if(string != nil) {
+ if(stdout != nil) {
if (status == 0) {
- return [string autorelease];
+ return [stdout autorelease];
} else {
- NSLog(@"Error for “%@”", command);
- [string release];
+ NSString *error = [[[NSString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] description];
+ SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, nil,
+ [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), command, [error description]]);
+ [stdout release];
+ [error release];
NSBeep();
return @"";
}
} else {
- if(string) [string release];
+ if(stdout) [stdout release];
NSLog(@"Couldn't read return string from “%@” by using UTF-8 encoding.", command);
+ NSBeep();
}
} else {
if(bashTask) [bashTask release];
@@ -1343,6 +1376,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
}
+
/*
* Checks whether the current caret position in inside of a defined snippet range
*/
@@ -2960,8 +2994,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSColor *tokenColor;
- BOOL autouppercaseKeywords = [prefs boolForKey:SPCustomQueryAutoUppercaseKeywords];
-
size_t tokenEnd, token;
NSRange tokenRange;
@@ -3022,7 +3054,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
// If the current token is marked as SQL keyword, uppercase it if required.
tokenEnd = tokenRange.location+tokenRange.length-1;
// Check the end of the token
- if (textBufferSizeIncreased && allowToCheckForUpperCase && autouppercaseKeywords && !delBackwardsWasPressed
+ if (textBufferSizeIncreased && allowToCheckForUpperCase && autouppercaseKeywordsEnabled && !delBackwardsWasPressed
&& [(NSString*)NSMutableAttributedStringAttributeAtIndex(textStore, kSQLkeyword, tokenEnd, nil) length])
// check if next char is not a kSQLkeyword or current kSQLkeyword is at the end;
// if so then upper case keyword if not already done