Example #1
0
    def create_application(self):
        """
        Create an `Application` instance for use in a `CommandLineInterface`.
        """
        buffers = {
            'docstring': Buffer(read_only=True),
            'realtime_display': Buffer(read_only=True),
            'vars': Buffer(read_only=True),
            'parenthesizer_buffer': Buffer(read_only=True),
        }
        for key in r_iterm_comm.python_input_buffers:
            buffers[key] = Buffer(read_only=True)
        buffers.update(self._extra_buffers or {})

        return Application(
            layout=create_layout(
                self,
                lexer=self._lexer,
                input_buffer_height=self._input_buffer_height,
                extra_buffer_processors=self._extra_buffer_processors,
                extra_body=self._extra_layout_body,
                extra_toolbars=self._extra_toolbars),
            buffer=self._create_buffer(),
            buffers=buffers,
            key_bindings_registry=self.key_bindings_registry,
            paste_mode=Condition(lambda cli: self.paste_mode),
            mouse_support=Condition(lambda cli: self.enable_mouse_support),
            on_abort=AbortAction.RETRY,
            on_exit=self._on_exit,
            style=DynamicStyle(lambda: self._current_style),
            get_title=lambda: self.terminal_title,
            reverse_vi_search_direction=True,
            on_initialize=self._on_cli_initialize,
            on_start=self._on_start,
            on_input_timeout=self._on_input_timeout)
Example #2
0
    def _create_buffer(self):
        """
        Create the `Buffer` for the Python input.
        """
        def is_buffer_multiline():
            return (self.paste_mode or self.accept_input_on_enter is None
                    or document_is_multiline_python(python_buffer.document))

        python_buffer = Buffer(
            is_multiline=Condition(is_buffer_multiline),
            complete_while_typing=Condition(
                lambda: self.complete_while_typing),
            enable_history_search=Condition(
                lambda: self.enable_history_search),
            tempfile_suffix='.py',
            history=self.history,
            completer=self._completer,
            validator=ConditionalValidator(
                self._validator,
                Condition(lambda: self.enable_input_validation)),
            auto_suggest=ConditionalAutoSuggest(
                AutoSuggestFromHistory(),
                Condition(lambda cli: self.enable_auto_suggest)),
            accept_action=self._accept_action)

        return python_buffer
Example #3
0
    def create_python_input_window():
        def menu_position(cli):
            """
            When there is no autocompletion menu to be shown, and we have a signature,
            set the pop-up position at `bracket_start`.
            """
            b = cli.buffers[DEFAULT_BUFFER]

            if b.complete_state is None and python_input.signatures:
                row, col = python_input.signatures[0].bracket_start
                index = b.document.translate_row_col_to_index(row - 1, col)
                return index

        return Window(
            BufferControl(
                buffer_name=DEFAULT_BUFFER,
                lexer=lexer,
                input_processors=[
                    ConditionalProcessor(
                        processor=HighlightSearchProcessor(
                            preview_search=True),
                        filter=HasFocus(SEARCH_BUFFER),
                    ),
                    HighlightSelectionProcessor(),
                    DisplayMultipleCursors(DEFAULT_BUFFER),
                    # Show matching parentheses, but only while editing.
                    ConditionalProcessor(
                        processor=HighlightMatchingBracketProcessor(
                            chars='[](){}'),
                        filter=HasFocus(DEFAULT_BUFFER) & ~IsDone()
                        & Condition(lambda cli: python_input.
                                    highlight_matching_parenthesis)),
                    ConditionalProcessor(processor=AppendAutoSuggestion(),
                                         filter=~IsDone())
                ] + extra_buffer_processors,
                menu_position=menu_position,

                # Make sure that we always see the result of an reverse-i-search:
                preview_search=True,
            ),
            left_margins=[PythonPromptMargin(python_input)],
            # Scroll offsets. The 1 at the bottom is important to make sure the
            # cursor is never below the "Press [Meta+Enter]" message which is a float.
            scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4),
            # As long as we're editing, prefer a minimal height of 6.
            get_height=(lambda cli:
                        (None if cli.is_done or python_input.
                         show_exit_confirmation else input_buffer_height)),
            wrap_lines=Condition(lambda cli: python_input.wrap_lines),
        )
