예제 #1
0
    def _trim_text(self, text, max_width):
        """
        Trim the text to `max_width`, append dots when the text is too long.
        Returns (text, width) tuple.
        """
        width = get_cwidth(text)

        # When the text is too wide, trim it.
        if width > max_width:
            # When there are no double width characters, just use slice operation.
            if len(text) == width:
                trimmed_text = (text[:max(1, max_width-3)] + '...')[:max_width]
                return trimmed_text, len(trimmed_text)

            # Otherwise, loop until we have the desired width. (Rather
            # inefficient, but ok for now.)
            else:
                trimmed_text = ''
                for c in text:
                    if get_cwidth(trimmed_text + c) <= max_width - 3:
                        trimmed_text += c
                trimmed_text += '...'

                return (trimmed_text, get_cwidth(trimmed_text))
        else:
            return text, width
예제 #2
0
    def _trim_text(self, text, max_width):
        """
        Trim the text to `max_width`, append dots when the text is too long.
        Returns (text, width) tuple.
        """
        width = get_cwidth(text)

        # When the text is too wide, trim it.
        if width > max_width:
            # When there are no double width characters, just use slice operation.
            if len(text) == width:
                trimmed_text = (text[:max(1, max_width - 3)] +
                                '...')[:max_width]
                return trimmed_text, len(trimmed_text)

            # Otherwise, loop until we have the desired width. (Rather
            # inefficient, but ok for now.)
            else:
                trimmed_text = ''
                for c in text:
                    if get_cwidth(trimmed_text + c) <= max_width - 3:
                        trimmed_text += c
                trimmed_text += '...'

                return (trimmed_text, get_cwidth(trimmed_text))
        else:
            return text, width
예제 #3
0
    def wrap_line(self, lineno, width, get_line_prefix, slice_stop=None):
        """
        :param slice_stop: Wrap only "line[:slice_stop]" and return that
            partial result. This is needed for scrolling the window correctly
            when line wrapping.
        :returns: A tuple `(height, computed_fragment_list)`.
        """
        # TODO: If no prefix is given, use the fast path
        #       that we had before.
        if get_line_prefix is None:
            get_line_prefix = lambda *a: []

        # Instead of using `get_line_prefix` as key, we use render_counter
        # instead. This is more reliable, because this function could still be
        # the same, while the content would change over time.
        key = get_app().render_counter, lineno, width, slice_stop

        try:
            return self._line_heights_and_fragments[key]
        except KeyError:
            if width == 0:
                height = 10**8
            else:
                # Get line.
                line = fragment_list_to_text(
                    self.get_line(lineno))[:slice_stop]

                # Start with this line + first prefix.
                fragments = [('', line)]
                fragments.extend(to_formatted_text(get_line_prefix(lineno, 0)))

                # Calculate text width first.
                text_width = get_cwidth(fragment_list_to_text(fragments))

                # Keep wrapping as long as the line doesn't fit.
                # Keep adding new prefixes for every wrapped line.
                height = 1

                while text_width > width:
                    height += 1
                    text_width -= width

                    fragments2 = to_formatted_text(
                        get_line_prefix(lineno, height - 1))
                    fragments.extend(fragments2)
                    prefix_width = get_cwidth(
                        fragment_list_to_text(fragments2))

                    if prefix_width > width:  # Prefix doesn't fit.
                        height = 10**8
                        break

                    text_width += prefix_width

            # Cache and return
            self._line_heights_and_fragments[key] = height, fragments
            return height, fragments
    def __init__(self, start='[', end=']', sym_a='=', sym_b='>', sym_c=' ', unknown='#'):
        assert len(sym_a) == 1 and get_cwidth(sym_a) == 1
        assert len(sym_c) == 1 and get_cwidth(sym_c) == 1

        self.start = start
        self.end = end
        self.sym_a = sym_a
        self.sym_b = sym_b
        self.sym_c = sym_c
        self.unknown = unknown
예제 #5
0
    def __init__(self, start='[', end=']', sym_a='=', sym_b='>', sym_c=' ', unknown='#'):
        assert len(sym_a) == 1 and get_cwidth(sym_a) == 1
        assert len(sym_c) == 1 and get_cwidth(sym_c) == 1

        self.start = start
        self.end = end
        self.sym_a = sym_a
        self.sym_b = sym_b
        self.sym_c = sym_c
        self.unknown = unknown
