diff --git a/src/search.js b/src/search.js index 613560a8c5..c7549fafb4 100644 --- a/src/search.js +++ b/src/search.js @@ -116,8 +116,9 @@ class Search { row = row + len - 2; } } else { - for (var matches, i = 0, lng = lines.length; i < lng; i++) { + for (var matches, i = 0; i < lines.length; i++) { if (this.$isMultilineSearch(options)) { + var lng = lines.length - 1; matches = this.$multiLineForward(session, re, i, lng); if (matches) { var end_row = matches.endRow <= lng ? matches.endRow - 1 : lng; @@ -162,6 +163,8 @@ class Search { DollarSign: 36, Ampersand: 38, Digit0: 48, + Digit1: 49, + Digit9: 57, Backslash: 92, n: 110, t: 116 @@ -175,21 +178,22 @@ class Search { i++; if (i >= len) { // string ends with a \ + replacement += "\\"; break; } var nextChCode = replaceString.charCodeAt(i); switch (nextChCode) { case CharCode.Backslash: // \\ => inserts a "\" - replacement = '\\'; + replacement += "\\"; break; case CharCode.n: // \n => inserts a LF - replacement= '\n'; + replacement += "\n"; break; case CharCode.t: // \t => inserts a TAB - replacement = '\t'; + replacement += "\t"; break; } continue; @@ -200,21 +204,29 @@ class Search { i++; if (i >= len) { // string ends with a $ + replacement += "$"; break; } const nextChCode = replaceString.charCodeAt(i); if (nextChCode === CharCode.DollarSign) { // $$ => inserts a "$" - replacement = '$'; + replacement += "$$"; continue; } - if (nextChCode === CharCode.Digit0) { + if (nextChCode === CharCode.Digit0 || nextChCode === CharCode.Ampersand) { // replace $0 to $&, making it compatible with JavaScript // $0 and $& => inserts the matched substring. - replaceString = replaceString.replace(/\$0/, '$$&'); + replacement += "$&"; + continue; + } + if (CharCode.Digit1 <= nextChCode && nextChCode <= CharCode.Digit9) { + // $n + replacement += "$" + replaceString[i]; continue; } } + + replacement += replaceString[i]; } return replacement || replaceString; } diff --git a/src/search_test.js b/src/search_test.js index 0ac18959e7..c6caf0e4b3 100644 --- a/src/search_test.js +++ b/src/search_test.js @@ -153,7 +153,7 @@ module.exports = { var range = search.find(session); assert.position(range.start, 0, 12); assert.position(range.end, 0, 13); - + search.set({ needle: "ab\\{2}" }); range = search.find(session); assert.position(range.start, 1, 8); @@ -369,8 +369,8 @@ module.exports = { assert.position(ranges[1].start, 2, 1); assert.position(ranges[1].end, 2, 3); }, - - + + "test: find all multiline matches" : function() { var session = new EditSession(["juhu", "juhu", "juhu", "juhu"]); @@ -420,13 +420,14 @@ module.exports = { "test: replace with RegExp match and capture groups" : function() { var search = new Search().set({ - needle: "ab(\\d\\d)", + needle: "ab((\\d)\\d)", regExp: true }); assert.equal(search.replace("ab12", "cd$1"), "cd12"); + assert.equal(search.replace("ab56", "pr$17$2"), "pr5675"); assert.equal(search.replace("ab12", "-$&-"), "-ab12-"); - assert.equal(search.replace("ab12", "$$"), "$"); + assert.equal(search.replace("ab12", "_$0_"), "_ab12_"); }, "test: replace() should correctly handle $$ in the replacement string": function () { @@ -435,11 +436,11 @@ module.exports = { }); // Expecting $$ to be preserved in the output + assert.equal(search.replace("example", "$test"), "$test"); assert.equal(search.replace("example", "$$test"), "$$test"); - - // Expecting $$$$ to be preserved as $$$$ + assert.equal(search.replace("example", "$$$test"), "$$$test"); assert.equal(search.replace("example", "$$$$test"), "$$$$test"); - + search.set({ regExp: true, needle: "(example)" @@ -448,11 +449,40 @@ module.exports = { // Tests that $1 is replaced by the text that matches the capturing group. assert.equal(search.replace("example", "$1test"), "exampletest"); - search.set({regExp: false}); + assert.equal(search.replace("example", "$"), "$"); + assert.equal(search.replace("example", "$$"), "$"); + assert.equal(search.replace("example", "$$$"), "$$"); + assert.equal(search.replace("example", "$$$$"), "$$"); + assert.equal(search.replace("example", "$$$$$"), "$$$"); + assert.equal(search.replace("example", "$$$$$$"), "$$$"); + assert.equal(search.replace("example", "$$$$$$$"), "$$$$"); + + search.set({ regExp: false }); // Tests that without regular expression, "$1test" is treated as a literal string with $ escape. assert.equal(search.replace("(example)", "$1test"), "$1test"); }, + "test: replace() should correctly handle \\\\ in the replacement string": function () { + var search = new Search().set({ + needle: "example" + }); + + // Expecting \\ to be preserved in the output + assert.equal(search.replace("example", "\\test"), "\\test"); + assert.equal(search.replace("example", "\\\\test"), "\\\\test"); + assert.equal(search.replace("example", "\\\\\\test"), "\\\\\\test"); + + search.set({ regExp: true }); + + assert.equal(search.replace("example", "\\"), "\\"); + assert.equal(search.replace("example", "\\\\"), "\\"); + assert.equal(search.replace("example", "\\\\\\"), "\\\\"); + assert.equal(search.replace("example", "\\\\\\\\"), "\\\\"); + assert.equal(search.replace("example", "\\\\\\\\\\"), "\\\\\\"); + assert.equal(search.replace("example", "\\\\\\\\\\\\"), "\\\\\\"); + assert.equal(search.replace("example", "\\\\\\\\\\\\\\"), "\\\\\\\\"); + }, + "test: find all using regular expresion containing $" : function() { var session = new EditSession(["a", " b", "c ", "d"]); @@ -518,7 +548,7 @@ module.exports = { "test: find next empty range" : function() { var session = new EditSession("foo foobar foo"); var editor = new Editor(new MockRenderer(), session); - + var options = { needle: "o*", wrap: true, @@ -526,7 +556,7 @@ module.exports = { backwards: false }; var positions = [4, 5.2, 7, 8, 9, 10, 11, 12.2, 14, 0, 1.2, 3]; - + session.selection.moveCursorTo(0, 3); for (var i = 0; i < positions.length; i++) { editor.find(options); @@ -545,10 +575,11 @@ module.exports = { assert.equal(start + 0.1 * len, positions[i]); } }, + "test: repeating text": function() { var session = new EditSession("tttttt\ntttttt\ntttttt\ntttttt\ntttttt\ntttttt"); var editor = new Editor(new MockRenderer(), session); - + var options = { needle: "^", wrap: true, @@ -560,15 +591,15 @@ module.exports = { var range = editor.selection.getRange(); assert.range(range, sl, sc, el, ec); } - + session.selection.moveCursorTo(1, 3); check(2, 0, 2, 0); - + options.needle = "tttt\ntttt"; check(2, 2, 3, 4); check(4, 2, 5, 4); check(0, 2, 1, 4); - + options.backwards = true; check(4, 2, 5, 4); check(2, 2, 3, 4); @@ -601,6 +632,72 @@ module.exports = { assert.position(ranges[1].end, 1, 39); assert.position(ranges[2].start, 2, 4); assert.position(ranges[2].end, 2, 7); + }, + + "test: find all line breaks (\\r\\n, \\n) using regular expression" : function() { + var session = new EditSession('\nfunction foo(items, nada) {\n for (var i=0; i