Example #4
0
def python_sidebar_help(python_input):
    """
    Create the `Layout` for the help text for the current item in the sidebar.
    """
    token = Token.Sidebar.HelpText

    def get_current_description():
        """
        Return the description of the selected option.
        """
        i = 0
        for category in python_input.options:
            for option in category.options:
                if i == python_input.selected_option_index:
                    return option.description
                i += 1
        return ''

    def get_tokens(cli):
        return [(token, get_current_description())]

    return ConditionalContainer(
        content=Window(TokenListControl(get_tokens, Char(token=token)),
                       height=LayoutDimension(min=3)),
        filter=ShowSidebar(python_input)
        & Condition(lambda cli: python_input.show_sidebar_help) & ~IsDone())
Example #5
0
    def add_key_binding(self):
        """
        Shortcut for adding new key bindings.
        (Mostly useful for a .ptpython/config.py file, that receives
        a PythonInput/Repl instance as input.)

        ::

            @python_input.add_key_binding(Keys.ControlX, filter=...)
            def handler(event):
                ...
        """
        # Extra key bindings should not be active when the sidebar is visible.
        sidebar_visible = Condition(lambda cli: self.show_sidebar)

        def add_binding_decorator(*keys, **kw):
            # Pop default filter keyword argument.
            filter = kw.pop('filter', Always())
            assert not kw

            return self.key_bindings_registry.add_binding(*keys,
                                                          filter=filter
                                                          & ~sidebar_visible)

        return add_binding_decorator
Example #6
0
    def __init__(self, min_rows=3, suggested_max_column_width=30, show_meta=True, extra_filter=True):
        show_meta = to_cli_filter(show_meta)
        extra_filter = to_cli_filter(extra_filter)

        # Display filter: show when there are completions but not at the point
        # we are returning the input.
        full_filter = HasCompletions() & ~IsDone() & extra_filter

        any_completion_has_meta = Condition(lambda cli:
                any(c.display_meta for c in cli.current_buffer.complete_state.current_completions))

        # Create child windows.
        completions_window = ConditionalContainer(
            content=Window(
                content=MultiColumnCompletionMenuControl(
                    min_rows=min_rows, suggested_max_column_width=suggested_max_column_width),
                width=LayoutDimension(min=8),
                height=LayoutDimension(min=1)),
            filter=full_filter)

        meta_window = ConditionalContainer(
            content=Window(content=_SelectedCompletionMetaControl()),
            filter=show_meta & full_filter & any_completion_has_meta)

        # Initialise split.
        super(MultiColumnCompletionsMenu, self).__init__([
            completions_window,
            meta_window
        ])
Example #7
0
    def __init__(self, python_input):
        self.python_input = python_input

        def get_prompt_style():
            return python_input.all_prompt_styles[python_input.prompt_style]

        def get_prompt(cli):
            return get_prompt_style().in_tokens(cli)

        def get_continuation_prompt(cli, width):
            return get_prompt_style().in2_tokens(cli, width)

        super(PythonPromptMargin, self).__init__(
            get_prompt,
            get_continuation_prompt,
            show_numbers=Condition(lambda cli: python_input.show_line_numbers))
Example #8
0
def load_basic_system_bindings():
    """
    Basic system bindings (For both Emacs and Vi mode.)
    """
    registry = Registry()

    suspend_supported = Condition(
        lambda cli: suspend_to_background_supported())

    @registry.add_binding(Keys.ControlZ, filter=suspend_supported)
    def _(event):
        """
        Suspend process to background.
        """
        event.cli.suspend_to_background()

    return registry
