Esempio n. 1
0
    def apply_transformation(
            self, transformation_input: TransformationInput) -> Transformation:

        buffer_control, document, lineno, source_to_display, fragments, _, _ = transformation_input.unpack(
        )

        # When the application is in the 'done' state, don't highlight.
        if get_app().is_done:
            return Transformation(fragments)

        # Get the highlight positions.
        key = (get_app().render_counter, document.text,
               document.cursor_position)
        positions = self._positions_cache.get(
            key, lambda: self._get_positions_to_highlight(document))

        # Apply if positions were found at this line.
        if positions:
            for row, col in positions:
                if row == lineno:
                    col = source_to_display(col)
                    fragments = explode_text_fragments(fragments)
                    style, text, *_ = fragments[col]

                    if col == document.cursor_position_col:
                        style += ' class:matching-bracket.cursor '
                    else:
                        style += ' class:matching-bracket.other '

                    fragments[col] = (style, text)

        return Transformation(fragments)
    def request_absolute_cursor_position(self) -> None:
        """
        Get current cursor position.

        We do this to calculate the minimum available height that we can
        consume for rendering the prompt. This is the available space below te
        cursor.

        For vt100: Do CPR request. (answer will arrive later.)
        For win32: Do API call. (Answer comes immediately.)
        """
        # Only do this request when the cursor is at the top row. (after a
        # clear or reset). We will rely on that in `report_absolute_cursor_row`.
        assert self._cursor_pos.y == 0

        # In full-screen mode, always use the total height as min-available-height.
        if self.full_screen:
            self._min_available_height = self.output.get_size().rows

        # For Win32, we have an API call to get the number of rows below the
        # cursor.
        elif is_windows():
            self._min_available_height = self.output.get_rows_below_cursor_position(
            )

        # Use CPR.
        else:
            if self.cpr_support == CPR_Support.NOT_SUPPORTED:
                return

            def do_cpr() -> None:
                # Asks for a cursor position report (CPR).
                self._waiting_for_cpr_futures.append(Future())
                self.output.ask_for_cpr()

            if self.cpr_support == CPR_Support.SUPPORTED:
                do_cpr()

            # If we don't know whether CPR is supported, only do a request if
            # none is pending, and test it, using a timer.
            elif self.cpr_support == CPR_Support.UNKNOWN and not self.waiting_for_cpr:
                do_cpr()

                async def timer() -> None:
                    await sleep(self.CPR_TIMEOUT)

                    # Not set in the meantime -> not supported.
                    if self.cpr_support == CPR_Support.UNKNOWN:
                        self.cpr_support = CPR_Support.NOT_SUPPORTED

                        if self.cpr_not_supported_callback:
                            # Make sure to call this callback in the main thread.
                            self.cpr_not_supported_callback()

                get_app().create_background_task(timer())
Esempio n. 3
0
def completion_is_selected() -> bool:
    """
    True when the user selected a completion.
    """
    complete_state = get_app().current_buffer.complete_state
    return (complete_state is not None and
            complete_state.current_completion is not None)
    def _start_timeout(self) -> None:
        """
        Start auto flush timeout. Similar to Vim's `timeoutlen` option.

        Start a background thread with a timer. When this timeout expires and
        no key was pressed in the meantime, we flush all data in the queue and
        call the appropriate key binding handlers.
        """
        app = get_app()
        timeout = app.timeoutlen

        if timeout is None:
            return

        counter = self._keys_pressed

        async def wait() -> None:
            " Wait for timeout. "
            await sleep(timeout)

            if len(self.key_buffer) > 0 and counter == self._keys_pressed:
                # (No keys pressed in the meantime.)
                flush_keys()

        def flush_keys() -> None:
            " Flush keys. "
            self.feed(_Flush)
            self.process_keys()

        # Automatically flush keys.
        # (_daemon needs to be set, otherwise, this will hang the
        # application for .5 seconds before exiting.)
        app.create_background_task(wait())
Esempio n. 5
0
def emacs_insert_mode() -> bool:
    app = get_app()
    if (app.editing_mode != EditingMode.EMACS
            or app.current_buffer.selection_state
            or app.current_buffer.read_only()):
        return False
    return True
