aboutsummaryrefslogtreecommitdiffstats
path: root/Source/CMTextView.m
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CMTextView.m')
-rw-r--r--Source/CMTextView.m252
1 files changed, 95 insertions, 157 deletions
diff --git a/Source/CMTextView.m b/Source/CMTextView.m
index 06ada98e..6c2bd7a3 100644
--- a/Source/CMTextView.m
+++ b/Source/CMTextView.m
@@ -262,7 +262,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if(aTableNameExists) {
[sortedTables addObject:aTableName];
} else {
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:db, @"display", @"database-small", @"image", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:db, @"display", @"database-small", @"image", @"", @"isRef", nil]];
[sortedTables addObjectsFromArray:[allTables sortedArrayUsingDescriptors:[NSArray arrayWithObject:desc]]];
// if(aDbName == nil && aTableName) {
if([sortedTables count] > 1 && [sortedTables containsObject:currentTable]) {
@@ -279,17 +279,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", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-small-square", @"image", db, @"path", @"", @"isRef", nil]];
break;
case 1:
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-view-small-square", @"image", db, @"path", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-view-small-square", @"image", db, @"path", @"", @"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", @"", @"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", @"", @"isRef", nil]];
breakFlag = YES;
break;
}
@@ -297,7 +297,7 @@ 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", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:field, @"display", @"field-small-square", @"image", [NSString stringWithFormat:@"%@⇠%@",table,db], @"path", [theTable objectForKey:field], @"type", @"", @"isRef", nil]];
}
}
}
@@ -310,34 +310,34 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
// Add all database names to completions list
for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allDatabaseNames"])
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"database-small", @"image", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"database-small", @"image", @"", @"isRef", nil]];
// Add all system database names to completions list
for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allSystemDatabaseNames"])
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"database-small", @"image", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"database-small", @"image", @"", @"isRef", nil]];
// Add table names to completions list
for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allTableNames"])
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"table-small-square", @"image", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"table-small-square", @"image", @"", @"isRef", nil]];
// Add view names to completions list
for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allViewNames"])
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"table-view-small-square", @"image", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"table-view-small-square", @"image", @"", @"isRef", nil]];
// Add field names to completions list for currently selected table
if ([[[self window] delegate] table] != nil)
for (id obj in [[[[self window] delegate] valueForKeyPath:@"tableDataInstance"] valueForKey:@"columnNames"])
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"field-small-square", @"image", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"field-small-square", @"image", @"", @"isRef", nil]];
// Add proc/func only for MySQL version 5 or higher
if(mySQLmajorVersion > 4) {
// Add all procedures to completions list for currently selected table
for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allProcedureNames"])
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"proc-small", @"image", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"proc-small", @"image", @"", @"isRef", nil]];
// Add all function to completions list for currently selected table
for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allFunctionNames"])
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"func-small", @"image", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"func-small", @"image", @"", @"isRef", nil]];
}
}
}
@@ -364,19 +364,18 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSString* dbName = nil;
NSString* tableName = nil;
NSRange completionRange = [self getRangeForCurrentWord];
+ NSRange parseRange = completionRange;
NSString* currentWord = [[self string] substringWithRange:completionRange];
NSString* prefix = @"";
NSString* allow = @"_. "; // additional chars which not close the popup
NSString *currentDb = nil;
BOOL dbBrowseMode = NO;
- BOOL backtickMode = NO;
+ NSInteger backtickMode = 0; // 0 none, 1 rigth only, 2 left only, 3 both
BOOL caseInsensitive = YES;
- if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"selectedDatabase"] != nil)
- currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"];
- else
- currentDb = @"";
+ currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"];
+ if(!currentDb) currentDb = @"";
// Check if the caret is inside quotes "" or ''; if so
// return the normal word suggestion due to the spelling's settings
@@ -388,153 +387,91 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if(!isDictMode) {
- // Check if user wants to insert a db/table/field name
- // currentWord == ` : user invoked completion via `|`
- dbBrowseMode = ([currentWord isEqualToString:@"`"]) ? YES : NO;
- // Is the caret inside backticks
- dbBrowseMode = ([[[self textStorage] attribute:kBTQuote atIndex:[self selectedRange].location-1 effectiveRange:nil] isEqualToString:kBTQuoteValue]) ? YES : dbBrowseMode;
- backtickMode = dbBrowseMode;
-
- // Refresh quote attributes
- [[self textStorage] removeAttribute:kQuote range:NSMakeRange(0, [[self string] length])];
-
- if([currentWord hasSuffix:@"."]) {
- dbBrowseMode = YES;
- filter = @"";
- completionRange = NSMakeRange(completionRange.location+completionRange.length,0);
- } else {
- filter = [NSString stringWithString:currentWord];
- }
-
+ NSInteger caretPos = [self selectedRange].location;
+ BOOL caretIsInsideBackticks = NO;
- NSInteger caretPos = completionRange.location + completionRange.length;
+ // Is the caret inside backticks
+ // Do not using attribute:atIndex: since it could return wrong results due to editing.
+ // This approach counts the number of ` up to the beginning of the current line from caret position
+ NSRange lineHeadRange = [[self string] lineRangeForRange:NSMakeRange(caretPos, 0)];
+ NSString *lineHead = [[self string] substringWithRange:NSMakeRange(lineHeadRange.location, caretPos - lineHeadRange.location)];
+ for(NSInteger i=0; i<[lineHead length]; i++)
+ if([lineHead characterAtIndex:i]=='`') caretIsInsideBackticks = !caretIsInsideBackticks;
+
+ NSCharacterSet *whiteSpaceCharSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSInteger start = caretPos;
- BOOL breakParsing = YES;
-
- // Parse string left from currentWord …
- if(start > 1) {
- // … for possible db/table/field completion
- // table.foo| or `table`.foo| or `table`.`foo|` to set filter to "foo"
- start--;
- BOOL insideBackticks = backtickMode;
- unichar currentCharacter;
- NSCharacterSet *whiteSpaceCharSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
- BOOL runFlag = YES;
- while(start >= 0 && runFlag) {
- currentCharacter = [[self string] characterAtIndex:start];
- if([whiteSpaceCharSet characterIsMember:currentCharacter] && !insideBackticks) {
- breakParsing = YES;
- break;
- }
- switch(currentCharacter) {
- case '`':
- insideBackticks = !insideBackticks;
- break;
- case '.':
- if(!insideBackticks) {
- start++;
- NSInteger offset = (backtickMode) ? 1 : 0;
- completionRange = NSMakeRange(start+offset,caretPos-start-offset);
- filter = [[self string] substringWithRange:completionRange];
- caretPos = start;
- runFlag = NO;
- dbBrowseMode = YES;
- breakParsing = NO;
- }
+ NSInteger backticksCounter = (caretIsInsideBackticks) ? 1 : 0;
+ NSInteger pointCounter = 0;
+ NSInteger firstPoint = 0;
+ NSInteger secondPoint = 0;
+ BOOL doParsing = YES;
+ unichar currentCharacter;
+ while(start > 0 && doParsing) {
+ currentCharacter = [[self string] characterAtIndex:--start];
+ if(!(backticksCounter%2) && [whiteSpaceCharSet characterIsMember:currentCharacter]) {
+ start++;
+ break;
+ }
+ if(currentCharacter == '.' && !(backticksCounter%2)) {
+ pointCounter++;
+ switch(pointCounter) {
+ case 1:
+ firstPoint = start;
break;
- }
- if(start == 0) {
- breakParsing = YES;
+ case 2:
+ secondPoint = start;
break;
+ default:
+ doParsing = NO;
+ start++;
}
- start--;
}
-
- // … go further and look for a table name
- if(!breakParsing && start > 0) {
- start--;
- caretPos--;
- runFlag = YES;
- breakParsing = YES;
- insideBackticks = NO;
- while(runFlag) {
- currentCharacter = [[self string] characterAtIndex:start];
- if(start == 0 || ([whiteSpaceCharSet characterIsMember:currentCharacter] && !insideBackticks)) {
- if(start > 0) start++;
- tableName = [[[self string] substringWithRange:NSMakeRange(start,caretPos-start)] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
- caretPos = start;
- runFlag = NO;
- breakParsing = !(start == 0);
- break;
- }
- switch(currentCharacter) {
- case '`':
- insideBackticks = !insideBackticks;
- break;
- case '.':
- if(!insideBackticks) {
- start++;
- tableName = [[[self string] substringWithRange:NSMakeRange(start,caretPos-start)] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
- caretPos = start;
- runFlag = NO;
- breakParsing = NO;
- }
- break;
- }
- if(start == 0) {
- breakParsing = YES;
- break;
+ if(doParsing && currentCharacter == '`') {
+ backticksCounter++;
+ if(!(backticksCounter%2) && start > 0) {
+ currentCharacter = [[self string] characterAtIndex:start-1];
+ if(currentCharacter != '`' && currentCharacter != '.') break;
+ if(currentCharacter == '`') { // ignore ``
+ backticksCounter++;
+ start-=2;
}
- if(start == 0) {
- breakParsing = YES;
- break;
- }
- start--;
}
}
+ }
- // … go further and look for a db name
- if(!breakParsing && start > 0) {
- start--;
- caretPos--;
- runFlag = YES;
- breakParsing = YES;
- insideBackticks = NO;
- while(runFlag) {
- currentCharacter = [[self string] characterAtIndex:start];
- if(start == 0 || ([whiteSpaceCharSet characterIsMember:currentCharacter] && !insideBackticks)) {
- if(start > 0) start++;
- dbName = [[[self string] substringWithRange:NSMakeRange(start,caretPos-start)] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
- caretPos = start;
- runFlag = NO;
- breakParsing = !(start == 0);
- break;
- }
- switch(currentCharacter) {
- case '`':
- insideBackticks = !insideBackticks;
- break;
- case '.':
- if(!insideBackticks) {
- start++;
- dbName = [[[self string] substringWithRange:NSMakeRange(start,caretPos-start)] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
- caretPos = start;
- runFlag = NO;
- breakParsing = NO;
- }
- break;
- }
- if(start == 0) {
- breakParsing = YES;
- break;
- }
- if(start == 0) {
- breakParsing = YES;
- break;
- }
- start--;
+ dbBrowseMode = (pointCounter || backticksCounter);
+
+ if(dbBrowseMode) {
+ parseRange = NSMakeRange(start, caretPos-start);
+ NSString *parsedString = [[self string] substringWithRange:parseRange];
+
+ // Check if parsed string is wrapped by ``
+ if([parsedString hasPrefix:@"`"]) backtickMode+=1;
+ if([[self string] length] > parseRange.location+parseRange.length) {
+ if([[self string] characterAtIndex:parseRange.location+parseRange.length] == '`') {
+ backtickMode+=2;
+ NSLog(@"a %@", NSStringFromRange(parseRange));
+ parseRange.length++; // adjust parse string for right `
+ NSLog(@"b %@", NSStringFromRange(parseRange));
}
}
+
+ // Normalize point positions
+ firstPoint-=start;
+ secondPoint-=start;
+
+ if(secondPoint>0) {
+ dbName = [[[parsedString substringWithRange:NSMakeRange(0, secondPoint)] stringByReplacingOccurrencesOfString:@"``" withString:@"`"] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
+ tableName = [[[parsedString substringWithRange:NSMakeRange(secondPoint+1,firstPoint-secondPoint-1)] stringByReplacingOccurrencesOfString:@"``" withString:@"`"] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
+ filter = [[[parsedString substringWithRange:NSMakeRange(firstPoint+1,[parsedString length]-firstPoint-1)] stringByReplacingOccurrencesOfString:@"``" withString:@"`"] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
+ } else if(firstPoint>0) {
+ tableName = [[[parsedString substringWithRange:NSMakeRange(0, firstPoint)] stringByReplacingOccurrencesOfString:@"``" withString:@"`"] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
+ filter = [[[parsedString substringWithRange:NSMakeRange(firstPoint+1,[parsedString length]-firstPoint-1)] stringByReplacingOccurrencesOfString:@"``" withString:@"`"] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
+ } else {
+ filter = [[parsedString stringByReplacingOccurrencesOfString:@"``" withString:@"`"] stringByReplacingOccurrencesOfRegex:@"^`|`$" withString:@""];
+ }
+ } else {
+ filter = [NSString stringWithString:currentWord];
}
} else {
filter = [NSString stringWithString:currentWord];
@@ -546,6 +483,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
additionalWordCharacters:allow
caseSensitive:!caseInsensitive
charRange:completionRange
+ parseRange:parseRange
inView:self
dictMode:isDictMode
dbMode:dbBrowseMode
@@ -553,24 +491,24 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
withDbName:dbName
withTableName:tableName
selectedDb:currentDb];
-
+
//Get the NSPoint of the first character of the current word
NSRange range = NSMakeRange(completionRange.location,0);
NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:range actualCharacterRange:NULL];
NSRect boundingRect = [[self layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]];
boundingRect = [self convertRect: boundingRect toView: NULL];
NSPoint pos = [[self window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
-
+
// TODO: check if needed
// if(filter)
// pos.x -= [filter sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
// Adjust list location to be under the current word or insertion point
pos.y -= [[self font] pointSize]*1.25;
-
+
[completionPopUp setCaretPos:pos];
[completionPopUp orderFront:self];
-
+
}