Repository: spyder-ide/spyder

Issue:
Case sensitive replace does not respect case
Performing a Replace ("Search -> Replace Text") with "Case Sensitive" selected does not respect case, i.e. text is matched for any combination of upper and lower case characters and replaced with the "Replace with:" text.

To reproduce:

1.  From the menu, select "File -> New file...".
2. Enter the following text: """
Sample text to replace
"""
3. From the menu, select "Search -> Replace Text"
4. Enter the text "sample" in text to look for
5. Enter the text "Should not appear" in text to replace with
6. Ensure "Case Sensitive" is selected / checked / active.
7. Click "Replace / Find"

Expected:  Text should remain as is because "sample" does not match "Sample" in a case sensitive manner.

Instead: The text "sample" is replaced with the replace text "Should not appear".

## Versions and main components

* Spyder Version: Spyder 3.1.2 
* Python Version: Python 3.6.0 32bits, Qt 5.7.1, PyQt5 5.7.1 on Windows 
* Operating system: Windoes

## Dependencies

jedi >=0.8.1     :  0.9.0 (OK)
matplotlib >=1.0 :  None (NOK)
nbconvert >=4.0  :  5.1.1 (OK)
numpy >=1.7      :  None (NOK)
pandas >=0.13.1  :  None (NOK)
pep8 >=0.6       :  1.7.0 (OK)
pyflakes >=0.6.0 :  1.5.0 (OK)
pygments >=2.0   :  2.2.0 (OK)
pylint >=0.25    :  1.6.5 (OK)
qtconsole >=4.2.0:  4.2.1 (OK)
rope >=0.9.4     :  0.9.4-1 (OK)
sphinx >=0.6.6   :  1.5.2 (OK)
sympy >=0.7.3    :  None (NOK)



Discussion:
Yup, I have the same problem. I was about to open a 2nd ticket with the same problem as the case sensitivity thing surfaced for me while testing this [bug](https://github.com/spyder-ide/spyder/issues/4005)

BTW I'm curious to know if Find in Files works for you or if you also have the same issue. If you do the bugs might be related.

Resolving patch:
diff --git a/spyder/utils/fixtures.py b/spyder/utils/fixtures.py
--- a/spyder/utils/fixtures.py
+++ b/spyder/utils/fixtures.py
@@ -11,11 +11,18 @@
 # Standard library imports
 import shutil
 import tempfile
+try:
+    from unittest.mock import Mock
+except ImportError:
+    from mock import Mock # Python 2
 
 # Third party imports
 import pytest
 
 # Local imports
+# Local imports
+from spyder.widgets.editor import EditorStack
+from spyder.widgets.findreplace import FindReplace
 from spyder.config.user import UserConfig
 from spyder.config.main import CONF_VERSION, DEFAULTS
 
@@ -41,3 +48,22 @@ def fin():
 
     request.addfinalizer(fin)
     return CONF
+
+@pytest.fixture
+def setup_editor(qtbot):
+    """
+    Set up EditorStack with CodeEditor containing some Python code.
+    The cursor is at the empty line below the code.
+    Returns tuple with EditorStack and CodeEditor.
+    """
+    text = ('a = 1\n'
+            'print(a)\n'
+            '\n'
+            'x = 2')  # a newline is added at end
+    editorStack = EditorStack(None, [])
+    editorStack.set_introspector(Mock())
+    editorStack.set_find_widget(FindReplace(editorStack))
+    editorStack.set_io_actions(Mock(), Mock(), Mock(), Mock())
+    finfo = editorStack.new('foo.py', 'utf-8', text)
+    qtbot.addWidget(editorStack)
+    return editorStack, finfo.editor
diff --git a/spyder/widgets/mixins.py b/spyder/widgets/mixins.py
--- a/spyder/widgets/mixins.py
+++ b/spyder/widgets/mixins.py
@@ -477,6 +477,8 @@ def find_text(self, text, changed=True, forward=True, case=False,
         findflag = QTextDocument.FindFlag()
         if not forward:
             findflag = findflag | QTextDocument.FindBackward
+        if case:
+            findflag = findflag | QTextDocument.FindCaseSensitively
         moves = [QTextCursor.NoMove]
         if forward:
             moves += [QTextCursor.NextWord, QTextCursor.Start]


Test patch:
diff --git a/spyder/widgets/tests/test_editor.py b/spyder/widgets/tests/test_editor.py
--- a/spyder/widgets/tests/test_editor.py
+++ b/spyder/widgets/tests/test_editor.py
@@ -8,35 +8,11 @@
 Tests for editor.py
 """
 
-# Standard library imports
-try:
-    from unittest.mock import Mock
-except ImportError:
-    from mock import Mock # Python 2
-
 # Third party imports
 import pytest
 
 # Local imports
-from spyder.widgets.editor import EditorStack
-
-def setup_editor(qtbot):
-    """
-    Set up EditorStack with CodeEditor containing some Python code.
-    The cursor is at the empty line below the code.
-    Returns tuple with EditorStack and CodeEditor.
-    """
-    text = ('a = 1\n'
-            'print(a)\n'
-            '\n'
-            'x = 2')  # a newline is added at end
-    editorStack = EditorStack(None, [])
-    editorStack.set_introspector(Mock())
-    editorStack.set_find_widget(Mock())
-    editorStack.set_io_actions(Mock(), Mock(), Mock(), Mock())
-    finfo = editorStack.new('foo.py', 'utf-8', text)
-    qtbot.addWidget(editorStack)
-    return editorStack, finfo.editor
+from spyder.utils.fixtures import setup_editor
 
 def test_run_top_line(qtbot):
     editorStack, editor = setup_editor(qtbot)
@@ -84,6 +60,20 @@ def test_run_last_line_when_nonempty(qtbot):
     assert editor.toPlainText() == expected_new_text # check blank line got added
     assert editor.get_cursor_line_column() == (4, 0) # check cursor moves down
 
-    
+def test_find_replace_case_sensitive(qtbot):
+    editorStack, editor = setup_editor(qtbot)
+    editorStack.find_widget.case_button.setChecked(True)
+    text = ' test \nTEST \nTest \ntesT '
+    editor.set_text(text)
+    editorStack.find_widget.search_text.add_text('test')
+    editorStack.find_widget.replace_text.add_text('pass')
+    editorStack.find_widget.replace_find()
+    editorStack.find_widget.replace_find()
+    editorStack.find_widget.replace_find()
+    editorStack.find_widget.replace_find()
+    editor_text = editor.toPlainText()
+    assert editor_text == ' pass \nTEST \nTest \ntesT '
+
+
 if __name__ == "__main__":
     pytest.main()