Example #9
0
def show_sidebar_button_info(python_input):
    """
    Create `Layout` for the information in the right-bottom corner.
    (The right part of the status bar.)
    """
    @if_mousedown
    def toggle_sidebar(cli, mouse_event):
        " Click handler for the menu. "
        python_input.show_sidebar = not python_input.show_sidebar

    token = Token.Toolbar.Status

    version = sys.version_info
    import datetime
    remove_beginning_zero = lambda x: x[1:] if x[0] == '0' else x
    import rp
    get_tokens = lambda cli: [
        (token.BatteryPluggedIn
         if rp.battery_plugged_in() else token.BatteryNotPluggedIn, "🔋 " * 0
         + str(rp.battery_percentage()) + '%'),  # put the time here
        (token, ' '),
        (token, (remove_beginning_zero(datetime.datetime.now().strftime(
            "%I:%M%p")).lower())),  # put the time here
        (token, ' '),
        (token.Key, '[F2]', toggle_sidebar),
        (token, ' Menu', toggle_sidebar),
        # (token, ' - '),
        # (token.PythonVersion, '%s %i.%i.%i' % (platform.python_implementation(),
        #                                        version[0], version[1], version[2])),
        (token, ' '),
    ]
    width = token_list_width(get_tokens(None))

    # def get_tokens(cli):
    #     Python version
    # return tokens

    return ConditionalContainer(
        content=Window(TokenListControl(get_tokens,
                                        default_char=Char(token=token)),
                       height=LayoutDimension.exact(1),
                       width=LayoutDimension.exact(width)),
        filter=~IsDone() & RendererHeightIsKnown()
        & Condition(lambda cli: python_input.show_status_bar and
                    not python_input.show_exit_confirmation))
Example #10
0
    def __init__(self,
                 eventloop=None,
                 python_input=None,
                 input=None,
                 output=None):
        assert python_input is None or isinstance(python_input, PythonInput)

        python_input = python_input or PythonInput()

        # Make sure that the rp.prompt_toolkit 'renderer' knows about the
        # 'true_color' property of PythonInput.
        if output is None:
            output = create_output(
                true_color=Condition(lambda: python_input.true_color))

        super(PythonCommandLineInterface,
              self).__init__(application=python_input.create_application(),
                             eventloop=eventloop,
                             input=input,
                             output=output)
Example #11
0
def meta_enter_message(python_input):
    """
    Create the `Layout` for the 'Meta+Enter` message.
    """
    def get_tokens(cli):
        return [(Token.AcceptMessage, ' [Meta+Enter] Execute ')]

    def extra_condition(cli):
        " Only show when... "
        b = cli.buffers[DEFAULT_BUFFER]

        return (python_input.show_meta_enter_message
                and (not b.document.is_cursor_at_the_end
                     or python_input.accept_input_on_enter is None)
                and b.is_multiline())

    visible = ~IsDone() & HasFocus(DEFAULT_BUFFER) & Condition(extra_condition)

    return ConditionalContainer(content=Window(TokenListControl(get_tokens)),
                                filter=visible)
Example #12
0
def exit_confirmation(python_input, token=Token.ExitConfirmation):
    """
    Create `Layout` for the exit message.
    """
    def get_tokens(cli):
        # Show "Do you really want to exit?"
        return [
            (token, '\n %s ([y]/n)' % python_input.exit_message),
            (Token.SetCursorPosition, ''),
            (token, '  \n'),
        ]

    visible = ~IsDone() & Condition(
        lambda cli: python_input.show_exit_confirmation)

    return ConditionalContainer(content=Window(
        TokenListControl(get_tokens,
                         default_char=Char(token=token),
                         has_focus=visible)),
                                filter=visible)
Example #13
0
def load_auto_suggestion_bindings():
    """
    Key bindings for accepting auto suggestion text.
    """
    registry = Registry()
    handle = registry.add_binding

    suggestion_available = Condition(
        lambda cli: cli.current_buffer.suggestion is not None and cli.
        current_buffer.document.is_cursor_at_the_end)

    @handle(Keys.ControlF, filter=suggestion_available)
    @handle(Keys.ControlE, filter=suggestion_available)
    @handle(Keys.Right, filter=suggestion_available)
    def _(event):
        " Accept suggestion. "
        b = event.current_buffer
        suggestion = b.suggestion

        if suggestion:
            b.insert_text(suggestion.text)

    return registry