예제 #6
0
 def _get_column_width(self, complete_state):
     """
     Return the width of each column.
     """
     return max(
         get_cwidth(c.display)
         for c in complete_state.current_completions) + 1
예제 #7
0
def _trim_formatted_text(formatted_text: StyleAndTextTuples,
                         max_width: int) -> Tuple[StyleAndTextTuples, int]:
    """
    Trim the text to `max_width`, append dots when the text is too long.
    Returns (text, width) tuple.
    """
    width = fragment_list_width(formatted_text)

    # When the text is too wide, trim it.
    if width > max_width:
        result = []  # Text fragments.
        remaining_width = max_width - 3

        for style_and_ch in explode_text_fragments(formatted_text):
            ch_width = get_cwidth(style_and_ch[1])

            if ch_width <= remaining_width:
                result.append(style_and_ch)
                remaining_width -= ch_width
            else:
                break

        result.append(("", "..."))

        return result, max_width - remaining_width
    else:
        return formatted_text, width
예제 #8
0
    def display(page):
        # Display completions.
        page_completions = completions[page * completions_per_page:
                                       (page + 1) * completions_per_page]

        page_row_count = int(math.ceil(len(page_completions) / float(column_count)))
        page_columns = [page_completions[i * page_row_count:(i + 1) * page_row_count]
                   for i in range(column_count)]

        result = []  # FormattedText list: (style,text) tuples.

        for r in range(page_row_count):
            for c in range(column_count):
                try:
                    completion = page_columns[c][r]
                    style = 'class:readline-like-completions.completion ' + (completion.style or '')

                    result.extend(to_formatted_text(completion.display, style=style))

                    # Add padding.
                    padding = max_compl_width - get_cwidth(completion.display_text)
                    result.append((completion.style, ' ' * padding,))
                except IndexError:
                    pass
            result.append(('', '\n'))

        app.print_text(to_formatted_text(result, 'class:readline-like-completions'))
예제 #9
0
 def _get_menu_width(
         self, max_width: int, complete_state: CompletionState) -> int:
     """
     Return the width of the main column.
     """
     return min(max_width, max(self.MIN_WIDTH, max(get_cwidth(c.display_text)
                for c in complete_state.completions) + 2))
예제 #10
0
파일: widgets.py 프로젝트: plotski/upsies
    def __init__(self, text, content, group=None, style=''):
        self.text = text
        self.width = get_cwidth(text)
        self.label = Window(
            content=FormattedTextControl(text=text),
            dont_extend_width=True,
            dont_extend_height=True,
            style=style,
            align=WindowAlign.RIGHT,
        )
        self.container = VSplit([
            # The HSplit makes the label non-greedy, i.e. the label itself takes
            # only one line and the space below is empty with default background
            # color.
            HSplit([self.label]),
            hspacer,
            content,
        ])

        self._group = group
        if group is not None:
            self._groups[group].append(self)
            max_width = max(label.width for label in self._groups[group])
            for label in self._groups[group]:
                label.label.width = max_width + 1
예제 #11
0
    def display(page: int) -> None:
        # Display completions.
        page_completions = completions[
            page * completions_per_page : (page + 1) * completions_per_page
        ]

        page_row_count = int(math.ceil(len(page_completions) / float(column_count)))
        page_columns = [
            page_completions[i * page_row_count : (i + 1) * page_row_count]
            for i in range(column_count)
        ]

        result: StyleAndTextTuples = []

        for r in range(page_row_count):
            for c in range(column_count):
                try:
                    completion = page_columns[c][r]
                    style = "class:readline-like-completions.completion " + (
                        completion.style or ""
                    )

                    result.extend(to_formatted_text(completion.display, style=style))

                    # Add padding.
                    padding = max_compl_width - get_cwidth(completion.display_text)
                    result.append((completion.style, " " * padding,))
                except IndexError:
                    pass
            result.append(("", "\n"))

        app.print_text(to_formatted_text(result, "class:readline-like-completions"))
def _trim_formatted_text(formatted_text, max_width):
    """
    Trim the text to `max_width`, append dots when the text is too long.
    Returns (text, width) tuple.
    """
    width = fragment_list_width(formatted_text)

    # When the text is too wide, trim it.
    if width > max_width:
        result = []  # Text fragments.
        remaining_width = max_width - 3

        for style_and_ch in explode_text_fragments(formatted_text):
            ch_width = get_cwidth(style_and_ch[1])

            if ch_width <= remaining_width:
                result.append(style_and_ch)
                remaining_width -= ch_width
            else:
                break

        result.append(('', '...'))

        return result, max_width - remaining_width
    else:
        return formatted_text, width