Esempio n. 6
0
    def create_content(self, width: int, height: int) -> UIContent:
        """
        Create a UIContent object for this control.
        """
        complete_state = get_app().current_buffer.complete_state
        if complete_state:
            completions = complete_state.completions
            index = complete_state.complete_index  # Can be None!

            # Calculate width of completions menu.
            menu_width = self._get_menu_width(width, complete_state)
            menu_meta_width = self._get_menu_meta_width(width - menu_width, complete_state)
            show_meta = self._show_meta(complete_state)

            def get_line(i: int) -> StyleAndTextTuples:
                c = completions[i]
                is_current_completion = (i == index)
                result = _get_menu_item_fragments(
                    c, is_current_completion, menu_width, space_after=True)

                if show_meta:
                    result += self._get_menu_item_meta_fragments(c, is_current_completion, menu_meta_width)
                return result

            return UIContent(get_line=get_line,
                             cursor_position=Point(x=0, y=index or 0),
                             line_count=len(completions))

        return UIContent()
Esempio n. 7
0
def control_is_searchable() -> bool:
    " When the current UIControl is searchable. "
    from prompt_toolkit_dev.layout.controls import BufferControl
    control = get_app().layout.current_control

    return (isinstance(control, BufferControl) and
            control.search_buffer_control is not None)
Esempio n. 8
0
    def _get_menu_fragments(self) -> StyleAndTextTuples:
        focused = get_app().layout.has_focus(self.window)

        # This is called during the rendering. When we discover that this
        # widget doesn't have the focus anymore. Reset menu state.
        if not focused:
            self.selected_menu = [0]

        # Generate text fragments for the main menu.
        def one_item(i: int, item: MenuItem) -> Iterable[OneStyleAndTextTuple]:
            def mouse_handler(mouse_event: MouseEvent) -> None:
                if mouse_event.event_type == MouseEventType.MOUSE_UP:
                    # Toggle focus.
                    app = get_app()
                    if app.layout.has_focus(self.window):
                        if self.selected_menu == [i]:
                            app.layout.focus_last()
                    else:
                        app.layout.focus(self.window)
                    self.selected_menu = [i]

            yield ('class:menu-bar', ' ', mouse_handler)
            if i == self.selected_menu[0] and focused:
                yield ('[SetMenuPosition]', '', mouse_handler)
                style = 'class:menu-bar.selected-item'
            else:
                style = 'class:menu-bar'
            yield style, item.text, mouse_handler

        result: StyleAndTextTuples = []
        for i, item in enumerate(self.menu_items):
            result.extend(one_item(i, item))

        return result
Esempio n. 9
0
def vi_recording_macro() -> bool:
    " When recording a Vi macro. "
    app = get_app()
    if app.editing_mode != EditingMode.VI:
        return False

    return app.vi_state.recording_register is not None
Esempio n. 10
0
 def mouse_handler(mouse_event: MouseEvent) -> None:
     if mouse_event.event_type == MouseEventType.MOUSE_UP:
         app = get_app()
         if item.handler:
             app.layout.focus_last()
             item.handler()
         else:
             self.selected_menu = self.selected_menu[:level + 1] + [i]
Esempio n. 11
0
 def _get_formatted_text_cached(self) -> StyleAndTextTuples:
     """
     Get fragments, but only retrieve fragments once during one render run.
     (This function is called several times during one rendering, because
     we also need those for calculating the dimensions.)
     """
     return self._fragment_cache.get(
         get_app().render_counter,
         lambda: to_formatted_text(self.text, self.style))
Esempio n. 12
0
    def preferred_height(
            self, width: int, max_available_height: int, wrap_lines: bool,
            get_line_prefix: Optional[GetLinePrefixCallable]) -> Optional[int]:

        complete_state = get_app().current_buffer.complete_state
        if complete_state:
            return len(complete_state.completions)
        else:
            return 0
Esempio n. 13
0
    def preferred_width(self, max_available_width: int) -> Optional[int]:
        complete_state = get_app().current_buffer.complete_state
        if complete_state:
            menu_width = self._get_menu_width(500, complete_state)
            menu_meta_width = self._get_menu_meta_width(500, complete_state)

            return menu_width + menu_meta_width
        else:
            return 0
Esempio n. 14
0
        def get_formatted_text() -> StyleAndTextTuples:
            arg = get_app().key_processor.arg or ''
            if arg == '-':
                arg = '-1'

            return [
                ('class:arg-toolbar', 'Repeat: '),
                ('class:arg-toolbar.text', arg),
            ]
