示例#1
0
    def __init__(self, process):
        assert isinstance(process, Process)

        self.process = process
        self.chosen_name = None

        # Displayed the clock instead of this pane content.
        self.clock_mode = False

        # Give unique ID.
        Pane._pane_counter += 1
        self.pane_id = Pane._pane_counter

        # Prompt_toolkit buffer, for displaying scrollable text.
        # (In copy mode, or help mode.)
        # Note: Because the scroll_buffer can only contain text, we also use the
        #       copy_token_list, that contains a token list with color information.
        self.scroll_buffer = Buffer(read_only=True)
        self.copy_token_list = []
        self.display_scroll_buffer = False
        self.scroll_buffer_title = ''

        # Search buffer, for use in copy mode. (Each pane gets its own search buffer.)
        self.search_buffer = Buffer()
        self.is_searching = False
        self.search_state = SearchState(ignore_case=False)
示例#2
0
 def current_search_state(self):
     """
     Return the current `SearchState`. (The one for the focused
     `BufferControl`.)
     """
     ui_control = self.layout.current_control
     if isinstance(ui_control, BufferControl):
         return ui_control.search_state
     else:
         return SearchState()  # Dummy search state.  (Don't return None!)
示例#3
0
    def _enter_scroll_buffer(self, title, document, token_list):
        # Suspend child process.
        self.process.suspend()

        self.scroll_buffer.set_document(document, bypass_readonly=True)
        self.copy_token_list = token_list
        self.display_scroll_buffer = True
        self.scroll_buffer_title = title

        # Reset search state.
        self.search_state = SearchState(ignore_case=False)
示例#4
0
    def create_content(self, cli, width, height):
        """
        Create a UIContent.
        """
        buffer = self._buffer(cli)

        # Get the document to be shown. If we are currently searching (the
        # search buffer has focus, and the preview_search filter is enabled),
        # then use the search document, which has possibly a different
        # text/cursor position.)
        def preview_now():
            """ True when we should preview a search. """
            return bool(
                self.preview_search(cli)
                and cli.buffers[self.search_buffer_name].text)

        if preview_now():
            if self.get_search_state:
                ss = self.get_search_state(cli)
            else:
                ss = cli.search_state

            document = buffer.document_for_search(
                SearchState(text=cli.current_buffer.text,
                            direction=ss.direction,
                            ignore_case=ss.ignore_case))
        else:
            document = buffer.document

        get_processed_line = self._create_get_processed_line_func(
            cli, document)
        self._last_get_processed_line = get_processed_line

        def translate_rowcol(row, col):
            " Return the content column for this coordinate. "
            return Point(y=row,
                         x=get_processed_line(row).source_to_display(col))

        def get_line(i):
            " Return the tokens for a given line number. "
            tokens = get_processed_line(i).tokens

            # Add a space at the end, because that is a possible cursor
            # position. (When inserting after the input.) We should do this on
            # all the lines, not just the line containing the cursor. (Because
            # otherwise, line wrapping/scrolling could change when moving the
            # cursor around.)
            tokens = tokens + [(self.default_char.token, ' ')]
            return tokens

        content = UIContent(get_line=get_line,
                            line_count=document.line_count,
                            cursor_position=translate_rowcol(
                                document.cursor_position_row,
                                document.cursor_position_col),
                            default_char=self.default_char)

        # If there is an auto completion going on, use that start point for a
        # pop-up menu position. (But only when this buffer has the focus --
        # there is only one place for a menu, determined by the focussed buffer.)
        if cli.current_buffer_name == self.buffer_name:
            menu_position = self.menu_position(
                cli) if self.menu_position else None
            if menu_position is not None:
                assert isinstance(menu_position, int)
                menu_row, menu_col = buffer.document.translate_index_to_position(
                    menu_position)
                content.menu_position = translate_rowcol(menu_row, menu_col)
            elif buffer.complete_state:
                # Position for completion menu.
                # Note: We use 'min', because the original cursor position could be
                #       behind the input string when the actual completion is for
                #       some reason shorter than the text we had before. (A completion
                #       can change and shorten the input.)
                menu_row, menu_col = buffer.document.translate_index_to_position(
                    min(
                        buffer.cursor_position, buffer.complete_state.
                        original_document.cursor_position))
                content.menu_position = translate_rowcol(menu_row, menu_col)
            else:
                content.menu_position = None

        return content