예제 #13
0
파일: webdb.py 프로젝트: plotski/upsies
    def _get_text_fragments(self):
        if self.is_searching:
            return [('class:dialog.search.results', self._activity_indicator_string)]
        elif not self._results:
            return 'No results'

        frags = []
        for i, result in enumerate(self._results):
            if i == self._focused_index:
                title_style = 'class:dialog.search.results.focused'
                frags.append(('[SetCursorPosition]', ''))
                self._focused_result = result
            else:
                title_style = 'class:dialog.search.results'

            if get_cwidth(result.title) > self._title_width:
                title = result.title[:self._title_width - 1] + '…'
            else:
                title = result.title
            frags.append((title_style, title.ljust(self._title_width)))

            frags.append(('', (
                ' '
                f'{str(result.year or "").rjust(4)}'
                ' '
                f'{str(result.type).rjust(6)}'
            )))

            frags.append(('', '\n'))
        frags.pop()  # Remove last newline
        return frags
예제 #14
0
    def _get_text_fragments(self) -> StyleAndTextTuples:
        width = self.width - (get_cwidth(self.left_symbol) +
                              get_cwidth(self.right_symbol))
        text = ("{{:^{}}}".format(width)).format(self.text)

        def handler(mouse_event: MouseEvent) -> None:
            if (self.handler is not None
                    and mouse_event.event_type == MouseEventType.MOUSE_UP):
                self.handler()

        return [
            ("class:button.arrow", self.left_symbol, handler),
            ("[SetCursorPosition]", ""),
            ("class:button.text", text, handler),
            ("class:button.arrow", self.right_symbol, handler),
        ]
예제 #15
0
 def preferred_width(self, cli, max_available_width):
     """
     Return the preferred width for this control.
     That is the width of the longest line.
     """
     text = token_list_to_text(self._get_tokens_cached(cli))
     line_lengths = [get_cwidth(l) for l in text.split('\n')]
     return max(line_lengths)
예제 #16
0
def token_list_width(tokenlist):
    """
    Return the character width of this token list.
    (Take double width characters into account.)

    :param tokenlist: List of (token, text) tuples.
    """
    return sum(get_cwidth(c) for k, word in tokenlist for c in word)
 def preferred_width(self, max_available_width: int) -> int:
     """
     Return the preferred width for this control.
     That is the width of the longest line.
     """
     text = fragment_list_to_text(self._get_formatted_text_cached())
     line_lengths = [get_cwidth(l) for l in text.split('\n')]
     return max(line_lengths)
예제 #18
0
파일: controls.py 프로젝트: a-domingu/tbcnn
 def preferred_width(self, max_available_width: int) -> int:
     """
     Return the preferred width for this control.
     That is the width of the longest line.
     """
     text = fragment_list_to_text(self._get_formatted_text_cached())
     line_lengths = [get_cwidth(l) for l in text.split("\n")]
     return max(line_lengths)
예제 #19
0
 def preferred_width(self, cli, max_available_width):
     """
     Return the preferred width for this control.
     That is the width of the longest line.
     """
     text = token_list_to_text(self._get_tokens_cached(cli))
     line_lengths = [get_cwidth(l) for l in text.split('\n')]
     return max(line_lengths)
 def preferred_width(self, cli):
     """
     Return the preferred width for this control.
     That is the width of the longest line.
     """
     text = ''.join(t[1] for t in self.get_tokens(cli))
     line_lengths = [get_cwidth(l) for l in text.split('\n')]
     return max(line_lengths)
예제 #21
0
 def preferred_width(self, cli):
     """
     Return the preferred width for this control.
     That is the width of the longest line.
     """
     text = ''.join(t[1] for t in self.get_tokens(cli))
     line_lengths = [get_cwidth(l) for l in text.split('\n')]
     return max(line_lengths)
예제 #22
0
 def _get_menu_meta_width(self, max_width, complete_state):
     """
     Return the width of the meta column.
     """
     if self._show_meta(complete_state):
         return min(max_width, max(get_cwidth(c.display_meta) for c in complete_state.current_completions) + 2)
     else:
         return 0
예제 #23
0
    def __init__(self,
                 start: str = '[',
                 end: str = ']',
                 sym_a: str = '=',
                 sym_b: str = '>',
                 sym_c: str = ' ',
                 unknown: str = '#') -> None:

        assert len(sym_a) == 1 and get_cwidth(sym_a) == 1
        assert len(sym_c) == 1 and get_cwidth(sym_c) == 1

        self.start = start
        self.end = end
        self.sym_a = sym_a
        self.sym_b = sym_b
        self.sym_c = sym_c
        self.unknown = unknown
예제 #24
0
def token_list_width(tokenlist):
    """
    Return the character width of this token list.
    (Take double width characters into account.)

    :param tokenlist: List of (token, text) tuples.
    """
    return sum(get_cwidth(c) for k, word in tokenlist for c in word)
예제 #25
0
 def preferred_width(self, max_width):
     items = self._get_items()
     if self._display_format:
         items = (self._display_format(i) for i in items)
     max_item_width = max(get_cwidth(i) for i in items)
     max_item_width += self._format_overhead
     if max_item_width < self.MIN_WIDTH:
         max_item_width = self.MIN_WIDTH
     return min(max_width, max_item_width)
예제 #26
0
 def _get_menu_width(self, max_width, complete_state):
     """
     Return the width of the main column.
     """
     return min(
         max_width,
         max(
             get_cwidth(c.display)
             for c in complete_state.current_completions) + 2)
예제 #27
0
    def get_height_for_text(text, width):
        # Get text width for this line.
        line_width = get_cwidth(text)

        # Calculate height.
        quotient, remainder = divmod(line_width, width)
        if remainder:
            quotient += 1  # Like math.ceil.
        return max(1, quotient)
예제 #28
0
def token_list_width(tokenlist):
    """
    Return the character width of this token list.
    (Take double width characters into account.)

    :param tokenlist: List of (token, text) or (token, text, mouse_handler)
                      tuples.
    """
    return sum(get_cwidth(c) for item in tokenlist for c in item[1])
예제 #29
0
 def _get_menu_meta_width(self, max_width, complete_state):
     """
     Return the width of the meta column.
     """
     if self._show_meta(complete_state):
         return min(max_width, max(get_cwidth(c.display_meta)
                    for c in complete_state.current_completions) + 2)
     else:
         return 0
예제 #30
0
def token_list_width(tokenlist):
    """
    Return the character width of this token list.
    (Take double width characters into account.)

    :param tokenlist: List of (token, text) or (token, text, mouse_handler)
                      tuples.
    """
    return sum(get_cwidth(c) for item in tokenlist for c in item[1])
예제 #31
0
def fragment_list_width(fragments):
    """
    Return the character width of this text fragment list.
    (Take double width characters into account.)

    :param fragments: List of ``(style_str, text)`` or
        ``(style_str, text, mouse_handler)`` tuples.
    """
    ZeroWidthEscape = '[ZeroWidthEscape]'
    return sum(get_cwidth(c) for item in fragments for c in item[1] if ZeroWidthEscape not in item[0])
예제 #32
0
    def __init__(self, char=' ', token=Token):
        # If this character has to be displayed otherwise, take that one.
        char = self.display_mappings.get(char, char)

        self.char = char
        self.token = token

        # Calculate width. (We always need this, so better to store it directly
        # as a member for performance.)
        self.width = get_cwidth(char)
def fragment_list_width(fragments: StyleAndTextTuples) -> int:
    """
    Return the character width of this text fragment list.
    (Take double width characters into account.)

    :param fragments: List of ``(style_str, text)`` or
        ``(style_str, text, mouse_handler)`` tuples.
    """
    ZeroWidthEscape = '[ZeroWidthEscape]'
    return sum(get_cwidth(c) for item in fragments for c in item[1] if ZeroWidthEscape not in item[0])
예제 #34
0
    def __init__(
        self,
        start: str = "[",
        end: str = "]",
        sym_a: str = "=",
        sym_b: str = ">",
        sym_c: str = " ",
        unknown: str = "#",
    ) -> None:

        assert len(sym_a) == 1 and get_cwidth(sym_a) == 1
        assert len(sym_c) == 1 and get_cwidth(sym_c) == 1

        self.start = start
        self.end = end
        self.sym_a = sym_a
        self.sym_b = sym_b
        self.sym_c = sym_c
        self.unknown = unknown
