6

I understand that because Ipython 5.0.0 uses a new input library (prompt_toolkit) it no longer defaults to the editor mode specified in .inputrc (*nix). This option has to be set in an Ipython profile configuration file (see https://stackoverflow.com/a/38329940/2915339).

My question is: having set vi-mode in the profile configuration file, how does one specify a particular keybinding? I like to use 'jk' for escape, for instance.

Community
  • 1
  • 1
jeicher
  • 73
  • 5

2 Answers2

11

You're right. prompt_toolkit ignores .inputrc. There does not seem to be a way to define custom keybindings for the vi mode in the IPython 5.0.0 profile configuration file.

Here's workaround I'm currently using. It's not pretty, but it works for now.

According to the IPython docs, you can specify Keyboard Shortcuts in a startup configuration script.

Instead of rebinding jk to ESC, I'm making a unicode "j" (u'j') followed by a unicode "k" (u'k') inside of VimInsertMode() a shortcut for a prompt_toolkit event that switches to navigation mode.

I created a .ipython/profile_default/startup/keybindings.py with the following code:

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)

The prompt_toolkit source will help you implement other shortcuts as needed.

Community
  • 1
  • 1
jellycola
  • 494
  • 1
  • 5
  • 12
  • 6
    This `keybindings.py` stopped working some time between IPython 5.1.0 and 7.0.1, with error message `'TerminalInteractiveShell' object has no attribute 'pt_cli'`. See https://github.com/ipython/ipython/pull/11426/commits/e08dc1b4dd30e8d2b607134693f226e8a22100ec#diff-d47c339db04f28c44a59c6662dbd53eaR228 for a fix. – Aaron Feldman Oct 27 '18 at 01:04
  • 1
    The following link also contains the updated keybindings code: https://ipython.readthedocs.io/en/stable/config/details.html#keyboard-shortcuts – Olivier Moindrot Mar 14 '19 at 13:07
  • 1
    I should have read better: running the code in `.ipython/profile_default/startup/` is crucial here. I've tried this in `ipython_config.py` which failed miserably... So, years later: this still works in IPython 7.12.0 and there's no better alternative (I searched thoroughly, all I found is that this solution is now part of the official IPython documentation). – freeo Feb 04 '20 at 18:21
  • Any idea how to get this to work with ipdb as well? – Patrick Sep 14 '21 at 14:09
8

This is an old post but it helped me find my answer so I thought I would post how I added a couple of bindings to vi mode in ipython. I added the following code in ~/.ipython/profile_default/startup/00-keybindings.py to bind to K and J in vi navigation mode.

"""Improve history access so I can skip over functions"""

from IPython import get_ipython
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.filters import HasFocus, ViNavigationMode
from prompt_toolkit.key_binding.bindings.named_commands import get_by_name

ip = get_ipython()
registry = ip.pt_app.key_bindings
ph = get_by_name('previous-history')
nh = get_by_name('next-history')
registry.add_binding('K',
                     filter=(HasFocus(DEFAULT_BUFFER) &
                             ViNavigationMode()))(ph)
registry.add_binding('J',
                     filter=(HasFocus(DEFAULT_BUFFER) &
                             ViNavigationMode()))(nh)
GaryBishop
  • 3,204
  • 2
  • 23
  • 19
  • I have been searching and searching for exactly this keybinding. I stumbled on this when looking for the right way to setup vi editing mode and I am so thankful. Thank you @GaryBishop! – Jonathan Branam Jan 21 '19 at 02:02