Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPython 8 regression: escaping insert mode in vi editing mode takes too long. #13443

Closed
mario-grgic opened this issue Jan 12, 2022 · 18 comments · Fixed by #13861
Closed

IPython 8 regression: escaping insert mode in vi editing mode takes too long. #13443

mario-grgic opened this issue Jan 12, 2022 · 18 comments · Fixed by #13861

Comments

@mario-grgic
Copy link

After updating to IPython 8.0 and with vi editing mode enabled, it now takes about 1 second to exit insert mode and go back to nav mode. Even worse, if you hit ESC (to exit insert mode) and continue typing your editing command, exiting inset mode is cancelled altogether.

This is extremely jarring. For example: type something in insert mode hit ESC and start typing editing commands (e.g. dd to delete a line) and instead you get single d echoed in the line and exiting insert mode is cancelled altogether (you stay in insert mode).

This is a regression introduced in IPython 8, and there doesn't appear to be a setting that controls this delay.

@mario-grgic
Copy link
Author

It appears that if you flip the setting

c.TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode = False

this is fixed. So, some Emacs bindings must use Escape + <some_other_key> sequence, which means we have to wait after ESC for another key press.

Perhaps solution here is to not add Emacs bindings by default if vi editing mode is used?

@Ellana42
Copy link

Same issue here. The setting helped with the editing commands not being written, but leaving insert mode is still slow and the commands I issue while switching from insert mode to normal mode are not taken into account. For example a Esc + dd typed fast will not delete the whole line.

@0xConnorRhodes
Copy link

I also have the same issue as Ellana42. Adding c.TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode = False does not get rid of the lag when switching to normal mode. Notably switching from normal mode to inset mode is instant. It's only switching from insert to normal mode which is slow.

@jerpint
Copy link

jerpint commented Feb 4, 2022

Same issue as well, it makes vi mode essentially unusable

@willthbill
Copy link

willthbill commented Feb 8, 2022

The following configuration in ~/.ipython/profile_default/ipython_config.py, makes it much more comfortable to work with.

c.TerminalInteractiveShell.editing_mode = 'vi'
c.TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode = False
c.TerminalInteractiveShell.timeoutlen = 0.25

c.TerminalInteractiveShell.timeoutlen has a default value of 0.5. Setting it to 0 makes <esc> from insert mode instant, but it also makes dd not work properly. Maybe some kind of setting to disable delays in insert mode would solve this completely?

@g15ecb
Copy link

g15ecb commented Feb 9, 2022

In addition to that mentioned above I also noticed that the cursor shape is set to | in the terminal upon exit of IPython 8 if you exit when in 'insert mode'; exiting in normal mode will leave the terminal default cursor shape intact. I am using zsh and have bindkey -v in ~/.zshrc.

@jtherrmann
Copy link
Contributor

jtherrmann commented Feb 16, 2022

I'm having the same issue with 7.31.1, does anyone at least have a solution for that version? The options emacs_bindings_in_vi_insert_mode and timeoutlen are not recognized.

@MathieuTuli
Copy link

this took me a while to figure out and plagued me for so long! having emacs bindings default off if using vim seems like a good idea to me. I spent a while thinking the vi-mode was just not working

@max-sixty
Copy link

The c.TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode = False works for me!

One disadvantage of this is that C-w is useful, and that's turned off. If anyone figures out how to get the previous behavior back, please do share!

@ianbrody
Copy link

Maybe this should be a different issue, but it's also treating f<letter> in normal mode as subject to the timeout. I should be able to type f in normal mode, wait a long time, and hit a letter, and have it jump to that letter.

@bbliem
Copy link

bbliem commented Apr 28, 2022

One disadvantage of this is that C-w is useful, and that's turned off. If anyone figures out how to get the previous behavior back, please do share!

@max-sixty I managed to get CTRL-W to work by putting the following in ~/.ipython/profile_default/startup/00-keybindings.py (adapted from the documentation about keyboard shortcuts)

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

from prompt_toolkit.key_binding.bindings.named_commands import get_by_name

ip = get_ipython()
insert_mode = ViInsertMode() | EmacsInsertMode()

# Register the shortcut if IPython is using prompt_toolkit
if getattr(ip, 'pt_app', None):
    registry = ip.pt_app.key_bindings
    registry.add_binding(Keys.ControlW,
                     filter=(HasFocus(DEFAULT_BUFFER)
                             & ~HasSelection()
                             & insert_mode))(get_by_name('backward-kill-word'))

My ipython config file contains this, by the way:

c.TerminalInteractiveShell.editing_mode = 'vi'
c.TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode = False

@minjae
Copy link

minjae commented Jul 1, 2022

There are multiple related issues reported in this thread. I may have found the cause.

Summary: an Esc-related keybinding is making prompt_toolkit to wait for the next keystroke after an Esc keypress.

Details:
In vi/vim keymaps, there are two main timeout values: timeoutlen and ttimeoutlen (note the double t). timeoutlen is responsible for the timeout between keystrokes (e.g. for deleting till character 'a' dta); ttimeoutlen is responsible for distinguishing a lone Esc from a start of an escape sequence (n.b. VT100, ANSI escape sequence). These two parameters should be orthogonal. In ipython, the default setting is timeoutlen = 0.5 and ttimeoutlen = 0.01.

As people noted previously in the thread, reducing timeoutlen from 0.5 is actually making the Esc behavior faster. This should not happen. This work-around in turn makes dd and f<letter> not work properly as noted by multiple people in this thread. This suggests some tangling of the two parameters.

The culprint is terminal/shortcuts.py:

 55     kb.add('escape', 'enter', filter=(has_focus(DEFAULT_BUFFER)
 56                             & ~has_selection
 57                             & insert_mode
 58                                       ))(reformat_and_execute)
 59 

So, you can comment out the above lines and your vi-mode should work fine. No need to mess with timeoutlen which in turn breaks normal vim commands.

As noted before, your config should have the following lines.

c.TerminalInteractiveShell.editing_mode = 'vi'
c.TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode = False

I will submit a pull request if people have no objections. I think it makes sense to tie the offending lines to ebivim condition in the source code like other Esc-initiated keybindings. reformat_and_execute does not do much on my setup anyway -- what is the correct behavior of this code?

@jerpint
Copy link

jerpint commented Jul 11, 2022

@minjae your solution of commenting out seems to work on my end, thanks!

@jacktose
Copy link

jacktose commented Oct 3, 2022

@minjae Thanks for that deep info! Did you ever open a PR to straighten out the behavior?

@jtherrmann
Copy link
Contributor

I opened #13861 for @minjae's workaround to get this moving forward. I'm not familiar with the ipython codebase so don't know if this is the best solution.

@minjae
Copy link

minjae commented Dec 9, 2022

@jtherrmann thanks for getting to making the PR. I was initially hoping for more concrete feedback, but even months after, it seems like there isn't much feedback. I still do not know what the correct behavior of reformat_and_execute is. At least on my machine, it does not seem to reformat anything (but it executes). Could someone chime in before we go ahead with the PR?

@jtherrmann
Copy link
Contributor

I confirmed that this is now fixed as of IPython 8.8.0. To summarize, your config file must have the following lines:

c.TerminalInteractiveShell.editing_mode = 'vi'
c.TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode = False

No other workarounds are required.

The second line is required for eliminating the delay when escaping insert mode. Thanks again to @minjae for providing the workaround in #13443 (comment).

@max-sixty
Copy link

This works great!! It's let me remove a bunch of configs, such as those in prompt-toolkit/python-prompt-toolkit#192 (comment)

Thanks a lot!

Carreau added a commit that referenced this issue Mar 30, 2023
Fixes #13970. Relates to
#13443.

Documents #12603 with a new
`emacs_like_insert_mode` filter alias.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.