diff --git a/Documents/Users/FeatureList.md b/Documents/Users/FeatureList.md index 7ece9f6f..ad1c1ba8 100644 --- a/Documents/Users/FeatureList.md +++ b/Documents/Users/FeatureList.md @@ -53,6 +53,12 @@ J Normal mode: >, >>, <, << +## Filter + +Normal mode: == + +Visual mode: = + ## Case change operations Normal mode: ~, gu, gU, g~ diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m index 5a2d4dd6..c2a41ec7 100644 --- a/XVim/NSTextView+VimOperation.m +++ b/XVim/NSTextView+VimOperation.m @@ -789,29 +789,31 @@ - (void)xvim_filter:(XVimMotion*)motion{ if( self.insertionPoint == 0 && [[self xvim_string] length] == 0 ){ return ; } - - NSUInteger insertionAfterFilter = self.insertionPoint; - NSRange filterRange; - if( self.selectionMode == XVIM_VISUAL_NONE ){ - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( to.end == NSNotFound ){ - return; + + NSArray* ranges = [self xvim_selectedRanges]; + self.insertionPoint = [[ranges objectAtIndex:0] rangeValue].location; + NSUInteger insertionAfterFilter = [self.textStorage firstNonblankInLine:self.insertionPoint]; + + for( NSValue* val in ranges){ + NSRange filterRange = val.rangeValue; + NSString *lineString = [[self xvim_string] substringWithRange:filterRange]; + NSRange whiteSpaceRange = [lineString rangeOfString:@"^\\s*" options:NSRegularExpressionSearch]; + if (whiteSpaceRange.length == filterRange.length) { + whiteSpaceRange.length = whiteSpaceRange.length - 1; + lineString = [lineString stringByReplacingCharactersInRange:whiteSpaceRange withString:@""]; + whiteSpaceRange.location = filterRange.location + whiteSpaceRange.location; + [self insertText:lineString replacementRange:filterRange]; + filterRange.length = filterRange.length - whiteSpaceRange.length; + } + else { + [self xvim_indentCharacterRange: filterRange]; } - filterRange = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:LINEWISE]; - }else{ - insertionAfterFilter = [[[self xvim_selectedRanges] lastObject] rangeValue].location; - NSUInteger start = [[[self xvim_selectedRanges] objectAtIndex:0] rangeValue].location; - NSRange lastSelection = [[[self xvim_selectedRanges] lastObject] rangeValue]; - NSUInteger end = lastSelection.location + lastSelection.length - 1; - filterRange = NSMakeRange(start, end-start+1); } - - [self xvim_indentCharacterRange: filterRange]; + [self xvim_moveCursor:insertionAfterFilter preserveColumn:NO]; [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; } - - (void)xvim_shiftRight:(XVimMotion*)motion{ [self xvim_shfit:motion right:YES]; } @@ -1556,18 +1558,20 @@ - (NSArray*)xvim_selectedRanges{ [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(selectionStart,selectionEnd-selectionStart+1)]]; } }else if(self.selectionMode == XVIM_VISUAL_LINE ){ - NSUInteger min = MIN(self.insertionPoint,self.selectionBegin); - NSUInteger max = MAX(self.insertionPoint,self.selectionBegin); - selectionStart = [self.textStorage beginningOfLine:min]; - selectionEnd = [self.textStorage endOfLine:max]; - if( [self.textStorage isEOF:selectionStart] ){ - // EOF can not be selected - [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(selectionStart,0)]]; - }else if( [self.textStorage isEOF:selectionEnd] ){ - selectionEnd--; - [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(selectionStart,selectionEnd-selectionStart+1)]]; - }else{ - [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(selectionStart,selectionEnd-selectionStart+1)]]; + NSUInteger top = MIN( [self.textStorage lineNumber:self.insertionPoint], [self.textStorage lineNumber:self.selectionBegin] ); + NSUInteger bottom = MAX( [self.textStorage lineNumber:self.insertionPoint], [self.textStorage lineNumber:self.selectionBegin] ); + for( NSUInteger i = 0; i < bottom-top+1 ; i++ ){ + selectionStart = [self.textStorage positionAtLineNumber:top+i column:0]; + selectionEnd = [self.textStorage positionAtLineNumber:top+i column:[self.textStorage numberOfLines]]; + if( [self.textStorage isEOF:selectionStart] || [self.textStorage isEOL:selectionStart]){ + // EOF or EOL can not be selected + [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(selectionStart,0)]]; // 0 means No selection. This information is important and used in operators like 'delete' + }else if( [self.textStorage isEOF:selectionEnd] || [self.textStorage isEOL:selectionEnd]){ + selectionEnd--; + [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(selectionStart,selectionEnd-selectionStart+1)]]; + }else{ + [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(selectionStart,selectionEnd-selectionStart+1)]]; + } } }else if( self.selectionMode == XVIM_VISUAL_BLOCK){ // Define the block as a rect by line and column number diff --git a/XVim/Test/XVimTester+Operator.m b/XVim/Test/XVimTester+Operator.m index 6af6298b..162f2a12 100644 --- a/XVim/Test/XVimTester+Operator.m +++ b/XVim/Test/XVimTester+Operator.m @@ -55,7 +55,16 @@ - (NSArray*)operator_testcases{ @"ddd\n" // 52 @"eee\n" // 56 @"fff\n"; // 60 - + + static NSString* text7 = @"aaa\n" // 0 (index of each WORD) + @"{\n" // 4 + @" bbb\n" // 6 + @"}\n"; // 18 + + static NSString* text8 = @" \n"; // 0 (index of each WORD) + + static NSString* text9 = @"\t\t\n"; // 0 (index of each WORD) + static NSString* a_result = @"aAa bbXXXb ccc\n"; static NSString* a_result2 = @"aAa bbXXXXXXXXXb ccc\n"; static NSString* a_result3 = @"aXXXaa\n" @@ -304,7 +313,14 @@ - (NSArray*)operator_testcases{ @" ddd\n" // 28 @" eee\n" // 38 @" fff"; // 46 - + + static NSString* filter_result0 = @"aaa\n" // 0 (index of each WORD) + @"{\n" // 4 + @" bbb\n" // 6 + @"}\n"; // 14 + + static NSString* filter_result1 = @"\n"; // 0 (index of each WORD) + return [NSArray arrayWithObjects: // All changes/insertions must be repeated by dot(.) // All insertions must set hat(^) mark @@ -485,9 +501,12 @@ - (NSArray*)operator_testcases{ XVimMakeTestCase(text5, 1, 0, @"2<