Repository: spyder-ide/spyder

Issue:
TypeError with snippets
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

just input following codes:
def demo(bisect_fn):
    for needle in reversed(NEEDLES):
        position = bisect_fn(HAYSTACK, needle)
        offset=position*' \'
        print(ROW_FMT.format(needle,position,offset))

### Traceback
```python-traceback
  File "/home/ddd/anaconda3/lib/python3.8/site-packages/spyder/plugins/editor/extensions/snippets.py", line 229, in _on_key_pressed
    self._process_text(text)
  File "/home/dddd/anaconda3/lib/python3.8/site-packages/spyder/plugins/editor/extensions/snippets.py", line 52, in wrapper
    return f(self, *args, **kwargs)
  File "/home/dddd/anaconda3/lib/python3.8/site-packages/spyder/plugins/editor/extensions/snippets.py", line 237, in _process_text
    self.insert_text(text, line, column)
  File "/home/ddd/anaconda3/lib/python3.8/site-packages/spyder/plugins/editor/extensions/snippets.py", line 379, in insert_text
    position = ((x, y), (x, y + 1))
TypeError: can only concatenate tuple (not "int") to tuple
```

## Versions

* Spyder version: 4.2.5 
* Python version: 3.8.5
* Qt version: 5.9.7
* PyQt5 version: 5.9.2
* Operating System: Linux 5.11.0-27-generic

### Dependencies

```

# Mandatory:
atomicwrites >=1.2.0            :  1.4.0 (OK)
chardet >=2.0.0                 :  4.0.0 (OK)
cloudpickle >=0.5.0             :  1.6.0 (OK)
diff_match_patch >=20181111     :  20200713 (OK)
intervaltree >=3.0.2            :  3.1.0 (OK)
IPython >=7.6.0                 :  7.26.0 (OK)
jedi =0.17.2                    :  0.17.2 (OK)
jsonschema >=3.2.0              :  3.2.0 (OK)
keyring >=17.0.0                :  23.0.1 (OK)
nbconvert >=4.0                 :  6.1.0 (OK)
numpydoc >=0.6.0                :  1.1.0 (OK)
parso =0.7.0                    :  0.7.0 (OK)
pexpect >=4.4.0                 :  4.8.0 (OK)
pickleshare >=0.4               :  0.7.5 (OK)
psutil >=5.3                    :  5.8.0 (OK)
pygments >=2.0                  :  2.10.0 (OK)
pylint >=1.0                    :  2.6.0 (OK)
pyls >=0.36.2;<1.0.0            :  0.36.2 (OK)
pyls_black >=0.4.6              :  0.4.6 (OK)
pyls_spyder >=0.3.2             :  0.3.2 (OK)
qdarkstyle >=2.8;<3.0           :  2.8.1 (OK)
qtawesome >=0.5.7               :  1.0.2 (OK)
qtconsole >=5.0.3               :  5.1.0 (OK)
qtpy >=1.5.0                    :  1.10.0 (OK)
rtree >=0.8.3                   :  0.9.7 (OK)
setuptools >=39.0.0             :  52.0.0.post20210125 (OK)
sphinx >=0.6.6                  :  4.0.2 (OK)
spyder_kernels >=1.10.2;<1.11.0 :  1.10.2 (OK)
textdistance >=4.2.0            :  4.2.1 (OK)
three_merge >=0.1.1             :  0.1.1 (OK)
watchdog >=0.10.3;<2.0.0        :  1.0.2 (OK)
xdg >=0.26                      :  0.27 (OK)
zmq >=17                        :  22.2.1 (OK)

# Optional:
cython >=0.21                   :  0.29.24 (OK)
matplotlib >=2.0.0              :  3.4.2 (OK)
numpy >=1.7                     :  1.18.5 (OK)
pandas >=1.1.1                  :  1.3.2 (OK)
scipy >=0.17.0                  :  1.4.1 (OK)
sympy >=0.7.3                   :  1.8 (OK)
```



Discussion:
Hey @expertcxxmoon, thanks for reporting. We'll fix this error in our next version (5.1.3), to be released this weekend.

Resolving patch:
diff --git a/spyder/plugins/editor/extensions/snippets.py b/spyder/plugins/editor/extensions/snippets.py
--- a/spyder/plugins/editor/extensions/snippets.py
+++ b/spyder/plugins/editor/extensions/snippets.py
@@ -9,7 +9,6 @@
 # Standard library imports
 import copy
 import functools
-import inspect
 
 # Third party imports
 from qtpy.QtGui import QTextCursor, QColor
@@ -359,25 +358,41 @@ def _delete_token(self, node, text_node, line, column):
     def insert_text(self, text, line, column):
         has_selected_text = self.editor.has_selected_text()
         start, end = self.editor.get_selection_start_end()
+
         if has_selected_text:
-            self._remove_selection(start, end)
+            # Catch error with Kite snippets
+            # Fixes spyder-ide/spyder#16358
+            try:
+                self._remove_selection(start, end)
+            except Exception:
+                return
             line, column = start
+
         node, snippet, text_node = self._find_node_by_position(line, column)
         if node is None or snippet is None or text_node is None:
             self.reset()
             return
+
         tokens = tokenize(text)
         token_nodes = [nodes.LeafNode(t.token, t.value) for t in tokens]
         for token in token_nodes:
             token.compute_position((line, column))
+
         if node.name == 'EPSILON':
             new_text_node = nodes.TextNode(*token_nodes)
             snippet.placeholder = new_text_node
             return
+
         position = node.position
         if len(position) == 1:
             x, y = position[0]
-            position = ((x, y), (x, y + 1))
+            # Catch error with Kite snippets.
+            # Fixes spyder-ide/spyder#16358
+            try:
+                position = ((x, y), (x, y + 1))
+            except TypeError:
+                return
+
         leaf_start, leaf_end = position
         node_index = node.index_in_parent
         text_node_tokens = list(text_node.tokens)
@@ -387,6 +402,7 @@ def insert_text(self, text, line, column):
             right_offset = 0
             first_token = token_nodes[0]
             last_token = token_nodes[-1]
+
             if node_index > 0 and len(text_node_tokens) > 1:
                 previous_node = text_node_tokens[node_index - 1]
                 if first_token.mark_for_position:
@@ -395,6 +411,7 @@ def insert_text(self, text, line, column):
                             left_offset = 1
                             first_token.value = (
                                 previous_node.value + first_token.value)
+
             if last_token.mark_for_position:
                 if last_token.name in MERGE_ALLOWED:
                     if last_token.name == node.name:
@@ -448,6 +465,7 @@ def insert_text(self, text, line, column):
                     if first_token == node.name:
                         left_merge = True
                         first_token.value = first_part + first_token.value
+
             if not left_merge:
                 first_tokens.append(nodes.LeafNode(node.name, first_part))
 
@@ -457,6 +475,7 @@ def insert_text(self, text, line, column):
                     if last_token == node.name:
                         right_merge = True
                         last_token.value = last_token.value + second_part
+
             if not right_merge:
                 second_tokens.insert(0, nodes.LeafNode(node.name, second_part))
 


Test patch: