Beispiel #1
0
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
Beispiel #2
0
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()
Beispiel #3
0
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