diff options
-rw-r--r-- | Source/CMTextView.m | 252 | ||||
-rw-r--r-- | Source/SPNarrowDownCompletion.h | 8 | ||||
-rw-r--r-- | Source/SPNarrowDownCompletion.m | 49 |
3 files changed, 120 insertions, 189 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]; - + } diff --git a/Source/SPNarrowDownCompletion.h b/Source/SPNarrowDownCompletion.h index c01c5880..88e76567 100644 --- a/Source/SPNarrowDownCompletion.h +++ b/Source/SPNarrowDownCompletion.h @@ -44,10 +44,10 @@ BOOL caseSensitive; BOOL dictMode; BOOL dbStructureMode; - BOOL backtickMode; + NSInteger backtickMode; NSFont *tableFont; NSRange theCharRange; - NSRange theInitRange; + NSRange theParseRange; NSArray *words; id theView; @@ -56,9 +56,9 @@ - (id)initWithItems:(NSArray*)someSuggestions alreadyTyped:(NSString*)aUserString staticPrefix:(NSString*)aStaticPrefix additionalWordCharacters:(NSString*)someAdditionalWordCharacters caseSensitive:(BOOL)isCaseSensitive - charRange:(NSRange)initRange inView:(id)aView + charRange:(NSRange)initRange parseRange:(NSRange)parseRange inView:(id)aView dictMode:(BOOL)mode dbMode:(BOOL)theDbMode - backtickMode:(BOOL)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName selectedDb:(NSString*)selectedDb; + backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName selectedDb:(NSString*)selectedDb; - (void)setCaretPos:(NSPoint)aPos; - (void)insert_text:(NSString* )aString; diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m index a9d9c143..16438a56 100644 --- a/Source/SPNarrowDownCompletion.m +++ b/Source/SPNarrowDownCompletion.m @@ -30,6 +30,7 @@ #import "SPNarrowDownCompletion.h" #import "SPArrayAdditions.h" +#import "SPStringAdditions.h" #import "ImageAndTextCell.h" #import "SPConstants.h" #include <tgmath.h> @@ -126,9 +127,9 @@ - (id)initWithItems:(NSArray*)someSuggestions alreadyTyped:(NSString*)aUserString staticPrefix:(NSString*)aStaticPrefix additionalWordCharacters:(NSString*)someAdditionalWordCharacters caseSensitive:(BOOL)isCaseSensitive - charRange:(NSRange)initRange inView:(id)aView + charRange:(NSRange)initRange parseRange:(NSRange)parseRange inView:(id)aView dictMode:(BOOL)mode dbMode:(BOOL)theDbMode - backtickMode:(BOOL)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName selectedDb:(NSString*)selectedDb + backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName selectedDb:(NSString*)selectedDb { if(self = [self init]) { @@ -155,7 +156,7 @@ theCharRange.location++; } - theInitRange = theCharRange; + theParseRange = parseRange; theView = aView; dictMode = mode; @@ -428,6 +429,7 @@ [NSApp sendEvent: event]; [mutablePrefix appendString:[event characters]]; theCharRange = NSMakeRange(theCharRange.location, theCharRange.length+[[event characters] length]); + theParseRange = NSMakeRange(theParseRange.location, theParseRange.length+[[event characters] length]); [self filter]; } else if((flags & NSControlKeyMask) || (flags & NSAlternateKeyMask) || (flags & NSCommandKeyMask)) @@ -451,6 +453,7 @@ [mutablePrefix deleteCharactersInRange:NSMakeRange([mutablePrefix length]-1, 1)]; theCharRange = NSMakeRange(theCharRange.location, theCharRange.length-1); + theParseRange = NSMakeRange(theParseRange.location, theParseRange.length-1); [self filter]; } else if(key == NSTabCharacter) @@ -474,6 +477,7 @@ [NSApp sendEvent:event]; [mutablePrefix appendString:[event characters]]; theCharRange = NSMakeRange(theCharRange.location, theCharRange.length+1); + theParseRange = NSMakeRange(theParseRange.location, theParseRange.length+1); [self filter]; } else @@ -568,31 +572,20 @@ } else { NSMutableDictionary* selectedItem = [[[filtered objectAtIndex:[theTableView selectedRow]] mutableCopy] autorelease]; NSString* candidateMatch = [selectedItem objectForKey:@"match"] ?: [selectedItem objectForKey:@"display"]; - if([[NSApp currentEvent] modifierFlags] & (NSShiftKeyMask)) { - NSArray *path = [NSArray arrayWithArray:[[selectedItem objectForKey:@"path"] componentsSeparatedByString:@"⇠"]]; - if([path count]) { - NSMutableString *p = [NSMutableString string]; - NSEnumerator *enumerator = [path reverseObjectEnumerator]; - if(backtickMode) - for (id element in enumerator) { - if(![element isEqualToString:currentDb]) { - [p appendString:element]; - [p appendString:@"`.`"]; - } - } - else - for (id element in enumerator) { - if(![element isEqualToString:currentDb]) { - [p appendString:element]; - [p appendString:@"."]; - } - } - [p appendString:candidateMatch]; - [self insert_text:p]; - } else { - if([[self filterString] length] < [candidateMatch length]) - [self insert_text:candidateMatch]; - } + if([[selectedItem objectForKey:@"path"] length] && [[NSApp currentEvent] modifierFlags] & (NSShiftKeyMask)) { + NSString *path = [NSString stringWithFormat:@"%@.%@", + [[[[[selectedItem objectForKey:@"path"] componentsSeparatedByString:@"⇠"] reverseObjectEnumerator] allObjects] componentsJoinedByPeriodAndBacktickQuoted], + [candidateMatch backtickQuotedString]]; + + // Check if path's db name is the current selected db name + NSRange r = [path rangeOfString:[currentDb backtickQuotedString] options:NSCaseInsensitiveSearch range:NSMakeRange(0, [[currentDb backtickQuotedString] length])]; + theCharRange = theParseRange; + backtickMode = 0; // suppress move the caret one step rightwards + if(path && [path length] && r.length) + [self insert_text:[path substringFromIndex:r.length+1]]; + else + [self insert_text:path]; + } else { if([[self filterString] length] < [candidateMatch length]) [self insert_text:candidateMatch]; |