Install to:
ipython_config.py
—~/.ipython/profile_default/ipython_config.py
00-keybindings.py
—~/.ipython/profile_default/startup/00-keybindings.py
10-line-magics.py
—~/.ipython/profile_default/startup/10-line-magics.py
Install to:
ipython_config.py
— ~/.ipython/profile_default/ipython_config.py
00-keybindings.py
— ~/.ipython/profile_default/startup/00-keybindings.py
10-line-magics.py
— ~/.ipython/profile_default/startup/10-line-magics.py
def __initialize_custom_keybindings(): | |
from prompt_toolkit import VERSION as PTK_VERSION | |
from prompt_toolkit.application.current import get_app | |
from prompt_toolkit.filters import ( | |
Condition, | |
emacs_insert_mode, | |
has_selection, | |
in_paste_mode, | |
is_multiline, | |
vi_insert_mode, | |
) | |
from prompt_toolkit.key_binding.bindings.named_commands import get_by_name, register | |
from prompt_toolkit.key_binding.key_processor import KeyPress, KeyPressEvent | |
from prompt_toolkit.keys import Keys | |
IS_PTK2 = IS_PTK3 = False | |
if ('3',) <= PTK_VERSION < ('4',): | |
IS_PTK3 = True | |
elif ('2',) <= PTK_VERSION < ('3',): | |
IS_PTK2 = True | |
E = KeyPressEvent | |
@register('insert-line-above') | |
def _(event: E) -> None: | |
""" | |
Newline, before current (in case of multiline input. | |
""" | |
event.current_buffer.insert_line_above(copy_margin=not in_paste_mode()) | |
@register('accept-buffer') | |
def _(event: E) -> None: | |
""" | |
Execute the current buffer | |
""" | |
event.current_buffer.validate_and_handle() | |
insert_mode = vi_insert_mode | emacs_insert_mode | |
@Condition | |
def has_text_before_cursor() -> bool: | |
return bool(get_app().current_buffer.text) | |
kb = get_ipython().pt_app.key_bindings | |
kb.add('c-backspace') \ | |
(get_by_name('backward-kill-word')) | |
kb.add('c-enter', filter=insert_mode & is_multiline) \ | |
(get_by_name('insert-line-above')) | |
kb.add('s-enter', filter=insert_mode & is_multiline) \ | |
(get_by_name('insert-line-above')) | |
kb.add('c-s-enter') \ | |
(get_by_name('accept-buffer')) | |
if IS_PTK2: | |
@register("beginning-of-buffer") | |
def beginning_of_buffer(event: E) -> None: | |
""" | |
Move to the start of the buffer. | |
""" | |
buff = event.current_buffer | |
buff.cursor_position = 0 | |
@register("end-of-buffer") | |
def end_of_buffer(event: E) -> None: | |
""" | |
Move to the end of the buffer. | |
""" | |
buff = event.current_buffer | |
buff.cursor_position = len(buff.text) | |
# Don't add c-home and c-end input sequences into the buffer | |
@kb.add('c-home') | |
@kb.add('c-end') | |
def _(event: E) -> None: | |
pass | |
kb.add('c-home')(get_by_name('beginning-of-buffer')) | |
kb.add('c-end')(get_by_name('end-of-buffer')) | |
__initialize_custom_keybindings() | |
del __initialize_custom_keybindings | |
None # hide output from ipython |
def __initialize_custom_line_magics(): | |
from functools import wraps | |
from IPython.core.magic import needs_local_scope, register_line_magic | |
def register_value_magic(fn): | |
@register_line_magic | |
@needs_local_scope | |
@wraps(fn) | |
def wrapper(line, local_ns): | |
if not line.strip(): | |
line = '_' | |
value = eval(line, globals(), local_ns) | |
return fn(value) | |
return wrapper | |
@register_value_magic | |
def v(value): | |
if hasattr(value, '__dict__'): | |
return value.__dict__ | |
elif hasattr(value, '__slots__'): | |
from itertools import chain | |
slots = chain.from_iterable(getattr(cls, '__slots__', ()) | |
for cls in type(value).__mro__) | |
return {k: getattr(value, k, None) for k in slots} | |
else: | |
return None | |
@register_value_magic | |
def p(value): | |
return print(value) | |
@register_value_magic | |
def l(value): | |
return list(value) | |
@register_value_magic | |
def t(value): | |
return tuple(value) | |
@register_value_magic | |
def s(value): | |
return set(value) | |
@register_value_magic | |
def d(value): | |
return dict(value) | |
__initialize_custom_line_magics() | |
del __initialize_custom_line_magics | |
None # hide output from ipython |
""" | |
This ipython config file installs a few extra prompt key bindings for a more | |
familiar editing interface. In particular, it adds support for: | |
- Ctrl+Home: | |
move to the beginning of the buffer | |
- Ctrl+End: | |
move to the end of the buffer | |
- Ctrl+Backspace: | |
delete the last word (only in supporting terminals, i.e. all modern terminals) | |
For weird, customized terminals like mine, a few additional bindings are supported: | |
- Ctrl+Enter (bound to F34): | |
Shift+Enter (bound to F35): | |
add a newline before the current line | |
- Ctrl+Shift+Enter (bound to F36): | |
execute the current buffer (alias of Esc, Enter) | |
""" | |
from enum import Enum | |
from itertools import chain | |
from prompt_toolkit import keys, application, VERSION as PTK_VERSION | |
from prompt_toolkit.input import ansi_escape_sequences, vt100_parser | |
from prompt_toolkit.key_binding import key_bindings, key_processor | |
from traitlets.config import Config | |
IS_PTK2 = IS_PTK3 = False | |
if ('3',) <= PTK_VERSION < ('4',): | |
IS_PTK3 = True | |
elif ('2',) <= PTK_VERSION < ('3',): | |
IS_PTK2 = True | |
def install_prompt_customizations(c: Config) -> None: | |
class StrEnum(str, Enum): | |
pass | |
class ExtraKeys(str, Enum): | |
value: str | |
ControlBackspace = 'c-backspace' | |
ControlEnter = 'c-enter' | |
ShiftEnter = 's-enter' | |
ControlShiftEnter = 'c-s-enter' | |
if IS_PTK3: | |
Keys = StrEnum('Keys', [(a.name, a.value) for a in chain(keys.Keys, ExtraKeys)], module='ipython_config') | |
Keys.__bases__ += (keys.Keys,) | |
keys.Keys = \ | |
key_bindings.Keys = \ | |
application.Keys = \ | |
vt100_parser.Keys = \ | |
Keys | |
def calculate_ALL_KEYS(): | |
return [k.value for k in Keys] | |
keys.ALL_KEYS = key_bindings.ALL_KEYS = calculate_ALL_KEYS() | |
elif IS_PTK2: | |
Keys = keys.Keys | |
Keys.ControlHome = 'c-home' | |
Keys.ControlEnd = 'c-end' | |
for key in ExtraKeys: | |
setattr(Keys, key.name, key.value) | |
def calculate_ALL_KEYS(): | |
return [getattr(Keys, k) for k in dir(Keys) if not k.startswith('_')] | |
keys.ALL_KEYS = key_bindings.ALL_KEYS = key_processor.ALL_KEYS = calculate_ALL_KEYS() | |
else: | |
# We only support v2 and v3 | |
return | |
ansi_escape_sequences.ANSI_SEQUENCES.update({ | |
'\x08': Keys.ControlBackspace, # Control-H (8) (Identical to '\b') | |
'\x1b[1;5H': Keys.ControlHome, | |
'\x1b[1;5F': Keys.ControlEnd, | |
'\x1b[21;5~': Keys.ControlEnter, # F34 | |
'\x1b[23;5~': Keys.ShiftEnter, # F35 | |
'\x1b[24;5~': Keys.ControlShiftEnter, # F36 | |
}) | |
ansi_escape_sequences.REVERSE_ANSI_SEQUENCES.clear() | |
ansi_escape_sequences.REVERSE_ANSI_SEQUENCES.update(ansi_escape_sequences._get_reverse_ansi_sequences()) | |
install_prompt_customizations(c) |