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];