diff --git a/Headers/AppKit/NSTextView.h b/Headers/AppKit/NSTextView.h index 278d9467c..4d2d98369 100644 --- a/Headers/AppKit/NSTextView.h +++ b/Headers/AppKit/NSTextView.h @@ -153,6 +153,7 @@ therefore be stored in the NSLayoutManager to avoid problems. unsigned uses_find_panel:1; unsigned accepts_glyph_info:1; unsigned allows_document_background_color_change:1; + unsigned isAutoCompleting:2; } _tf; diff --git a/Source/GSAutocompleteWindow.m b/Source/GSAutocompleteWindow.m index b439364b7..a2954499c 100644 --- a/Source/GSAutocompleteWindow.m +++ b/Source/GSAutocompleteWindow.m @@ -26,6 +26,7 @@ #import #import #import +#import "Foundation/NSUndoManager.h" #import "AppKit/NSApplication.h" #import "AppKit/NSBox.h" #import "AppKit/NSEvent.h" @@ -316,7 +317,7 @@ static GSAutocompleteWindow *gsWindow = nil; if ([event window] != self) { [self updateTextViewWithMovement: NSCancelTextMovement - isFinal: NO]; + isFinal: YES]; break; } else @@ -352,7 +353,7 @@ static GSAutocompleteWindow *gsWindow = nil; key == NSLeftArrowFunctionKey) { [self updateTextViewWithMovement: NSCancelTextMovement - isFinal: NO]; + isFinal: YES]; break; } else @@ -382,7 +383,7 @@ static GSAutocompleteWindow *gsWindow = nil; { _stopped = YES; [self updateTextViewWithMovement: NSCancelTextMovement - isFinal: NO]; + isFinal: YES]; } - (void) reloadData @@ -431,25 +432,28 @@ static GSAutocompleteWindow *gsWindow = nil; { NSString *word; - if (movement != NSCancelTextMovement) + // If this is a cancelling request... + if (movement == NSCancelTextMovement) { - NSInteger rowIndex = [_tableView selectedRow]; - word = [[_words objectAtIndex: rowIndex] description]; + // Invocation with flag==YES indicates we've inserted at least once + // causing text view to push an undo sequence that we need to undo + // here... + if (flag) + [[_textView undoManager] undo]; } else { - word = _originalWord; + NSInteger rowIndex = [_tableView selectedRow]; + word = [[_words objectAtIndex: rowIndex] description]; + [_textView insertCompletion: word + forPartialWordRange: _range + movement: movement + isFinal: flag]; } - [_textView insertCompletion: word - forPartialWordRange: _range - movement: movement - isFinal: flag]; - // Release _words and _originalWords if // autocomplete is final or canceled. - if ( (flag) || - (movement == NSCancelTextMovement) ) + if ((flag) || (movement == NSCancelTextMovement) ) { ASSIGN(_originalWord, nil); ASSIGN(_words, nil); diff --git a/Source/NSTextView.m b/Source/NSTextView.m index 925914031..b1cfab588 100644 --- a/Source/NSTextView.m +++ b/Source/NSTextView.m @@ -2653,34 +2653,34 @@ TextDidEndEditing notification _without_ asking the delegate replacementString: (NSString *)replacementString { BOOL result = YES; - + if (_tf.is_editable == NO) return NO; - + /* - We need to send the textShouldBeginEditing: / - textDidBeginEditingNotification only once. - */ - + We need to send the textShouldBeginEditing: / + textDidBeginEditingNotification only once. + */ + if (BEGAN_EDITING == NO) { if (([_delegate respondsToSelector: @selector(textShouldBeginEditing:)]) - && ([_delegate textShouldBeginEditing: _notifObject] == NO)) - return NO; + && ([_delegate textShouldBeginEditing: _notifObject] == NO)) + return NO; SET_BEGAN_EDITING(YES); [notificationCenter postNotificationName: NSTextDidBeginEditingNotification - object: _notifObject]; + object: _notifObject]; } - + if (_tf.delegate_responds_to_should_change) { result = [_delegate textView: self - shouldChangeTextInRange: affectedCharRange - replacementString: replacementString]; + shouldChangeTextInRange: affectedCharRange + replacementString: replacementString]; } - + if (result && [self allowsUndo]) { NSUndoManager *undo; @@ -2690,62 +2690,69 @@ TextDidEndEditing notification _without_ asking the delegate BOOL isTyping; NSEvent *event; static BOOL undoManagerCanCoalesce = NO; - + { - // FIXME This code (together with undoManagerCanCoalesce) is a - // temporary workaround to allow using an out of date version of - // base. Removed this upon the next release of base. - static BOOL didCheck = NO; - if (!didCheck) - { - undoManagerCanCoalesce = - [NSUndoManager instancesRespondToSelector: - @selector(_canCoalesceUndoWithTarget:selector:object:)]; - if (!undoManagerCanCoalesce) - { - NSLog(@"This version of NSUndoManager does not\n" - @"support coalescing undo operations. " - @"Upgrade gnustep-base to r29163 or newer to\n" - @"get rid of this one-time warning."); - } - didCheck = YES; - } + // FIXME This code (together with undoManagerCanCoalesce) is a + // temporary workaround to allow using an out of date version of + // base. Removed this upon the next release of base. + static BOOL didCheck = NO; + if (!didCheck) + { + undoManagerCanCoalesce = + [NSUndoManager instancesRespondToSelector: + @selector(_canCoalesceUndoWithTarget:selector:object:)]; + if (!undoManagerCanCoalesce) + { + NSLog(@"This version of NSUndoManager does not\n" + @"support coalescing undo operations. " + @"Upgrade gnustep-base to r29163 or newer to\n" + @"get rid of this one-time warning."); + } + didCheck = YES; + } } - + undo = [self undoManager]; - + /* Coalesce consecutive typing events into a single undo action using - currently private undo manager functionality. An event is considered - a typing event if it is a keyboard event and the event's characters - match our replacement string. - Note: Typing events are coalesced only when the previous action was - a typing event too and the current character follows the previous one - immediately. We never coalesce actions when the current selection is - not empty. */ - event = [NSApp currentEvent]; - isTyping = [event type] == NSKeyDown - && [[event characters] isEqualToString: replacementString]; + currently private undo manager functionality. An event is considered + a typing event if it is a keyboard event and the event's characters + match our replacement string. + Note: Typing events are coalesced only when the previous action was + a typing event too and the current character follows the previous one + immediately. We never coalesce actions when the current selection is + not empty. */ + event = [NSApp currentEvent]; + isTyping = (([event type] == NSKeyDown) && + ([[event characters] isEqualToString: replacementString])); if (undoManagerCanCoalesce && _undoObject) - { - undoRange = [_undoObject range]; - if (isTyping && - NSMaxRange(undoRange) == affectedCharRange.location && - affectedCharRange.length == 0 && - [undo _canCoalesceUndoWithTarget: _textStorage - selector: @selector(_undoTextChange:) - object: _undoObject]) - { - undoRange.length += [replacementString length]; - [_undoObject setRange: undoRange]; - return result; - } - DESTROY(_undoObject); + { + if ([undo _canCoalesceUndoWithTarget: _textStorage + selector: @selector(_undoTextChange:) + object: _undoObject]) + { + undoRange = [_undoObject range]; + if (isTyping && + NSMaxRange(undoRange) == affectedCharRange.location && + affectedCharRange.length == 0) + { + undoRange.length += [replacementString length]; + [_undoObject setRange: undoRange]; + return result; + } + else if (_tf.isAutoCompleting == 2) + { + [_undoObject setRange: affectedCharRange]; + return result; + } + DESTROY(_undoObject); + } } - + // The length of the undoRange is the length of the replacement, if any. if (replacementString != nil) { - undoRange = NSMakeRange(affectedCharRange.location, + undoRange = NSMakeRange(affectedCharRange.location, [replacementString length]); } else @@ -2753,19 +2760,21 @@ TextDidEndEditing notification _without_ asking the delegate undoRange = affectedCharRange; } undoString = [self attributedSubstringFromRange: affectedCharRange]; - + undoObject = - [[NSTextViewUndoObject alloc] initWithRange: undoRange - attributedString: undoString]; + [[NSTextViewUndoObject alloc] initWithRange: undoRange + attributedString: undoString]; [undo registerUndoWithTarget: _textStorage - selector: @selector(_undoTextChange:) - object: undoObject]; - if (isTyping) - _undoObject = undoObject; + selector: @selector(_undoTextChange:) + object: undoObject]; + if (isTyping || _tf.isAutoCompleting) + _undoObject = undoObject; else - RELEASE(undoObject); + RELEASE(undoObject); + if (_tf.isAutoCompleting == 1) + _tf.isAutoCompleting = 2; } - + return result; } @@ -6022,7 +6031,9 @@ configuation! */ range.location != NSNotFound && range.length != 0) { GSAutocompleteWindow *window = [GSAutocompleteWindow defaultWindow]; + _tf.isAutoCompleting = 1; [window displayForTextView: self]; + _tf.isAutoCompleting = 0; } }