예제 #35
0
    def __init__(self, char=' ', token=Token):
        # If this character has to be displayed otherwise, take that one.
        char = self.display_mappings.get(char, char)

        self.char = char
        self.token = token

        # Calculate width. (We always need this, so better to store it directly
        # as a member for performance.)
        self.width = get_cwidth(char)
예제 #36
0
    def get_width(self, progress_bar):
        if self.width:
            return self.width

        all_names = [self._add_suffix(c.label) for c in progress_bar.counters]
        if all_names:
            max_widths = max(get_cwidth(name) for name in all_names)
            return D(preferred=max_widths, max=max_widths)
        else:
            return D()
예제 #37
0
 def get_width():
     if width is None:
         text_fragments = to_formatted_text(self.text)
         text = fragment_list_to_text(text_fragments)
         if text:
             longest_line = max(get_cwidth(line) for line in text.splitlines())
         else:
             return D(preferred=0)
         return D(preferred=longest_line)
     else:
         return width
예제 #38
0
 def get_width() -> AnyDimension:
     if width is None:
         text_fragments = to_formatted_text(self.text)
         text = fragment_list_to_text(text_fragments)
         if text:
             longest_line = max(get_cwidth(line) for line in text.splitlines())
         else:
             return D(preferred=0)
         return D(preferred=longest_line)
     else:
         return width
예제 #39
0
    def __init__(self, char=' ', style=''):
        # If this character has to be displayed otherwise, take that one.
        if char in self.display_mappings:
            char = self.display_mappings[char]
            style += 'class:control-character'

        self.char = char
        self.style = style

        # Calculate width. (We always need this, so better to store it directly
        # as a member for performance.)
        self.width = get_cwidth(char)
예제 #40
0
    def format(self, progress_bar, progress, width):
        label = self._add_suffix(progress.label)
        cwidth = get_cwidth(label)

        if cwidth > width:
            # It doesn't fit -> scroll task name.
            max_scroll = cwidth - width
            current_scroll = int(time.time() * 3 % max_scroll)
            label = label[current_scroll:]

        # It does fit.
        return HTML(self.template).format(label=label)
예제 #41
0
    def preferred_width(self, cli, max_available_width):
        """
        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.)
        """
        if cli.current_buffer.complete_state:
            state = cli.current_buffer.complete_state
            return 2 + max(get_cwidth(c.display_meta) for c in state.current_completions)
        else:
            return 0
예제 #42
0
    def preferred_width(self, cli, max_available_width):
        """
        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.)
        """
        if cli.current_buffer.complete_state:
            state = cli.current_buffer.complete_state
            return 2 + max(get_cwidth(c.display_meta) for c in state.current_completions)
        else:
            return 0
예제 #43
0
    def _get_line_prefix(self, line_number, wrap_count, get_prompt_text_2):
        """
        Return whatever needs to be inserted before every line.
        (the prompt, or a line continuation.)
        """
        # First line: display the "arg" or the prompt.
        if line_number == 0 and wrap_count == 0:
            if not is_true(self.multiline) and get_app().key_processor.arg is not None:
                return self._inline_arg()
            else:
                return get_prompt_text_2()

        # For the next lines, display the appropriate continuation.
        prompt_width = get_cwidth(fragment_list_to_text(get_prompt_text_2()))
        return self._get_continuation(prompt_width, line_number, wrap_count)
    def get_height_for_text(text, width):
        # Get text width for this line.
        line_width = get_cwidth(text)

        # Calculate height.
        try:
            quotient, remainder = divmod(line_width, width)
        except ZeroDivisionError:
            # Return something very big.
            # (This can happen, when the Window gets very small.)
            return 10 ** 10
        else:
            if remainder:
                quotient += 1  # Like math.ceil.
            return max(1, quotient)
    def _get_line_prefix(self, line_number, wrap_count, get_prompt_text_2):
        """
        Return whatever needs to be inserted before every line.
        (the prompt, or a line continuation.)
        """
        # First line: display the "arg" or the prompt.
        if line_number == 0 and wrap_count == 0:
            if not is_true(self.multiline) and get_app().key_processor.arg is not None:
                return self._inline_arg()
            else:
                return get_prompt_text_2()

        # For the next lines, display the appropriate continuation.
        prompt_width = get_cwidth(fragment_list_to_text(get_prompt_text_2()))
        return self._get_continuation(prompt_width, line_number, wrap_count)