Esempio n. 15
0
 def _get_main_buffer(
         self,
         buffer_control: 'BufferControl') -> Optional['BufferControl']:
     from prompt_toolkit_dev.layout.controls import BufferControl
     prev_control = get_app().layout.search_target_buffer_control
     if isinstance(prev_control, BufferControl) and \
             prev_control.search_buffer_control == buffer_control:
         return prev_control
     return None
Esempio n. 16
0
            def test() -> bool:
                # Consider focused when any window inside this container is
                # focused.
                current_window = get_app().layout.current_window

                for c in walk(cast(Container, value)):
                    if isinstance(c, Window) and c == current_window:
                        return True
                return False
Esempio n. 17
0
 def mouse_handler(mouse_event: MouseEvent) -> None:
     if mouse_event.event_type == MouseEventType.MOUSE_UP:
         # Toggle focus.
         app = get_app()
         if app.layout.has_focus(self.window):
             if self.selected_menu == [i]:
                 app.layout.focus_last()
         else:
             app.layout.focus(self.window)
         self.selected_menu = [i]
Esempio n. 18
0
def renderer_height_is_known() -> bool:
    """
    Only True when the renderer knows it's real height.

    (On VT100 terminals, we have to wait for a CPR response, before we can be
    sure of the available height between the cursor position and the bottom of
    the terminal. And usually it's nicer to wait with drawing bottom toolbars
    until we receive the height, in order to avoid flickering -- first drawing
    somewhere in the middle, and then again at the bottom.)
    """
    return get_app().renderer.height_is_known
Esempio n. 19
0
    def _get_text_fragments(self) -> StyleAndTextTuples:
        style = 'class:completion-menu.multi-column-meta'
        state = get_app().current_buffer.complete_state

        if state and state.current_completion and state.current_completion.display_meta_text:
            return to_formatted_text(
                cast(StyleAndTextTuples, [('', ' ')]) +
                state.current_completion.display_meta +
                [('', ' ')],
                style=style)

        return []
Esempio n. 20
0
    def _get_text_fragments(self) -> StyleAndTextTuples:
        app = get_app()
        if app.key_processor.arg is None:
            return []
        else:
            arg = app.key_processor.arg

            return [
                ('class:prompt.arg', '(arg: '),
                ('class:prompt.arg.text', str(arg)),
                ('class:prompt.arg', ') '),
            ]
Esempio n. 21
0
def vi_replace_mode() -> bool:
    from prompt_toolkit_dev.key_binding.vi_state import InputMode
    app = get_app()

    if (app.editing_mode != EditingMode.VI
            or app.vi_state.operator_func
            or app.vi_state.waiting_for_digraph
            or app.current_buffer.selection_state
            or app.vi_state.temporary_navigation_mode
            or app.current_buffer.read_only()):
        return False

    return app.vi_state.input_mode == InputMode.REPLACE
Esempio n. 22
0
        def filter() -> bool:
            " Only handle key bindings if this menu is visible. "
            app = get_app()
            complete_state = app.current_buffer.complete_state

            # There need to be completions, and one needs to be selected.
            if complete_state is None or complete_state.complete_index is None:
                return False

            # This menu needs to be visible.
            return any(
                window.content == self
                for window in app.layout.visible_windows)