Example #14
0
def status_bar(python_input):
    """
    Create the `Layout` for the status bar.
    """
    TB = Token.Toolbar.Status

    @if_mousedown
    def toggle_mouse_support(cli, mouse_event):
        python_input.enable_mouse_support = not python_input.enable_mouse_support

    @if_mousedown
    def toggle_paste_mode(cli, mouse_event):
        python_input.paste_mode = not python_input.paste_mode

    @if_mousedown
    def enter_history(cli, mouse_event):
        python_input.enter_history(cli)

    def get_tokens(cli):
        python_buffer = cli.buffers[DEFAULT_BUFFER]

        result = []
        append = result.append

        append((TB, ' '))
        result.extend(get_inputmode_tokens(cli, python_input))

        # Position in history.
        append((TB, '%i/%i ' % (python_buffer.working_index + 1,
                                len(python_buffer._working_lines))))

        # Shortcuts.
        if not python_input.vi_mode and cli.current_buffer_name == SEARCH_BUFFER:
            append((TB, '[Ctrl-G] Cancel search [Enter] Go to this position.'))
        elif bool(cli.current_buffer.selection_state
                  ) and not python_input.vi_mode:
            # Emacs cut/copy keys.
            append(
                (TB,
                 '[Ctrl-W] Cut [Meta-W] Copy [Ctrl-Y] Paste [Ctrl-G] Cancel'))
        else:
            result.extend([
                (TB.Key, '[F3]', enter_history),
                (TB, 'History ', enter_history),
                (TB.Key, '[F6]', toggle_paste_mode),
                # (TB, ' ', toggle_paste_mode),
            ])

            if python_input.paste_mode:
                append((TB.PasteModeOn, 'Paste:On  ', toggle_paste_mode))
            else:
                append((TB, 'Paste:Off ', toggle_paste_mode))

            result.extend([
                (TB.Key, '[F1]', toggle_mouse_support),
            ])
            if python_input.enable_mouse_support:
                append((TB.PasteModeOn, 'Mouse:On  ', toggle_mouse_support))
            else:
                append((TB, 'Mouse:Off ', toggle_mouse_support))
        #region RYAN BURGERT CODE GOES HERE FOR PSEUDOTERMINAL STUFF
        append((TB, ' '))

        @if_mousedown
        def testytest(cli, mouse_event):
            # python_input.enter_history(cli)
            pass

        import rp.r_iterm_comm
        append((TB.PseudoTerminalCurrentVariable,
                repr(rp.r_iterm_comm.last_assignable_comm), testytest))
        append((TB, ' '))
        return result

    return TokenListToolbar(
        get_tokens,
        default_char=Char(token=TB),
        filter=~IsDone() & RendererHeightIsKnown()
        & Condition(lambda cli: python_input.show_status_bar and
                    not python_input.show_exit_confirmation))
Example #15
0
def show_completions_menu(python_input):
    return Condition(lambda cli: python_input.completion_visualisation ==
                     CompletionVisualisation.POP_UP)
Example #16
0
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
Example #17
0
def show_completions_toolbar(python_input):
    return Condition(lambda cli: python_input.completion_visualisation ==
                     CompletionVisualisation.TOOLBAR)
