Repository: spyder-ide/spyder

Issue:
delete (context="editor", name="delete") keyboard shortcut doesn't work
## Description of your problem

**What steps will reproduce the problem?**

1. go to tools > preferences > keyboard shorcuts
2. assign keyboard shortcut for context = "editor", name = "delete".
    I tried at least: meta+x, meta+q, meta+a, ctrl+y, alt+y.
    meta+a and meta+q works as shortcut for start of line (context = "editor")
3. use assign shortcut in text editor
4. nothing changes

**What is the expected output? What do you see instead?**

1. character after cursor will be deleted

**Please provide any additional information below**

I try to use shortcuts similar to emacs, to edit files using blind method for typing. moving finger to delete button doesn't work for it.
No backspace key binding at all - is it some sort of system limitation?

## Versions and main components

* Spyder Version: 3.1.2
* Python Version: 2.7.13 64bits
* Qt Version: 5.6.2
* PyQt Version: PyQt5 5.6 on Linux
* Operating system: ubuntu xenial x86_64 4.4.0-57-generic


## Dependencies

Please go to the menu entry `Help > Optional Dependencies` (or
`Help > Dependencies`), press the button `Copy to clipboard`
and paste the contents below:
jedi >=0.8.1     :  0.9.0 (OK)
matplotlib >=1.0 :  2.0.0 (OK)
nbconvert >=4.0  :  4.2.0 (OK)
numpy >=1.7      :  1.11.3 (OK)
pandas >=0.13.1  :  0.19.2 (OK)
pep8 >=0.6       :  1.7.0 (OK)
psutil >=0.3     :  5.0.1 (OK)
pyflakes >=0.5.0 :  1.5.0 (OK)
pygments >=2.0   :  2.1.3 (OK)
pylint >=0.25    :  1.6.4 (OK)
qtconsole >=4.2.0:  4.2.1 (OK)
rope >=0.9.4     :  0.9.4 (OK)
sphinx >=0.6.6   :  1.5.1 (OK)
sympy >=0.7.3    :  1.0 (OK)



Discussion:
Thanks for reporting. We'll try to solve this one for Spyder 3.2

Resolving patch:
diff --git a/spyder/widgets/sourcecode/codeeditor.py b/spyder/widgets/sourcecode/codeeditor.py
--- a/spyder/widgets/sourcecode/codeeditor.py
+++ b/spyder/widgets/sourcecode/codeeditor.py
@@ -971,9 +971,14 @@ def get_current_object(self):
 
     @Slot()
     def delete(self):
-        """Remove selected text"""
-        if self.has_selected_text():
-            self.remove_selected_text()
+        """Remove selected text or next character."""
+        if not self.has_selected_text():
+            cursor = self.textCursor()
+            position = cursor.position()
+            if not cursor.atEnd():
+                cursor.setPosition(position + 1, QTextCursor.KeepAnchor)
+            self.setTextCursor(cursor)
+        self.remove_selected_text()
 
     #------Find occurrences
     def __find_first(self, text):


Test patch:
diff --git a/spyder/widgets/sourcecode/tests/test_codeeditor.py b/spyder/widgets/sourcecode/tests/test_codeeditor.py
new file mode 100644
--- /dev/null
+++ b/spyder/widgets/sourcecode/tests/test_codeeditor.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © Spyder Project Contributors
+# Licensed under the terms of the MIT License
+#
+
+"""
+Tests for codeeditor.py.
+"""
+
+try:
+    from unittest.mock import Mock
+except ImportError:
+    from mock import Mock  # Python 2
+
+# Third party imports
+import pytest
+from qtpy.QtGui import QTextCursor
+
+# Local imports
+from spyder import version_info
+import spyder.widgets.sourcecode.codeeditor as codeeditor
+
+
+@pytest.fixture
+def code_editor_bot(qtbot):
+    """Create code editor with default Python code."""
+    editor = codeeditor.CodeEditor(parent=None)
+    indent_chars = ' ' * 4
+    tab_stop_width_spaces = 4
+    editor.setup_editor(language='Python', indent_chars=indent_chars,
+                        tab_stop_width_spaces=tab_stop_width_spaces)
+    # Mock the screen updates and signal emits to test when they've been
+    # called.
+    editor.linenumberarea = Mock()
+    if version_info > (4, ):
+        editor.sig_flags_changed = Mock()
+    else:
+        editor.get_linenumberarea_width = Mock(return_value=1)
+    editor.breakpoints_changed = Mock()
+    return editor, qtbot
+
+
+# --- Tests
+# -----------------------------------------------------------------------------
+def test_delete(code_editor_bot, mocker):
+    """Test CodeEditor.delete()."""
+    editor, qtbot = code_editor_bot
+    text = ('def f1(a, b):\n')
+    editor.set_text(text)
+
+    # Move to start and delete next character without selection.
+    cursor = editor.textCursor()
+    cursor.movePosition(QTextCursor.Start)
+    editor.setTextCursor(cursor)
+    editor.delete()
+    assert editor.get_text_line(0) == 'ef f1(a, b):'
+
+    # Delete selection.
+    cursor = editor.textCursor()
+    cursor.select(QTextCursor.WordUnderCursor)
+    editor.setTextCursor(cursor)
+    editor.delete()
+    assert editor.get_text_line(0) == ' f1(a, b):'
+
+    # Move to end of document - nothing to delete after cursor.
+    cursor = editor.textCursor()
+    cursor.movePosition(QTextCursor.End)
+    editor.setTextCursor(cursor)
+    editor.delete()
+    assert editor.get_text_line(0) == ' f1(a, b):'
+
+
+if __name__ == "__main__":
+    pytest.main()