diff --git a/Documents/Users/FeatureList.md b/Documents/Users/FeatureList.md index 3c7045dd..0e59b83f 100644 --- a/Documents/Users/FeatureList.md +++ b/Documents/Users/FeatureList.md @@ -137,6 +137,10 @@ The layout is forced to change with Ctrl-w,s or Ctrl-w,v . Regex search is supported using the [ICU regex](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSRegularExpression_Class/Reference/Reference.html) format. +Search and replace using matched string(&) in the replacement text is supported: + +Applying `:%s/foo/&bar/` to `foo bar` gives `foobar bar` + ## Insert mode commands C-y, C-e diff --git a/XVim/Info.plist b/XVim/Info.plist index b53f5ddb..09a6404b 100644 --- a/XVim/Info.plist +++ b/XVim/Info.plist @@ -20,6 +20,7 @@ 1 DVTPlugInCompatibilityUUIDs + ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C AD68E85B-441B-4301-B564-A45E4919A6AD A2E4D43F-41F4-4FB9-BB94-7177011C9AED 37B30044-3B14-46BA-ABAA-F01000C27B63 diff --git a/XVim/Test/XVimTester+Search.m b/XVim/Test/XVimTester+Search.m index d7d9eeca..83f9e145 100644 --- a/XVim/Test/XVimTester+Search.m +++ b/XVim/Test/XVimTester+Search.m @@ -88,6 +88,8 @@ - (NSArray*)search_testcases{ static NSString* replace11_result = @"aaa ddd ccc\n" @"aaa.ddd.ccc\n\n"; + static NSString* replace12_result = @"aaa bbbbb ccc\n" + @"bbbbb ccc ccc\n\n"; return [NSArray arrayWithObjects: // // replace(:s) @@ -107,6 +109,9 @@ - (NSArray*)search_testcases{ XVimMakeTestCase(text6, 0, 0, @"Vj:s/$/fffff/g", replace7_result, 32, 0), // $, two XVimMakeTestCase(text7, 0, 0, @"Vj:s/$/fffff/g", replace8_result, 32, 0), + + XVimMakeTestCase(text7, 0, 0, @":%s/bbb/&bb/g", replace12_result, 19, 0), + XVimMakeTestCase(text7, 0, 0, @":%s/bb/&bb/g", replace12_result, 18, 0), // c, quit XVimMakeTestCase(text6, 0, 0, @"Vj:s/ccc/eeeee/gcq", text6, 8, 0), @@ -205,6 +210,7 @@ - (NSArray*)search_testcases{ // search followed by implicit replace. added to cover https://github.com/XVimProject/XVim/issues/730 XVimMakeTestCase(text8, 0, 0, @":set vimregex/bbb:%s//ddd/g", replace11_result, 19, 0), + nil]; } @end diff --git a/XVim/XVimSearch.m b/XVim/XVimSearch.m index 72e7bac9..c1f0da55 100644 --- a/XVim/XVimSearch.m +++ b/XVim/XVimSearch.m @@ -522,11 +522,11 @@ - (void)substitute:(NSString*)ex_command from:(NSUInteger)from to:(NSUInteger)to } } -- (void)updateStartLocationInWindow:(XVimWindow*)window +- (void)updateStartLocationInWindow:(XVimWindow*)window replacementString:(NSString*)replacementString { // global option on; consider all matches on each line if (self.isGlobal) { - self.replaceStartLocation = self.lastFoundRange.location + self.lastReplacementString.length; + self.replaceStartLocation = self.lastFoundRange.location + replacementString.length; // If search string contained a $, move to the next line if ([self.lastSearchCmd rangeOfString:@"$"].length > 0) { @@ -539,22 +539,37 @@ - (void)updateStartLocationInWindow:(XVimWindow*)window } } -- (void) updateEndLocationInWindow:(XVimWindow*)window +- (void) updateEndLocationInWindow:(XVimWindow*)windo replacementString:(NSString*)replacementString { - self.replaceEndLocation += ([self.lastReplacementString length] - self.lastFoundRange.length); + self.replaceEndLocation += ([replacementString length] - self.lastFoundRange.length); } - (void)replaceCurrentInWindow:(XVimWindow*)window findNext:(BOOL)findNext { NSTextView* srcView = [window sourceView]; + + NSString *textToReplace = [[srcView string] substringWithRange:self.lastFoundRange]; + NSString *replacementString = self.lastReplacementString; + NSArray *splitsOnEscapedAmpersands = [replacementString componentsSeparatedByString:@"\\&"]; + NSMutableArray *stringsToJoin = [[NSMutableArray alloc] init]; + + for (NSString* stringWithoutEscapedAmpersands in splitsOnEscapedAmpersands) { + NSArray *splitsOnAmpersands = [stringWithoutEscapedAmpersands componentsSeparatedByString:@"&"]; + NSString *springsWithMatch = [splitsOnAmpersands componentsJoinedByString:textToReplace]; + [stringsToJoin addObject:springsWithMatch]; + } - [srcView insertText:self.lastReplacementString replacementRange:self.lastFoundRange]; - [srcView xvim_moveCursor:self.lastFoundRange.location + self.lastReplacementString.length preserveColumn:NO]; + if ([stringsToJoin count] > 0) { + replacementString = [stringsToJoin componentsJoinedByString:@"\\&"]; + } + + [srcView insertText:replacementString replacementRange:self.lastFoundRange]; + [srcView xvim_moveCursor:self.lastFoundRange.location + replacementString.length preserveColumn:NO]; self.numReplacements++; if (findNext) { - [self updateStartLocationInWindow:window]; - [self updateEndLocationInWindow:window]; + [self updateStartLocationInWindow:window replacementString:replacementString]; + [self updateEndLocationInWindow:window replacementString:replacementString]; [self findForwardFrom:self.replaceStartLocation to:self.replaceEndLocation inWindow:window]; } else { @@ -578,7 +593,7 @@ - (void)replaceCurrentToEndInWindow:(XVimWindow*)window [srcView insertText:self.lastReplacementString replacementRange:self.lastFoundRange]; self.numReplacements++; - [self updateEndLocationInWindow:window]; + [self updateEndLocationInWindow:window replacementString:self.lastReplacementString]; [self findForwardFrom:self.replaceStartLocation to:self.replaceEndLocation inWindow:window]; } while (self.lastFoundRange.location != NSNotFound); [self showStatusIfDoneInWindow:window];