Re: can't select NSTextView completion when partial word length < 2
Re: can't select NSTextView completion when partial word length < 2
- Subject: Re: can't select NSTextView completion when partial word length < 2
- From: Bjoern Kriews <email@hidden>
- Date: Wed, 25 Feb 2004 20:13:54 +0100
On 25.02.2004, at 19:05, Jim Rankin wrote:
Hi.
I have a text view with a delegate where I have implemented
textView:completions:forPartialWordRange:indexOfSelectedItem:. But if
the string for the partial word range is 0 or 1 characters, my returned
completions list is displayed, but my selection from the list is not
inserted into the text. If the partial word is two characters or
longer, selection and insertion from the list works fine.
Has anyone else seen this? Is it a known issue? Am I doing something
wrong?
I have solved this by subclassing NSTextView.
My need was to complete things like database.tablename.fieldname in a
most
convenient way, also I wanted to allow users an option to use the TAB
key
to complete because they are used to this from the CLI and it is much
more convenient
than hitting alt-esc or F5 if you use it often. Losing field navigation
is
not a problem in this app.
This is very much work in progress, I ripped out some interesting
methods:
- (NSRange) rangeForUserCompletion
Here I scan for a word using my own set of delimiters and replace an
empty completion with a space so Cocoa will complete:.
- (NSArray *)completionsForPartialWordRange:(NSRange)charRange
indexOfSelectedItem:(int *)index
Sort out completions that are exact matches so one does not "get stuck"
while completing.
(I think this is also possible with a delegate method - still
experimenting)
- (void)insertCompletion:(NSString *)word
forPartialWordRange:(NSRange)charRange movement:(int)movement
isFinal:(BOOL)final {
I didn't like the reaction to certain keys while selecting a completion
(related to tabbing),
so I override this too - seem to me the most dangerous part to get
wrong (I probably did).
Hope that helps, Bjoern
--- code ---
- (NSRange) rangeForUserCompletion {
static NSCharacterSet *fieldCharacterSet = nil;
if( !fieldCharacterSet )
fieldCharacterSet = [[[NSCharacterSet
characterSetWithCharactersInString:
@"0123456789abcdefghijklmnopqrstuvwxyz_." ] invertedSet] retain]; //
leak, hardcoded, FIXME
NSRange userRange;
NSRange selection = [self selectedRange];
NSString *string = [self string];
debug_string_range(9, @"selection", string, selection);
if( selection.length == 0 ) { // selection is point (aka no
selection)
// find the first delimiter backwards from the insertion point
userRange = [string rangeOfCharacterFromSet: fieldCharacterSet
options: NSBackwardsSearch|NSLiteralSearch range: NSMakeRange(0,
selection.location)];
debug_string_range(8, @"delimiter", string, userRange);
if( userRange.location == NSNotFound ) {
if( selection.location == 0 ) { // completion called at
start of text
debug(8, @"emptinessAtStart");
// cocoa wont complete for an empty range, so we make
sure there is a space and use that
[self insertText: @" "];
userRange = NSMakeRange(0,1);
} else {
debug(8, @"partAtStart");
// patch up what rangeOfCharacterFromSet did miss
userRange = NSMakeRange( 0, selection.location );
}
} else {
if( userRange.length > 0 ) {
if( userRange.location == selection.location - 1 ) { //
just before cursor means empty completion
debug(8, @"emptiness");
// use userRange as it is, it contains the
character signaling emptiness
} else { // normal completion
debug(8, @"part");
userRange.location++; // skip past first bad one
userRange.length = selection.location -
userRange.location;
}
} else {
warning(@"weird userRange %@",
NSStringFromRange(userRange));
userRange = NSMakeRange(NSNotFound, 0);
}
}
} else {
// if the user has invoked completion with an active selection
then we try to complete that
// still have to think about it - does not feel right
debug(8, @"forSelection");
userRange = selection;
// deselect and place cursor at end of selection to avoid
trouble (cocoa does not expect this to happen)
[self setSelectedRange: NSMakeRange( selection.location +
selection.length, 0)];
}
debug_string_range(7, @"return", string, userRange);
return userRange;
}
- (NSArray *)completionsForPartialWordRange:(NSRange)charRange
indexOfSelectedItem:(int *)index {
NSString *part = [[self string] substringWithRange: charRange];
NSLog(@"completion called for '%@' at %d len %d", part,
charRange.location, charRange.length);
NSArray *wordList = completions;
NSMutableArray *candidates = [NSMutableArray arrayWithCapacity:
[wordList count]];
NSEnumerator *e = [wordList objectEnumerator];
NSString *word;
while( word = [e nextObject] ) {
if( [ word hasPrefix: part ] && [word length] !=
charRange.length ) // exclude exact matches, to avoid being stuck
[candidates addObject: word];
}
return candidates;
}
- (void)insertCompletion:(NSString *)word
forPartialWordRange:(NSRange)charRange movement:(int)movement
isFinal:(BOOL)final {
NSString *part = [[self string] substringWithRange: charRange];
NSLog(@"insertCompletion: '%@' for part '%@' (%d,%d) move: %d
final: %d", word, part,
charRange.location, charRange.length, movement, final );
if( movement == NSCancelTextMovement ) {
NSLog(@"completion cancelled");
[self replaceCharactersInRange: [self selectedRange]
withString: @""];
return;
}
// we know now that the user wants the completion
NSEvent *event = [[self window] currentEvent];
NSEventType eventType = [event type];
if( final && eventType == NSKeyDown ) {
NSString *characters = [event characters];
unsigned int length = [characters length];
NSLog(@"keyDown characters: (%d) '%@'", length, characters);
if(length) {
unichar first = [characters characterAtIndex: 0];
NSLog(@"first: %d", first);
if( first == NSDeleteCharacter ) {
NSLog(@"completion cancelled by delete");
return;
}
}
}
NSRange insertRange = NSMakeRange( charRange.length, [word length]
- charRange.length);
NSLog(@"insertRange is at %d length %d", insertRange.location,
insertRange.length);
if( insertRange.length > 0 ) {
[self insertText: [word substringWithRange: insertRange]];
if( final ) {
NSLog(@"de-selecting");
[self setSelectedRange: NSMakeRange([[self string] length],
0)];
} else {
NSLog(@"selecting");
[self setSelectedRange: NSMakeRange(charRange.location +
charRange.length, insertRange.length)];
}
} else {
NSLog(@"weird: completion for empty range ?");
}
}
@end
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.