예제 #46
0
    def get_height_for_text(text, width):
        # Get text width for this line.
        line_width = get_cwidth(text)

        # Calculate height.
        try:
            quotient, remainder = divmod(line_width, width)
        except ZeroDivisionError:
            # Return something very big.
            # (This can happen, when the Window gets very small.)
            return 10**10
        else:
            if remainder:
                quotient += 1  # Like math.ceil.
            return max(1, quotient)
    def __init__(self, char=' ', style=''):
        # If this character has to be displayed otherwise, take that one.
        if char in self.display_mappings:
            if char == '\xa0':
                style += ' class:nbsp '  # Will be underlined.
            else:
                style += ' class:control-character '

            char = self.display_mappings[char]

        self.char = char
        self.style = style

        # Calculate width. (We always need this, so better to store it directly
        # as a member for performance.)
        self.width = get_cwidth(char)
예제 #48
0
파일: screen.py 프로젝트: amjith/pymux-test
    def draw(self, char):
        pt_screen = self.pt_screen

        # Translating a given character.
        if self.charset:
            char = char.translate(self.g1_charset)
        else:
            char = char.translate(self.g0_charset)

        # Calculate character width. (We use the prompt_toolkit function which
        # has built-in caching.)
        char_width = get_cwidth(char)

        # If this was the last column in a line and auto wrap mode is
        # enabled, move the cursor to the beginning of the next line,
        # otherwise replace characters already displayed with newly
        # entered.
        if pt_screen.cursor_position.x >= self.columns:
            if mo.DECAWM in self.mode:
                self.carriage_return()
                self.linefeed()
            else:
                pt_screen.cursor_position.x -= char_width

        # If Insert mode is set, new characters move old characters to
        # the right, otherwise terminal is in Replace mode and new
        # characters replace old characters at cursor position.
        if mo.IRM in self.mode:
            self.insert_characters(char_width)

        token = ('C', ) + self._attrs
        row = pt_screen.data_buffer[pt_screen.cursor_position.y]
        row[pt_screen.cursor_position.x] = Char(char, token)

        if char_width > 1:
            row[pt_screen.cursor_position.x + 1] = Char(' ', token)

        # .. note:: We can't use :meth:`cursor_forward()`, because that
        #           way, we'll never know when to linefeed.
        pt_screen.cursor_position.x += char_width

        self.max_y = max(self.max_y, pt_screen.cursor_position.y)
    def format(self, progress_bar, progress, width):
        # Subtract left, bar_b and right.
        width -= get_cwidth(self.start + self.sym_b + self.end)

        if progress.total:
            pb_a = int(progress.percentage * width / 100)
            bar_a = self.sym_a * pb_a
            bar_b = self.sym_b
            bar_c = self.sym_c * (width - pb_a)
        else:
            # Total is unknown.
            pb_a = int(time.time() * 20  % 100 * width / 100)
            bar_a = self.sym_c * pb_a
            bar_b = self.unknown
            bar_c = self.sym_c * (width - pb_a)

        return HTML(self.template).format(
            start=self.start,
            end=self.end,
            bar_a=bar_a,
            bar_b=bar_b,
            bar_c=bar_c)
예제 #50
0
파일: layout.py 프로젝트: deKross/ptpython
 def get_width(self, cli):
     # Take the width from the first line.
     text = ''.join(t[1] for t in self.python_input.get_input_prompt_tokens(cli))
     return get_cwidth(text)
    def get_height_for_line(
            self, lineno: int, width: int,
            get_line_prefix: Optional[GetLinePrefixCallable],
            slice_stop: Optional[int] = None) -> int:
        """
        Return the height that a given line would need if it is rendered in a
        space with the given width (using line wrapping).

        :param get_line_prefix: None or a `Window.get_line_prefix` callable
            that returns the prefix to be inserted before this line.
        :param slice_stop: Wrap only "line[:slice_stop]" and return that
            partial result. This is needed for scrolling the window correctly
            when line wrapping.
        :returns: The computed height.
        """
        # Instead of using `get_line_prefix` as key, we use render_counter
        # instead. This is more reliable, because this function could still be
        # the same, while the content would change over time.
        key = get_app().render_counter, lineno, width, slice_stop

        try:
            return self._line_heights_cache[key]
        except KeyError:
            if width == 0:
                height = 10 ** 8
            else:
                # Calculate line width first.
                line = fragment_list_to_text(self.get_line(lineno))[:slice_stop]
                text_width = get_cwidth(line)

                if get_line_prefix:
                    # Add prefix width.
                    text_width += fragment_list_width(
                        to_formatted_text(get_line_prefix(lineno, 0)))

                    # Slower path: compute path when there's a line prefix.
                    height = 1

                    # Keep wrapping as long as the line doesn't fit.
                    # Keep adding new prefixes for every wrapped line.
                    while text_width > width:
                        height += 1
                        text_width -= width

                        fragments2 = to_formatted_text(
                            get_line_prefix(lineno, height - 1))
                        prefix_width = get_cwidth(fragment_list_to_text(fragments2))

                        if prefix_width >= width:  # Prefix doesn't fit.
                            height = 10 ** 8
                            break

                        text_width += prefix_width
                else:
                    # Fast path: compute height when there's no line prefix.
                    try:
                        quotient, remainder = divmod(text_width, width)
                    except ZeroDivisionError:
                        height = 10 ** 8
                    else:
                        if remainder:
                            quotient += 1  # Like math.ceil.
                        height = max(1, quotient)

            # Cache and return
            self._line_heights_cache[key] = height
            return height
 def meta_width(completion):
     return get_cwidth(completion.display_meta_text)
