Repository: spyder-ide/spyder

Issue:
Bug: Replace Selection changes double backslashes to single backslashes
## Description of your problem

When doing a Replace in a selection that has a string with double backslashes, the Replace command will change the double backslashes to single backslashes even though something completely different is being replaced.

**What steps will reproduce the problem?**

1. Example code:
```
a = 1
b = 2
c = a+b
s = "C:\\Test\\Test2\\output.txt"
```

2. Mark the example code and replace selection `a` with `x`
3. The output will be this:
```
x = 1
b = 2
c = x+b
s = "E:\Test\Test2\output.txt"
```

**What is the expected output?**
```
x = 1
b = 2
c = x+b
s = "E:\\Test\\Test2\\output.txt"
```

**Please provide any additional information below**

It only happens on Replace selection, not when doing Replace all.

## Versions and main components

* Spyder Version: 3.2.6
* Python Version: 3.6.4
* Qt Version: 5.6.2
* PyQt Version: 5.6
* Operating system: Windows 10


## Dependencies

IPython >=4.0    :  6.2.1 (OK)
cython >=0.21    :  0.27.3 (OK)
jedi >=0.9.0     :  0.11.1 (OK)
nbconvert >=4.0  :  5.3.1 (OK)
numpy >=1.7      :  1.13.3 (OK)
pandas >=0.13.1  :  0.22.0 (OK)
pycodestyle >=2.3:  2.3.1 (OK)
pyflakes >=0.6.0 :  1.6.0 (OK)
pygments >=2.0   :  2.2.0 (OK)
pylint >=0.25    :  1.8.1 (OK)
qtconsole >=4.2.0:  4.3.1 (OK)
rope >=0.9.4     :  0.10.7 (OK)
sphinx >=0.6.6   :  1.6.6 (OK)
sympy >=0.7.3    :  1.1.1 (OK)




Discussion:
Thanks for reporting. We'll take a look at this in a future release.
@ccordoba12 Likely related to #5494 and the associated #5555 ; that apparently fixes other escaped cases but not escaping the escape char itself.
This becomes even more of a problem considering raw strings. While adding \ to the current reg-exp would solve this particular issue, it will still break on lines like:

```
b = r'\a\v\b'
```

Now, one may think that since all the escaped characters in the reg-exp are non-letters one can simply add all letters (and numbers). However, in e.g. SymPy, there is LaTeX printing, so a line in the editor may very well look like

```
r'\{7 \& 8\}'
```

If it wasn't for the word option, one could simply use subs instead for queries that are not regular expressions. Right now, I do not really have any idea how to deal with it...

Resolving patch:
diff --git a/spyder/widgets/findreplace.py b/spyder/widgets/findreplace.py
--- a/spyder/widgets/findreplace.py
+++ b/spyder/widgets/findreplace.py
@@ -553,7 +553,7 @@ def replace_find_selection(self, focus_replace_text=False):
                 pattern = search_text
             else:
                 pattern = re.escape(search_text)
-                replace_text = re.escape(replace_text)
+                replace_text = replace_text.replace('\\', r'\\')
             if words:  # match whole words only
                 pattern = r'\b{pattern}\b'.format(pattern=pattern)
 
@@ -573,8 +573,6 @@ def replace_find_selection(self, focus_replace_text=False):
                 cursor = self.editor.textCursor()
                 cursor.beginEditBlock()
                 cursor.removeSelectedText()
-                if not self.re_button.isChecked():
-                    replacement = re.sub(r'\\(?![nrtf])(.)', r'\1', replacement)
                 cursor.insertText(replacement)
                 cursor.endEditBlock()
             if focus_replace_text:


Test patch:
diff --git a/spyder/plugins/editor/widgets/tests/test_editor.py b/spyder/plugins/editor/widgets/tests/test_editor.py
--- a/spyder/plugins/editor/widgets/tests/test_editor.py
+++ b/spyder/plugins/editor/widgets/tests/test_editor.py
@@ -424,6 +424,30 @@ def test_selection_escape_characters(editor_find_replace_bot, qtbot):
     assert editor.toPlainText() == expected_new_text
 
 
+def test_selection_backslash(editor_find_replace_bot, qtbot):
+    editor_stack, editor, finder = editor_find_replace_bot
+    expected_new_text = ('spam bacon\n'
+                         'spam sausage\n'
+                         'spam egg\n'
+                         r'a = r"\left\{" + "\\}\\right\n"')
+    text_to_add = 'a = r"\\leeft\\{" + "\\\\}\\\\right\\n"'
+    qtbot.keyClicks(editor, text_to_add)
+
+    finder.show()
+    finder.show_replace()
+    qtbot.keyClicks(finder.search_text, 'leeft')
+    qtbot.keyClicks(finder.replace_text, 'left')
+
+    # Select last line
+    cursor = editor.textCursor()
+    cursor.select(QTextCursor.LineUnderCursor)
+    assert cursor.selection().toPlainText() == text_to_add
+
+    # Replace
+    finder.replace_find_selection()
+    assert editor.toPlainText() == expected_new_text
+
+
 def test_advance_cell(editor_cells_bot):
     editor_stack, editor = editor_cells_bot