Repository: ipython/ipython

Issue:
Cannot enter docstring in IPython
Type the following snippet and then hit `C-m` or `C-j` or `C-o` then IPython produces "Exception list index out of range" from `IPython/core/inputtransformer2.py`

```
In [1]: def f():
   ...:     """
```

Traceback:

```pytb
Unhandled exception in event loop:
  File "/home/takafumi/.virtualenvs/ipy7/lib/python3.7/site-packages/prompt_toolkit/eventloop/posix.py", line 154, in _run_task
    t()
  File "/home/takafumi/.virtualenvs/ipy7/lib/python3.7/site-packages/prompt_toolkit/eventloop/context.py", line 115, in new_func
    return func(*a, **kw)
  File "/home/takafumi/.virtualenvs/ipy7/lib/python3.7/site-packages/prompt_toolkit/application/application.py", line 548, in read_from_input
    self.key_processor.process_keys()
  File "/home/takafumi/.virtualenvs/ipy7/lib/python3.7/site-packages/prompt_toolkit/key_binding/key_processor.py", line 272, in process_keys
    self._process_coroutine.send(key_press)
  File "/home/takafumi/.virtualenvs/ipy7/lib/python3.7/site-packages/prompt_toolkit/key_binding/key_processor.py", line 179, in _process
    self._call_handler(matches[-1], key_sequence=buffer[:])
  File "/home/takafumi/.virtualenvs/ipy7/lib/python3.7/site-packages/prompt_toolkit/key_binding/key_processor.py", line 321, in _call_handler
    handler.call(event)
  File "/home/takafumi/.virtualenvs/ipy7/lib/python3.7/site-packages/prompt_toolkit/key_binding/key_bindings.py", line 78, in call
    return self.handler(event)
  File "/home/takafumi/repos/watch/ipython/IPython/terminal/shortcuts.py", line 109, in newline_or_execute
    status, indent = shell.check_complete(check_text)
  File "/home/takafumi/repos/watch/ipython/IPython/core/interactiveshell.py", line 3307, in check_complete
    status, nspaces = self.input_transformer_manager.check_complete(code)
  File "/home/takafumi/repos/watch/ipython/IPython/core/inputtransformer2.py", line 627, in check_complete
    lines = self.do_token_transforms(lines)
  File "/home/takafumi/repos/watch/ipython/IPython/core/inputtransformer2.py", line 551, in do_token_transforms
    changed, lines = self.do_one_token_transform(lines)
  File "/home/takafumi/repos/watch/ipython/IPython/core/inputtransformer2.py", line 534, in do_one_token_transform
    transformer = transformer_cls.find(tokens_by_line)
  File "/home/takafumi/repos/watch/ipython/IPython/core/inputtransformer2.py", line 359, in find
    while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:

Exception list index out of range
Press ENTER to continue...
```

Checked with current master eb8dac350abdabc350883ee72c12a151c6a0d138



Discussion:

Resolving patch:
diff --git a/IPython/core/inputtransformer2.py b/IPython/core/inputtransformer2.py
--- a/IPython/core/inputtransformer2.py
+++ b/IPython/core/inputtransformer2.py
@@ -355,9 +355,14 @@ def find(cls, tokens_by_line):
         """Find the first escaped command (%foo, !foo, etc.) in the cell.
         """
         for line in tokens_by_line:
+            if not line:
+                continue
             ix = 0
-            while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
+            ll = len(line)
+            while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
                 ix += 1
+            if ix >= ll:
+                continue
             if line[ix].string in ESCAPE_SINGLES:
                 return cls(line[ix].start)
 


Test patch:
diff --git a/IPython/core/tests/test_inputtransformer2.py b/IPython/core/tests/test_inputtransformer2.py
--- a/IPython/core/tests/test_inputtransformer2.py
+++ b/IPython/core/tests/test_inputtransformer2.py
@@ -233,6 +233,17 @@ def test_check_complete():
         for k in short:
             cc(c+k)
 
+def test_check_complete_II():
+    """
+    Test that multiple line strings are properly handled.
+
+    Separate test function for convenience
+
+    """
+    cc = ipt2.TransformerManager().check_complete
+    nt.assert_equal(cc('''def foo():\n    """'''), ('incomplete', 4))
+
+
 def test_null_cleanup_transformer():
     manager = ipt2.TransformerManager()
     manager.cleanup_transforms.insert(0, null_cleanup_transformer)