aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Source/CMTextView.m112
-rw-r--r--Source/SPEditorTokens.l20
-rw-r--r--Source/SPQueryController.m3
-rw-r--r--Source/SPQueryFavoriteManager.h3
4 files changed, 90 insertions, 48 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
diff --git a/Source/SPEditorTokens.l b/Source/SPEditorTokens.l
index b3daa531..b0c2dee3 100644
--- a/Source/SPEditorTokens.l
+++ b/Source/SPEditorTokens.l
@@ -61,24 +61,32 @@ keywords (X(OR|509|A)|S(MALLINT|SL|H(OW({s}(E(NGINE(S)?|RRORS)|M(ASTER|UTEX)|BIN
%x comment
+%x snippet
%x equation
%x varequation
%%
+
+
+\$\{1?[0-9]: { BEGIN(snippet); return SPT_OTHER; }
+<snippet>\\\} { return SPT_OTHER; }
+<snippet>\} { BEGIN(INITIAL); return SPT_OTHER; }
+
+
\"([^"\\]|\\(.|[\n\r]))*\"? { return SPT_DOUBLE_QUOTED_TEXT; } /* double quoted strings */
'([^'\\]|\\(.|[\n\r]))*'? { return SPT_SINGLE_QUOTED_TEXT; } /* single quoted strings */
-`[^`]*`? { return SPT_BACKTICK_QUOTED_TEXT; } /* backtick quoted string */
+`[^`]*`? { return SPT_BACKTICK_QUOTED_TEXT; } /* backtick quoted string */
-"/*" { BEGIN(comment); return SPT_COMMENT; } /* beginning of a c style comment */
-<comment>[^*]* { return SPT_COMMENT; } /* anything except * in a c cmnt */
-<comment>"*"+ { return SPT_COMMENT; } /* a range of * */
-<comment>"*"+"/" { BEGIN(INITIAL); return SPT_COMMENT; } /* a range of * with trailing /
+"/*" { BEGIN(comment); return SPT_COMMENT; } /* beginning of a c style comment */
+<comment>[^*]* { return SPT_COMMENT; } /* anything except * in a c cmnt */
+<comment>"*"+ { return SPT_COMMENT; } /* a range of * */
+<comment>"*"+"/" { BEGIN(INITIAL); return SPT_COMMENT; } /* a range of * with trailing /
Thanks to John Dickinson for publishing
this method of parsing C comments on
http://www.stillhq.com/pdfdb/000561/data.pdf
*/
#[^\n\r]*(\n|\r)? | /* # Comments */
---[ \t][^\n\r]*(\n|\r)? { return SPT_COMMENT; } /* -- Comments */
+--[ \t][^\n\r]*(\n|\r)? { return SPT_COMMENT; } /* -- Comments */
{variable}/{ops} { BEGIN(varequation); return SPT_VARIABLE; }/* SQL variables before operator*/
<varequation>{ops} { BEGIN(INITIAL); return SPT_OTHER; }
diff --git a/Source/SPQueryController.m b/Source/SPQueryController.m
index 2eddf018..e19a49e5 100644
--- a/Source/SPQueryController.m
+++ b/Source/SPQueryController.m
@@ -27,6 +27,7 @@
#import "SPConsoleMessage.h"
#import "SPArrayAdditions.h"
#import "SPConstants.h"
+#import "CustomQuery.h"
#define MESSAGE_TRUNCATE_CHARACTER_LENGTH 256
#define MESSAGE_TIME_STAMP_FORMAT @"%H:%M:%S"
@@ -655,7 +656,7 @@ static SPQueryController *sharedQueryController = nil;
if([historyContainer objectForKey:[fileURL absoluteString]]) {
NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:[[historyContainer objectForKey:[fileURL absoluteString]] count]];
NSMenuItem *historyMenuItem;
- for(id history in [historyContainer objectForKey:[fileURL absoluteString]]) {
+ for(NSString* history in [historyContainer objectForKey:[fileURL absoluteString]]) {
historyMenuItem = [[[NSMenuItem alloc] initWithTitle:([history length] > 64) ? [NSString stringWithFormat:@"%@…", [history substringToIndex:63]] : history
action:NULL
keyEquivalent:@""] autorelease];
diff --git a/Source/SPQueryFavoriteManager.h b/Source/SPQueryFavoriteManager.h
index 2a9c3c56..9e207356 100644
--- a/Source/SPQueryFavoriteManager.h
+++ b/Source/SPQueryFavoriteManager.h
@@ -24,6 +24,7 @@
// More info at <http://code.google.com/p/sequel-pro/>
#import <Cocoa/Cocoa.h>
+#import "CMTextView.h"
@class BWAnchoredButtonBar;
@@ -42,7 +43,7 @@
IBOutlet NSTableView *favoritesTableView;
IBOutlet NSTextField *favoriteNameTextField;
IBOutlet NSTextField *favoriteTabTriggerTextField;
- IBOutlet NSTextView *favoriteQueryTextView;
+ IBOutlet CMTextView *favoriteQueryTextView;
IBOutlet NSButton *removeButton;
IBOutlet BWAnchoredButtonBar *splitViewButtonBar;