Repository: ipython/ipython

Issue:
Keyboard shortcuts not working
Hi,

Before I upgraded `IPython` to the latest version, I had used `jj` to exit `vi` insert mode.

It has been broken since the update.

I had used `~/.ipython/profile_default/startup/keybindings.py` Python script with the following code to bind `jj`:

``` py
from IPython import get_ipython
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.filters import HasFocus, ViInsertMode
from prompt_toolkit.key_binding.vi_state import InputMode


ip = get_ipython()

def switch_to_navigation_mode(event):
    vi_state = event.cli.vi_state
    vi_state.reset(InputMode.NAVIGATION)

if getattr(ip, 'pt_cli'):
    registry = ip.pt_cli.application.key_bindings_registry
    registry.add_binding(u'j',u'k',
                         filter=(HasFocus(DEFAULT_BUFFER)
                                 & ViInsertMode()))(switch_to_navigation_mode)
```

I removed `keybindings.py` file from `startup` directory today and created `~/.ipython/profile_default/startup/keyboard_shortcuts.py` with the code from the example on [`IPython` Read the Docs website](https://ipython.readthedocs.io/en/stable/config/details.html#keyboard-shortcuts):

``` py
from IPython import get_ipython
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.keys import Keys
from prompt_toolkit.filters import HasFocus, HasSelection, ViInsertMode, EmacsInsertMode

ip = get_ipython()
insert_mode = ViInsertMode() | EmacsInsertMode()

def insert_unexpected(event):
    buf = event.current_buffer
    buf.insert_text('The Spanish Inquisition')

# Register the shortcut if IPython is using prompt_toolkit
if getattr(ip, 'pt_cli'):
    registry = ip.pt_cli.application.key_bindings_registry
    registry.add_binding(Keys.ControlN,
                     filter=(HasFocus(DEFAULT_BUFFER)
                             & ~HasSelection()
                             & insert_mode))(insert_unexpected)
```

It fails and the error message is the same:

```
[TerminalIPythonApp] WARNING | Unknown error in handling startup files:
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~/Library/Python/3.7/lib/python/site-packages/IPython/core/shellapp.py in _exec_file(self, fname, shell_futures)
    321                                                  self.shell.user_ns,
    322                                                  shell_futures=shell_futures,
--> 323                                                  raise_exceptions=True)
    324         finally:
    325             sys.argv = save_argv

~/Library/Python/3.7/lib/python/site-packages/IPython/core/interactiveshell.py in safe_execfile(self, fname, exit_ignore, raise_exceptions, shell_futures, *where)
   2679                 py3compat.execfile(
   2680                     fname, glob, loc,
-> 2681                     self.compile if shell_futures else None)
   2682             except SystemExit as status:
   2683                 # If the call was made with 0 or None exit status (sys.exit(0)

~/Library/Python/3.7/lib/python/site-packages/IPython/utils/py3compat.py in execfile(fname, glob, loc, compiler)
    186     with open(fname, 'rb') as f:
    187         compiler = compiler or compile
--> 188         exec(compiler(f.read(), fname, 'exec'), glob, loc)
    189
    190 # Refactor print statements in doctests.

~/.ipython/profile_default/startup/keyboard_shortcuts.py in <module>
     12
     13 # Register the shortcut if IPython is using prompt_toolkit
---> 14 if getattr(ip, 'pt_cli'):
     15     registry = ip.pt_cli.application.key_bindings_registry
     16     registry.add_binding(Keys.ControlN,

AttributeError: 'TerminalInteractiveShell' object has no attribute 'pt_cli'
```

How can I customise keyboard shortcuts?



Discussion:
Thanks for the bug report, we'll investigate and try to provide a fix. 

Resolving patch:
diff --git a/IPython/sphinxext/ipython_directive.py b/IPython/sphinxext/ipython_directive.py
--- a/IPython/sphinxext/ipython_directive.py
+++ b/IPython/sphinxext/ipython_directive.py
@@ -1009,6 +1009,15 @@ def run(self):
 
                 if figure is not None:
                     figures.append(figure)
+            else:
+                message = 'Code input with no code at {}, line {}'\
+                            .format(
+                                self.state.document.current_source,
+                                self.state.document.current_line)
+                if self.shell.warning_is_error:
+                    raise RuntimeError(message)
+                else:
+                    warnings.warn(message)
 
         for figure in figures:
             lines.append('')


Test patch: