Issue
In older (I believe pre-5.0) versions of IPython, if I was working on a line/block, and suddenly discovered I needed to investigate something else to finish it, my approach was to hit Ctrl-C, which left the incomplete line/block on screen, but unexecuted, and gave me a fresh prompt. That is, I'd see something like:
In [1]: def foo():
...: stuff^C # <-- Just realized I needed to check something on stuff usage
In [2]: # <-- cursor goes to new line, but old stuff still on terminal
In newer IPython (which seems to have switched from readline
to prompt_toolkit
as the "CLI support framework"), the behavior of Ctrl-C differs; now, instead of giving me a newline, it just resets the current one, discarding everything I've typed and returning the cursor to the beginning of the line.
# Before:
In [1]: def foo():
...: stuff
# After Ctrl-C:
In [1]: # Hey, where'd everything go?
This is extremely annoying, since I can no longer see or copy/paste the code I was working on to resume my work after I've done whatever side task precipitated the need for a fresh prompt.
My question is: Is there any way to restore the old IPython behavior, where Ctrl-C does the following things:
- Does not execute the line/block typed so far
- Leaves it on the screen
- Ability to choose (at config time is fine) whether to add to the history (this would be personal preference; do you want half-formed stuff in the history, or just on the terminal for copy/paste?)
- Provides me with a fresh prompt below the text typed so far
I've searched everywhere, and the most I've found is a bug report comment that mentions this new behavior in passing as "...a change from earlier versions of IPython, but it is intentional."
I haven't been able to find anything documented about modifying the behavior in the IPython or prompt_toolkit
documentation; I've found where a lot of these handlers are installed, but attempts at monkey-patching to alter the current behavior have failed (and frankly, monkey-patching undocumented code means I risk it breaking every upgrade, so I'd like to find some semi-supported fix for this; failing that, hacky monkey-patching is acceptable though).
Solution
And after more research, I found what appears to be a supported approach, relying on the IPython keyboard shortcuts documentation (documented slightly differently for 5.x and 6.x).
The solution is to create a file in ~/.ipython/profile_default/startup
(any name, ending with .py
or ipy
is fine, e.g. fixctrlc.py
), and add the following:
from IPython import get_ipython
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.keys import Keys
from prompt_toolkit.filters import HasFocus, ViInsertMode, EmacsInsertMode
def on_ctrlc(event):
# Move cursor to end of line and redraw (so whole block is preserved)
event.cli.current_buffer.cursor_position = len(event.cli.current_buffer.text)
event.cli._redraw(**redraw_args)
# (Optional) Put non-empty partial commands in history
if event.cli.current_buffer.text.strip():
event.cli.current_buffer.append_to_history()
if doprint:
print() # Skip to next line past cursor
event.cli.reset() # Reset/redraw prompt
event.cli.current_buffer.reset() # Clear buffer so new line is fresh (empty)
ip = get_ipython()
try:
try:
# IPython 5-6; render_as_done doesn't exist, but manual print works
registry = ip.pt_cli.application.key_bindings_registry
redraw_args = {}
doprint = True
except AttributeError:
# IPython 7+; render_as_done necessary, and removes need for print
registry = ip.pt_app.key_bindings
redraw_args = {'render_as_done': True}
doprint = False
except AttributeError:
pass # Old IPython doesn't need special handler
else:
registry.add_binding(Keys.ControlC,
filter=(HasFocus(DEFAULT_BUFFER) & (ViInsertMode() | EmacsInsertMode()))
)(on_ctrlc)
Please feel free to contribute if you find a better solution.
Answered By - ShadowRanger
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.