aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBibiko <bibiko@eva.mpg.de>2009-05-24 20:26:28 +0000
committerBibiko <bibiko@eva.mpg.de>2009-05-24 20:26:28 +0000
commit6df5d2147ef4ac72760a8ab776ab8a4d489aebe9 (patch)
tree3cef97edd3846b76ad49522e16716da83b561eb4
parent99e4d0223293aee1acd6b4196263385a9e6a7ecc (diff)
downloadsequelpro-6df5d2147ef4ac72760a8ab776ab8a4d489aebe9.tar.gz
sequelpro-6df5d2147ef4ac72760a8ab776ab8a4d489aebe9.tar.bz2
sequelpro-6df5d2147ef4ac72760a8ab776ab8a4d489aebe9.zip
• improved narrow-down completion
- added support for "in-quote completion" based on suggestions of the spell checker dict and the current used words in the textView - still bound to F5
-rw-r--r--Source/CMTextView.h2
-rw-r--r--Source/CMTextView.m56
-rw-r--r--Source/SPNarrowDownCompletion.h1
-rw-r--r--Source/SPNarrowDownCompletion.m86
4 files changed, 101 insertions, 44 deletions
diff --git a/Source/CMTextView.h b/Source/CMTextView.h
index 723bde8f..0ada18ed 100644
--- a/Source/CMTextView.h
+++ b/Source/CMTextView.h
@@ -82,6 +82,6 @@
- (void) makeTextSizeSmaller;
- (void) setConnection:(CMMCPConnection *)theConnection withVersion:(int)majorVersion;
- (void) doCompletion;
-- (NSArray *)suggestionsForSQLCompletionWith:(NSString *)currentWord;
+- (NSArray *)suggestionsForSQLCompletionWith:(NSString *)currentWord dictMode:(BOOL)isDictMode;
@end
diff --git a/Source/CMTextView.m b/Source/CMTextView.m
index 7ab696db..f2b80a16 100644
--- a/Source/CMTextView.m
+++ b/Source/CMTextView.m
@@ -108,7 +108,21 @@ YY_BUFFER_STATE yy_scan_string (const char *);
mySQLmajorVersion = majorVersion;
}
-- (NSArray *)suggestionsForSQLCompletionWith:(NSString *)currentWord
+/*
+ * Sort function (mainly used to sort the words in the textView)
+ */
+NSInteger alphabeticSort(id string1, id string2, void *reverse)
+{
+ return [string1 localizedCaseInsensitiveCompare:string2];
+}
+
+/*
+ * Return an array of NSDictionary containing the sorted strings representing
+ * the set of unique words, SQL keywords, user-defined funcs/procs, tables etc.
+ * NSDic key "display" := the displayed and to be inserted word
+ * NSDic key "image" := an image to be shown left from "display" (optional)
+ */
+- (NSArray *)suggestionsForSQLCompletionWith:(NSString *)currentWord dictMode:(BOOL)isDictMode
{
NSMutableArray *compl = [[NSMutableArray alloc] initWithCapacity:32];
NSMutableArray *possibleCompletions = [[NSMutableArray alloc] initWithCapacity:32];
@@ -116,7 +130,7 @@ YY_BUFFER_STATE yy_scan_string (const char *);
unsigned i, insindex;
insindex = 0;
- if([mySQLConnection isConnected])
+ if([mySQLConnection isConnected] && !isDictMode)
{
// Add table names to completions list
MCPResult *queryResult = [mySQLConnection listTables];
@@ -164,24 +178,36 @@ YY_BUFFER_STATE yy_scan_string (const char *);
}
}
+
// If caret is not inside backticks add keywords and all words coming from the view.
if([[self string] length] && ![[[self textStorage] attribute:kBTQuote atIndex:[self selectedRange].location-1 effectiveRange:nil] isEqualToString:kBTQuoteValue] )
{
// Only parse for words if text size is less than 6MB
if([[self string] length]<6000000)
{
- NSCharacterSet *separators = [NSCharacterSet characterSetWithCharactersInString:@" \t\r\n,()\"'`-!;=+|?:~@"];
+ NSCharacterSet *separators = [NSCharacterSet characterSetWithCharactersInString:@" \t\r\n,()[]{}\"'`-!;=+|?:~@"];
NSArray *textViewWords = [[self string] componentsSeparatedByCharactersInSet:separators];
- [possibleCompletions addObjectsFromArray:textViewWords];
+ NSMutableArray *uniqueArray = [NSMutableArray array];
+ NSString *s;
+ enumerate(textViewWords, s)
+ if(![uniqueArray containsObject:s])
+ [uniqueArray addObject:s];
+
+ int reverseSort = NO;
+ NSArray *sortedArray = [[[uniqueArray mutableCopy] autorelease] sortedArrayUsingFunction:alphabeticSort context:&reverseSort];
+ [possibleCompletions addObjectsFromArray:sortedArray];
}
- [possibleCompletions addObjectsFromArray:[self keywords]];
}
+
+ // Add predefined keywords
+ if(!isDictMode)
+ [possibleCompletions addObjectsFromArray:[self keywords]];
// Remove the current word
[possibleCompletions removeObject:currentWord];
- // Build array of dictionaries as in:
- // as in: [NSDictionary dictionaryWithObjectsAndKeys:@"foo", @"display", @"`foo`", @"insert", @"func-small", @"image", nil]
+ // Build array of dictionaries as e.g.:
+ // [NSDictionary dictionaryWithObjectsAndKeys:@"foo", @"display", @"`foo`", @"insert", @"func-small", @"image", nil]
NSString* candidate;
enumerate(possibleCompletions, candidate)
{
@@ -197,7 +223,7 @@ YY_BUFFER_STATE yy_scan_string (const char *);
- (void)doCompletion
{
- // No completion for a selection
+ // No completion for a selection (yet?)
if ([self selectedRange].length > 0) return;
// Refresh quote attributes
@@ -206,22 +232,22 @@ YY_BUFFER_STATE yy_scan_string (const char *);
// Check if the caret is inside quotes "" or ''; if so
// return the normal word suggestion due to the spelling's settings
- // if([[[self textStorage] attribute:kQuote atIndex:charRange.location effectiveRange:nil] isEqualToString:kQuoteValue] )
- // return [[NSSpellChecker sharedSpellChecker] completionsForPartialWordRange:NSMakeRange(0,charRange.length) inString:[[self string] substringWithRange:charRange] language:nil inSpellDocumentWithTag:0];
+ // plus all unique words used in the textView
+ BOOL isDictMode = ([[[self textStorage] attribute:kQuote atIndex:[self getRangeForCurrentWord].location effectiveRange:nil] isEqualToString:kQuoteValue] );
NSString* filter = [[self string] substringWithRange:[self getRangeForCurrentWord]];
NSString* prefix = @"";
- NSString* allow = @" ";
+ NSString* allow = @" _."; // additional chars which not close the popup
BOOL caseInsensitive = YES;
- SPNarrowDownCompletion* completionPopUp = [[SPNarrowDownCompletion alloc] initWithItems:[self suggestionsForSQLCompletionWith:filter]
+ SPNarrowDownCompletion* completionPopUp = [[SPNarrowDownCompletion alloc] initWithItems:[self suggestionsForSQLCompletionWith:filter dictMode:isDictMode]
alreadyTyped:filter
staticPrefix:prefix
additionalWordCharacters:allow
caseSensitive:!caseInsensitive
charRange:[self getRangeForCurrentWord]
inView:self
- dictMode:NO];
+ dictMode:isDictMode];
//Get the NSPoint of the first character of the current word
NSRange range = NSMakeRange([self getRangeForCurrentWord].location,0);
@@ -231,6 +257,7 @@ YY_BUFFER_STATE yy_scan_string (const char *);
NSPoint pos = [[self window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
NSFont* font = [self font];
+ // TODO: check if needed
// if(filter)
// pos.x -= [filter sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
@@ -2293,9 +2320,12 @@ YY_BUFFER_STATE yy_scan_string (const char *);
- (void)changeColor:(id)sender
{
[self setInsertionPointColor:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:@"CustomQueryEditorCaretColor"]]];
+ // Remember the old selected range
NSRange oldRange = [self selectedRange];
+ // Invoke syntax highlighting
[self setSelectedRange:NSMakeRange(oldRange.location,0)];
[self insertText:@""];
+ // Reset old selected range
[self setSelectedRange:oldRange];
}
diff --git a/Source/SPNarrowDownCompletion.h b/Source/SPNarrowDownCompletion.h
index 15176cf6..22657d15 100644
--- a/Source/SPNarrowDownCompletion.h
+++ b/Source/SPNarrowDownCompletion.h
@@ -53,6 +53,7 @@
BOOL dictMode;
NSFont *tableFont;
NSRange theCharRange;
+ NSArray *words;
id theView;
NSMutableCharacterSet* textualInputCharacters;
diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m
index 10988241..4f54924e 100644
--- a/Source/SPNarrowDownCompletion.m
+++ b/Source/SPNarrowDownCompletion.m
@@ -119,7 +119,6 @@
{
if(self = [self init])
{
- suggestions = [someSuggestions retain];
if(aUserString)
[mutablePrefix appendString:aUserString];
@@ -134,6 +133,13 @@
theCharRange = initRange;
theView = aView;
dictMode = mode;
+
+ if(dictMode) {
+ words = [NSArray arrayWithArray:suggestions];
+ } else {
+ suggestions = [someSuggestions retain];
+ words = nil;
+ }
}
return self;
}
@@ -205,12 +211,15 @@
{
NSImage* image = nil;
NSString* imageName = nil;
- imageName = [[filtered objectAtIndex:rowIndex] objectForKey:@"image"];
- if(imageName)
- image = [NSImage imageNamed:imageName];
- [[aTableColumn dataCell] setImage:image];
+ if(!dictMode) {
+ imageName = [[filtered objectAtIndex:rowIndex] objectForKey:@"image"];
+ if(imageName)
+ image = [NSImage imageNamed:imageName];
+ [[aTableColumn dataCell] setImage:image];
+ return [[filtered objectAtIndex:rowIndex] objectForKey:@"display"];
+ }
+ return [filtered objectAtIndex:rowIndex];
- return [[filtered objectAtIndex:rowIndex] objectForKey:@"display"];
}
// ====================
@@ -223,16 +232,24 @@
NSArray* newFiltered;
if([mutablePrefix length] > 0)
{
- NSPredicate* predicate;
- if(caseSensitive)
- predicate = [NSPredicate predicateWithFormat:@"match BEGINSWITH %@ OR (match == NULL AND display BEGINSWITH %@)", [self filterString], [self filterString]];
- else
- predicate = [NSPredicate predicateWithFormat:@"match BEGINSWITH[c] %@ OR (match == NULL AND display BEGINSWITH[c] %@)", [self filterString], [self filterString]];
- newFiltered = [suggestions filteredArrayUsingPredicate:predicate];
+ if(dictMode)
+ {
+ newFiltered = [[NSSpellChecker sharedSpellChecker] completionsForPartialWordRange:NSMakeRange(0,[[self filterString] length]) inString:[self filterString] language:nil inSpellDocumentWithTag:0];
+ } else {
+ NSPredicate* predicate;
+ if(caseSensitive)
+ predicate = [NSPredicate predicateWithFormat:@"match BEGINSWITH %@ OR (match == NULL AND display BEGINSWITH %@)", [self filterString], [self filterString]];
+ else
+ predicate = [NSPredicate predicateWithFormat:@"match BEGINSWITH[c] %@ OR (match == NULL AND display BEGINSWITH[c] %@)", [self filterString], [self filterString]];
+ newFiltered = [suggestions filteredArrayUsingPredicate:predicate];
+ }
}
else
{
- newFiltered = suggestions;
+ if(dictMode)
+ newFiltered = nil;
+ else
+ newFiltered = suggestions;
}
NSPoint old = NSMakePoint([self frame].origin.x, [self frame].origin.y + [self frame].size.height);
@@ -247,7 +264,10 @@
{
for(i=0; i<[newFiltered count]; i++)
{
- item = [[newFiltered objectAtIndex:i] objectForKey:@"display"];
+ if(dictMode)
+ item = [newFiltered objectAtIndex:i];
+ else
+ item = [[newFiltered objectAtIndex:i] objectForKey:@"display"];
if([item length]>maxLen)
maxLen = [item length];
}
@@ -267,7 +287,7 @@
// newHeight is currently the new height for theTableView, but we need to resize the whole window
// so here we use the difference in height to find the new height for the window
// newHeight = [[self contentView] frame].size.height + (newHeight - [theTableView frame].size.height);
- [self setFrame:NSMakeRect(old.x,old.y-newHeight,maxWidth,newHeight) display:YES];
+ [self setFrame:NSMakeRect(old.x, old.y-newHeight, maxWidth, newHeight) display:YES];
[filtered release];
filtered = [newFiltered retain];
[theTableView reloadData];
@@ -401,7 +421,11 @@
return;
id cur = [filtered objectAtIndex:row];
- NSString* curMatch = [cur objectForKey:@"match"] ?: [cur objectForKey:@"display"];
+ NSString* curMatch;
+ if(dictMode)
+ curMatch = [NSString stringWithString:cur];
+ else
+ curMatch = [cur objectForKey:@"match"] ?: [cur objectForKey:@"display"];
if([[self filterString] length] + 1 < [curMatch length])
{
NSString* prefix = [curMatch substringToIndex:[[self filterString] length] + 1];
@@ -409,7 +433,11 @@
for(int i = row; i < [filtered count]; ++i)
{
id candidate = [filtered objectAtIndex:i];
- NSString* candidateMatch = [candidate objectForKey:@"match"] ?: [candidate objectForKey:@"display"];
+ NSString* candidateMatch;
+ if(dictMode)
+ candidateMatch = [filtered objectAtIndex:i];
+ else
+ candidateMatch = [candidate objectForKey:@"match"] ?: [candidate objectForKey:@"display"];
if([candidateMatch hasPrefix:prefix])
[candidates addObject:candidateMatch];
}
@@ -421,10 +449,10 @@
if([[self filterString] length] < [commonPrefix length])
{
- // NSString* toInsert = [commonPrefix substringFromIndex:[[self filterString] length]];
- // [mutablePrefix appendString:toInsert];
- // [self insert_text:toInsert];
[self insert_text:commonPrefix];
+ NSString* toInsert = [commonPrefix substringFromIndex:[[self filterString] length]];
+ [mutablePrefix appendString:toInsert];
+ theCharRange = NSMakeRange(theCharRange.location,[commonPrefix length]);
[self filter];
}
}
@@ -446,16 +474,14 @@
if([theTableView selectedRow] == -1)
return;
- NSMutableDictionary* selectedItem = [[[filtered objectAtIndex:[theTableView selectedRow]] mutableCopy] autorelease];
-
- NSString* candidateMatch = [selectedItem objectForKey:@"match"] ?: [selectedItem objectForKey:@"display"];
- if([[self filterString] length] < [candidateMatch length])
- // [self insert_text:[candidateMatch substringFromIndex:[[self filterString] length]]];
- [self insert_text:candidateMatch];
-
- // NSString* toInsert = [selectedItem objectForKey:@"insert"];
- // [self insert_text:toInsert];
-
+ if(dictMode){
+ [self insert_text:[[[filtered objectAtIndex:[theTableView selectedRow]] mutableCopy] autorelease]];
+ } else {
+ NSMutableDictionary* selectedItem = [[[filtered objectAtIndex:[theTableView selectedRow]] mutableCopy] autorelease];
+ NSString* candidateMatch = [selectedItem objectForKey:@"match"] ?: [selectedItem objectForKey:@"display"];
+ if([[self filterString] length] < [candidateMatch length])
+ [self insert_text:candidateMatch];
+ }
closeMe = YES;
}
@end