diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index a5f1f671..0a5a211c 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -822,7 +822,12 @@ - (BOOL)xvim_delete:(XVimMotion*)motion withMotionPoint:(NSUInteger)motionPoint newPos = [self.textStorage xvim_indexOfLineNumber:sel.top column:sel.left]; } - [self.xvimDelegate textView:self didDelete:self.lastYankedText withType:self.lastYankedType]; + // in case yank:NO is passed in and no yanking has been done yet + // usually we don't want to report unyanked deletes anyway, as they are usually + // internal plumbing + if (self.lastYankedText != NULL) { + [self.xvimDelegate textView:self didDelete:self.lastYankedText withType:self.lastYankedType]; + } [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; if (newPos != NSNotFound) { [self xvim_moveCursor:newPos preserveColumn:NO]; diff --git a/XVim/Test/XVimTester+Issues.m b/XVim/Test/XVimTester+Issues.m index d5c7130c..0b2c7b31 100644 --- a/XVim/Test/XVimTester+Issues.m +++ b/XVim/Test/XVimTester+Issues.m @@ -49,7 +49,12 @@ - (NSArray*)issues_testcases{ static NSString* issue_606_result_spaces = @" aaa bbb ccc\n"; static NSString* issue_606_result_tabs = @" aaa bbb ccc\n"; - + static NSString* issue_770_text = @"aaaa(.)a\n"; + static NSString* issue_770_newline_result = @"a\naa(.)a\n"; + static NSString* issue_770_replace_multichar_result = @"bbbb(.)a\n"; + static NSString* issue_770_eol_text = @"1234\n1234\n"; + static NSString* issue_770_eol_result = @"123\n\n1234\n"; + static NSString* issue_776_text = @""; static NSString* issue_776_result = @"\n"; static NSString* issue_805_text = @"aaaa bbbb cccc dddd eeee ffff gggg\n" @@ -83,6 +88,12 @@ - (NSArray*)issues_testcases{ ? XVimMakeTestCase( text0, 0, 0, @"i.", issue_606_result_tabs, 1, 0 ) // Issue #606. Repeating tab insertion crashes Xcode. : XVimMakeTestCase( text0, 0, 0, @"i.", issue_606_result_spaces, 7, 0 ), // Issue #606. Repeating tab insertion crashes Xcode. + XVimMakeTestCase(issue_770_text, 0, 0, @"ru", issue_770_text, 0, 0), // Issue #770 + XVimMakeTestCase(issue_770_text, 0, 0, @"r u", issue_770_text, 0, 0), // Issue #770 + XVimMakeTestCase(issue_770_text, 1, 0, @"r", issue_770_newline_result, 2, 0), // Issue #770 + XVimMakeTestCase(issue_770_text, 0, 0, @"Rbbbb", issue_770_replace_multichar_result, 4, 0), // Issue #770 + XVimMakeTestCase(issue_770_eol_text, 3, 0, @"rkj", issue_770_eol_result, 4, 0), // Issue #770 + XVimMakeTestCase(issue_776_text, 0, 0, @"O", issue_776_result, 0, 0), // Issue #776 crash XVimMakeTestCase(issue_805_text, 33, 0, @"dd", issue_805_result, 0, 0), // Issue #805 XVimMakeTestCase(issue_809_a_text, 5, 0, @"dw", issue_809_a_result, 4, 0), diff --git a/XVim/XVimKeyStroke.h b/XVim/XVimKeyStroke.h index bc5e1ed4..bef5a6e6 100644 --- a/XVim/XVimKeyStroke.h +++ b/XVim/XVimKeyStroke.h @@ -29,6 +29,7 @@ NSString* XVimKeyNotationFromXVimString(XVimString* string); @property unsigned char modifier; @property (nonatomic, readonly) BOOL isNumeric; @property (nonatomic, readonly) BOOL isPrintable; +@property (nonatomic, readonly) BOOL isWhitespace; - (id)initWithCharacter:(unichar)c modifier:(unsigned char)mod; diff --git a/XVim/XVimKeyStroke.m b/XVim/XVimKeyStroke.m index f69425f2..106ea6e0 100644 --- a/XVim/XVimKeyStroke.m +++ b/XVim/XVimKeyStroke.m @@ -323,6 +323,13 @@ NS_INLINE BOOL isPrintable(unichar c) return !isNSFunctionKey(c) && iswprint_l(c, s_locale); } +NS_INLINE BOOL isWhitespace(unichar c) +{ + init_maps(); + + return !isNSFunctionKey(c) && iswspace_l(c, s_locale); +} + NS_INLINE BOOL isValidKey(NSString *key) { init_maps(); @@ -620,6 +627,11 @@ - (BOOL)isPrintable return !_modifier && isPrintable(_character); } +- (BOOL)isWhitespace +{ + return !_modifier && isWhitespace(_character); +} + - (NSString*)keyNotation{ NSMutableString *keyStr = [[NSMutableString alloc] init]; unichar charcode = _character; diff --git a/XVim/XVimReplaceEvaluator.m b/XVim/XVimReplaceEvaluator.m index a5564a12..67a2ffec 100644 --- a/XVim/XVimReplaceEvaluator.m +++ b/XVim/XVimReplaceEvaluator.m @@ -90,15 +90,31 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ // Here we pass the key input to original text view. // The input coming to this method is already handled by "Input Method" // and the input maight be non ascii like 'あ' - if (self.oneCharMode || keyStroke.isPrintable) { - if (!keyStroke.isPrintable) { - nextEvaluator = [XVimEvaluator invalidEvaluator]; - } else if (![self.sourceView xvim_replaceCharacters:keyStroke.character count:1]) { - nextEvaluator = [XVimEvaluator invalidEvaluator]; - } else if (self.oneCharMode) { - nextEvaluator = nil; + BOOL relayEvent = NO; + BOOL newlinePressed = NO; + unichar replaceWith = keyStroke.character; + + // injecting a CR right into the sourceView causes issues + if (replaceWith == '\r') { + replaceWith = '\n'; + newlinePressed = YES; + } + + if (!keyStroke.isPrintable && !keyStroke.isWhitespace) { + nextEvaluator = [XVimEvaluator invalidEvaluator]; + } else if (!newlinePressed && ![self.sourceView xvim_replaceCharacters:replaceWith count:1]) { + nextEvaluator = [XVimEvaluator invalidEvaluator]; + } else if (self.oneCharMode) { + if (newlinePressed) { + relayEvent = YES; } - } else { + nextEvaluator = nil; + } + if (newlinePressed) { + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + [self.sourceView xvim_delete:m andYank:NO]; + } + if (relayEvent) { NSEvent *event = [keyStroke toEventwithWindowNumber:0 context:nil]; [self.sourceView interpretKeyEvents:[NSArray arrayWithObject:event]]; }