From eaa8a6bce3c016dbeebe515d66e9b1a92084f244 Mon Sep 17 00:00:00 2001 From: ficristo Date: Fri, 21 Aug 2015 16:46:15 +0200 Subject: [PATCH] Added new line and tab as replacement in case of regex search --- src/search/FindInFilesUI.js | 5 ++ src/search/FindReplace.js | 2 +- src/search/FindUtils.js | 43 ++++++++++++- test/spec/FindInFiles-test.js | 31 ++++++++++ .../replace-escaped-chars/bar.txt | 3 + .../replace-escaped-chars/css/foo.css | 13 ++++ .../replace-escaped-chars/foo.html | 22 +++++++ .../replace-escaped-chars/foo.js | 13 ++++ .../replace-unescaped-chars/bar.txt | 3 + .../replace-unescaped-chars/css/foo.css | 13 ++++ .../replace-unescaped-chars/foo.html | 22 +++++++ .../replace-unescaped-chars/foo.js | 13 ++++ test/spec/FindReplace-test.js | 61 +++++++++++++++++++ 13 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 test/spec/FindReplace-known-goods/replace-escaped-chars/bar.txt create mode 100644 test/spec/FindReplace-known-goods/replace-escaped-chars/css/foo.css create mode 100644 test/spec/FindReplace-known-goods/replace-escaped-chars/foo.html create mode 100644 test/spec/FindReplace-known-goods/replace-escaped-chars/foo.js create mode 100644 test/spec/FindReplace-known-goods/replace-unescaped-chars/bar.txt create mode 100644 test/spec/FindReplace-known-goods/replace-unescaped-chars/css/foo.css create mode 100644 test/spec/FindReplace-known-goods/replace-unescaped-chars/foo.html create mode 100644 test/spec/FindReplace-known-goods/replace-unescaped-chars/foo.js diff --git a/src/search/FindInFilesUI.js b/src/search/FindInFilesUI.js index a8a7e8e4dcb..a9bf07941c2 100644 --- a/src/search/FindInFilesUI.js +++ b/src/search/FindInFilesUI.js @@ -201,6 +201,11 @@ define(function (require, exports, module) { // Single-file scope: don't use any file filters filter = null; } + + if (queryInfo.isRegexp) { + replaceText = FindUtils.parseString(replaceText); + } + searchAndShowResults(queryInfo, scope, filter, replaceText, candidateFilesPromise); } return null; diff --git a/src/search/FindReplace.js b/src/search/FindReplace.js index 435bab79ae2..2d0bd862803 100644 --- a/src/search/FindReplace.js +++ b/src/search/FindReplace.js @@ -685,7 +685,7 @@ define(function (require, exports, module) { // Delegate to Replace in Files. FindInFilesUI.searchAndShowResults(state.queryInfo, editor.document.file, null, replaceText); } else { - cm.replaceSelection(state.queryInfo.isRegexp ? FindUtils.parseDollars(replaceText, state.lastMatch) : replaceText); + cm.replaceSelection(state.queryInfo.isRegexp ? FindUtils.parseRegexp(replaceText, state.lastMatch) : replaceText); updateResultSet(editor); // we updated the text, so result count & tickmarks must be refreshed diff --git a/src/search/FindUtils.js b/src/search/FindUtils.js index d56b69768fa..a5ddb6cc536 100644 --- a/src/search/FindUtils.js +++ b/src/search/FindUtils.js @@ -105,6 +105,43 @@ define(function (require, exports, module) { return replaceWith; } + /** + * Parses a string and replace the \r, \n and \t sequences of characters + * with a character based on the regex meaning of these sequences. + * \n => Line Feed + * \r => Carriage Return + * \t => Tab + * + * @param {string} string - The string to parse. + * @return {string} The replaced text. + */ + function parseString(string) { + return string.replace(/\\(.)/g, function (match, ch) { + if (ch === "n") { + return "\n"; + } + if (ch === "r") { + return "\r"; + } + if (ch === "t") { + return "\t"; + } + return ch; + }); + } + + /** + * Parses a string through parseDollars and parseString functions. + * + * @param {string} string - The string to parse. + * @param {Object} match - The match data from the regexp. + * @return {string} The replaced text. + */ + function parseRegexp(string, match) { + var str = parseDollars(string, match); + return parseString(str); + } + /** * Does a set of replacements in a single document in memory. * @param {!Document} doc The document to do the replacements in. @@ -129,7 +166,7 @@ define(function (require, exports, module) { doc.batchOperation(function () { matchInfo.matches.reverse().forEach(function (match) { if (match.isChecked) { - doc.replaceRange(isRegexp ? parseDollars(replaceText, match.result) : replaceText, match.start, match.end); + doc.replaceRange(isRegexp ? parseRegexp(replaceText, match.result) : replaceText, match.start, match.end); } }); }); @@ -161,7 +198,7 @@ define(function (require, exports, module) { matchInfo.matches.forEach(function (match) { if (match.isChecked) { result.push(contents.slice(lastIndex, match.startOffset)); - result.push(isRegexp ? parseDollars(replaceText, match.result) : replaceText); + result.push(isRegexp ? parseRegexp(replaceText, match.result) : replaceText); lastIndex = match.endOffset; } }); @@ -523,6 +560,8 @@ define(function (require, exports, module) { } exports.parseDollars = parseDollars; + exports.parseString = parseString; + exports.parseRegexp = parseRegexp; exports.hasCheckedMatches = hasCheckedMatches; exports.performReplacements = performReplacements; exports.labelForScope = labelForScope; diff --git a/test/spec/FindInFiles-test.js b/test/spec/FindInFiles-test.js index 8ddc4fbe0e0..e801861dcf2 100644 --- a/test/spec/FindInFiles-test.js +++ b/test/spec/FindInFiles-test.js @@ -1308,6 +1308,37 @@ define(function (require, exports, module) { }); }); + it("should replace instances of a string in a project with escaped chars", function () { + openTestProjectCopy(defaultSourcePath); + doBasicTest({ + queryInfo: {query: "bar"}, + numMatches: 7, + replaceText: "\\r\\n\\t", + knownGoodFolder: "replace-escaped-chars" + }); + }); + + it("should replace instances of a string in a project with escaped chars in regex mode", function () { + openTestProjectCopy(defaultSourcePath); + doBasicTest({ + queryInfo: {query: "bar", isRegexp: true}, + numMatches: 7, + replaceText: "\\\\r\\\\n\\\\t", + knownGoodFolder: "replace-escaped-chars" + }); + }); + + it("should replace instances of a regexp in a project with unescaped chars in regex mode", function () { + openTestProjectCopy(defaultSourcePath, FileUtils.LINE_ENDINGS_LF); + doBasicTest({ + queryInfo: {query: "\n", isRegexp: true}, + numMatches: 51, + replaceText: "\\t\\r\\n", + knownGoodFolder: "replace-unescaped-chars", + lineEndings: FileUtils.LINE_ENDINGS_CRLF + }); + }); + it("should replace instances of a string in a project respecting CRLF line endings", function () { openTestProjectCopy(defaultSourcePath, FileUtils.LINE_ENDINGS_CRLF); doBasicTest({ diff --git a/test/spec/FindReplace-known-goods/replace-escaped-chars/bar.txt b/test/spec/FindReplace-known-goods/replace-escaped-chars/bar.txt new file mode 100644 index 00000000000..78ae212e4c0 --- /dev/null +++ b/test/spec/FindReplace-known-goods/replace-escaped-chars/bar.txt @@ -0,0 +1,3 @@ +\r\n\t.txt file + +This file should *not* show up in certain searches diff --git a/test/spec/FindReplace-known-goods/replace-escaped-chars/css/foo.css b/test/spec/FindReplace-known-goods/replace-escaped-chars/css/foo.css new file mode 100644 index 00000000000..8ea41871d22 --- /dev/null +++ b/test/spec/FindReplace-known-goods/replace-escaped-chars/css/foo.css @@ -0,0 +1,13 @@ +/* foo.css */ +body { + margin: 0; +} +h1, footer { + padding: 2px auto; +} +ul.foo { + list-style: none; +} +.\r\n\t { + font-size: large; +} diff --git a/test/spec/FindReplace-known-goods/replace-escaped-chars/foo.html b/test/spec/FindReplace-known-goods/replace-escaped-chars/foo.html new file mode 100644 index 00000000000..89f423e2c33 --- /dev/null +++ b/test/spec/FindReplace-known-goods/replace-escaped-chars/foo.html @@ -0,0 +1,22 @@ + + + + +Foo + + + + + + +

Foo

+

Intro to foo

+ +

It's all about the \r\n\t

+ + + diff --git a/test/spec/FindReplace-known-goods/replace-escaped-chars/foo.js b/test/spec/FindReplace-known-goods/replace-escaped-chars/foo.js new file mode 100644 index 00000000000..b52621fb488 --- /dev/null +++ b/test/spec/FindReplace-known-goods/replace-escaped-chars/foo.js @@ -0,0 +1,13 @@ +/* Test comment */ +define(function (require, exports, module) { + var Foo = require("modules/Foo"), + \r\n\t = require("modules/\r\n\t"), + Baz = require("modules/Baz"); + + function callFoo() { + + foo(); + + } + +} diff --git a/test/spec/FindReplace-known-goods/replace-unescaped-chars/bar.txt b/test/spec/FindReplace-known-goods/replace-unescaped-chars/bar.txt new file mode 100644 index 00000000000..6ee30fb0749 --- /dev/null +++ b/test/spec/FindReplace-known-goods/replace-unescaped-chars/bar.txt @@ -0,0 +1,3 @@ +bar.txt file + +This file should *not* show up in certain searches diff --git a/test/spec/FindReplace-known-goods/replace-unescaped-chars/css/foo.css b/test/spec/FindReplace-known-goods/replace-unescaped-chars/css/foo.css new file mode 100644 index 00000000000..904c6a39429 --- /dev/null +++ b/test/spec/FindReplace-known-goods/replace-unescaped-chars/css/foo.css @@ -0,0 +1,13 @@ +/* foo.css */ +body { + margin: 0; +} +h1, footer { + padding: 2px auto; +} +ul.foo { + list-style: none; +} +.bar { + font-size: large; +} diff --git a/test/spec/FindReplace-known-goods/replace-unescaped-chars/foo.html b/test/spec/FindReplace-known-goods/replace-unescaped-chars/foo.html new file mode 100644 index 00000000000..ae243b22eb3 --- /dev/null +++ b/test/spec/FindReplace-known-goods/replace-unescaped-chars/foo.html @@ -0,0 +1,22 @@ + + + + +Foo + + + + + + +

Foo

+

Intro to foo

+ +

It's all about the bar

+ + + diff --git a/test/spec/FindReplace-known-goods/replace-unescaped-chars/foo.js b/test/spec/FindReplace-known-goods/replace-unescaped-chars/foo.js new file mode 100644 index 00000000000..85b7ab62de8 --- /dev/null +++ b/test/spec/FindReplace-known-goods/replace-unescaped-chars/foo.js @@ -0,0 +1,13 @@ +/* Test comment */ +define(function (require, exports, module) { + var Foo = require("modules/Foo"), + Bar = require("modules/Bar"), + Baz = require("modules/Baz"); + + function callFoo() { + + foo(); + + } + +} diff --git a/test/spec/FindReplace-test.js b/test/spec/FindReplace-test.js index fb73c490f06..db6d862c635 100644 --- a/test/spec/FindReplace-test.js +++ b/test/spec/FindReplace-test.js @@ -1423,6 +1423,67 @@ define(function (require, exports, module) { expect(/_modules\/Foo-Foo\$&/i.test(myEditor.getSelectedText())).toBe(true); }); }); + + it("should replace a string with \\r\\n\\t chars", function () { + runs(function () { + twCommandManager.execute(Commands.CMD_REPLACE); + enterSearchText("Foo"); + enterReplaceText("\\r\\n\\t"); + + var expectedMatch = {start: {line: LINE_FIRST_REQUIRE, ch: 8}, end: {line: LINE_FIRST_REQUIRE, ch: 11}}; + + expectSelection(expectedMatch); + expect(/Foo/.test(myEditor.getSelectedText())).toBe(true); + + expect(tw$("#replace-yes").is(":enabled")).toBe(true); + tw$("#replace-yes").click(); + + myEditor.setSelection({line: LINE_FIRST_REQUIRE, ch: 8}, {line: LINE_FIRST_REQUIRE, ch: 14}); + expect(myEditor.getSelectedText()).toEqual("\\r\\n\\t"); + }); + }); + + it("should replace a string with \\r\\n\\t chars in regex mode", function () { + runs(function () { + twCommandManager.execute(Commands.CMD_REPLACE); + toggleRegexp(true); + enterSearchText("Foo"); + enterReplaceText("\\\\r\\\\n\\\\t"); + + var expectedMatch = {start: {line: LINE_FIRST_REQUIRE, ch: 8}, end: {line: LINE_FIRST_REQUIRE, ch: 11}}; + + expectSelection(expectedMatch); + expect(/Foo/.test(myEditor.getSelectedText())).toBe(true); + + expect(tw$("#replace-yes").is(":enabled")).toBe(true); + tw$("#replace-yes").click(); + + myEditor.setSelection({line: LINE_FIRST_REQUIRE, ch: 8}, {line: LINE_FIRST_REQUIRE, ch: 14}); + expect(myEditor.getSelectedText()).toEqual("\\r\\n\\t"); + }); + }); + + it("should replace a string with a new line and a tab in regex mode", function () { + runs(function () { + twCommandManager.execute(Commands.CMD_REPLACE); + toggleRegexp(true); + enterSearchText("Foo"); + enterReplaceText("\\r\\n\\t"); + + var expectedMatch = {start: {line: LINE_FIRST_REQUIRE, ch: 8}, end: {line: LINE_FIRST_REQUIRE, ch: 11}}; + + expectSelection(expectedMatch); + expect(/Foo/.test(myEditor.getSelectedText())).toBe(true); + + expect(tw$("#replace-yes").is(":enabled")).toBe(true); + tw$("#replace-yes").click(); + + myEditor.setSelection({line: LINE_FIRST_REQUIRE, ch: 4}, {line: LINE_FIRST_REQUIRE + 1, ch: 3}); + // TODO: The test supposes \n line endings. + // Once this will change the code check below should be changed as well. + expect(myEditor.getSelectedText()).toEqual("var \n\t ="); + }); + }); });