From d6097cbbb95c78fa16b7bd0305a494186c5dd583 Mon Sep 17 00:00:00 2001 From: Bibiko Date: Thu, 21 Jan 2010 00:00:36 +0000 Subject: =?UTF-8?q?=E2=80=A2=20completion=20enhancements=20-=20TAB=20and?= =?UTF-8?q?=20mouse=20double-click=20inserts=20suggestion=20-=20field=20ty?= =?UTF-8?q?pe=20info=20for=20'set'=20and=20'enum'=20is=20fixed;=20in=20add?= =?UTF-8?q?ition=20show=20an=20arrow=20to=20show=20the=20definition=20-=20?= =?UTF-8?q?preparations=20for=20fuzzy=20search=20completion=20(not=20yet?= =?UTF-8?q?=20active)=20-=20reduced=20the=20font=20size=20by=201pt=20to=20?= =?UTF-8?q?avoid=20truncating?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/CMTextView.h | 2 +- Source/CMTextView.m | 53 +++++++++++++---- Source/SPNarrowDownCompletion.h | 8 ++- Source/SPNarrowDownCompletion.m | 122 ++++++++++++++++++++++++++++++++-------- 4 files changed, 147 insertions(+), 38 deletions(-) diff --git a/Source/CMTextView.h b/Source/CMTextView.h index 80515928..868e7093 100644 --- a/Source/CMTextView.h +++ b/Source/CMTextView.h @@ -87,7 +87,7 @@ static inline void NSMutableAttributedStringAddAttributeValueRange (NSMutableAtt - (void) autoHelp; - (void) doSyntaxHighlighting; - (void) setConnection:(MCPConnection *)theConnection withVersion:(NSInteger)majorVersion; -- (void) doCompletionByUsingSpellChecker:(BOOL)isDictMode; +- (void) doCompletionByUsingSpellChecker:(BOOL)isDictMode fuzzyMode:(BOOL)fuzzySearch; - (NSArray *)suggestionsForSQLCompletionWith:(NSString *)currentWord dictMode:(BOOL)isDictMode browseMode:(BOOL)dbBrowseMode withTableName:(NSString*)aTableName withDbName:(NSString*)aDbName; - (void) selectCurrentQuery; diff --git a/Source/CMTextView.m b/Source/CMTextView.m index 6df89d03..1cceac97 100644 --- a/Source/CMTextView.m +++ b/Source/CMTextView.m @@ -270,7 +270,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) if(aTableNameExists) { [sortedTables addObject:aTableName]; } else { - [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:db, @"display", @"database-small", @"image", @"", @"isRef", nil]]; + [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:db, @"display", @"database-small", @"image", db, @"isRef", nil]]; [sortedTables addObjectsFromArray:[allTables sortedArrayUsingDescriptors:[NSArray arrayWithObject:desc]]]; if([sortedTables count] > 1 && [sortedTables containsObject:currentTable]) { [sortedTables removeObject:currentTable]; @@ -285,17 +285,17 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) if(!aTableNameExists) switch(structtype) { case 0: - [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-small-square", @"image", db, @"path", @"", @"isRef", nil]]; + [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-small-square", @"image", db, @"path", [NSString stringWithFormat:@"%@.%@",db,table], @"isRef", nil]]; break; case 1: - [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-view-small-square", @"image", db, @"path", @"", @"isRef", nil]]; + [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-view-small-square", @"image", db, @"path", [NSString stringWithFormat:@"%@.%@",db,table], @"isRef", nil]]; break; case 2: - [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"proc-small", @"image", db, @"path", nil]]; + [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"proc-small", @"image", db, @"path", [NSString stringWithFormat:@"%@.%@",db,table], @"isRef", nil]]; breakFlag = YES; break; case 3: - [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"func-small", @"image", db, @"path", nil]]; + [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"func-small", @"image", db, @"path", [NSString stringWithFormat:@"%@.%@",db,table], @"isRef", nil]]; breakFlag = YES; break; } @@ -303,7 +303,27 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) NSArray *sortedFields = [allFields sortedArrayUsingDescriptors:[NSArray arrayWithObject:desc]]; for(id field in sortedFields) { if(![field hasPrefix:@" "]) { - [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:field, @"display", @"field-small-square", @"image", [NSString stringWithFormat:@"%@⇠%@",table,db], @"path", [theTable objectForKey:field], @"type", @"", @"isRef", nil]]; + NSString *typ = [theTable objectForKey:field]; + if(typ && [typ hasPrefix:@"set("] || [typ hasPrefix:@"enum("]) { + NSString *t = [typ stringByReplacingOccurrencesOfRegex:@"\\(.*?\\)" withString:@"(…)"]; + NSString *lst = [typ stringByMatching:@"\\(([^\\)]*?)\\)" capture:1L]; + [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys: + field, @"display", + @"field-small-square", @"image", + [NSString stringWithFormat:@"%@⇠%@",table,db], @"path", + t, @"type", + lst, @"list", + [NSString stringWithFormat:@"%@.%@.%@",db,table,field], @"isRef", + nil]]; + } else { + [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys: + field, @"display", + @"field-small-square", @"image", + [NSString stringWithFormat:@"%@⇠%@",table,db], @"path", + typ, @"type", + [NSString stringWithFormat:@"%@.%@.%@",db,table,field], @"isRef", + nil]]; + } } } } @@ -361,13 +381,14 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) } -- (void) doCompletionByUsingSpellChecker:(BOOL)isDictMode +- (void) doCompletionByUsingSpellChecker:(BOOL)isDictMode fuzzyMode:(BOOL)fuzzySearch { // No completion for a selection (yet?) and if caret positiopn == 0 if([self selectedRange].length > 0 || ![self selectedRange].location) return; NSInteger caretPos = [self selectedRange].location; + BOOL caretMovedLeft = NO; // Check if caret is located after a ` - if so move caret inside if([[self string] characterAtIndex:caretPos-1] == '`') { @@ -375,6 +396,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) ; } else { caretPos--; + caretMovedLeft = YES; [self setSelectedRange:NSMakeRange(caretPos, 0)]; } } @@ -517,6 +539,12 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) tableName = [tableName substringFromIndex:2]; } + if(fuzzySearch) { + filter = [[NSString stringWithString:[[self string] substringWithRange:parseRange]] stringByReplacingOccurrencesOfString:@"`" withString:@""]; + if([filter length]>15) return; + completionRange = parseRange; + } + } else { filter = [NSString stringWithString:currentWord]; } @@ -534,10 +562,12 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) inView:self dictMode:isDictMode dbMode:dbBrowseMode + fuzzySearch:fuzzySearch backtickMode:backtickMode withDbName:dbName withTableName:tableName - selectedDb:currentDb]; + selectedDb:currentDb + caretMovedLeft:caretMovedLeft]; //Get the NSPoint of the first character of the current word NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(completionRange.location,1) actualCharacterRange:NULL]; @@ -784,14 +814,17 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) long curFlags = ([theEvent modifierFlags] & allFlags); if ([theEvent keyCode] == 53){ // ESC key for internal completion - [self doCompletionByUsingSpellChecker:NO]; + // if(curFlags==(NSControlKeyMask)) + // [self doCompletionByUsingSpellChecker:NO fuzzyMode:YES]; + // else + [self doCompletionByUsingSpellChecker:NO fuzzyMode:NO]; // Remove that attribute to suppress auto-uppercasing of certain keyword combinations if(![self selectedRange].length && [self selectedRange].location) [[self textStorage] removeAttribute:kSQLkeyword range:[self getRangeForCurrentWord]]; return; } if (insertedCharacter == NSF5FunctionKey){ // F5 for completion based on spell checker - [self doCompletionByUsingSpellChecker:YES]; + [self doCompletionByUsingSpellChecker:YES fuzzyMode:NO]; // Remove that attribute to suppress auto-uppercasing of certain keyword combinations if(![self selectedRange].length && [self selectedRange].location) [[self textStorage] removeAttribute:kSQLkeyword range:[self getRangeForCurrentWord]]; diff --git a/Source/SPNarrowDownCompletion.h b/Source/SPNarrowDownCompletion.h index 06f9264a..f8f12a83 100644 --- a/Source/SPNarrowDownCompletion.h +++ b/Source/SPNarrowDownCompletion.h @@ -44,12 +44,15 @@ BOOL caseSensitive; BOOL dictMode; BOOL dbStructureMode; + BOOL fuzzyMode; BOOL noFilterString; + BOOL cursorMovedLeft; NSInteger backtickMode; NSFont *tableFont; NSRange theCharRange; NSRange theParseRange; NSString *theDbName; + id theView; NSInteger maxWindowWidth; @@ -60,8 +63,9 @@ - (id)initWithItems:(NSArray*)someSuggestions alreadyTyped:(NSString*)aUserString staticPrefix:(NSString*)aStaticPrefix additionalWordCharacters:(NSString*)someAdditionalWordCharacters caseSensitive:(BOOL)isCaseSensitive charRange:(NSRange)initRange parseRange:(NSRange)parseRange inView:(id)aView - dictMode:(BOOL)mode dbMode:(BOOL)theDbMode - backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName selectedDb:(NSString*)selectedDb; + dictMode:(BOOL)mode dbMode:(BOOL)theDbMode fuzzySearch:(BOOL)fuzzySearch + backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName + selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft; - (void)setCaretPos:(NSPoint)aPos; - (void)insert_text:(NSString* )aString; - (void)insertCommonPrefix; diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m index 12d3532f..80d8c20c 100644 --- a/Source/SPNarrowDownCompletion.m +++ b/Source/SPNarrowDownCompletion.m @@ -131,8 +131,9 @@ - (id)initWithItems:(NSArray*)someSuggestions alreadyTyped:(NSString*)aUserString staticPrefix:(NSString*)aStaticPrefix additionalWordCharacters:(NSString*)someAdditionalWordCharacters caseSensitive:(BOOL)isCaseSensitive charRange:(NSRange)initRange parseRange:(NSRange)parseRange inView:(id)aView - dictMode:(BOOL)mode dbMode:(BOOL)theDbMode - backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName selectedDb:(NSString*)selectedDb + dictMode:(BOOL)mode dbMode:(BOOL)theDbMode fuzzySearch:(BOOL)fuzzySearch + backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName + selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft { if(self = [self init]) { @@ -141,8 +142,14 @@ if(aUserString) [mutablePrefix appendString:aUserString]; + fuzzyMode = fuzzySearch; + if(fuzzyMode) + [theTableView setBackgroundColor:[NSColor colorWithCalibratedRed:0.9f green:0.9f blue:0.9f alpha:1.0f]]; + else + [theTableView setBackgroundColor:[NSColor whiteColor]]; + dbStructureMode = theDbMode; - + cursorMovedLeft = caretMovedLeft; backtickMode = theBackTickMode; if(aStaticPrefix) @@ -202,7 +209,7 @@ - (void)setupInterface { [self setReleasedWhenClosed:YES]; - [self setLevel:NSStatusWindowLevel]; + [self setLevel:NSNormalWindowLevel]; [self setHidesOnDeactivate:YES]; [self setHasShadow:YES]; [self setAlphaValue:0.9]; @@ -233,17 +240,21 @@ [theTableView addTableColumn:column1]; [column1 setWidth:170]; - NSTableColumn *column2 = [[[NSTableColumn alloc] initWithIdentifier:@"type"] autorelease]; + NSTableColumn *column3 = [[[NSTableColumn alloc] initWithIdentifier:@"type"] autorelease]; + [column3 setEditable:NO]; + [theTableView addTableColumn:column3]; + [column3 setWidth:139]; + + NSTableColumn *column2 = [[[NSTableColumn alloc] initWithIdentifier:@"list"] autorelease]; [column2 setEditable:NO]; - [[column2 dataCell] setTextColor:[NSColor darkGrayColor]]; [theTableView addTableColumn:column2]; - [column2 setWidth:145]; + [column0 setMinWidth:0]; + [column2 setWidth:6]; - NSTableColumn *column3 = [[[NSTableColumn alloc] initWithIdentifier:@"path"] autorelease]; - [column3 setEditable:NO]; - [[column3 dataCell] setTextColor:[NSColor darkGrayColor]]; - [theTableView addTableColumn:column3]; - [column3 setWidth:95]; + NSTableColumn *column4 = [[[NSTableColumn alloc] initWithIdentifier:@"path"] autorelease]; + [column4 setEditable:NO]; + [theTableView addTableColumn:column4]; + [column4 setWidth:95]; [theTableView setDataSource:self]; [scrollView setDocumentView:theTableView]; @@ -274,8 +285,34 @@ return @""; } else if([[aTableColumn identifier] isEqualToString:@"name"]) { + // NSTextFieldCell *b = [[[NSTextFieldCell new] initTextCell:[[filtered objectAtIndex:rowIndex] objectForKey:@"display"]] autorelease]; + [[aTableColumn dataCell] setFont:[NSFont systemFontOfSize:12]]; return [[filtered objectAtIndex:rowIndex] objectForKey:@"display"]; + } else if ([[aTableColumn identifier] isEqualToString:@"list"]) { + if(dictMode) { + return @""; + } else { + if([[filtered objectAtIndex:rowIndex] objectForKey:@"list"]) { + NSPopUpButtonCell *b = [[NSPopUpButtonCell new] autorelease]; + [b setPullsDown:NO]; + [b setAltersStateOfSelectedItem:NO]; + [b setControlSize:NSMiniControlSize]; + NSMenu *m = [[NSMenu alloc] init]; + [m addItemWithTitle:[[filtered objectAtIndex:rowIndex] objectForKey:@"list"] action:NULL keyEquivalent:@""]; + [b setMenu:m]; + [m release]; + [b setPreferredEdge:NSMinXEdge]; + [b setArrowPosition:NSPopUpArrowAtCenter]; + [b setFont:[NSFont systemFontOfSize:11]]; + [b setBordered:NO]; + [aTableColumn setDataCell:b]; + } else { + [aTableColumn setDataCell:[[NSTextFieldCell new] autorelease]]; + } + return @""; + } + } else if([[aTableColumn identifier] isEqualToString:@"type"]) { if(dictMode) { return @""; @@ -284,6 +321,7 @@ // return ([[filtered objectAtIndex:rowIndex] objectForKey:@"type"]) ? [[filtered objectAtIndex:rowIndex] objectForKey:@"type"] : @""; NSTokenFieldCell *b = [[[NSTokenFieldCell alloc] initTextCell:([[filtered objectAtIndex:rowIndex] objectForKey:@"type"]) ? [[filtered objectAtIndex:rowIndex] objectForKey:@"type"] : @""] autorelease]; [b setEditable:NO]; + [b setAlignment:NSRightTextAlignment]; [b setFont:[NSFont systemFontOfSize:11]]; [b setDelegate:self]; return b; @@ -342,17 +380,45 @@ NSMutableArray* newFiltered = [[NSMutableArray alloc] initWithCapacity:5]; 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 addObjectsFromArray:[suggestions filteredArrayUsingPredicate:predicate]]; if(dictMode) { for(id w in [[NSSpellChecker sharedSpellChecker] completionsForPartialWordRange:NSMakeRange(0,[[self filterString] length]) inString:[self filterString] language:nil inSpellDocumentWithTag:0]) [newFiltered addObject:[NSDictionary dictionaryWithObjectsAndKeys:w, @"display", nil]]; } else { - [self checkSpaceForAllowedCharacter]; + @try{ + NSPredicate* predicate; + if(fuzzyMode) { + NSMutableString *fuzzyRegexp = [[NSMutableString alloc] initWithCapacity:3]; + [fuzzyRegexp setString:@".*"]; + NSInteger i; + unichar c; + for(i=0; i<[[self filterString] length]; i++) { + if(i>20) break; + c = [[self filterString] characterAtIndex:i]; + if(c != '`') { + if(c == '.' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}') + [fuzzyRegexp appendString:[NSString stringWithFormat:@"\\%c.*",c]]; + else + [fuzzyRegexp appendString:[NSString stringWithFormat:@"%c.*",c]]; + } + } + // NSLog(@"re %@", fuzzyRegexp); + if(caseSensitive) + predicate = [NSPredicate predicateWithFormat:@"display MATCHES %@ OR isRef MATCHES %@", fuzzyRegexp, fuzzyRegexp]; + else + predicate = [NSPredicate predicateWithFormat:@"display MATCHES[c] %@ OR isRef MATCHES[c] %@", fuzzyRegexp, fuzzyRegexp]; + [fuzzyRegexp release]; + } else { + 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 addObjectsFromArray:[suggestions filteredArrayUsingPredicate:predicate]]; + } + @catch(id ae) { + NSLog(@"%@", @"Couldn't filter suggestion due to internal regexp error"); + } + } } else @@ -384,6 +450,8 @@ if (filtered) [filtered release]; filtered = [newFiltered retain]; [newFiltered release]; + if(!dictMode) + [self checkSpaceForAllowedCharacter]; [theTableView reloadData]; } @@ -459,7 +527,7 @@ { break; } - else if(key == NSCarriageReturnCharacter) + else if(key == NSCarriageReturnCharacter || key == NSTabCharacter) { [self completeAndInsertSnippet]; } @@ -491,15 +559,20 @@ } else if(t == NSRightMouseDown || t == NSLeftMouseDown) { - [NSApp sendEvent:event]; - if(!NSPointInRect([NSEvent mouseLocation], [self frame])) - break; + if(([event clickCount] == 2)) { + [self completeAndInsertSnippet]; + } else { + [NSApp sendEvent:event]; + if(!NSPointInRect([NSEvent mouseLocation], [self frame])) + break; + } } else { [NSApp sendEvent:event]; } } + if(cursorMovedLeft) [theView performSelector:@selector(moveRight:)]; [self close]; usleep(70); // tiny delay to suppress while continously pressing of ESC overlapping } @@ -510,7 +583,7 @@ - (void)insertCommonPrefix { - if([theTableView selectedRow] == -1) + if([theTableView selectedRow] == -1 || fuzzyMode) return; id cur = [filtered objectAtIndex:0]; @@ -521,7 +594,6 @@ if(![curMatch length]) return; - NSMutableArray* candidates = [NSMutableArray array]; NSMutableString *commonPrefix = [NSMutableString string]; [commonPrefix setString:curMatch]; for(id candidate in filtered) { -- cgit v1.2.3