Example #18
0
def load_emacs_bindings():
    """
    Some e-macs extensions.
    """
    # Overview of Readline emacs commands:
    # http://www.catonmat.net/download/readline-emacs-editing-mode-cheat-sheet.pdf
    registry = ConditionalRegistry(Registry(), EmacsMode())
    handle = registry.add_binding

    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, 'y', filter=insert_mode)(get_by_name('yank-pop'))
    handle(Keys.Escape, Keys.ControlH,
           filter=insert_mode)(get_by_name('backward-kill-word'))
    handle(Keys.Escape, Keys.Backspace,
           filter=insert_mode)(get_by_name('backward-kill-word'))
    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.Escape, '.', filter=insert_mode)(get_by_name('yank-last-arg'))
    handle(Keys.Escape, '_', filter=insert_mode)(get_by_name('yank-last-arg'))
    handle(Keys.Escape, Keys.ControlY,
           filter=insert_mode)(get_by_name('yank-nth-arg'))
    handle(Keys.Escape, '#', filter=insert_mode)(get_by_name('insert-comment'))
    handle(Keys.ControlO)(get_by_name('operate-and-get-next'))

    # ControlQ does a quoted insert. Not that for vt100 terminals, you have to
    # disable flow control by running ``stty -ixon``, otherwise Ctrl-Q and
    # Ctrl-S are captured by the terminal.
    handle(Keys.ControlQ, filter=~has_selection)(get_by_name('quoted-insert'))

    handle(Keys.ControlX, '(')(get_by_name('start-kbd-macro'))
    handle(Keys.ControlX, ')')(get_by_name('end-kbd-macro'))
    handle(Keys.ControlX, 'e')(get_by_name('call-last-kbd-macro'))

    @handle(Keys.ControlN)
    def _(event):
        " Next line. "
        event.current_buffer.auto_down()

    @handle(Keys.ControlP)
    def _(event):
        " Previous line. "
        event.current_buffer.auto_up(count=event.arg)

    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. "
        # Also named 'character-search'
        character_search(event.current_buffer, event.data, event.arg)

    @handle(Keys.Escape, Keys.ControlSquareClose, Keys.Any)
    def _(event):
        " Like Ctl-], but backwards. "
        # Also named 'character-search-backward'
        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):
        """
        `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)

    return registry
Example #19
0
def create_history_application(python_input, original_document):
    """
    Create an `Application` for the history screen.
    This has to be run as a sub application of `python_input`.

    When this application runs and returns, it retuns the selected lines.
    """
    history_mapping = HistoryMapping(python_input.history, original_document)

    def default_buffer_pos_changed(_):
        """ When the cursor changes in the default buffer. Synchronize with
        history buffer. """
        # Only when this buffer has the focus.
        if buffer_mapping.focus_stack[-1] == DEFAULT_BUFFER:
            try:
                line_no = default_buffer.document.cursor_position_row - \
                    history_mapping.result_line_offset

                if line_no < 0:  # When the cursor is above the inserted region.
                    raise IndexError

                history_lineno = sorted(
                    history_mapping.selected_lines)[line_no]
            except IndexError:
                pass
            else:
                history_buffer.cursor_position = \
                    history_buffer.document.translate_row_col_to_index(history_lineno, 0)

    def history_buffer_pos_changed(_):
        """ When the cursor changes in the history buffer. Synchronize. """
        # Only when this buffer has the focus.
        if buffer_mapping.focus_stack[-1] == HISTORY_BUFFER:
            line_no = history_buffer.document.cursor_position_row

            if line_no in history_mapping.selected_lines:
                default_lineno = sorted(history_mapping.selected_lines).index(line_no) + \
                    history_mapping.result_line_offset

                default_buffer.cursor_position = \
                    default_buffer.document.translate_row_col_to_index(default_lineno, 0)

    history_buffer = Buffer(
        initial_document=Document(history_mapping.concatenated_history),
        on_cursor_position_changed=history_buffer_pos_changed,
        accept_action=AcceptAction(
            lambda cli, buffer: cli.set_return_value(default_buffer.document)),
        read_only=True)

    default_buffer = Buffer(
        initial_document=history_mapping.get_new_document(),
        on_cursor_position_changed=default_buffer_pos_changed,
        read_only=True)

    help_buffer = Buffer(initial_document=Document(HELP_TEXT, 0),
                         accept_action=AcceptAction.IGNORE,
                         read_only=True)

    buffer_mapping = BufferMapping(
        {
            HISTORY_BUFFER: history_buffer,
            DEFAULT_BUFFER: default_buffer,
            HELP_BUFFER: help_buffer,
        },
        initial=HISTORY_BUFFER)

    application = Application(
        layout=create_layout(python_input, history_mapping),
        use_alternate_screen=True,
        buffers=buffer_mapping,
        style=python_input._current_style,
        mouse_support=Condition(lambda cli: python_input.enable_mouse_support),
        key_bindings_registry=create_key_bindings(python_input,
                                                  history_mapping))
    return application
Example #20
0
    def __init__(
            self,
            get_globals=None,
            get_locals=None,
            history_filename=None,
            vi_mode=False,
            # For internal use.
            _completer=None,
            _validator=None,
            _lexer=None,
            _extra_buffers=None,
            _extra_buffer_processors=None,
            _on_start=None,
            _extra_layout_body=None,
            _extra_toolbars=None,
            _input_buffer_height=None,
            _accept_action=AcceptAction.RETURN_DOCUMENT,
            _on_exit=AbortAction.RAISE_EXCEPTION):
        self.get_globals = get_globals or (lambda: {})
        self.get_locals = get_locals or self.get_globals

        self.show_parenthesis_automator = False

        self._completer = _completer or PythonCompleter(
            self.get_globals, self.get_locals)
        self._validator = _validator or PythonValidator(
            self.get_compiler_flags)
        self.history = FileHistory(
            history_filename) if history_filename else InMemoryHistory()
        self._lexer = _lexer or PygmentsLexer(PythonLexer)
        self._extra_buffers = _extra_buffers
        self._accept_action = _accept_action
        self._on_exit = _on_exit
        self._on_start = _on_start

        self._input_buffer_height = _input_buffer_height
        self._extra_layout_body = _extra_layout_body or []
        self._extra_toolbars = _extra_toolbars or []
        self._extra_buffer_processors = _extra_buffer_processors or []

        # Settings.
        self.show_signature = False
        self.show_docstring = False
        self.show_realtime_input = False
        self.show_vars = False
        self.show_meta_enter_message = True
        self.completion_visualisation = CompletionVisualisation.MULTI_COLUMN
        self.completion_menu_scroll_offset = 1

        self.show_line_numbers = False
        self.show_status_bar = True
        self.wrap_lines = True
        self.complete_while_typing = True
        self.vi_mode = vi_mode
        self.paste_mode = False  # When True, don't insert whitespace after newline.
        self.confirm_exit = True  # Ask for confirmation when Control-D is pressed.
        self.accept_input_on_enter = 2  # Accept when pressing Enter 'n' times.
        # 'None' means that meta-enter is always required.
        self.enable_open_in_editor = True
        self.enable_system_bindings = True
        self.enable_input_validation = True
        self.enable_auto_suggest = False
        self.enable_mouse_support = False
        self.enable_history_search = False  # When True, like readline, going
        # back in history will filter the
        # history on the records starting
        # with the current input.
        self.highlight_matching_parenthesis = False
        self.show_sidebar = False  # Currently show the sidebar.
        self.show_sidebar_help = True  # When the sidebar is visible, also show the help text.
        self.show_exit_confirmation = False  # Currently show 'Do you really want to exit?'
        self.terminal_title = None  # The title to be displayed in the terminal. (None or string.)
        self.exit_message = 'Do you really want to exit?'
        self.insert_blank_line_after_output = True  # (For the REPL.)

        # Tokens to be shown at the prompt.
        self.prompt_style = 'classic'  # The currently active style.

        self.all_prompt_styles={  # Styles selectable from the menu.
            'ipython':IPythonPrompt(self),
            'classic':ClassicPrompt(),
        }

        self.get_input_prompt_tokens=lambda cli: \
            self.all_prompt_styles[self.prompt_style].in_tokens(cli)

        self.get_output_prompt_tokens=lambda cli: \
            self.all_prompt_styles[self.prompt_style].out_tokens(cli)

        #: Load styles.
        self.code_styles = get_all_code_styles()
        self.ui_styles = get_all_ui_styles()
        self._current_code_style_name = 'default'
        self._current_ui_style_name = 'default'

        if is_windows():
            self._current_code_style_name = 'win32'

        self._current_style = self._generate_style()
        self.true_color = False

        # Options to be configurable from the sidebar.
        self.options = self._create_options()
        self.selected_option_index = 0

        #: Incremeting integer counting the current statement.
        self.current_statement_index = 1

        # Code signatures. (This is set asynchronously after a timeout.)
        self.signatures = []

        self.waa = True  # RYAN BURGERT STUFF
        self.sand_creature = 'dust ball'

        # Create a Registry for the key bindings.
        self.key_bindings_registry = MergedRegistry([
            ConditionalRegistry(
                registry=load_key_bindings_for_prompt(
                    enable_abort_and_exit_bindings=True,
                    enable_search=True,
                    enable_open_in_editor=Condition(
                        lambda cli: self.enable_open_in_editor),
                    enable_system_bindings=Condition(
                        lambda cli: self.enable_system_bindings),
                    enable_auto_suggest_bindings=Condition(
                        lambda cli: self.enable_auto_suggest)),
                # Disable all default key bindings when the sidebar or the exit confirmation
                # are shown.
                filter=Condition(lambda cli: not (self.show_sidebar or self.
                                                  show_exit_confirmation))),
            load_mouse_bindings(),
            load_python_bindings(self),
            load_sidebar_bindings(self),
            load_confirm_exit_bindings(self),
        ])

        # Boolean indicating whether we have a signatures thread running.
        # (Never run more than one at the same time.)
        self._get_signatures_thread_running = False
Example #21
0
def show_multi_column_completions_menu(python_input):
    return Condition(lambda cli: python_input.completion_visualisation ==
                     CompletionVisualisation.MULTI_COLUMN)
Example #22
0
def create_key_bindings(python_input, history_mapping):
    """
    Key bindings.
    """
    registry = load_key_bindings(enable_search=True,
                                 enable_extra_page_navigation=True)
    handle = registry.add_binding

    @handle(' ', filter=HasFocus(HISTORY_BUFFER))
    def _(event):
        """
        Space: select/deselect line from history pane.
        """
        b = event.current_buffer
        line_no = b.document.cursor_position_row

        if line_no in history_mapping.selected_lines:
            # Remove line.
            history_mapping.selected_lines.remove(line_no)
            history_mapping.update_default_buffer(event.cli)
        else:
            # Add line.
            history_mapping.selected_lines.add(line_no)
            history_mapping.update_default_buffer(event.cli)

            # Update cursor position
            default_buffer = event.cli.buffers[DEFAULT_BUFFER]
            default_lineno = sorted(history_mapping.selected_lines).index(line_no) + \
                history_mapping.result_line_offset
            default_buffer.cursor_position = \
                default_buffer.document.translate_row_col_to_index(default_lineno, 0)

        # Also move the cursor to the next line. (This way they can hold
        # space to select a region.)
        b.cursor_position = b.document.translate_row_col_to_index(
            line_no + 1, 0)

    @handle(' ', filter=HasFocus(DEFAULT_BUFFER))
    @handle(Keys.Delete, filter=HasFocus(DEFAULT_BUFFER))
    @handle(Keys.ControlH, filter=HasFocus(DEFAULT_BUFFER))
    def _(event):
        """
        Space: remove line from default pane.
        """
        b = event.current_buffer
        line_no = b.document.cursor_position_row - history_mapping.result_line_offset

        if line_no >= 0:
            try:
                history_lineno = sorted(
                    history_mapping.selected_lines)[line_no]
            except IndexError:
                pass  # When `selected_lines` is an empty set.
            else:
                history_mapping.selected_lines.remove(history_lineno)

            history_mapping.update_default_buffer(event.cli)

    help_focussed = HasFocus(HELP_BUFFER)
    main_buffer_focussed = HasFocus(HISTORY_BUFFER) | HasFocus(DEFAULT_BUFFER)

    @handle(Keys.Tab, filter=main_buffer_focussed)
    @handle(Keys.ControlX, filter=main_buffer_focussed, eager=True)
    # Eager: ignore the Emacs [Ctrl-X Ctrl-X] binding.
    @handle(Keys.ControlW, filter=main_buffer_focussed)
    def _(event):
        " Select other window. "
        _select_other_window(event.cli)

    @handle(Keys.F4)
    def _(event):
        " Switch between Emacs/Vi mode. "
        python_input.vi_mode = not python_input.vi_mode

    @handle(Keys.F1)
    def _(event):
        " Display/hide help. "
        _toggle_help(event.cli)

    @handle(Keys.ControlJ, filter=help_focussed)
    @handle(Keys.ControlC, filter=help_focussed)
    @handle(Keys.ControlG, filter=help_focussed)
    @handle(Keys.Escape, filter=help_focussed)
    def _(event):
        " Leave help. "
        event.cli.pop_focus()

    @handle('q', filter=main_buffer_focussed)
    @handle(Keys.F3, filter=main_buffer_focussed)
    @handle(Keys.ControlC, filter=main_buffer_focussed)
    @handle(Keys.ControlG, filter=main_buffer_focussed)
    def _(event):
        " Cancel and go back. "
        event.cli.set_return_value(None)

    enable_system_bindings = Condition(
        lambda cli: python_input.enable_system_bindings)

    @handle(Keys.ControlZ, filter=enable_system_bindings)
    def _(event):
        " Suspend to background. "
        event.cli.suspend_to_background()

    return registry