예제 #53
0
 def get_width(self, cli, ui_content):
     " Width to report to the `Window`. "
     # Take the width from the first line.
     text = ''.join(t[1] for t in self.get_prompt_tokens(cli))
     return get_cwidth(text)
예제 #54
0
 def _get_menu_width(self, max_width, complete_state):
     """
     Return the width of the main column.
     """
     return min(max_width, max(get_cwidth(c.display)
                for c in complete_state.current_completions) + 2)
예제 #55
0
 def _get_column_width(self, complete_state):
     """
     Return the width of each column.
     """
     return max(get_cwidth(c.display) for c in complete_state.current_completions) + 1
예제 #56
0
def _display_completions_like_readline(cli, completions):
    """
    Display the list of completions in columns above the prompt.
    This will ask for a confirmation if there are too many completions to fit
    on a single page and provide a paginator to walk through them.
    """
    from prompt_toolkit.shortcuts import create_confirm_application
    assert isinstance(completions, list)

    # Get terminal dimensions.
    term_size = cli.output.get_size()
    term_width = term_size.columns
    term_height = term_size.rows

    # Calculate amount of required columns/rows for displaying the
    # completions. (Keep in mind that completions are displayed
    # alphabetically column-wise.)
    max_compl_width = min(term_width,
        max(get_cwidth(c.text) for c in completions) + 1)
    column_count = max(1, term_width // max_compl_width)
    completions_per_page = column_count * (term_height - 1)
    page_count = int(math.ceil(len(completions) / float(completions_per_page)))
        # Note: math.ceil can return float on Python2.

    def display(page):
        # Display completions.
        page_completions = completions[page * completions_per_page:
                                       (page+1) * completions_per_page]

        page_row_count = int(math.ceil(len(page_completions) / float(column_count)))
        page_columns = [page_completions[i * page_row_count:(i+1) * page_row_count]
                   for i in range(column_count)]

        result = []
        for r in range(page_row_count):
            for c in range(column_count):
                try:
                    result.append(page_columns[c][r].text.ljust(max_compl_width))
                except IndexError:
                    pass
            result.append('\n')
        cli.output.write(''.join(result))
        cli.output.flush()

    # User interaction through an application generator function.
    def run():
        if len(completions) > completions_per_page:
            # Ask confirmation if it doesn't fit on the screen.
            message = 'Display all {} possibilities? (y on n) '.format(len(completions))
            confirm = yield create_confirm_application(message)

            if confirm:
                # Display pages.
                for page in range(page_count):
                    display(page)

                    if page != page_count - 1:
                        # Display --MORE-- and go to the next page.
                        show_more = yield _create_more_application()
                        if not show_more:
                            return
            else:
                cli.output.write('\n'); cli.output.flush()
        else:
            # Display all completions.
            display(0)

    cli.run_application_generator(run, render_cli_done=True)
 def get_width(self, cli, ui_content):
     " Width to report to the `Window`. "
     # Take the width from the first line.
     text = token_list_to_text(self.get_prompt_tokens(cli))
     return get_cwidth(text)
    def write(self, data):
        if self._hidden:
            data = ' ' * get_cwidth(data)

        self._buffer.append(data)