示例#5
0
    def create_screen(self, cli, width, height):
        buffer = self._buffer(cli)

        # Get the document to be shown. If we are currently searching (the
        # search buffer has focus, and the preview_search filter is enabled),
        # then use the search document, which has possibly a different
        # text/cursor position.)
        def preview_now():
            """ True when we should preview a search. """
            return bool(
                self.preview_search(cli) and cli.is_searching
                and cli.current_buffer.text)

        if preview_now():
            document = buffer.document_for_search(
                SearchState(text=cli.current_buffer.text,
                            direction=cli.search_state.direction,
                            ignore_case=cli.search_state.ignore_case))
        else:
            document = buffer.document

        # Wrap.
        wrap_width = width if self.wrap_lines(cli) else None

        def _create_screen():
            screen = Screen(self.default_char, initial_width=width)

            # Get tokens
            # Note: we add the space character at the end, because that's where
            #       the cursor can also be.
            input_tokens, source_to_display, display_to_source = self._get_input_tokens(
                cli, document)
            input_tokens += [(self.default_char.token, ' ')]

            indexes_to_pos = screen.write_data(input_tokens, width=wrap_width)
            pos_to_indexes = dict((v, k) for k, v in indexes_to_pos.items())

            def cursor_position_to_xy(cursor_position):
                """ Turn a cursor position in the buffer into x/y coordinates
                on the screen. """
                # First get the real token position by applying all transformations.
                cursor_position = source_to_display(cursor_position)

                # Then look up into the table.
                try:
                    return indexes_to_pos[cursor_position]
                except KeyError:
                    # This can fail with KeyError, but only if one of the
                    # processors is returning invalid key locations.
                    raise
                    # return 0, 0

            def xy_to_cursor_position(x, y):
                """ Turn x/y screen coordinates back to the original cursor
                position in the buffer. """
                # Look up reverse in table.
                while x > 0 or y > 0:
                    try:
                        index = pos_to_indexes[x, y]
                        break
                    except KeyError:
                        # No match found -> mouse click outside of region
                        # containing text. Look to the left or up.
                        if x: x -= 1
                        elif y: y -= 1
                else:
                    # Nobreak.
                    index = 0

                # Transform.
                return display_to_source(index)

            return screen, cursor_position_to_xy, xy_to_cursor_position

        # Build a key for the caching. If any of these parameters changes, we
        # have to recreate a new screen.
        key = (
            # When the text changes, we obviously have to recreate a new screen.
            document.text,

            # When the width changes, line wrapping will be different.
            # (None when disabled.)
            wrap_width,

            # Include invalidation_hashes from all processors.
            tuple(
                p.invalidation_hash(cli, document)
                for p in self.input_processors),
        )

        # Get from cache, or create if this doesn't exist yet.
        screen, cursor_position_to_xy, self._xy_to_cursor_position = self._screen_lru_cache.get(
            key, _create_screen)

        x, y = cursor_position_to_xy(document.cursor_position)
        screen.cursor_position = Point(y=y, x=x)

        # If there is an auto completion going on, use that start point for a
        # pop-up menu position. (But only when this buffer has the focus --
        # there is only one place for a menu, determined by the focussed buffer.)
        if cli.current_buffer_name == self.buffer_name:
            menu_position = self.menu_position(
                cli) if self.menu_position else None
            if menu_position is not None:
                assert isinstance(menu_position, int)
                x, y = cursor_position_to_xy(menu_position)
                screen.menu_position = Point(y=y, x=x)
            elif buffer.complete_state:
                # Position for completion menu.
                # Note: We use 'min', because the original cursor position could be
                #       behind the input string when the actual completion is for
                #       some reason shorter than the text we had before. (A completion
                #       can change and shorten the input.)
                x, y = cursor_position_to_xy(
                    min(
                        buffer.cursor_position, buffer.complete_state.
                        original_document.cursor_position))
                screen.menu_position = Point(y=y, x=x)
            else:
                screen.menu_position = None

        return screen
