Control more directly the completion range for group chats. Fixes #13237.
authorZachary West <zacw@adium.im>
Sun Oct 25 21:21:42 2009 -0400 (2009-10-25)
changeset 2776862f4e3f05e1
parent 2775 0a721697cd88
child 2777 a55a449072d4
Control more directly the completion range for group chats. Fixes #13237.

We now go back to the nearest whitespace and see if it would autocomplete any nicks at that point. If it would, we perform the autocompletion starting at that point. If it doesn't, we let the OS do its normal thing.

This fixes autocompleting things like "[mynick]" or the channel name (such as "#adium") since they start with non-word characters, but are perfectly valid.
Frameworks/Adium Framework/Source/AIMessageEntryTextView.h
Frameworks/Adium Framework/Source/AIMessageEntryTextView.m
Plugins/Dual Window Interface/AIMessageViewController.m
     1.1 --- a/Frameworks/Adium Framework/Source/AIMessageEntryTextView.h	Sat Oct 24 18:29:23 2009 -0400
     1.2 +++ b/Frameworks/Adium Framework/Source/AIMessageEntryTextView.h	Sun Oct 25 21:21:42 2009 -0400
     1.3 @@ -26,6 +26,7 @@
     1.4   * @brief Should the tab key trigger an autocomplete?
     1.5   */
     1.6  - (BOOL)textViewShouldTabComplete:(NSTextView *)inTextView;
     1.7 +- (NSRange)textView:(NSTextView *)inTextView rangeForCompletion:(NSRange)completionRange;
     1.8  - (void)textViewDidCancel:(NSTextView *)inTextView;
     1.9  @end
    1.10  
     2.1 --- a/Frameworks/Adium Framework/Source/AIMessageEntryTextView.m	Sat Oct 24 18:29:23 2009 -0400
     2.2 +++ b/Frameworks/Adium Framework/Source/AIMessageEntryTextView.m	Sun Oct 25 21:21:42 2009 -0400
     2.3 @@ -1504,6 +1504,18 @@
     2.4  										group:PREF_GROUP_DUAL_WINDOW_INTERFACE];
     2.5  }
     2.6  
     2.7 +#pragma mark Autocompleting
     2.8 +- (NSRange)rangeForUserCompletion
     2.9 +{
    2.10 +	NSRange completionRange = [super rangeForUserCompletion];
    2.11 +	
    2.12 +	if ([self.delegate respondsToSelector:@selector(textView:rangeForCompletion:)]) {
    2.13 +		completionRange = [self.delegate textView:self rangeForCompletion:completionRange];
    2.14 +	}
    2.15 +	
    2.16 +	return completionRange;
    2.17 +}
    2.18 +
    2.19  #pragma mark Writing Direction
    2.20  - (void)toggleBaseWritingDirection:(id)sender
    2.21  {
     3.1 --- a/Plugins/Dual Window Interface/AIMessageViewController.m	Sat Oct 24 18:29:23 2009 -0400
     3.2 +++ b/Plugins/Dual Window Interface/AIMessageViewController.m	Sun Oct 25 21:21:42 2009 -0400
     3.3 @@ -90,6 +90,8 @@
     3.4  - (void)setUserListVisible:(BOOL)inVisible;
     3.5  - (void)setupShelfView;
     3.6  - (void)updateUserCount;
     3.7 +
     3.8 +- (NSArray *)contactsMatchingBeginningString:(NSString *)partialWord;
     3.9  @end
    3.10  
    3.11  @implementation AIMessageViewController
    3.12 @@ -974,6 +976,12 @@
    3.13  }
    3.14  
    3.15  #pragma mark Autocompletion
    3.16 +- (BOOL)canTabCompleteForPartialWord:(NSString *)partialWord
    3.17 +{
    3.18 +	return ([self contactsMatchingBeginningString:partialWord].count > 0 ||
    3.19 +			[self.chat.displayName rangeOfString:partialWord options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound);
    3.20 +}
    3.21 +
    3.22  /*!
    3.23   * @brief Should the tab key cause an autocompletion if possible?
    3.24   *
    3.25 @@ -981,55 +989,90 @@
    3.26   */
    3.27  - (BOOL)textViewShouldTabComplete:(NSTextView *)inTextView
    3.28  {
    3.29 -	return self.chat.isGroupChat;
    3.30 +	if (self.chat.isGroupChat) {
    3.31 +		NSRange completionRange = inTextView.rangeForUserCompletion;
    3.32 +		NSString *partialWord = [inTextView.textStorage.string substringWithRange:completionRange];
    3.33 +		return [self canTabCompleteForPartialWord:partialWord]; 
    3.34 +	}
    3.35 +	
    3.36 +	return NO;
    3.37 +}
    3.38 +
    3.39 +- (NSRange)textView:(NSTextView *)inTextView rangeForCompletion:(NSRange)charRange
    3.40 +{
    3.41 +	if (self.chat.isGroupChat && charRange.location > 0) {
    3.42 +		NSString *partialWord = nil;
    3.43 +		NSString *allText = [inTextView.textStorage.string substringWithRange:NSMakeRange(0, NSMaxRange(charRange))];
    3.44 +		NSRange whitespacePosition = [allText rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet] options:NSBackwardsSearch];
    3.45 +		
    3.46 +		if (whitespacePosition.location == NSNotFound) {
    3.47 +			// We went back to the beginning of the string and still didn't find a whitespace; use the whole thing.
    3.48 +			partialWord = allText;
    3.49 +			whitespacePosition = NSMakeRange(0, 0);
    3.50 +		} else {
    3.51 +			// We found a whitespace, use from it until our current position.
    3.52 +			partialWord = [allText substringWithRange:NSMakeRange(NSMaxRange(whitespacePosition), allText.length - NSMaxRange(whitespacePosition))];
    3.53 +		}
    3.54 +		
    3.55 +		// If this matches any contacts or the room name, use this new range for autocompletion.
    3.56 +		if ([self canTabCompleteForPartialWord:partialWord]) {
    3.57 +			charRange = NSMakeRange(NSMaxRange(whitespacePosition), allText.length - NSMaxRange(whitespacePosition));
    3.58 +		}
    3.59 +	}
    3.60 +	
    3.61 +	return charRange;
    3.62 +}
    3.63 +
    3.64 +- (NSArray *)contactsMatchingBeginningString:(NSString *)partialWord
    3.65 +{
    3.66 +	NSMutableArray *contacts = [NSMutableArray array];
    3.67 +	
    3.68 +	for (AIListContact *listContact in self.chat) {
    3.69 +		if ([listContact.UID rangeOfString:partialWord
    3.70 +								   options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound ||
    3.71 +			[listContact.formattedUID rangeOfString:partialWord
    3.72 +											options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound ||
    3.73 +			[listContact.displayName rangeOfString:partialWord
    3.74 +										   options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound) {
    3.75 +				[contacts addObject:listContact];
    3.76 +		}
    3.77 +	}
    3.78 +	
    3.79 +	return contacts;
    3.80  }
    3.81  
    3.82  - (NSArray *)textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index
    3.83  {
    3.84 -	NSMutableArray	*completions;
    3.85 +	NSMutableArray	*completions = nil;
    3.86  	
    3.87  	if (self.chat.isGroupChat) {
    3.88 -		NSString		*partialWord = [[textView.textStorage attributedSubstringFromRange:charRange] string];
    3.89 -		
    3.90 +		NSString *suffix = nil;
    3.91 +		NSString *partialWord = [textView.textStorage.string substringWithRange:charRange];
    3.92  		BOOL autoCompleteUID = [self.chat.account chatShouldAutocompleteUID:self.chat];
    3.93  		
    3.94 -		NSString		*suffix;
    3.95 +		//At the start of a line, append ": "
    3.96  		if (charRange.location == 0) {
    3.97 -			//At the start of a line, append ": "
    3.98  			suffix = @": ";
    3.99 -		} else {
   3.100 -			suffix = nil;
   3.101  		}
   3.102  		
   3.103  		completions = [NSMutableArray array];
   3.104 -		for(AIListContact *listContact in self.chat) {
   3.105 -			if ([listContact.displayName rangeOfString:partialWord
   3.106 -												 options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound ||
   3.107 -				[listContact.formattedUID rangeOfString:partialWord
   3.108 -												options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound ||
   3.109 -				[listContact.UID rangeOfString:partialWord
   3.110 -										 options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound) {
   3.111 -
   3.112 -				NSString *displayName = [self.chat aliasForContact:listContact];
   3.113 -				
   3.114 -				if (!displayName) {
   3.115 -					displayName = autoCompleteUID ? listContact.formattedUID : listContact.displayName;
   3.116 -				}
   3.117 -
   3.118 -				[completions addObject:(suffix ? [displayName stringByAppendingString:suffix] : displayName)];
   3.119 -			}
   3.120 +		
   3.121 +		for (AIListContact *listContact in [self contactsMatchingBeginningString:partialWord]) {
   3.122 +			NSString *displayName = [self.chat aliasForContact:listContact];
   3.123 +			
   3.124 +			if (!displayName)
   3.125 +				displayName = autoCompleteUID ? listContact.formattedUID : listContact.displayName;
   3.126 +			
   3.127 +			[completions addObject:(suffix ? [displayName stringByAppendingString:suffix] : displayName)];
   3.128  		}
   3.129  		
   3.130 -		if ([self.chat.name rangeOfString:partialWord options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound) {
   3.131 -			[completions addObject:self.chat.name];
   3.132 +		if ([self.chat.displayName rangeOfString:partialWord options:(NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound) {
   3.133 +			[completions addObject:self.chat.displayName];
   3.134  		}
   3.135  
   3.136  		if ([completions count]) {			
   3.137  			*index = 0;
   3.138  		}
   3.139 -
   3.140 -	} else {
   3.141 -		completions = nil;
   3.142  	}
   3.143  
   3.144  	return [completions count] ? completions : words;