Repository: spyder-ide/spyder

Issue:
Enable automatic insertion of closing quotes inside function calls
## Problem Description
I want the same behaviour as in jupyter notebook of automatic closing of quotes. I change the setting of spyder editor to enable automatic insertion of closing quotes. 

It works fine while typing in the editor but does not works inside functions.

Could you help me  how to resolve this issue!?

### What steps will reproduce the problem?

1. 
2. 
3. 

### What is the expected output? What do you see instead?



### Paste Traceback/Error Below (if applicable)

```python
import numpy as np
import matplotlib.pyplot as plt

a = np.linspace(0,10,2001)
b = np.cos(a*np.pi)

plt.plot(a, b)
plt.title(') # does not work here: only opening quotes

'' # works here: automatically closes the quotes
```

## Package Versions

* Spyder: 3.2.6
* Python: 3.6.4
* Qt:
* PyQt:
* Operating System: windows 10

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```
PASTE DEPENDENCIES HERE
```



Discussion:
I can confirm this behavior also occurs on my machine. However, I am not sure if it is intended for some reason, or is just a current limitation of the system. I'd certainly prefer if it worked.

@ccordoba12 ?
I think this happens when there's some text to the right of the quote you're inserting. @Khalilsqu, could you verity it?
@ccordoba12 Yup, that's it. In this case, its the auto-insertion of the closing paren of the function call that blocks it.
@ccordoba12 yes, whenever there is a leading character in the line, the automatic insertion of closing quotes does not work.
@Khalilsqu A leading character? According to my tests and confirming @ccordoba12 's suspicion above, it was the *trailing* character that was the problem, not the leading one.
Hi, Thanks for all efforts toward releasing spyder 4, I installed the dev version but still have this issue. 
That's why this issue is still open.
A fix for this should close the quotes when any closing bracket type or a comma is the next character to the right. A similar functionality is in close brackets.
So is this going to be fixed by your PR too?
No but after it's merged I can look at refactoring to make one function work for both extensions
That'd be really handy, thanks! I can't tell you how many times I forget this, haha.

Resolving patch:
diff --git a/spyder/plugins/editor/extensions/closequotes.py b/spyder/plugins/editor/extensions/closequotes.py
--- a/spyder/plugins/editor/extensions/closequotes.py
+++ b/spyder/plugins/editor/extensions/closequotes.py
@@ -78,11 +78,12 @@ def _autoinsert_quotes(self, key):
             self.editor.setTextCursor(cursor)
         elif self.editor.in_comment():
             self.editor.insert_text(char)
-        elif len(trailing_text) > 0 and not \
-                unmatched_quotes_in_line(line_to_cursor) == char:
+        elif (len(trailing_text) > 0 and
+                not unmatched_quotes_in_line(line_to_cursor) == char and
+                not trailing_text[0] in (',', ':', ';', ')', ']', '}')):
             self.editor.insert_text(char)
-        elif unmatched_quotes_in_line(line_text) and \
-                (not last_three == 3*char):
+        elif (unmatched_quotes_in_line(line_text) and
+                (not last_three == 3*char)):
             self.editor.insert_text(char)
         # Move to the right if we are before a quote
         elif self.editor.next_char() == char:


Test patch:
diff --git a/spyder/plugins/editor/extensions/tests/test_closequotes.py b/spyder/plugins/editor/extensions/tests/test_closequotes.py
--- a/spyder/plugins/editor/extensions/tests/test_closequotes.py
+++ b/spyder/plugins/editor/extensions/tests/test_closequotes.py
@@ -7,6 +7,7 @@
 
 # Third party imports
 import pytest
+from qtpy.QtCore import Qt
 from qtpy.QtGui import QTextCursor
 
 # Local imports
@@ -59,6 +60,33 @@ def test_close_quotes(qtbot, editor_close_quotes, text, expected_text,
     assert cursor_column == TextHelper(editor).current_column_nbr()
 
 
+@pytest.mark.parametrize(
+    'text, expected_text, cursor_column',
+    [
+        ('()', '("")', 2),  # Complete in brackets
+        ('{}', '{""}', 2),
+        ('[]', '[""]', 2),
+        (',', '"",', 1),  # Complete before commas, colons and semi-colons
+        (':', '"":', 1),
+        (';', '"";', 1),
+        ('a', '"a', 1),  # No Completion before other text
+    ])
+def test_trailing_text(qtbot, editor_close_quotes, text, expected_text,
+                       cursor_column):
+    """
+    Test insertion of extra quotes inside brackets and before commas,
+    colons and semi-colons.
+    """
+    editor = editor_close_quotes
+
+    qtbot.keyClicks(editor, text)
+    editor.move_cursor(-1)
+    qtbot.keyClicks(editor, '"')
+    assert editor.toPlainText() == expected_text
+
+    assert cursor_column == TextHelper(editor).current_column_nbr()
+
+
 def test_selected_text(qtbot, editor_close_quotes):
     """Test insert surronding quotes to selected text."""
     editor = editor_close_quotes
@@ -109,6 +137,43 @@ def test_selected_text_multiple_lines(qtbot, editor_close_quotes):
                                     'some""" text')
 
 
+def test_close_quotes_in_brackets(qtbot, editor_close_quotes):
+    """Test quote completion in nested brackets."""
+    editor = editor_close_quotes
+    # Test closing when following character is a right parentheses
+    editor.textCursor().insertText('foo()')
+    editor.move_cursor(-1)
+    qtbot.keyClicks(editor, '"')
+    assert editor.toPlainText() == 'foo("")'
+    assert editor.textCursor().columnNumber() == 5
+    # Test not closing when single quote is before a bracket
+    qtbot.keyPress(editor, Qt.Key_Delete)  # now 'foo(")'
+    qtbot.keyClicks(editor, '"')
+    assert editor.toPlainText() == 'foo("")'
+    assert editor.textCursor().columnNumber() == 6
+    # Test closing when following character is a comma
+    qtbot.keyClicks(editor, ', ,')
+    editor.move_cursor(-1)
+    qtbot.keyClicks(editor, '"')
+    assert editor.toPlainText() == 'foo("", "",)'
+    assert editor.textCursor().columnNumber() == 9
+    # Test closing when following character is a right brace
+    # and white preceding the next character
+    editor.move_cursor(2)
+    qtbot.keyClicks(editor, ' { },')
+    editor.move_cursor(-3)
+    qtbot.keyClicks(editor, '"')
+    assert editor.toPlainText() == 'foo("", "", {"" },)'
+    assert editor.textCursor().columnNumber() == 14
+    # Test not closing otherwise
+    editor.move_cursor(4)
+    qtbot.keyClicks(editor, ' bar')
+    editor.move_cursor(-3)
+    qtbot.keyClicks(editor, '"')
+    assert editor.toPlainText() == 'foo("", "", {"" }, "bar)'
+    assert editor.textCursor().columnNumber() == 20
+
+
 def test_activate_deactivate(qtbot, editor_close_quotes):
     """Test activating/desctivating close quotes editor extension."""
     editor = editor_close_quotes