示例#6
0
    def create_screen(self, cli, width, height):
        buffer = self._buffer(cli)

        # Get the document to be shown. If we are currently searching (the
        # search buffer has focus, and the preview_search filter is enabled),
        # then use the search document, which has possibly a different
        # text/cursor position.)
        def preview_now():
            """ True when we should preview a search. """
            return bool(
                self.preview_search(cli) and cli.is_searching
                and cli.current_buffer.text)

        if preview_now():
            document = buffer.document_for_search(
                SearchState(text=cli.current_buffer.text,
                            direction=cli.search_state.direction,
                            ignore_case=cli.search_state.ignore_case))
        else:
            document = buffer.document

        def _create_screen():
            screen = Screen(width)

            # Get tokens
            # Note: we add the space character at the end, because that's where
            #       the cursor can also be.
            input_tokens, cursor_transform_functions = self._get_input_tokens(
                cli, document)
            input_tokens += [(Token, ' ')]

            indexes_to_pos = screen.write_data(input_tokens,
                                               screen.width,
                                               margin=self._margin(
                                                   cli, buffer))

            def cursor_position_to_xy(cursor_position):
                # First get the real token position by applying all
                # transformations from the input processors.
                for f in cursor_transform_functions:
                    cursor_position = f(cursor_position)

                # Then look up into the table.
                try:
                    return indexes_to_pos[cursor_position]
                except KeyError:
                    # This can fail with KeyError, but only if one of the
                    # processors is returning invalid key locations.
                    raise
                    # return 0, 0

            return screen, cursor_position_to_xy

        # Build a key for the caching. If any of these parameters changes, we
        # have to recreate a new screen.
        key = (
            # When the text changes, we obviously have to recreate a new screen.
            document.text,

            # When the width changes, line wrapping will be different.
            # TODO: allow to disable line wrapping. + in that case, remove 'width'
            width,

            # When line numbers are enabled/disabled.
            self.show_line_numbers(cli),

            # Include invalidation_hashes from all processors.
            tuple(
                p.invalidation_hash(cli, document)
                for p in self.input_processors),
        )

        # Get from cache, or create if this doesn't exist yet.
        screen, cursor_position_to_xy = self._screen_lru_cache.get(
            key, _create_screen)

        x, y = cursor_position_to_xy(document.cursor_position)
        screen.cursor_position = Point(y=y, x=x)

        # If there is an auto completion going on, use that start point for a
        # pop-up menu position. (But only when this buffer has the focus --
        # there is only one place for a menu, determined by the focussed buffer.)
        if cli.current_buffer_name == self.buffer_name:
            menu_position = self.menu_position(
                cli) if self.menu_position else None
            if menu_position is not None:
                assert isinstance(menu_position, int)
                x, y = cursor_position_to_xy(menu_position)
                screen.menu_position = Point(y=y, x=x)
            elif buffer.complete_state:
                # Position for completion menu.
                # Note: We use 'min', because the original cursor position could be
                #       behind the input string when the actual completion is for
                #       some reason shorter than the text we had before. (A completion
                #       can change and shorten the input.)
                x, y = cursor_position_to_xy(
                    min(
                        buffer.cursor_position, buffer.complete_state.
                        original_document.cursor_position))
                screen.menu_position = Point(y=y, x=x)
            else:
                screen.menu_position = None

        return screen
示例#7
0
    def __init__(self,
                 buffer=None,
                 input_processor=None,
                 lexer=None,
                 preview_search=False,
                 focusable=True,
                 search_buffer_control=None,
                 get_search_buffer_control=None,
                 get_search_state=None,
                 menu_position=None,
                 focus_on_click=False,
                 key_bindings=None):
        from prompt_toolkit.key_binding.key_bindings import KeyBindingsBase
        assert buffer is None or isinstance(buffer, Buffer)
        assert input_processor is None or isinstance(input_processor,
                                                     Processor)
        assert menu_position is None or callable(menu_position)
        assert lexer is None or isinstance(lexer, Lexer)
        assert search_buffer_control is None or isinstance(
            search_buffer_control, BufferControl)
        assert get_search_buffer_control is None or callable(
            get_search_buffer_control)
        assert not (search_buffer_control and get_search_buffer_control)
        assert get_search_state is None or callable(get_search_state)
        assert key_bindings is None or isinstance(key_bindings,
                                                  KeyBindingsBase)

        # Default search state.
        if get_search_state is None:
            search_state = SearchState()

            def get_search_state():
                return search_state

        # Default input processor (display search and selection by default.)
        if input_processor is None:
            input_processor = merge_processors([
                HighlightSearchProcessor(),
                HighlightSelectionProcessor(),
                DisplayMultipleCursors(),
            ])

        self.preview_search = to_filter(preview_search)
        self.focusable = to_filter(focusable)
        self.get_search_state = get_search_state
        self.focus_on_click = to_filter(focus_on_click)

        self.input_processor = input_processor
        self.buffer = buffer or Buffer()
        self.menu_position = menu_position
        self.lexer = lexer or SimpleLexer()
        self.get_search_buffer_control = get_search_buffer_control
        self.key_bindings = key_bindings
        self._search_buffer_control = search_buffer_control

        #: Cache for the lexer.
        #: Often, due to cursor movement, undo/redo and window resizing
        #: operations, it happens that a short time, the same document has to be
        #: lexed. This is a fairly easy way to cache such an expensive operation.
        self._fragment_cache = SimpleCache(maxsize=8)

        self._xy_to_cursor_position = None
        self._last_click_timestamp = None
        self._last_get_processed_line = None