def register_ipython_shortcuts(registry, shell): """Set up the prompt_toolkit keyboard shortcuts for IPython""" insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode))( newline_or_execute_outer(shell)) registry.add_binding(Keys.ControlBackslash)(force_exit) registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER) ))(previous_history_or_previous_completion) registry.add_binding( Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))(next_history_or_next_completion) registry.add_binding(Keys.ControlG, filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions()))(dismiss_completion) registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))(reset_buffer) registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))(reset_search_buffer) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) registry.add_binding(Keys.ControlZ, filter=supports_suspend)(suspend_to_bg) # Ctrl+I == Tab registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws))(indent_buffer) registry.add_binding( Keys.ControlO, filter=(HasFocus(DEFAULT_BUFFER) & EmacsInsertMode()))(newline_with_copy_margin) if shell.display_completions == 'readlinelike': registry.add_binding( Keys.ControlI, filter=( HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & ~cursor_in_leading_ws))(display_completions_like_readline) if sys.platform == 'win32': registry.add_binding(Keys.ControlV, filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode()))(win_paste)
def custom_keybindings(bindings, **kw): from xonsh2.ptk_shell.key_bindings import carriage_return from prompt_toolkit.filters import EmacsInsertMode, ViInsertMode handler = bindings.add insert_mode = ViInsertMode() | EmacsInsertMode() @handler(" ", filter=IsMultiline() & insert_mode) def handle_space(event): buffer = event.app.current_buffer if not revert_abbrev(buffer): expand_abbrev(buffer) if last_expanded is None or not set_cursor_position(buffer): buffer.insert_text(" ") @handler(Keys.ControlJ, filter=IsMultiline() & insert_mode & ~completion_is_selected) @handler(Keys.ControlM, filter=IsMultiline() & insert_mode & ~completion_is_selected) def multiline_carriage_return(event): buffer = event.app.current_buffer current_char = buffer.document.current_char if not current_char or current_char.isspace(): expand_abbrev(buffer) carriage_return(buffer, event.cli)
def custom_keybindings(bindings, **kw): from prompt_toolkit.filters import EmacsInsertMode, ViInsertMode from xonsh.ptk_shell.key_bindings import carriage_return handler = bindings.add insert_mode = ViInsertMode() | EmacsInsertMode() abbrev = Abbreviation() @handler(" ", filter=IsMultiline() & insert_mode) def handle_space(event): buffer = event.app.current_buffer add_space = True if not abbrev.revert(buffer): position_changed = abbrev.expand(buffer) if position_changed: add_space = False if add_space: buffer.insert_text(" ") @handler(Keys.ControlJ, filter=IsMultiline() & insert_mode & ~completion_is_selected) @handler(Keys.ControlM, filter=IsMultiline() & insert_mode & ~completion_is_selected) def multiline_carriage_return(event): buffer = event.app.current_buffer current_char = buffer.document.current_char if not current_char or current_char.isspace(): abbrev.expand(buffer) carriage_return(buffer, event.cli)
def custom_keybindings(bindings, **kw): if ptk_shell_type() == "prompt_toolkit2": from xonsh.ptk2.key_bindings import carriage_return from prompt_toolkit.filters import EmacsInsertMode, ViInsertMode handler = bindings.add insert_mode = ViInsertMode() | EmacsInsertMode() else: from xonsh.ptk.key_bindings import carriage_return from prompt_toolkit.filters import to_filter handler = bindings.registry.add_binding insert_mode = to_filter(True) @handler(" ", filter=IsMultiline() & insert_mode) def handle_space(event): buffer = event.app.current_buffer expand_abbrev(buffer) buffer.insert_text(" ") @handler( Keys.ControlJ, filter=IsMultiline() & insert_mode & ~completion_is_selected ) @handler( Keys.ControlM, filter=IsMultiline() & insert_mode & ~completion_is_selected ) def multiline_carriage_return(event): buffer = event.app.current_buffer current_char = buffer.document.current_char if not current_char or current_char.isspace(): expand_abbrev(buffer) carriage_return(buffer, event.cli)
def custom_keybindings(bindings, **kw): handler = bindings.registry.add_binding insert_mode = ViInsertMode() | EmacsInsertMode() @Condition def last_command_exists(cli): return len(__xonsh_history__) > 0 @handler(Keys.Escape, '.', filter=last_command_exists & insert_mode) def recall_last_arg(event): arg = __xonsh_history__[-1].cmd.split()[-1] event.current_buffer.insert_text(arg)
def load_bindings(key_bindings_manager): handle = key_bindings_manager.registry.add_binding has_selection = HasSelection() @key_bindings_manager.registry.add_binding(Keys.ControlL)#, eager=True) def clear_(event): clear() print(env.welcome) PROMPT = env.prompt PROMPT = PROMPT.replace(r"\u", env.user).replace(r"\w", env.directory) print(unicode_(PROMPT), end="") @key_bindings_manager.registry.add_binding(Keys.ControlB) def list_(event): print("\n".join(ls(env, [], {}))) PROMPT = env.prompt PROMPT = PROMPT.replace(r"\u", env.user).replace(r"\w", env.directory) print(env.default_color, end="") print(unicode_(PROMPT), end="") @handle(Keys.ControlJ, filter= ~has_selection & (ViInsertMode() | EmacsInsertMode()) & HasFocus(DEFAULT_BUFFER) & IsMultiline()) def _(event): """ Behaviour of the Enter key. Auto indent after newline/Enter. (When not in Vi navigaton mode, and when multiline is enabled.) """ b = event.current_buffer empty_lines_required = 2 def at_the_end(b): """ we consider the cursor at the end when there is no text after the cursor, or only whitespace. """ text = b.document.text_after_cursor return text == '' or (text.isspace() and not '\n' in text) if at_the_end(b) and (b.document.text.replace(' ', '').endswith('\n' * (empty_lines_required - 1)) or (b.document.text.replace("\n", "").endswith(";"))): # When the cursor is at the end, and we have an empty line: # drop the empty lines, but return the value. b.document = Document( text=b.text.rstrip(), cursor_position=len(b.text.rstrip())) b.accept_action.validate_and_handle(event.cli, b) else: _auto_newline(b)
def custom_keybindings(bindings, **kw): if ptk_shell_type() == "prompt_toolkit2": handler = bindings.add else: handler = bindings.registry.add_binding insert_mode = ViInsertMode() | EmacsInsertMode() @Condition def last_command_exists(): return len(__xonsh__.history) > 0 @handler(Keys.Escape, ".", filter=last_command_exists & insert_mode) def recall_last_arg(event): arg = __xonsh__.history[-1].cmd.split()[-1] event.current_buffer.insert_text(arg)
def load_ipython_extension(ip): insert_mode = ViInsertMode() | EmacsInsertMode() def insert_unexpected(event): # buf = event.current_buffer # buf.insert_text("view(_)") # get_by_name('accept-line').handler(event) view(ip.user_ns['_']) # 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.ControlT, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode) )(insert_unexpected)
def pt_init(self): def get_prompt_tokens(cli): return [(Token.Prompt, self.prompt)] def patch_stdout(**kwargs): return self.pt_cli.patch_stdout_context(**kwargs) if self._ptcomp is None: compl = IPCompleter( shell=self.shell, namespace={}, global_namespace={}, use_readline=False, parent=self.shell, ) self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout) kbmanager = KeyBindingManager.for_prompt() supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)(suspend_to_bg) if self.shell.display_completions == 'readlinelike': kbmanager.registry.add_binding( Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & ViInsertMode() | EmacsInsertMode() & ~cursor_in_leading_ws ))(display_completions_like_readline) multicolumn = (self.shell.display_completions == 'multicolumn') self._pt_app = create_prompt_application( editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), key_bindings_registry=kbmanager.registry, history=self.shell.debugger_history, completer=self._ptcomp, enable_history_search=True, mouse_support=self.shell.mouse_support, get_prompt_tokens=get_prompt_tokens, display_completions_in_columns=multicolumn, ) self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
def init_prompt_toolkit_cli(self): if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty(): # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2( input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode)) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text) if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _reset_buffer(event): event.current_buffer.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _reset_search_buffer(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _suspend_to_bg(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws)) def _indent_buffer(event): event.current_buffer.insert_text(' ' * 4) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail( self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options()) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
def __rc(): import sys from pathlib import Path from IPython import get_ipython from IPython.core.magic import ( needs_local_scope, register_cell_magic, register_line_magic, ) from IPython.display import HTML, display from IPython.terminal.prompts import Prompts, Token from prompt_toolkit.enums import DEFAULT_BUFFER from prompt_toolkit.filters import EmacsInsertMode, HasFocus, HasSelection sys.path.append(str(Path(__file__).parents[3])) ip = get_ipython() insert_mode = 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( "escape", "m", filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode), ) def _(event): event.current_buffer.validate_and_handle() class MyPrompt(Prompts): def in_prompt_tokens(self, cli=None): return [(Token.Prompt, "❱ ")] def continuation_prompt_tokens(self, width=None): if width is None: width = self._width() return [ (Token.Prompt, (" " * (width - 2)) + "| "), ] def out_prompt_tokens(self): return [ (Token.OutPrompt, "❰ "), ] @register_line_magic @needs_local_scope def var(line, local_ns): import sympy result = sympy.symbols(line) try: for x in result: local_ns[x.name] = x return sympy.Matrix([result]) except TypeError: local_ns[x.name] = x return x def sympy_parse(line, local_ns, *args, **kwargs): import sympy try: return sympy.S(line, *args, **kwargs) except ValueError as err: if ":=" in line: name, expr = line.split(":=") result = sympy_parse(expr, local_ns, *args, **kwargs) local_ns[name.strip()] = result return result elif "=" in line: return sympy.S("Eq(" + ",".join(line.split("=")) + ")", *args, **kwargs) else: raise err @register_line_magic @needs_local_scope def s(line, local_ns): return sympy_parse(line, local_ns) @register_line_magic @needs_local_scope def sp(line, local_ns): return sympy_parse(line, local_ns, locals=local_ns) @register_cell_magic @needs_local_scope def interact(line, cell, local_ns): import ipywidgets decorator = eval( "ipywidgets.interact(" + line + ")", globals(), { **local_ns, "ipywidgets": ipywidgets }, ) lines = cell.strip().split("\n") last = "_result = " + lines[-1] local_ns["__interact_decorator"] = decorator src = ("\n@__interact_decorator" "\ndef __interact_f(" + ", ".join(decorator.kwargs) + "):" + "\n " + "\n ".join([*lines, last]) + "\n return _result") ip.run_cell(src, store_history=False) def pre_run_cell(info): display( HTML(f""" <script id="output-{id(info)}" >pre_run_cell && pre_run_cell('{id(info)}')</script> """)) def post_run_cell(result): display( HTML(f""" <script>post_run_cell && post_run_cell('{id(result.info)}', '{result.success}')</script> """)) def clear(): display( HTML(f""" <script>shell_initialized(); console.log('called')</script> """)) ip = get_ipython() ip.prompts = MyPrompt(ip) ip.events.register("pre_run_cell", pre_run_cell) ip.events.register("post_run_cell", post_run_cell) # matplotlib doesn't get my custom rc file if I don't run this # https://github.com/jupyter/notebook/issues/3385 ip.run_line_magic("matplotlib", "notebook")
def load_basic_bindings(): registry = Registry() insert_mode = ViInsertMode() | EmacsInsertMode() handle = registry.add_binding has_selection = HasSelection() @handle(Keys.ControlA) @handle(Keys.ControlB) @handle(Keys.ControlC) @handle(Keys.ControlD) @handle(Keys.ControlE) @handle(Keys.ControlF) @handle(Keys.ControlG) @handle(Keys.ControlH) @handle(Keys.ControlI) @handle(Keys.ControlJ) @handle(Keys.ControlK) @handle(Keys.ControlL) @handle(Keys.ControlM) @handle(Keys.ControlN) @handle(Keys.ControlO) @handle(Keys.ControlP) @handle(Keys.ControlQ) @handle(Keys.ControlR) @handle(Keys.ControlS) @handle(Keys.ControlT) @handle(Keys.ControlU) @handle(Keys.ControlV) @handle(Keys.ControlW) @handle(Keys.ControlX) @handle(Keys.ControlY) @handle(Keys.ControlZ) @handle(Keys.F1) @handle(Keys.F2) @handle(Keys.F3) @handle(Keys.F4) @handle(Keys.F5) @handle(Keys.F6) @handle(Keys.F7) @handle(Keys.F8) @handle(Keys.F9) @handle(Keys.F10) @handle(Keys.F11) @handle(Keys.F12) @handle(Keys.F13) @handle(Keys.F14) @handle(Keys.F15) @handle(Keys.F16) @handle(Keys.F17) @handle(Keys.F18) @handle(Keys.F19) @handle(Keys.F20) @handle(Keys.ControlSpace) @handle(Keys.ControlBackslash) @handle(Keys.ControlSquareClose) @handle(Keys.ControlCircumflex) @handle(Keys.ControlUnderscore) @handle(Keys.Backspace) @handle(Keys.Up) @handle(Keys.Down) @handle(Keys.Right) @handle(Keys.Left) @handle(Keys.ShiftUp) @handle(Keys.ShiftDown) @handle(Keys.ShiftRight) @handle(Keys.ShiftLeft) @handle(Keys.Home) @handle(Keys.End) @handle(Keys.Delete) @handle(Keys.ShiftDelete) @handle(Keys.ControlDelete) @handle(Keys.PageUp) @handle(Keys.PageDown) @handle(Keys.BackTab) @handle(Keys.Tab) @handle(Keys.ControlLeft) @handle(Keys.ControlRight) @handle(Keys.ControlUp) @handle(Keys.ControlDown) @handle(Keys.Insert) @handle(Keys.Ignore) def _(event): """ First, for any of these keys, Don't do anything by default. Also don't catch them in the 'Any' handler which will insert them as data. If people want to insert these characters as a literal, they can always do by doing a quoted insert. (ControlQ in emacs mode, ControlV in Vi mode.) """ pass # Readline-style bindings. handle(Keys.Home)(get_by_name('beginning-of-line')) handle(Keys.End)(get_by_name('end-of-line')) handle(Keys.Left)(get_by_name('backward-char')) handle(Keys.Right)(get_by_name('forward-char')) handle(Keys.ControlUp)(get_by_name('previous-history')) handle(Keys.ControlDown)(get_by_name('next-history')) handle(Keys.ControlL)(get_by_name('clear-screen')) handle(Keys.ControlK, filter=insert_mode)(get_by_name('kill-line')) handle(Keys.ControlU, filter=insert_mode)(get_by_name('unix-line-discard')) handle(Keys.ControlH, filter=insert_mode, save_before=if_no_repeat)( get_by_name('backward-delete-char')) handle(Keys.Backspace, filter=insert_mode, save_before=if_no_repeat)( get_by_name('backward-delete-char')) handle(Keys.Delete, filter=insert_mode, save_before=if_no_repeat)( get_by_name('delete-char')) handle(Keys.ShiftDelete, filter=insert_mode, save_before=if_no_repeat)( get_by_name('delete-char')) handle(Keys.Any, filter=insert_mode, save_before=if_no_repeat)( get_by_name('self-insert')) handle(Keys.ControlT, filter=insert_mode)(get_by_name('transpose-chars')) handle(Keys.ControlW, filter=insert_mode)(get_by_name('unix-word-rubout')) handle(Keys.ControlI, filter=insert_mode)(get_by_name('menu-complete')) handle(Keys.BackTab, filter=insert_mode)(get_by_name('menu-complete-backward')) handle(Keys.PageUp, filter= ~has_selection)(get_by_name('previous-history')) handle(Keys.PageDown, filter= ~has_selection)(get_by_name('next-history')) # CTRL keys. text_before_cursor = Condition(lambda cli: cli.current_buffer.text) handle(Keys.ControlD, filter=text_before_cursor & insert_mode)(get_by_name('delete-char')) is_multiline = Condition(lambda cli: cli.current_buffer.is_multiline()) is_returnable = Condition(lambda cli: cli.current_buffer.accept_action.is_returnable) @handle(Keys.ControlJ, filter=is_multiline & insert_mode) def _(event): " Newline (in case of multiline input. " event.current_buffer.newline(copy_margin=not event.cli.in_paste_mode) @handle(Keys.ControlJ, filter=~is_multiline & is_returnable) def _(event): " Enter, accept input. " buff = event.current_buffer buff.accept_action.validate_and_handle(event.cli, buff) # Delete the word before the cursor. @handle(Keys.Up) def _(event): event.current_buffer.auto_up(count=event.arg) @handle(Keys.Down) def _(event): event.current_buffer.auto_down(count=event.arg) @handle(Keys.Delete, filter=has_selection) def _(event): data = event.current_buffer.cut_selection() event.cli.clipboard.set_data(data) # Global bindings. @handle(Keys.ControlZ) def _(event): """ By default, control-Z should literally insert Ctrl-Z. (Ansi Ctrl-Z, code 26 in MSDOS means End-Of-File. In a Python REPL for instance, it's possible to type Control-Z followed by enter to quit.) When the system bindings are loaded and suspend-to-background is supported, that will override this binding. """ event.current_buffer.insert_text(event.data) @handle(Keys.CPRResponse, save_before=lambda e: False) def _(event): """ Handle incoming Cursor-Position-Request response. """ # The incoming data looks like u'\x1b[35;1R' # Parse row/col information. row, col = map(int, event.data[2:-1].split(';')) # Report absolute cursor position to the renderer. event.cli.renderer.report_absolute_cursor_row(row) @handle(Keys.BracketedPaste) def _(event): " Pasting from clipboard. " data = event.data # Be sure to use \n as line ending. # Some terminals (Like iTerm2) seem to paste \r\n line endings in a # bracketed paste. See: https://github.com/ipython/ipython/issues/9737 data = data.replace('\r\n', '\n') data = data.replace('\r', '\n') event.current_buffer.insert_text(data) @handle(Keys.Any, filter=Condition(lambda cli: cli.quoted_insert), eager=True) def _(event): """ Handle quoted insert. """ event.current_buffer.insert_text(event.data, overwrite=False) event.cli.quoted_insert = False return registry
def load_emacs_bindings(registry, filter=Always()): """ Some e-macs extensions. """ # Overview of Readline emacs commands: # http://www.catonmat.net/download/readline-emacs-editing-mode-cheat-sheet.pdf filter = to_cli_filter(filter) handle = create_handle_decorator(registry, filter & EmacsMode()) insert_mode = EmacsInsertMode() has_selection = HasSelection() @handle(Keys.Escape) def _(event): """ By default, ignore escape key. (If we don't put this here, and Esc is followed by a key which sequence is not handled, we'll insert an Escape character in the input stream. Something we don't want and happens to easily in emacs mode. Further, people can always use ControlQ to do a quoted insert.) """ pass handle(Keys.ControlA)(get_by_name('beginning-of-line')) handle(Keys.ControlB)(get_by_name('backward-char')) handle(Keys.ControlDelete, filter=insert_mode)(get_by_name('kill-word')) handle(Keys.ControlE)(get_by_name('end-of-line')) handle(Keys.ControlF)(get_by_name('forward-char')) handle(Keys.ControlLeft)(get_by_name('backward-word')) handle(Keys.ControlRight)(get_by_name('forward-word')) handle(Keys.ControlX, 'r', 'y', filter=insert_mode)(get_by_name('yank')) handle(Keys.ControlY, filter=insert_mode)(get_by_name('yank')) handle(Keys.Escape, 'b')(get_by_name('backward-word')) handle(Keys.Escape, 'c', filter=insert_mode)(get_by_name('capitalize-word')) handle(Keys.Escape, 'd', filter=insert_mode)(get_by_name('kill-word')) handle(Keys.Escape, 'f')(get_by_name('forward-word')) handle(Keys.Escape, 'l', filter=insert_mode)(get_by_name('downcase-word')) handle(Keys.Escape, 'u', filter=insert_mode)(get_by_name('uppercase-word')) handle(Keys.Escape, Keys.ControlH, filter=insert_mode)(get_by_name('unix-word-rubout')) handle(Keys.Escape, Keys.Backspace, filter=insert_mode)(get_by_name('unix-word-rubout')) handle(Keys.Escape, '\\', filter=insert_mode)(get_by_name('delete-horizontal-space')) handle(Keys.ControlUnderscore, save_before=(lambda e: False), filter=insert_mode)(get_by_name('undo')) handle(Keys.ControlX, Keys.ControlU, save_before=(lambda e: False), filter=insert_mode)(get_by_name('undo')) handle(Keys.Escape, '<', filter=~has_selection)(get_by_name('beginning-of-history')) handle(Keys.Escape, '>', filter=~has_selection)(get_by_name('end-of-history')) @handle(Keys.ControlN) def _(event): " Next line. " event.current_buffer.auto_down() @handle(Keys.ControlO, filter=insert_mode) def _(event): " Insert newline, but don't move the cursor. " event.current_buffer.insert_text('\n', move_cursor=False) @handle(Keys.ControlP) def _(event): " Previous line. " event.current_buffer.auto_up(count=event.arg) @handle(Keys.ControlQ, Keys.Any, filter=~has_selection) def _(event): """ Quoted insert. For vt100 terminals, you have to disable flow control by running ``stty -ixon``, otherwise Ctrl-Q and Ctrl-S are captured by the terminal. """ event.current_buffer.insert_text(event.data, overwrite=False) def handle_digit(c): """ Handle input of arguments. The first number needs to be preceeded by escape. """ @handle(c, filter=HasArg()) @handle(Keys.Escape, c) def _(event): event.append_to_arg_count(c) for c in '0123456789': handle_digit(c) @handle(Keys.Escape, '-', filter=~HasArg()) def _(event): """ """ if event._arg is None: event.append_to_arg_count('-') @handle('-', filter=Condition(lambda cli: cli.input_processor.arg == '-')) def _(event): """ When '-' is typed again, after exactly '-' has been given as an argument, ignore this. """ event.cli.input_processor.arg = '-' is_returnable = Condition( lambda cli: cli.current_buffer.accept_action.is_returnable) # Meta + Newline: always accept input. handle(Keys.Escape, Keys.ControlJ, filter=insert_mode & is_returnable)(get_by_name('accept-line')) def character_search(buff, char, count): if count < 0: match = buff.document.find_backwards(char, in_current_line=True, count=-count) else: match = buff.document.find(char, in_current_line=True, count=count) if match is not None: buff.cursor_position += match @handle(Keys.ControlSquareClose, Keys.Any) def _(event): " When Ctl-] + a character is pressed. go to that character. " character_search(event.current_buffer, event.data, event.arg) @handle(Keys.Escape, Keys.ControlSquareClose, Keys.Any) def _(event): " Like Ctl-], but backwards. " character_search(event.current_buffer, event.data, -event.arg) @handle(Keys.Escape, 'a') def _(event): " Previous sentence. " # TODO: @handle(Keys.Escape, 'e') def _(event): " Move to end of sentence. " # TODO: @handle(Keys.Escape, 't', filter=insert_mode) def _(event): """ Swap the last two words before the cursor. """ # TODO @handle(Keys.Escape, '.', filter=insert_mode) def _(event): """ Rotate through the last word (white-space delimited) of the previous lines in history. """ # TODO @handle(Keys.Escape, '*', filter=insert_mode) def _(event): """ `meta-*`: Insert all possible completions of the preceding text. """ buff = event.current_buffer # List all completions. complete_event = CompleteEvent(text_inserted=False, completion_requested=True) completions = list( buff.completer.get_completions(buff.document, complete_event)) # Insert them. text_to_insert = ' '.join(c.text for c in completions) buff.insert_text(text_to_insert) @handle(Keys.ControlX, Keys.ControlX) def _(event): """ Move cursor back and forth between the start and end of the current line. """ buffer = event.current_buffer if buffer.document.is_cursor_at_the_end_of_line: buffer.cursor_position += buffer.document.get_start_of_line_position( after_whitespace=False) else: buffer.cursor_position += buffer.document.get_end_of_line_position( ) @handle(Keys.ControlSpace) def _(event): """ Start of the selection (if the current buffer is not empty). """ # Take the current cursor position as the start of this selection. buff = event.current_buffer if buff.text: buff.start_selection(selection_type=SelectionType.CHARACTERS) @handle(Keys.ControlG, filter=~has_selection) def _(event): """ Control + G: Cancel completion menu and validation state. """ event.current_buffer.complete_state = None event.current_buffer.validation_error = None @handle(Keys.ControlG, filter=has_selection) def _(event): """ Cancel selection. """ event.current_buffer.exit_selection() @handle(Keys.ControlW, filter=has_selection) @handle(Keys.ControlX, 'r', 'k', filter=has_selection) def _(event): """ Cut selected text. """ data = event.current_buffer.cut_selection() event.cli.clipboard.set_data(data) @handle(Keys.Escape, 'w', filter=has_selection) def _(event): """ Copy selected text. """ data = event.current_buffer.copy_selection() event.cli.clipboard.set_data(data) @handle(Keys.Escape, Keys.Left) def _(event): """ Cursor to start of previous word. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.find_previous_word_beginning( count=event.arg) or 0 @handle(Keys.Escape, Keys.Right) def _(event): """ Cursor to start of next word. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.find_next_word_beginning(count=event.arg) or \ buffer.document.get_end_of_document_position() @handle(Keys.Escape, '/', filter=insert_mode) def _(event): """ M-/: Complete. """ b = event.current_buffer if b.complete_state: b.complete_next() else: event.cli.start_completion(select_first=True) @handle(Keys.ControlC, '>', filter=has_selection) def _(event): """ Indent selected text. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.get_start_of_line_position( after_whitespace=True) from_, to = buffer.document.selection_range() from_, _ = buffer.document.translate_index_to_position(from_) to, _ = buffer.document.translate_index_to_position(to) indent(buffer, from_, to + 1, count=event.arg) @handle(Keys.ControlC, '<', filter=has_selection) def _(event): """ Unindent selected text. """ buffer = event.current_buffer from_, to = buffer.document.selection_range() from_, _ = buffer.document.translate_index_to_position(from_) to, _ = buffer.document.translate_index_to_position(to) unindent(buffer, from_, to + 1, count=event.arg)
def init_prompt_toolkit_cli(self): self._app = None if self.simple_prompt: # Fall back to plain non-interactive output for tests. # This is very limited, and only accepts a single line. def prompt(): return cast_unicode_py2( input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode)) def _(event): b = event.current_buffer d = b.document if b.complete_state: cc = b.complete_state.current_completion if cc: b.apply_completion(cc) else: b.cancel_completion() return if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return status, indent = self.input_splitter.check_complete(d.text + '\n') if (status != 'incomplete') and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + (' ' * (indent or 0))) @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) def _previous_history_or_previous_completion(event): """ Control-P in vi edit mode on readline is history next, unlike default prompt toolkit. If completer is open this still select previous completion. """ event.current_buffer.auto_up() @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER))) def _next_history_or_next_completion(event): """ Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit. If completer is open this still select next completion. """ event.current_buffer.auto_down() @kbmanager.registry.add_binding(Keys.ControlG, filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions())) def _dismiss_completion(event): b = event.current_buffer if b.complete_state: b.cancel_completion() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _reset_buffer(event): b = event.current_buffer if b.complete_state: b.cancel_completion() else: b.reset() @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)) def _reset_search_buffer(event): if event.current_buffer.document.text: event.current_buffer.reset() else: event.cli.push_focus(DEFAULT_BUFFER) supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP')) @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend) def _suspend_to_bg(event): event.cli.suspend_to_background() @Condition def cursor_in_leading_ws(cli): before = cli.application.buffer.document.current_line_before_cursor return (not before) or before.isspace() # Ctrl+I == Tab @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & cursor_in_leading_ws)) def _indent_buffer(event): event.current_buffer.insert_text(' ' * 4) if self.display_completions == 'readlinelike': @kbmanager.registry.add_binding(Keys.ControlI, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode & ~cursor_in_leading_ws)) def _disaply_compl(ev): display_completions_like_readline(ev) if sys.platform == 'win32': from IPython.lib.clipboard import (ClipboardEmpty, win32_clipboard_get, tkinter_clipboard_get) @kbmanager.registry.add_binding(Keys.ControlV, filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode())) def _paste(event): try: text = win32_clipboard_get() except TryNext: try: text = tkinter_clipboard_get() except (TryNext, ClipboardEmpty): return except ClipboardEmpty: return event.current_buffer.insert_text(text.replace('\t', ' ' * 4)) # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for __, ___, cell in self.history_manager.get_tail( self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cell.rstrip() if cell and (cell != last_cell): history.append(cell) self._style = self._make_style_from_name(self.highlighting_style) style = DynamicStyle(lambda: self._style) editing_mode = getattr(EditingMode, self.editing_mode.upper()) self._app = create_prompt_application( editing_mode=editing_mode, key_bindings_registry=kbmanager.registry, history=history, completer=IPythonPTCompleter(self.Completer), enable_history_search=True, style=style, mouse_support=self.mouse_support, **self._layout_options()) self._eventloop = create_eventloop(self.inputhook) self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
def load_xonsh_bindings() -> KeyBindingsBase: """ Load custom key bindings. """ key_bindings = KeyBindings() handle = key_bindings.add has_selection = HasSelection() insert_mode = ViInsertMode() | EmacsInsertMode() @handle(Keys.Tab, filter=tab_insert_indent) def insert_indent(event): """ If there are only whitespaces before current cursor position insert indent instead of autocompleting. """ env = builtins.__xonsh__.env event.cli.current_buffer.insert_text(env.get("INDENT")) @handle(Keys.Tab, filter=~tab_insert_indent & tab_menu_complete) def menu_complete_select(event): """Start completion in menu-complete mode, or tab to next completion""" b = event.current_buffer if b.complete_state: b.complete_next() else: b.start_completion(select_first=True) @handle(Keys.ControlX, Keys.ControlE, filter=~has_selection) def open_editor(event): """Open current buffer in editor""" event.current_buffer.open_in_editor(event.cli) @handle(Keys.BackTab, filter=insert_mode) def insert_literal_tab(event): """Insert literal tab on Shift+Tab instead of autocompleting""" b = event.current_buffer if b.complete_state: b.complete_previous() else: env = builtins.__xonsh__.env event.cli.current_buffer.insert_text(env.get("INDENT")) def generate_parens_handlers(left, right): @handle(left, filter=autopair_condition) def insert_left_paren(event): buffer = event.cli.current_buffer if has_selection(): wrap_selection(buffer, left, right) elif whitespace_or_bracket_after(): buffer.insert_text(left) buffer.insert_text(right, move_cursor=False) else: buffer.insert_text(left) @handle(right, filter=autopair_condition) def overwrite_right_paren(event): buffer = event.cli.current_buffer if buffer.document.current_char == right: buffer.cursor_position += 1 else: buffer.insert_text(right) generate_parens_handlers("(", ")") generate_parens_handlers("[", "]") generate_parens_handlers("{", "}") def generate_quote_handler(quote): @handle(quote, filter=autopair_condition) def insert_quote(event): buffer = event.cli.current_buffer if has_selection(): wrap_selection(buffer, quote, quote) elif buffer.document.current_char == quote: buffer.cursor_position += 1 elif whitespace_or_bracket_before() and whitespace_or_bracket_after(): buffer.insert_text(quote) buffer.insert_text(quote, move_cursor=False) else: buffer.insert_text(quote) generate_quote_handler("'") generate_quote_handler('"') @handle(Keys.Backspace, filter=autopair_condition) def delete_brackets_or_quotes(event): """Delete empty pair of brackets or quotes""" buffer = event.cli.current_buffer before = buffer.document.char_before_cursor after = buffer.document.current_char if any( [before == b and after == a for (b, a) in ["()", "[]", "{}", "''", '""']] ): buffer.delete(1) buffer.delete_before_cursor(1) @handle(Keys.ControlD, filter=ctrl_d_condition) def call_exit_alias(event): """Use xonsh exit function""" b = event.cli.current_buffer b.validate_and_handle() xonsh_exit([]) @handle(Keys.ControlJ, filter=IsMultiline() & insert_mode) @handle(Keys.ControlM, filter=IsMultiline() & insert_mode) def multiline_carriage_return(event): """Wrapper around carriage_return multiline parser""" b = event.cli.current_buffer carriage_return(b, event.cli) @handle(Keys.ControlJ, filter=should_confirm_completion) @handle(Keys.ControlM, filter=should_confirm_completion) def enter_confirm_completion(event): """Ignore <enter> (confirm completion)""" event.current_buffer.complete_state = None @handle(Keys.Escape, filter=should_confirm_completion) def esc_cancel_completion(event): """Use <ESC> to cancel completion""" event.cli.current_buffer.cancel_completion() @handle(Keys.Escape, Keys.ControlJ) def execute_block_now(event): """Execute a block of text irrespective of cursor position""" b = event.cli.current_buffer b.validate_and_handle() @handle(Keys.Left, filter=beginning_of_line) def wrap_cursor_back(event): """Move cursor to end of previous line unless at beginning of document """ b = event.cli.current_buffer b.cursor_up(count=1) relative_end_index = b.document.get_end_of_line_position() b.cursor_right(count=relative_end_index) @handle(Keys.Right, filter=end_of_line) def wrap_cursor_forward(event): """Move cursor to beginning of next line unless at end of document""" b = event.cli.current_buffer relative_begin_index = b.document.get_start_of_line_position() b.cursor_left(count=abs(relative_begin_index)) b.cursor_down(count=1) @handle(Keys.ControlM, filter=IsSearching()) @handle(Keys.ControlJ, filter=IsSearching()) def accept_search(event): search.accept_search() @handle(Keys.ControlZ) def skip_control_z(event): """Prevents the writing of ^Z to the prompt, if Ctrl+Z was pressed during the previous command. """ pass @handle(Keys.ControlX, Keys.ControlX, filter=has_selection) def _cut(event): """Cut selected text.""" data = event.current_buffer.cut_selection() event.app.clipboard.set_data(data) @handle(Keys.ControlX, Keys.ControlC, filter=has_selection) def _copy(event): """Copy selected text.""" data = event.current_buffer.copy_selection() event.app.clipboard.set_data(data) @handle(Keys.ControlV, filter=insert_mode | has_selection) def _yank(event): """Paste selected text.""" buff = event.current_buffer if buff.selection_state: buff.cut_selection() get_by_name("yank").call(event) return key_bindings
def load_xonsh_bindings(key_bindings_manager): """ Load custom key bindings. """ handle = key_bindings_manager.registry.add_binding has_selection = HasSelection() insert_mode = ViInsertMode() | EmacsInsertMode() @handle(Keys.Tab, filter=TabShouldInsertIndentFilter()) def _(event): """ If there are only whitespaces before current cursor position insert indent instead of autocompleting. """ event.cli.current_buffer.insert_text(env.get('INDENT')) @handle(Keys.ControlX, Keys.ControlE, filter=~has_selection) def open_editor(event): """ Open current buffer in editor """ event.current_buffer.open_in_editor(event.cli) @handle(Keys.BackTab) def insert_literal_tab(event): """ Insert literal tab on Shift+Tab instead of autocompleting """ event.cli.current_buffer.insert_text(env.get('INDENT')) @handle(Keys.ControlD, filter=ctrl_d_condition) def call_exit_alias(event): """Use xonsh exit function""" b = event.cli.current_buffer b.accept_action.validate_and_handle(event.cli, b) xonsh_exit([]) @handle(Keys.ControlJ, filter=IsMultiline()) def multiline_carriage_return(event): """ Wrapper around carriage_return multiline parser """ b = event.cli.current_buffer carriage_return(b, event.cli) @handle(Keys.Left, filter=BeginningOfLine()) def wrap_cursor_back(event): """Move cursor to end of previous line unless at beginning of document""" b = event.cli.current_buffer b.cursor_up(count=1) relative_end_index = b.document.get_end_of_line_position() b.cursor_right(count=relative_end_index) @handle(Keys.Right, filter=EndOfLine()) def wrap_cursor_forward(event): """Move cursor to beginning of next line unless at end of document""" b = event.cli.current_buffer relative_begin_index = b.document.get_start_of_line_position() b.cursor_left(count=abs(relative_begin_index)) b.cursor_down(count=1) @handle(Keys.ControlI, filter=insert_mode) def generate_completions(event): """ Tab-completion: where the first tab completes the common suffix and the second tab lists all the completions. Notes ----- This method was forked from the mainline prompt-toolkit repo. Copyright (c) 2014, Jonathan Slenders, All rights reserved. """ b = event.current_buffer def second_tab(): if b.complete_state: b.complete_next() else: event.cli.start_completion(select_first=False) # On the second tab-press, or when already navigating through # completions. if event.is_repeat or b.complete_state: second_tab() else: event.cli.start_completion(insert_common_part=True, select_first=False)
def load_xonsh_bindings(ptk_bindings: KeyBindingsBase) -> KeyBindingsBase: """ Load custom key bindings. Parameters ---------- ptk_bindings : The default prompt toolkit bindings. We need these to add aliases to them. """ key_bindings = KeyBindings() handle = key_bindings.add has_selection = HasSelection() insert_mode = ViInsertMode() | EmacsInsertMode() if XSH.env["XONSH_CTRL_BKSP_DELETION"]: # Not all terminal emulators emit the same keys for backspace, therefore # ptk always maps backspace ("\x7f") to ^H ("\x08"), and all the backspace bindings are registered for ^H. # This means we can't re-map backspace and instead we register a new "real-ctrl-bksp" key. # See https://github.com/xonsh/xonsh/issues/4407 if ON_WINDOWS: # On windows BKSP is "\x08" and CTRL-BKSP is "\x7f" REAL_CTRL_BKSP = "\x7f" # PTK uses a second mapping from prompt_toolkit.input import win32 as ptk_win32 ptk_win32.ConsoleInputReader.mappings[b"\x7f"] = REAL_CTRL_BKSP # type: ignore else: REAL_CTRL_BKSP = "\x08" # Prompt-toolkit allows using single-character keys that aren't in the `Keys` enum. ansi_escape_sequences.ANSI_SEQUENCES[REAL_CTRL_BKSP] = REAL_CTRL_BKSP # type: ignore ansi_escape_sequences.REVERSE_ANSI_SEQUENCES[REAL_CTRL_BKSP] = REAL_CTRL_BKSP # type: ignore @handle(REAL_CTRL_BKSP, filter=insert_mode) def delete_word(event): """Delete a single word (like ALT-backspace)""" get_by_name("backward-kill-word").call(event) @handle(Keys.Tab, filter=tab_insert_indent) def insert_indent(event): """ If there are only whitespaces before current cursor position insert indent instead of autocompleting. """ env = XSH.env event.cli.current_buffer.insert_text(env.get("INDENT")) @handle(Keys.Tab, filter=~tab_insert_indent & tab_menu_complete) def menu_complete_select(event): """Start completion in menu-complete mode, or tab to next completion""" b = event.current_buffer if b.complete_state: b.complete_next() else: b.start_completion(select_first=True) @handle(Keys.ControlX, Keys.ControlE, filter=~has_selection) def open_editor(event): """Open current buffer in editor""" event.current_buffer.open_in_editor(event.cli) @handle(Keys.BackTab, filter=insert_mode) def insert_literal_tab(event): """Insert literal tab on Shift+Tab instead of autocompleting""" b = event.current_buffer if b.complete_state: b.complete_previous() else: env = XSH.env event.cli.current_buffer.insert_text(env.get("INDENT")) def generate_parens_handlers(left, right): @handle(left, filter=autopair_condition) def insert_left_paren(event): buffer = event.cli.current_buffer if has_selection(): wrap_selection(buffer, left, right) elif whitespace_or_bracket_after(): buffer.insert_text(left) buffer.insert_text(right, move_cursor=False) else: buffer.insert_text(left) @handle(right, filter=autopair_condition) def overwrite_right_paren(event): buffer = event.cli.current_buffer if buffer.document.current_char == right: buffer.cursor_position += 1 else: buffer.insert_text(right) generate_parens_handlers("(", ")") generate_parens_handlers("[", "]") generate_parens_handlers("{", "}") def generate_quote_handler(quote): @handle(quote, filter=autopair_condition) def insert_quote(event): buffer = event.cli.current_buffer if has_selection(): wrap_selection(buffer, quote, quote) elif buffer.document.current_char == quote: buffer.cursor_position += 1 elif whitespace_or_bracket_before() and whitespace_or_bracket_after(): buffer.insert_text(quote) buffer.insert_text(quote, move_cursor=False) else: buffer.insert_text(quote) generate_quote_handler("'") generate_quote_handler('"') @handle(Keys.Backspace, filter=autopair_condition) def delete_brackets_or_quotes(event): """Delete empty pair of brackets or quotes""" buffer = event.cli.current_buffer before = buffer.document.char_before_cursor after = buffer.document.current_char if any( [before == b and after == a for (b, a) in ["()", "[]", "{}", "''", '""']] ): buffer.delete(1) buffer.delete_before_cursor(1) @handle(Keys.ControlD, filter=ctrl_d_condition) def call_exit_alias(event): """Use xonsh exit function""" b = event.cli.current_buffer b.validate_and_handle() xonsh_exit([]) @handle(Keys.ControlJ, filter=IsMultiline() & insert_mode) @handle(Keys.ControlM, filter=IsMultiline() & insert_mode) def multiline_carriage_return(event): """Wrapper around carriage_return multiline parser""" b = event.cli.current_buffer carriage_return(b, event.cli) @handle(Keys.ControlJ, filter=should_confirm_completion) @handle(Keys.ControlM, filter=should_confirm_completion) def enter_confirm_completion(event): """Ignore <enter> (confirm completion)""" event.current_buffer.complete_state = None @handle(Keys.Escape, filter=should_confirm_completion) def esc_cancel_completion(event): """Use <ESC> to cancel completion""" event.cli.current_buffer.cancel_completion() @handle(Keys.Escape, Keys.ControlJ) def execute_block_now(event): """Execute a block of text irrespective of cursor position""" b = event.cli.current_buffer b.validate_and_handle() @handle(Keys.Left, filter=beginning_of_line) def wrap_cursor_back(event): """Move cursor to end of previous line unless at beginning of document """ b = event.cli.current_buffer b.cursor_up(count=1) relative_end_index = b.document.get_end_of_line_position() b.cursor_right(count=relative_end_index) @handle(Keys.Right, filter=end_of_line) def wrap_cursor_forward(event): """Move cursor to beginning of next line unless at end of document""" b = event.cli.current_buffer relative_begin_index = b.document.get_start_of_line_position() b.cursor_left(count=abs(relative_begin_index)) b.cursor_down(count=1) @handle(Keys.ControlM, filter=IsSearching()) @handle(Keys.ControlJ, filter=IsSearching()) def accept_search(event): search.accept_search() @handle(Keys.ControlZ) def skip_control_z(event): """Prevents the writing of ^Z to the prompt, if Ctrl+Z was pressed during the previous command. """ pass @handle(Keys.ControlX, Keys.ControlX, filter=has_selection) def _cut(event): """Cut selected text.""" data = event.current_buffer.cut_selection() event.app.clipboard.set_data(data) @handle(Keys.ControlX, Keys.ControlC, filter=has_selection) def _copy(event): """Copy selected text.""" data = event.current_buffer.copy_selection() event.app.clipboard.set_data(data) @handle(Keys.ControlV, filter=insert_mode | has_selection) def _yank(event): """Paste selected text.""" buff = event.current_buffer if buff.selection_state: buff.cut_selection() get_by_name("yank").call(event) def create_alias(new_keys, original_keys): bindings = ptk_bindings.get_bindings_for_keys(tuple(original_keys)) for original_binding in bindings: handle(*new_keys, filter=original_binding.filter)(original_binding.handler) # Complete a single auto-suggestion word create_alias([Keys.ControlRight], ["escape", "f"]) return key_bindings
def load_basic_bindings(registry, filter=Always()): assert isinstance(filter, CLIFilter) insert_mode = ViInsertMode() | EmacsInsertMode() handle = create_handle_decorator(registry, filter) has_selection = HasSelection() @handle(Keys.ControlA) @handle(Keys.ControlB) @handle(Keys.ControlC) @handle(Keys.ControlD) @handle(Keys.ControlE) @handle(Keys.ControlF) @handle(Keys.ControlG) @handle(Keys.ControlH) @handle(Keys.ControlI) @handle(Keys.ControlJ) @handle(Keys.ControlK) @handle(Keys.ControlL) @handle(Keys.ControlM) @handle(Keys.ControlN) @handle(Keys.ControlO) @handle(Keys.ControlP) @handle(Keys.ControlQ) @handle(Keys.ControlR) @handle(Keys.ControlS) @handle(Keys.ControlT) @handle(Keys.ControlU) @handle(Keys.ControlV) @handle(Keys.ControlW) @handle(Keys.ControlX) @handle(Keys.ControlY) @handle(Keys.ControlZ) @handle(Keys.F1) @handle(Keys.F2) @handle(Keys.F3) @handle(Keys.F4) @handle(Keys.F5) @handle(Keys.F6) @handle(Keys.F7) @handle(Keys.F8) @handle(Keys.F9) @handle(Keys.F10) @handle(Keys.F11) @handle(Keys.F12) @handle(Keys.F13) @handle(Keys.F14) @handle(Keys.F15) @handle(Keys.F16) @handle(Keys.F17) @handle(Keys.F18) @handle(Keys.F19) @handle(Keys.F20) @handle(Keys.ControlSpace) @handle(Keys.ControlBackslash) @handle(Keys.ControlSquareClose) @handle(Keys.ControlCircumflex) @handle(Keys.ControlUnderscore) @handle(Keys.Backspace) @handle(Keys.Up) @handle(Keys.Down) @handle(Keys.Right) @handle(Keys.Left) @handle(Keys.Home) @handle(Keys.End) @handle(Keys.Delete) @handle(Keys.ShiftDelete) @handle(Keys.ControlDelete) @handle(Keys.PageUp) @handle(Keys.PageDown) @handle(Keys.BackTab) @handle(Keys.Tab) @handle(Keys.ControlLeft) @handle(Keys.ControlRight) @handle(Keys.ControlUp) @handle(Keys.ControlDown) @handle(Keys.Insert) @handle(Keys.Ignore) def _(event): """ First, for any of these keys, Don't do anything by default. Also don't catch them in the 'Any' handler which will insert them as data. If people want to insert these characters as a literal, they can always do by doing a quoted insert. (ControlQ in emacs mode, ControlV in Vi mode.) """ pass @handle(Keys.Home) def _(event): b = event.current_buffer b.cursor_position += b.document.get_start_of_line_position() @handle(Keys.End) def _(event): b = event.current_buffer b.cursor_position += b.document.get_end_of_line_position() # CTRL keys. text_before_cursor = Condition(lambda cli: cli.current_buffer.text) @handle(Keys.ControlD, filter=text_before_cursor & insert_mode) def _(event): " Delete text before cursor. " event.current_buffer.delete(event.arg) # Tab completion. (ControlI == Tab) handle(Keys.ControlI, filter=insert_mode)(generate_completions) @handle(Keys.BackTab, filter=insert_mode) def _(event): """ Shift+Tab: go to previous completion. """ event.current_buffer.complete_previous() is_multiline = Condition(lambda cli: cli.current_buffer.is_multiline()) is_returnable = Condition(lambda cli: cli.current_buffer.accept_action.is_returnable) @handle(Keys.ControlJ, filter=is_multiline) def _(event): " Newline (in case of multiline input. " event.current_buffer.newline(copy_margin=not event.cli.in_paste_mode) @handle(Keys.ControlJ, filter=~is_multiline & is_returnable) def _(event): " Enter, accept input. " buff = event.current_buffer buff.accept_action.validate_and_handle(event.cli, buff) @handle(Keys.ControlK, filter=insert_mode) def _(event): buffer = event.current_buffer deleted = buffer.delete(count=buffer.document.get_end_of_line_position()) event.cli.clipboard.set_text(deleted) @handle(Keys.ControlT, filter=insert_mode) def _(event): """ Emulate Emacs transpose-char behavior: at the beginning of the buffer, do nothing. At the end of a line or buffer, swap the characters before the cursor. Otherwise, move the cursor right, and then swap the characters before the cursor. """ b = event.current_buffer p = b.cursor_position if p == 0: return elif p == len(b.text) or b.text[p] == '\n': b.swap_characters_before_cursor() else: b.cursor_position += b.document.get_cursor_right_position() b.swap_characters_before_cursor() @handle(Keys.ControlU, filter=insert_mode) def _(event): """ Clears the line before the cursor position. If you are at the end of the line, clears the entire line. """ buffer = event.current_buffer deleted = buffer.delete_before_cursor(count=-buffer.document.get_start_of_line_position()) event.cli.clipboard.set_text(deleted) @handle(Keys.ControlW, filter=insert_mode) def _(event): """ Delete the word before the cursor. """ buffer = event.current_buffer pos = buffer.document.find_start_of_previous_word(count=event.arg) if pos is None: # Nothing found? delete until the start of the document. (The # input starts with whitespace and no words were found before the # cursor.) pos = - buffer.cursor_position if pos: deleted = buffer.delete_before_cursor(count=-pos) # If the previous key press was also Control-W, concatenate deleted # text. if event.is_repeat: deleted += event.cli.clipboard.get_data().text event.cli.clipboard.set_text(deleted) else: # Nothing to delete. Bell. event.cli.output.bell() @handle(Keys.PageUp, filter= ~has_selection) @handle(Keys.ControlUp) def _(event): event.current_buffer.history_backward() @handle(Keys.PageDown, filter= ~has_selection) @handle(Keys.ControlDown) def _(event): event.current_buffer.history_forward() @handle(Keys.Left) def _(event): buffer = event.current_buffer buffer.cursor_position += buffer.document.get_cursor_left_position(count=event.arg) @handle(Keys.Right) def _(event): buffer = event.current_buffer buffer.cursor_position += buffer.document.get_cursor_right_position(count=event.arg) @handle(Keys.Up, filter= ~has_selection) def _(event): event.current_buffer.auto_up(count=event.arg) @handle(Keys.Up, filter=has_selection) def _(event): event.current_buffer.cursor_up(count=event.arg) @handle(Keys.Down, filter= ~has_selection) def _(event): event.current_buffer.auto_down(count=event.arg) @handle(Keys.Down, filter=has_selection) def _(event): event.current_buffer.cursor_down(count=event.arg) @handle(Keys.ControlH, filter=insert_mode, save_before=if_no_repeat) def _(event): " Backspace: delete before cursor. " deleted = event.current_buffer.delete_before_cursor(count=event.arg) if not deleted: event.cli.output.bell() @handle(Keys.Delete, filter=insert_mode, save_before=if_no_repeat) @handle(Keys.ShiftDelete, filter=insert_mode, save_before=if_no_repeat) def _(event): deleted = event.current_buffer.delete(count=event.arg) if not deleted: event.cli.output.bell() @handle(Keys.Delete, filter=has_selection) def _(event): data = event.current_buffer.cut_selection() event.cli.clipboard.set_data(data) @handle(Keys.Any, filter=insert_mode, save_before=if_no_repeat) def _(event): """ Insert data at cursor position. """ event.current_buffer.insert_text(event.data * event.arg) # Global bindings. These are never disabled and don't include the default filter. @handle(Keys.ControlL) def _(event): " Clear whole screen and redraw. " event.cli.renderer.clear() @handle(Keys.ControlZ) def _(event): """ By default, control-Z should literally insert Ctrl-Z. (Ansi Ctrl-Z, code 26 in MSDOS means End-Of-File. In a Python REPL for instance, it's possible to type Control-Z followed by enter to quit.) When the system bindings are loaded and suspend-to-background is supported, that will override this binding. """ event.current_buffer.insert_text(event.data) @registry.add_binding(Keys.CPRResponse) def _(event): """ Handle incoming Cursor-Position-Request response. """ # The incoming data looks like u'\x1b[35;1R' # Parse row/col information. row, col = map(int, event.data[2:-1].split(';')) # Report absolute cursor position to the renderer. event.cli.renderer.report_absolute_cursor_row(row) @registry.add_binding(Keys.BracketedPaste) def _(event): " Pasting from clipboard. " event.current_buffer.insert_text(event.data)
def load_bindings(key_bindings_manager): """ Load keybindings into prompt_toolkit. """ handle = key_bindings_manager.registry.add_binding has_selection = HasSelection() # for some reason Pylint doesn't think this function is "used" @key_bindings_manager.registry.add_binding(Keys.ControlL) def clear_(event): # pylint: disable=unused-variable """ Clear the screen. """ clear() print(env.welcome) print(env.get_prompt(), end="") @handle(Keys.Tab, filter=TabShouldInsertWhitespaceFilter()) def _(event): """ When tab should insert whitespace, do that instead of completion. """ event.cli.current_buffer.insert_text(' ') # prompt_toolkit _wants_ these two methods (they have different filter # attributes) @handle(Keys.ControlJ, filter=~has_selection & (ViInsertMode() | EmacsInsertMode()) & HasFocus(DEFAULT_BUFFER) & IsMultiline()) def _(event): # pylint: disable=function-redefined """ Behaviour of the Enter key. Auto indent after newline/Enter. (When not in Vi navigaton mode, and when multiline is enabled.) """ current_buffer = event.current_buffer empty_lines_required = 2 def at_the_end(ptk_buffer): """ we consider the cursor at the end when there is no text after the cursor, or only whitespace. """ text = ptk_buffer.document.text_after_cursor return text == '' or (text.isspace() and not '\n' in text) def all_blocks_closed(ptk_buffer): """Return True when all Ergonomica code blocks are closed.""" return tokenize(ptk_buffer.text).count("(") == tokenize( ptk_buffer.text).count(")") if at_the_end(current_buffer)\ and (current_buffer.document.text.replace(' ', '') .endswith('\n' * (empty_lines_required - 1) ) or all_blocks_closed(current_buffer)): current_buffer.document = Document( text=current_buffer.text.rstrip(), cursor_position=len(current_buffer.text.rstrip())) current_buffer.accept_action.validate_and_handle( event.cli, current_buffer) else: _auto_newline(current_buffer)
def custom_keybindings(bindings, **kw): insert_mode = ViInsertMode() | EmacsInsertMode() # Key bindings for jumping over whole words (everything that's not # white space) using Ctrl+Left and Ctrl+Right; # Alt+Left and Alt+Right still jump over smaller word segments. # See https://github.com/xonsh/xonsh/issues/2403 @bindings.add(Keys.ControlLeft) def ctrl_left(event): buff = event.current_buffer pos = buff.document.find_previous_word_beginning(count=event.arg, WORD=True) if pos: buff.cursor_position += pos @bindings.add(Keys.ControlRight) def ctrl_right(event): buff = event.current_buffer pos = buff.document.find_next_word_ending(count=event.arg, WORD=True) if pos: buff.cursor_position += pos @bindings.add(Keys.ShiftDelete, filter=insert_mode) def delete_surrounding_big_word(event): buff = event.current_buffer startpos, endpos = buff.document.find_boundaries_of_current_word( WORD=True) startpos = buff.cursor_position + startpos - 1 startpos = 0 if startpos < 0 else startpos endpos = buff.cursor_position + endpos endpos = endpos + 1 if startpos == 0 else endpos buff.text = buff.text[:startpos] + buff.text[endpos:] buff.cursor_position = startpos @bindings.add(Keys.ControlDelete, filter=insert_mode) def delete_big_word(event): buff = event.current_buffer pos = buff.document.find_next_word_ending(count=event.arg, WORD=True) if pos: buff.delete(count=pos) @bindings.add(Keys.Escape, Keys.Delete, filter=insert_mode) def delete_small_word(event): get_by_name("kill-word").call(event) # PTK sets both "\x7f" (^?) and "\x08" (^H) to the same behavior. Refs: # https://github.com/prompt-toolkit/python-prompt-toolkit/blob/65c3d0607c69c19d80abb052a18569a2546280e5/src/prompt_toolkit/input/ansi_escape_sequences.py#L65 # https://github.com/prompt-toolkit/python-prompt-toolkit/issues/257#issuecomment-190328366 # We patch the ANSI sequences used by PTK. This requires a terminal # that sends different codes for <backspace> and <control-h>. # PTK sets Keys.Backspace = Keys.ControlH, so we hardcode the code. # Windows has the codes reversed, see https://github.com/xonsh/xonsh/commit/406d20f78f18af39d9bbaf9580b0a763df78a0db if XSH.env.get("XONSH_WHOLE_WORD_CTRL_BKSP", True): CONTROL_BKSP = "\x08" if ON_WINDOWS: # On windows BKSP is "\x08" and CTRL-BKSP is "\x7f" CONTROL_BKSP = "\x7f" ptk_win32.ConsoleInputReader.mappings[b"\x7f"] = CONTROL_BKSP ansiseq.ANSI_SEQUENCES[CONTROL_BKSP] = CONTROL_BKSP ansiseq.REVERSE_ANSI_SEQUENCES[CONTROL_BKSP] = CONTROL_BKSP @bindings.add(CONTROL_BKSP, filter=insert_mode) def backward_delete_big_word(event): get_by_name("unix-word-rubout").call(event)
def load_emacs_bindings(registry, filter=Always()): """ Some e-macs extensions. """ # Overview of Readline emacs commands: # http://www.catonmat.net/download/readline-emacs-editing-mode-cheat-sheet.pdf filter = to_cli_filter(filter) handle = create_handle_decorator(registry, filter & EmacsMode()) insert_mode = EmacsInsertMode() has_selection = HasSelection() @handle(Keys.Escape) def _(event): """ By default, ignore escape key. (If we don't put this here, and Esc is followed by a key which sequence is not handled, we'll insert an Escape character in the input stream. Something we don't want and happens to easily in emacs mode. Further, people can always use ControlQ to do a quoted insert.) """ pass @handle(Keys.ControlA) def _(event): """ Start of line. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.get_start_of_line_position( after_whitespace=False) @handle(Keys.ControlB) def _(event): """ Character back. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.get_cursor_left_position( count=event.arg) @handle(Keys.ControlE) def _(event): """ End of line. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.get_end_of_line_position() @handle(Keys.ControlF) def _(event): """ Character forward. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.get_cursor_right_position( count=event.arg) @handle(Keys.ControlN, filter=~has_selection) def _(event): """ Next line. """ event.current_buffer.auto_down() @handle(Keys.ControlN, filter=has_selection) def _(event): """ Next line. """ event.current_buffer.cursor_down() @handle(Keys.ControlO, filter=insert_mode) def _(event): """ Insert newline, but don't move the cursor. """ event.current_buffer.insert_text('\n', move_cursor=False) @handle(Keys.ControlP, filter=~has_selection) def _(event): """ Previous line. """ event.current_buffer.auto_up(count=event.arg) @handle(Keys.ControlP, filter=has_selection) def _(event): """ Previous line. """ event.current_buffer.cursor_up(count=event.arg) @handle(Keys.ControlQ, Keys.Any, filter=~has_selection) def _(event): """ Quoted insert. For vt100 terminals, you have to disable flow control by running ``stty -ixon``, otherwise Ctrl-Q and Ctrl-S are captured by the terminal. """ event.current_buffer.insert_text(event.data, overwrite=False) @handle(Keys.ControlY, filter=insert_mode) @handle(Keys.ControlX, 'r', 'y', filter=insert_mode) def _(event): """ Paste before cursor. """ event.current_buffer.paste_clipboard_data( event.cli.clipboard.get_data(), count=event.arg, before=True) @handle(Keys.ControlUnderscore, save_before=(lambda e: False), filter=insert_mode) def _(event): """ Undo. """ event.current_buffer.undo() def handle_digit(c): """ Handle Alt + digit in the `meta_digit` method. """ @handle(Keys.Escape, c) def _(event): event.append_to_arg_count(c) for c in '0123456789': handle_digit(c) @handle(Keys.Escape, '-') def _(event): """ """ if event._arg is None: event.append_to_arg_count('-') is_returnable = Condition( lambda cli: cli.current_buffer.accept_action.is_returnable) @handle(Keys.Escape, Keys.ControlJ, filter=insert_mode & is_returnable) def _(event): """ Meta + Newline: always accept input. """ b = event.current_buffer b.accept_action.validate_and_handle(event.cli, b) @handle(Keys.ControlSquareClose, Keys.Any) def _(event): """ When Ctl-] + a character is pressed. go to that character. """ match = event.current_buffer.document.find(event.data, in_current_line=True, count=(event.arg)) if match is not None: event.current_buffer.cursor_position += match @handle(Keys.Escape, Keys.Backspace, filter=insert_mode) def _(event): """ Delete word backwards. """ buffer = event.current_buffer pos = buffer.document.find_start_of_previous_word(count=event.arg) if pos is None: # Nothing found. Only whitespace before the cursor? pos = -buffer.cursor_position if pos: deleted = buffer.delete_before_cursor(count=-pos) event.cli.clipboard.set_text(deleted) @handle(Keys.ControlDelete, filter=insert_mode) def _(event): """ Delete word after cursor. """ buff = event.current_buffer pos = buff.document.find_next_word_ending(count=event.arg) if pos: deleted = buff.delete(count=pos) event.cli.clipboard.set_text(deleted) @handle(Keys.Escape, 'a') def _(event): """ Previous sentence. """ # TODO: pass @handle(Keys.Escape, 'c', filter=insert_mode) def _(event): """ Capitalize the current (or following) word. """ buffer = event.current_buffer for i in range(event.arg): pos = buffer.document.find_next_word_ending() words = buffer.document.text_after_cursor[:pos] buffer.insert_text(words.title(), overwrite=True) @handle(Keys.Escape, 'd', filter=insert_mode) def _(event): """ Delete word forwards. """ buffer = event.current_buffer pos = buffer.document.find_next_word_ending(count=event.arg) if pos: deleted = buffer.delete(count=pos) event.cli.clipboard.set_text(deleted) @handle(Keys.Escape, 'e') def _(event): """ Move to end of sentence. """ # TODO: pass @handle(Keys.Escape, 'f') @handle(Keys.ControlRight) def _(event): """ Cursor to end of next word. """ buffer = event.current_buffer pos = buffer.document.find_next_word_ending(count=event.arg) if pos: buffer.cursor_position += pos @handle(Keys.Escape, 'b') @handle(Keys.ControlLeft) def _(event): """ Cursor to start of previous word. """ buffer = event.current_buffer pos = buffer.document.find_previous_word_beginning(count=event.arg) if pos: buffer.cursor_position += pos @handle(Keys.Escape, 'l', filter=insert_mode) def _(event): """ Lowercase the current (or following) word. """ buffer = event.current_buffer for i in range(event.arg): # XXX: not DRY: see meta_c and meta_u!! pos = buffer.document.find_next_word_ending() words = buffer.document.text_after_cursor[:pos] buffer.insert_text(words.lower(), overwrite=True) @handle(Keys.Escape, 't', filter=insert_mode) def _(event): """ Swap the last two words before the cursor. """ # TODO @handle(Keys.Escape, 'u', filter=insert_mode) def _(event): """ Uppercase the current (or following) word. """ buffer = event.current_buffer for i in range(event.arg): pos = buffer.document.find_next_word_ending() words = buffer.document.text_after_cursor[:pos] buffer.insert_text(words.upper(), overwrite=True) @handle(Keys.Escape, '.', filter=insert_mode) def _(event): """ Rotate through the last word (white-space delimited) of the previous lines in history. """ # TODO @handle(Keys.Escape, '\\', filter=insert_mode) def _(event): """ Delete all spaces and tabs around point. (delete-horizontal-space) """ buff = event.current_buffer text_before_cursor = buff.document.text_before_cursor text_after_cursor = buff.document.text_after_cursor delete_before = len(text_before_cursor) - len( text_before_cursor.rstrip('\t ')) delete_after = len(text_after_cursor) - len( text_after_cursor.lstrip('\t ')) buff.delete_before_cursor(count=delete_before) buff.delete(count=delete_after) @handle(Keys.Escape, '*', filter=insert_mode) def _(event): """ `meta-*`: Insert all possible completions of the preceding text. """ buff = event.current_buffer # List all completions. complete_event = CompleteEvent(text_inserted=False, completion_requested=True) completions = list( buff.completer.get_completions(buff.document, complete_event)) # Insert them. text_to_insert = ' '.join(c.text for c in completions) buff.insert_text(text_to_insert) @handle(Keys.ControlX, Keys.ControlU, save_before=(lambda e: False), filter=insert_mode) def _(event): event.current_buffer.undo() @handle(Keys.ControlX, Keys.ControlX) def _(event): """ Move cursor back and forth between the start and end of the current line. """ buffer = event.current_buffer if buffer.document.current_char == '\n': buffer.cursor_position += buffer.document.get_start_of_line_position( after_whitespace=False) else: buffer.cursor_position += buffer.document.get_end_of_line_position( ) @handle(Keys.ControlSpace) def _(event): """ Start of the selection (if the current buffer is not empty). """ # Take the current cursor position as the start of this selection. buff = event.current_buffer if buff.text: buff.start_selection(selection_type=SelectionType.CHARACTERS) @handle(Keys.ControlG, filter=~has_selection) def _(event): """ Control + G: Cancel completion menu and validation state. """ event.current_buffer.complete_state = None event.current_buffer.validation_error = None @handle(Keys.ControlG, filter=has_selection) def _(event): """ Cancel selection. """ event.current_buffer.exit_selection() @handle(Keys.ControlW, filter=has_selection) @handle(Keys.ControlX, 'r', 'k', filter=has_selection) def _(event): """ Cut selected text. """ data = event.current_buffer.cut_selection() event.cli.clipboard.set_data(data) @handle(Keys.Escape, 'w', filter=has_selection) def _(event): """ Copy selected text. """ data = event.current_buffer.copy_selection() event.cli.clipboard.set_data(data) @handle(Keys.Escape, '<', filter=~has_selection) def _(event): """ Move to the first line in the history. """ event.current_buffer.go_to_history(0) @handle(Keys.Escape, '>', filter=~has_selection) def _(event): """ Move to the end of the input history. This is the line we are editing. """ buffer = event.current_buffer buffer.go_to_history(len(buffer._working_lines) - 1) @handle(Keys.Escape, Keys.Left) def _(event): """ Cursor to start of previous word. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.find_previous_word_beginning( count=event.arg) or 0 @handle(Keys.Escape, Keys.Right) def _(event): """ Cursor to start of next word. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.find_next_word_beginning(count=event.arg) or \ buffer.document.get_end_of_document_position() @handle(Keys.Escape, '/', filter=insert_mode) def _(event): """ M-/: Complete. """ b = event.current_buffer if b.complete_state: b.complete_next() else: event.cli.start_completion(select_first=True) @handle(Keys.ControlC, '>', filter=has_selection) def _(event): """ Indent selected text. """ buffer = event.current_buffer buffer.cursor_position += buffer.document.get_start_of_line_position( after_whitespace=True) from_, to = buffer.document.selection_range() from_, _ = buffer.document.translate_index_to_position(from_) to, _ = buffer.document.translate_index_to_position(to) indent(buffer, from_, to + 1, count=event.arg) @handle(Keys.ControlC, '<', filter=has_selection) def _(event): """ Unindent selected text. """ buffer = event.current_buffer from_, to = buffer.document.selection_range() from_, _ = buffer.document.translate_index_to_position(from_) to, _ = buffer.document.translate_index_to_position(to) unindent(buffer, from_, to + 1, count=event.arg)
def load_python_bindings(key_bindings_manager, python_input): """ Custom key bindings. """ sidebar_visible = Condition(lambda cli: python_input.show_sidebar) handle = key_bindings_manager.registry.add_binding has_selection = HasSelection() vi_mode_enabled = Condition(lambda cli: python_input.vi_mode) @handle(Keys.ControlL) def _(event): """ Clear whole screen and render again -- also when the sidebar is visible. """ event.cli.renderer.clear() @handle(Keys.F2) def _(event): """ Show/hide sidebar. """ python_input.show_sidebar = not python_input.show_sidebar @handle(Keys.F3) def _(event): """ Select from the history. """ python_input.enter_history(event.cli) @handle(Keys.F4) def _(event): """ Toggle between Vi and Emacs mode. """ python_input.vi_mode = not python_input.vi_mode @handle(Keys.F6) def _(event): """ Enable/Disable paste mode. """ python_input.paste_mode = not python_input.paste_mode @handle(Keys.Tab, filter= ~sidebar_visible & ~has_selection & TabShouldInsertWhitespaceFilter()) def _(event): """ When tab should insert whitespace, do that instead of completion. """ event.cli.current_buffer.insert_text(' ') @handle(Keys.ControlJ, filter= ~sidebar_visible & ~has_selection & (ViInsertMode() | EmacsInsertMode()) & HasFocus(DEFAULT_BUFFER) & IsMultiline()) def _(event): """ Behaviour of the Enter key. Auto indent after newline/Enter. (When not in Vi navigaton mode, and when multiline is enabled.) """ b = event.current_buffer empty_lines_required = python_input.accept_input_on_enter or 10000 def at_the_end(b): """ we consider the cursor at the end when there is no text after the cursor, or only whitespace. """ text = b.document.text_after_cursor return text == '' or (text.isspace() and not '\n' in text) if python_input.paste_mode: # In paste mode, always insert text. b.insert_text('\n') elif at_the_end(b) and b.document.text.replace(' ', '').endswith( '\n' * (empty_lines_required - 1)): if b.validate(): # When the cursor is at the end, and we have an empty line: # drop the empty lines, but return the value. b.document = Document( text=b.text.rstrip(), cursor_position=len(b.text.rstrip())) b.accept_action.validate_and_handle(event.cli, b) else: auto_newline(b) @handle(Keys.ControlD, filter=~sidebar_visible & Condition(lambda cli: # Only when the `confirm_exit` flag is set. python_input.confirm_exit and # And the current buffer is empty. cli.current_buffer_name == DEFAULT_BUFFER and not cli.current_buffer.text)) def _(event): """ Override Control-D exit, to ask for confirmation. """ python_input.show_exit_confirmation = True
def load_xonsh_bindings(key_bindings_manager): """ Load custom key bindings. """ handle = key_bindings_manager.registry.add_binding has_selection = HasSelection() insert_mode = ViInsertMode() | EmacsInsertMode() @handle(Keys.Tab, filter=tab_insert_indent) def insert_indent(event): """ If there are only whitespaces before current cursor position insert indent instead of autocompleting. """ event.cli.current_buffer.insert_text(env.get('INDENT')) @handle(Keys.ControlX, Keys.ControlE, filter=~has_selection) def open_editor(event): """ Open current buffer in editor """ event.current_buffer.open_in_editor(event.cli) @handle(Keys.BackTab, filter=insert_mode) def insert_literal_tab(event): """ Insert literal tab on Shift+Tab instead of autocompleting """ b = event.current_buffer if b.complete_state: b.complete_previous() else: event.cli.current_buffer.insert_text(env.get('INDENT')) @handle('(', filter=autopair_condition) def insert_right_parens(event): event.cli.current_buffer.insert_text('(') event.cli.current_buffer.insert_text(')', move_cursor=False) @handle(')', filter=autopair_condition) def overwrite_right_parens(event): buffer = event.cli.current_buffer if buffer.document.current_char == ')': buffer.cursor_position += 1 else: buffer.insert_text(')') @handle('[', filter=autopair_condition) def insert_right_bracket(event): event.cli.current_buffer.insert_text('[') event.cli.current_buffer.insert_text(']', move_cursor=False) @handle(']', filter=autopair_condition) def overwrite_right_bracket(event): buffer = event.cli.current_buffer if buffer.document.current_char == ']': buffer.cursor_position += 1 else: buffer.insert_text(']') @handle('\'', filter=autopair_condition) def insert_right_quote(event): buffer = event.cli.current_buffer if buffer.document.current_char == '\'': buffer.cursor_position += 1 else: buffer.insert_text('\'') buffer.insert_text('\'', move_cursor=False) @handle('"', filter=autopair_condition) def insert_right_double_quote(event): buffer = event.cli.current_buffer if buffer.document.current_char == '"': buffer.cursor_position += 1 else: buffer.insert_text('"') buffer.insert_text('"', move_cursor=False) @handle(Keys.ControlD, filter=ctrl_d_condition) def call_exit_alias(event): """Use xonsh exit function""" b = event.cli.current_buffer b.accept_action.validate_and_handle(event.cli, b) xonsh_exit([]) @handle(Keys.ControlJ, filter=IsMultiline()) def multiline_carriage_return(event): """ Wrapper around carriage_return multiline parser """ b = event.cli.current_buffer carriage_return(b, event.cli) @handle(Keys.ControlJ, filter=should_confirm_completion) def enter_confirm_completion(event): """Ignore <enter> (confirm completion)""" event.current_buffer.complete_state = None @handle(Keys.Escape, filter=should_confirm_completion) def esc_cancel_completion(event): """Use <ESC> to cancel completion""" event.cli.current_buffer.cancel_completion() @handle(Keys.Escape, Keys.ControlJ) def execute_block_now(event): """Execute a block of text irrespective of cursor position""" b = event.cli.current_buffer b.accept_action.validate_and_handle(event.cli, b) @handle(Keys.Left, filter=beginning_of_line) def wrap_cursor_back(event): """Move cursor to end of previous line unless at beginning of document """ b = event.cli.current_buffer b.cursor_up(count=1) relative_end_index = b.document.get_end_of_line_position() b.cursor_right(count=relative_end_index) @handle(Keys.Right, filter=end_of_line) def wrap_cursor_forward(event): """Move cursor to beginning of next line unless at end of document""" b = event.cli.current_buffer relative_begin_index = b.document.get_start_of_line_position() b.cursor_left(count=abs(relative_begin_index)) b.cursor_down(count=1) @handle(Keys.ControlI, filter=insert_mode) def generate_completions(event): """ Tab-completion: where the first tab completes the common suffix and the second tab lists all the completions. Notes ----- This method was forked from the mainline prompt-toolkit repo. Copyright (c) 2014, Jonathan Slenders, All rights reserved. """ b = event.current_buffer def second_tab(): if b.complete_state: b.complete_next() else: event.cli.start_completion(select_first=False) # On the second tab-press, or when already navigating through # completions. if event.is_repeat or b.complete_state: second_tab() else: event.cli.start_completion(insert_common_part=True, select_first=False)
# -*- coding: utf-8 -*- 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() cursor_left = lambda ev: ev.current_buffer.cursor_left() cursor_right = lambda ev: ev.current_buffer.cursor_right() default_filters = HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode # 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.ControlH, filter=default_filters)(cursor_left) registry.add_binding(Keys.ControlL, filter=default_filters)(cursor_right) registry.add_binding(Keys.ControlB, filter=default_filters)(get_by_name('backward-word'))
def init_prompt_toolkit_cli(self): if self.simple_prompt or ('JUPYTER_CONSOLE_TEST' in os.environ): # Simple restricted interface for tests so we can find prompts with # pexpect. Multi-line input not supported. def prompt(): return cast_unicode_py2( input('In [%d]: ' % self.execution_count)) self.prompt_for_code = prompt self.print_out_prompt = \ lambda: print('Out[%d]: ' % self.execution_count, end='') return kbmanager = KeyBindingManager.for_prompt() insert_mode = ViInsertMode() | EmacsInsertMode() # Ctrl+J == Enter, seemingly @kbmanager.registry.add_binding(Keys.ControlJ, filter=(HasFocus(DEFAULT_BUFFER) & ~HasSelection() & insert_mode)) def _(event): b = event.current_buffer d = b.document if not (d.on_last_line or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()): b.newline() return # Pressing enter flushes any pending display. This also ensures # the displayed execution_count is correct. self.handle_iopub() more, indent = self.check_complete(d.text) if (not more) and b.accept_action.is_returnable: b.accept_action.validate_and_handle(event.cli, b) else: b.insert_text('\n' + indent) @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)) def _(event): event.current_buffer.reset() @kbmanager.registry.add_binding(Keys.ControlBackslash, filter=HasFocus(DEFAULT_BUFFER)) def _(event): raise EOFError # Pre-populate history from IPython's history database history = InMemoryHistory() last_cell = u"" for _, _, cell in self.history_manager.get_tail( self.history_load_length, include_latest=True): # Ignore blank lines and consecutive duplicates cell = cast_unicode_py2(cell.rstrip()) if cell and (cell != last_cell): history.append(cell) style_overrides = { Token.Prompt: '#009900', Token.PromptNum: '#00ff00 bold', Token.OutPrompt: '#ff2200', Token.OutPromptNum: '#ff0000 bold', } if self.highlighting_style: style_cls = get_style_by_name(self.highlighting_style) else: style_cls = get_style_by_name('default') # The default theme needs to be visible on both a dark background # and a light background, because we can't tell what the terminal # looks like. These tweaks to the default theme help with that. style_overrides.update({ Token.Number: '#007700', Token.Operator: 'noinherit', Token.String: '#BB6622', Token.Name.Function: '#2080D0', Token.Name.Class: 'bold #2080D0', Token.Name.Namespace: 'bold #2080D0', }) style_overrides.update(self.highlighting_style_overrides) style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls, style_dict=style_overrides) editing_mode = getattr(EditingMode, self.editing_mode.upper()) langinfo = self.kernel_info.get('language_info', {}) lexer = langinfo.get('pygments_lexer', langinfo.get('name', 'text')) app = create_prompt_application( multiline=True, editing_mode=editing_mode, lexer=PygmentsLexer(get_pygments_lexer(lexer)), get_prompt_tokens=self.get_prompt_tokens, get_continuation_tokens=self.get_continuation_tokens, key_bindings_registry=kbmanager.registry, history=history, completer=JupyterPTCompleter(self.Completer), enable_history_search=True, style=style, ) self._eventloop = create_eventloop() self.pt_cli = CommandLineInterface( app, eventloop=self._eventloop, output=create_output(true_color=self.true_color), )
def load_xonsh_bindings(key_bindings): """ Load custom key bindings. """ handle = key_bindings.add has_selection = HasSelection() insert_mode = ViInsertMode() | EmacsInsertMode() @handle(Keys.Tab, filter=tab_insert_indent) def insert_indent(event): """ If there are only whitespaces before current cursor position insert indent instead of autocompleting. """ env = builtins.__xonsh__.env event.cli.current_buffer.insert_text(env.get("INDENT")) @handle(Keys.ControlX, Keys.ControlE, filter=~has_selection) def open_editor(event): """ Open current buffer in editor """ event.current_buffer.open_in_editor(event.cli) @handle(Keys.BackTab, filter=insert_mode) def insert_literal_tab(event): """ Insert literal tab on Shift+Tab instead of autocompleting """ b = event.current_buffer if b.complete_state: b.complete_previous() else: env = builtins.__xonsh__.env event.cli.current_buffer.insert_text(env.get("INDENT")) @handle("(", filter=autopair_condition & whitespace_or_bracket_after) def insert_right_parens(event): event.cli.current_buffer.insert_text("(") event.cli.current_buffer.insert_text(")", move_cursor=False) @handle(")", filter=autopair_condition) def overwrite_right_parens(event): buffer = event.cli.current_buffer if buffer.document.current_char == ")": buffer.cursor_position += 1 else: buffer.insert_text(")") @handle("[", filter=autopair_condition & whitespace_or_bracket_after) def insert_right_bracket(event): event.cli.current_buffer.insert_text("[") event.cli.current_buffer.insert_text("]", move_cursor=False) @handle("]", filter=autopair_condition) def overwrite_right_bracket(event): buffer = event.cli.current_buffer if buffer.document.current_char == "]": buffer.cursor_position += 1 else: buffer.insert_text("]") @handle("{", filter=autopair_condition & whitespace_or_bracket_after) def insert_right_brace(event): event.cli.current_buffer.insert_text("{") event.cli.current_buffer.insert_text("}", move_cursor=False) @handle("}", filter=autopair_condition) def overwrite_right_brace(event): buffer = event.cli.current_buffer if buffer.document.current_char == "}": buffer.cursor_position += 1 else: buffer.insert_text("}") @handle("'", filter=autopair_condition) def insert_right_quote(event): buffer = event.cli.current_buffer if buffer.document.current_char == "'": buffer.cursor_position += 1 elif whitespace_or_bracket_before() and whitespace_or_bracket_after(): buffer.insert_text("'") buffer.insert_text("'", move_cursor=False) else: buffer.insert_text("'") @handle('"', filter=autopair_condition) def insert_right_double_quote(event): buffer = event.cli.current_buffer if buffer.document.current_char == '"': buffer.cursor_position += 1 elif whitespace_or_bracket_before() and whitespace_or_bracket_after(): buffer.insert_text('"') buffer.insert_text('"', move_cursor=False) else: buffer.insert_text('"') @handle(Keys.Backspace, filter=autopair_condition) def delete_brackets_or_quotes(event): """Delete empty pair of brackets or quotes""" buffer = event.cli.current_buffer before = buffer.document.char_before_cursor after = buffer.document.current_char if any( [before == b and after == a for (b, a) in ["()", "[]", "{}", "''", '""']] ): buffer.delete(1) buffer.delete_before_cursor(1) @handle(Keys.ControlD, filter=ctrl_d_condition) def call_exit_alias(event): """Use xonsh exit function""" b = event.cli.current_buffer b.validate_and_handle() xonsh_exit([]) @handle(Keys.ControlJ, filter=IsMultiline()) @handle(Keys.ControlM, filter=IsMultiline()) def multiline_carriage_return(event): """ Wrapper around carriage_return multiline parser """ b = event.cli.current_buffer carriage_return(b, event.cli) @handle(Keys.ControlJ, filter=should_confirm_completion) @handle(Keys.ControlM, filter=should_confirm_completion) def enter_confirm_completion(event): """Ignore <enter> (confirm completion)""" event.current_buffer.complete_state = None @handle(Keys.Escape, filter=should_confirm_completion) def esc_cancel_completion(event): """Use <ESC> to cancel completion""" event.cli.current_buffer.cancel_completion() @handle(Keys.Escape, Keys.ControlJ) def execute_block_now(event): """Execute a block of text irrespective of cursor position""" b = event.cli.current_buffer b.validate_and_handle() @handle(Keys.Left, filter=beginning_of_line) def wrap_cursor_back(event): """Move cursor to end of previous line unless at beginning of document """ b = event.cli.current_buffer b.cursor_up(count=1) relative_end_index = b.document.get_end_of_line_position() b.cursor_right(count=relative_end_index) @handle(Keys.Right, filter=end_of_line) def wrap_cursor_forward(event): """Move cursor to beginning of next line unless at end of document""" b = event.cli.current_buffer relative_begin_index = b.document.get_start_of_line_position() b.cursor_left(count=abs(relative_begin_index)) b.cursor_down(count=1) @handle(Keys.ControlM, filter=IsSearching()) @handle(Keys.ControlJ, filter=IsSearching()) def accept_search(event): search.accept_search()