Esempio n. 23
0
    def preferred_height(
            self, width: int, max_available_height: int, wrap_lines: bool,
            get_line_prefix: Optional[GetLinePrefixCallable]) -> Optional[int]:
        """
        Preferred height: as much as needed in order to display all the completions.
        """
        complete_state = get_app().current_buffer.complete_state
        if complete_state is None:
            return 0

        column_width = self._get_column_width(complete_state)
        column_count = max(1, (width - self._required_margin) // column_width)

        return int(math.ceil(len(complete_state.completions) / float(column_count)))
Esempio n. 24
0
    def preferred_width(self, max_available_width: int) -> Optional[int]:
        """
        Report the width of the longest meta text as the preferred width of this control.

        It could be that we use less width, but this way, we're sure that the
        layout doesn't change when we select another completion (E.g. that
        completions are suddenly shown in more or fewer columns.)
        """
        app = get_app()
        if app.current_buffer.complete_state:
            state = app.current_buffer.complete_state
            return 2 + max(get_cwidth(c.display_meta_text) for c in state.completions)
        else:
            return 0
Esempio n. 25
0
        def move(right: bool = False) -> None:
            buff = get_app().current_buffer
            complete_state = buff.complete_state

            if complete_state is not None and \
                    complete_state.complete_index is not None:
                # Calculate new complete index.
                new_index = complete_state.complete_index
                if right:
                    new_index += self._rendered_rows
                else:
                    new_index -= self._rendered_rows

                if 0 <= new_index < len(complete_state.completions):
                    buff.go_to_completion(new_index)
    def _call_handler(self, handler: Binding, key_sequence: List[KeyPress]) -> None:
        app = get_app()
        was_recording_emacs = app.emacs_state.is_recording
        was_recording_vi = bool(app.vi_state.recording_register)
        was_temporary_navigation_mode = app.vi_state.temporary_navigation_mode
        arg = self.arg
        self.arg = None

        event = KeyPressEvent(
            weakref.ref(self),
            arg=arg,
            key_sequence=key_sequence,
            previous_key_sequence=self._previous_key_sequence,
            is_repeat=(handler == self._previous_handler))

        # Save the state of the current buffer.
        if handler.save_before(event):
            event.app.current_buffer.save_to_undo_stack()

        # Call handler.
        from prompt_toolkit_dev.buffer import EditReadOnlyBuffer
        try:
            handler.call(event)
            self._fix_vi_cursor_position(event)

        except EditReadOnlyBuffer:
            # When a key binding does an attempt to change a buffer which is
            # read-only, we can ignore that. We sound a bell and go on.
            app.output.bell()

        if was_temporary_navigation_mode:
            self._leave_vi_temp_navigation_mode(event)

        self._previous_key_sequence = key_sequence
        self._previous_handler = handler

        # Record the key sequence in our macro. (Only if we're in macro mode
        # before and after executing the key.)
        if handler.record_in_macro():
            if app.emacs_state.is_recording and was_recording_emacs:
                recording = app.emacs_state.current_recording
                if recording is not None:  # Should always be true, given that
                                           # `was_recording_emacs` is set.
                    recording.extend(key_sequence)

            if app.vi_state.recording_register and was_recording_vi:
                for k in key_sequence:
                    app.vi_state.current_recording += k.data
    def __init__(self,
                 key_processor_ref: 'weakref.ReferenceType[KeyProcessor]',
                 arg: Optional[str],
                 key_sequence: List[KeyPress],
                 previous_key_sequence: List[KeyPress],
                 is_repeat: bool) -> None:

        self._key_processor_ref = key_processor_ref
        self.key_sequence = key_sequence
        self.previous_key_sequence = previous_key_sequence

        #: True when the previous key sequence was handled by the same handler.
        self.is_repeat = is_repeat

        self._arg = arg
        self._app = get_app()
Esempio n. 28
0
def vi_navigation_mode() -> bool:
    """
    Active when the set for Vi navigation key bindings are active.
    """
    from prompt_toolkit_dev.key_binding.vi_state import InputMode
    app = get_app()

    if (app.editing_mode != EditingMode.VI
            or app.vi_state.operator_func
            or app.vi_state.waiting_for_digraph
            or app.current_buffer.selection_state):
        return False

    return (app.vi_state.input_mode == InputMode.NAVIGATION or
            app.vi_state.temporary_navigation_mode or
            app.current_buffer.read_only())
Esempio n. 29
0
        def get_formatted_text() -> StyleAndTextTuples:
            buff = get_app().current_buffer

            if buff.validation_error:
                row, column = buff.document.translate_index_to_position(
                    buff.validation_error.cursor_position)

                if show_position:
                    text = '%s (line=%s column=%s)' % (
                        buff.validation_error.message, row + 1, column + 1)
                else:
                    text = buff.validation_error.message

                return [('class:validation-toolbar', text)]
            else:
                return []
Esempio n. 30
0
async def show_dialog_as_float(dialog):
    " Coroutine. "
    float_ = Float(content=dialog)
    root_container.floats.insert(0, float_)

    app = get_app()

    focused_before = app.layout.current_window
    app.layout.focus(dialog)
    result = await dialog.future
    app.layout.focus(focused_before)

    if float_ in root_container.floats:
        root_container.floats.remove(float_)

    return result