def test_ansi_formatting():
    value = ANSI('\x1b[32mHe\x1b[45mllo')

    assert to_formatted_text(value) == [
        ('ansigreen', 'H'),
        ('ansigreen', 'e'),
        ('ansigreen bg:ansimagenta', 'l'),
        ('ansigreen bg:ansimagenta', 'l'),
        ('ansigreen bg:ansimagenta', 'o'),
    ]

    # Bold and italic.
    value = ANSI('\x1b[1mhe\x1b[0mllo')

    assert to_formatted_text(value) == [
        ('bold', 'h'),
        ('bold', 'e'),
        ('', 'l'),
        ('', 'l'),
        ('', 'o'),
    ]

    # Zero width escapes.
    value = ANSI('ab\001cd\002ef')

    assert to_formatted_text(value) == [
        ('', 'a'),
        ('', 'b'),
        ('[ZeroWidthEscape]', 'cd'),
        ('', 'e'),
        ('', 'f'),
    ]

    assert isinstance(to_formatted_text(value), FormattedText)
def test_html_interpolation():
    # %-style interpolation.
    value = HTML('<b>%s</b>') % 'hello'
    assert to_formatted_text(value) == [
        ('class:b', 'hello')
    ]

    value = HTML('<b>%s</b>') % ('hello', )
    assert to_formatted_text(value) == [
        ('class:b', 'hello')
    ]

    value = HTML('<b>%s</b><u>%s</u>') % ('hello', 'world')
    assert to_formatted_text(value) == [
        ('class:b', 'hello'),
        ('class:u', 'world')
    ]

    # Format function.
    value = HTML('<b>{0}</b><u>{1}</u>').format('hello', 'world')
    assert to_formatted_text(value) == [
        ('class:b', 'hello'),
        ('class:u', 'world')
    ]

    value = HTML('<b>{a}</b><u>{b}</u>').format(a='hello', b='world')
    assert to_formatted_text(value) == [
        ('class:b', 'hello'),
        ('class:u', 'world')
    ]
def test_html_with_fg_bg():
    html = HTML('<style bg="ansired">hello</style>')
    assert to_formatted_text(html) == [
        ('bg:ansired', 'hello'),
    ]

    html = HTML('<style bg="ansired" fg="#ff0000">hello</style>')
    assert to_formatted_text(html) == [
        ('fg:#ff0000 bg:ansired', 'hello'),
    ]

    html = HTML('<style bg="ansired" fg="#ff0000">hello <world fg="ansiblue">world</world></style>')
    assert to_formatted_text(html) == [
        ('fg:#ff0000 bg:ansired', 'hello '),
        ('class:world fg:ansiblue bg:ansired', 'world'),
    ]
 def send_above_prompt(self, formatted_text: AnyFormattedText) -> None:
     """
     Send text to the client.
     This is asynchronous, returns a `Future`.
     """
     formatted_text = to_formatted_text(formatted_text)
     return self._run_in_terminal(lambda: self.send(formatted_text))
Beispiel #5
0
 def apply_transformation(self, ti: TransformationInput) -> Transformation:
     # Insert fragments after the last line.
     if ti.lineno == ti.document.line_count - 1:
         # Get fragments.
         fragments_after = to_formatted_text(self.text, self.style)
         return Transformation(fragments=ti.fragments + fragments_after)
     else:
         return Transformation(fragments=ti.fragments)
def test_merge_formatted_text():
    html1 = HTML('<u>hello</u>')
    html2 = HTML('<b>world</b>')
    result = merge_formatted_text([html1, html2])

    assert to_formatted_text(result) == [
        ('class:u', 'hello'),
        ('class:b', 'world'),
    ]
Beispiel #7
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))
def test_interpolation():
    value = Template(' {} ').format(HTML('<b>hello</b>'))

    assert to_formatted_text(value) == [
        ('', ' '),
        ('class:b', 'hello'),
        ('', ' '),
    ]

    value = Template('a{}b{}c').format(HTML('<b>hello</b>'), 'world')

    assert to_formatted_text(value) == [
        ('', 'a'),
        ('class:b', 'hello'),
        ('', 'b'),
        ('', 'world'),
        ('', 'c'),
    ]
Beispiel #9
0
    def create_margin(self, window_render_info: 'WindowRenderInfo',
                      width: int, height: int) -> StyleAndTextTuples:
        get_continuation = self.get_continuation
        result: StyleAndTextTuples = []

        # First line.
        result.extend(to_formatted_text(self.get_prompt()))

        # Next lines.
        if get_continuation:
            last_y = None

            for y in window_render_info.displayed_lines[1:]:
                result.append(('', '\n'))
                result.extend(to_formatted_text(get_continuation(width, y, y == last_y)))
                last_y = y

        return result
def test_basic_html():
    html = HTML('<i>hello</i>')
    assert to_formatted_text(html) == [('class:i', 'hello')]

    html = HTML('<i><b>hello</b></i>')
    assert to_formatted_text(html) == [('class:i,b', 'hello')]

    html = HTML('<i><b>hello</b>world<strong>test</strong></i>after')
    assert to_formatted_text(html) == [
        ('class:i,b', 'hello'),
        ('class:i', 'world'),
        ('class:i,strong', 'test'),
        ('', 'after'),
    ]

    # It's important that `to_formatted_text` returns a `FormattedText`
    # instance. Otherwise, `print_formatted_text` won't recognise it and will
    # print a list literal instead.
    assert isinstance(to_formatted_text(html), FormattedText)
Beispiel #11
0
def test_with_style():
    """
    Text `print_formatted_text` with `HTML` wrapped in `to_formatted_text`.
    """
    f = _Capture()

    html = HTML('<ansigreen>hello</ansigreen> <b>world</b>')
    formatted_text = to_formatted_text(html, style='class:myhtml')
    pt_print(formatted_text, file=f)

    assert f.data == \
        b'\x1b[0m\x1b[?7h\x1b[0;32mhello\x1b[0m \x1b[0;1mworld\x1b[0m\r\n\x1b[0m'
    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 []
Beispiel #13
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
def test_pygments_tokens():
    text = [
        (('A', 'B'), 'hello'),  # Token.A.B
        (('C', 'D', 'E'), 'hello'),  # Token.C.D.E
        ((), 'world'),  # Token
    ]

    assert to_formatted_text(PygmentsTokens(text)) == [
        ('class:pygments.a.b', 'hello'),
        ('class:pygments.c.d.e', 'hello'),
        ('class:pygments', 'world'),
    ]
    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'))
Beispiel #16
0
    def format(self, progress_bar: 'ProgressBar',
               progress: 'ProgressBarCounter[object]',
               width: int) -> AnyFormattedText:

        # Get formatted text from nested formatter, and explode it in
        # text/style tuples.
        result = self.formatter.format(progress_bar, progress, width)
        result = explode_text_fragments(to_formatted_text(result))

        # Insert colors.
        result2: StyleAndTextTuples = []
        shift = int(time.time() * 3) % len(self.colors)

        for i, (style, text, *_) in enumerate(result):
            result2.append((style + ' ' + self.colors[(i + shift) % len(self.colors)], text))
        return result2
Beispiel #17
0
    def _get_text_fragments(self) -> StyleAndTextTuples:
        def mouse_handler(mouse_event: MouseEvent) -> None:
            """
            Set `_selected_index` and `current_value` according to the y
            position of the mouse click event.
            """
            if mouse_event.event_type == MouseEventType.MOUSE_UP:
                self._selected_index = mouse_event.position.y
                self._handle_enter()

        result: StyleAndTextTuples = []
        for i, value in enumerate(self.values):
            if self.multiple_selection:
                checked = (value[0] in self.current_values)
            else:
                checked = (value[0] == self.current_value)
            selected = (i == self._selected_index)

            style = ''
            if checked:
                style += ' ' + self.checked_style
            if selected:
                style += ' ' + self.selected_style

            result.append((style, self.open_character))

            if selected:
                result.append(('[SetCursorPosition]', ''))

            if checked:
                result.append((style, '*'))
            else:
                result.append((style, ' '))

            result.append((style, self.close_character))
            result.append((self.default_style, ' '))
            result.extend(to_formatted_text(value[1],
                                            style=self.default_style))
            result.append(('', '\n'))

        # Add mouse handler to all fragments.
        for i in range(len(result)):
            result[i] = (result[i][0], result[i][1], mouse_handler)

        result.pop()  # Remove last newline.
        return result
    def _get_menu_item_meta_fragments(
            self, completion: Completion, is_current_completion: bool,
            width: int) -> StyleAndTextTuples:

        if is_current_completion:
            style_str = 'class:completion-menu.meta.completion.current'
        else:
            style_str = 'class:completion-menu.meta.completion'

        text, tw = _trim_formatted_text(completion.display_meta, width - 2)
        padding = ' ' * (width - 1 - tw)

        return to_formatted_text(
            cast(StyleAndTextTuples, []) +
            [('', ' ')] +
            text +
            [('', padding)],
            style=style_str)
Beispiel #19
0
    def create_content(self, width: int, height: int) -> UIContent:
        items: List[StyleAndTextTuples] = []

        for pr in self.progress_bar.counters:
            try:
                text = self.formatter.format(self.progress_bar, pr, width)
            except BaseException:
                traceback.print_exc()
                text = 'ERROR'

            items.append(to_formatted_text(text))

        def get_line(i: int) -> StyleAndTextTuples:
            return items[i]

        return UIContent(
            get_line=get_line,
            line_count=len(items),
            show_cursor=False)
Beispiel #20
0
    def apply_transformation(self, ti: TransformationInput) -> Transformation:
        source_to_display: Optional[SourceToDisplay]
        display_to_source: Optional[DisplayToSource]

        if ti.lineno == 0:
            # Get fragments.
            fragments_before = to_formatted_text(self.text, self.style)
            fragments = fragments_before + ti.fragments

            shift_position = fragment_list_len(fragments_before)
            source_to_display = lambda i: i + shift_position
            display_to_source = lambda i: i - shift_position
        else:
            fragments = ti.fragments
            source_to_display = None
            display_to_source = None

        return Transformation(fragments,
                              source_to_display=source_to_display,
                              display_to_source=display_to_source)
def print_formatted_text(
        output: Output,
        formatted_text: AnyFormattedText,
        style: BaseStyle,
        style_transformation: Optional[StyleTransformation] = None,
        color_depth: Optional[ColorDepth] = None) -> None:
    """
    Print a list of (style_str, text) tuples in the given style to the output.
    """
    fragments = to_formatted_text(formatted_text)
    style_transformation = style_transformation or DummyStyleTransformation()
    color_depth = color_depth or ColorDepth.default()

    # Reset first.
    output.reset_attributes()
    output.enable_autowrap()

    # Print all (style_str, text) tuples.
    attrs_for_style_string = _StyleStringToAttrsCache(
        style.get_attrs_for_style_str, style_transformation)

    for style_str, text, *_ in fragments:
        attrs = attrs_for_style_string[style_str]

        if attrs:
            output.set_attributes(attrs, color_depth)
        else:
            output.reset_attributes()

        # Eliminate carriage returns
        text = text.replace('\r', '')

        # Assume that the output is raw, and insert a carriage return before
        # every newline. (Also important when the front-end is a telnet client.)
        output.write(text.replace('\n', '\r\n'))

    # Reset again.
    output.reset_attributes()
    output.flush()
Beispiel #22
0
    def __init__(self,
                 text: str,
                 start_position: int = 0,
                 display: Optional[AnyFormattedText] = None,
                 display_meta: Optional[AnyFormattedText] = None,
                 style: str = '',
                 selected_style: str = '') -> None:

        from prompt_toolkit_dev.formatted_text import to_formatted_text
        self.text = text
        self.start_position = start_position
        self._display_meta = display_meta

        if display is None:
            display = text

        self.display = to_formatted_text(display)

        self.style = style
        self.selected_style = selected_style

        assert self.start_position <= 0
def _get_menu_item_fragments(
        completion: Completion, is_current_completion: bool, width: int,
        space_after: bool = False) -> StyleAndTextTuples:
    """
    Get the style/text tuples for a menu item, styled and trimmed to the given
    width.
    """
    if is_current_completion:
        style_str = 'class:completion-menu.completion.current %s %s' % (
            completion.style, completion.selected_style)
    else:
        style_str = 'class:completion-menu.completion ' + completion.style

    text, tw = _trim_formatted_text(
        completion.display,
        (width - 2 if space_after else width - 1))

    padding = ' ' * (width - 1 - tw)

    return to_formatted_text(
        cast(StyleAndTextTuples, []) +
        [('', ' ')] + text + [('', padding)],
        style=style_str)
Beispiel #24
0
 def _add_suffix(self, label: AnyFormattedText) -> StyleAndTextTuples:
     label = to_formatted_text(label, style='class:label')
     return label + [('', self.suffix)]
Beispiel #25
0
 def __init__(self, text: AnyFormattedText, style: str = '') -> None:
     self.text = to_formatted_text(text, style=style)
    def create_content(self, width: int, height: int) -> UIContent:
        """
        Create a UIContent object for this menu.
        """
        complete_state = get_app().current_buffer.complete_state
        if complete_state is None:
            return UIContent()

        column_width = self._get_column_width(complete_state)
        self._render_pos_to_completion = {}

        _T = TypeVar('_T')

        def grouper(n: int, iterable: Iterable[_T],
                    fillvalue: Optional[_T] = None) -> Iterable[List[_T]]:
            " grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx "
            args = [iter(iterable)] * n
            return zip_longest(fillvalue=fillvalue, *args)

        def is_current_completion(completion: Completion) -> bool:
            " Returns True when this completion is the currently selected one. "
            return (complete_state is not None and
                    complete_state.complete_index is not None and
                    c == complete_state.current_completion)

        # Space required outside of the regular columns, for displaying the
        # left and right arrow.
        HORIZONTAL_MARGIN_REQUIRED = 3

        # There should be at least one column, but it cannot be wider than
        # the available width.
        column_width = min(width - HORIZONTAL_MARGIN_REQUIRED, column_width)

        # However, when the columns tend to be very wide, because there are
        # some very wide entries, shrink it anyway.
        if column_width > self.suggested_max_column_width:
            # `column_width` can still be bigger that `suggested_max_column_width`,
            # but if there is place for two columns, we divide by two.
            column_width //= (column_width // self.suggested_max_column_width)

        visible_columns = max(1, (width - self._required_margin) // column_width)

        columns_ = list(grouper(height, complete_state.completions))
        rows_ = list(zip(*columns_))

        # Make sure the current completion is always visible: update scroll offset.
        selected_column = (complete_state.complete_index or 0) // height
        self.scroll = min(selected_column, max(self.scroll, selected_column - visible_columns + 1))

        render_left_arrow = self.scroll > 0
        render_right_arrow = self.scroll < len(rows_[0]) - visible_columns

        # Write completions to screen.
        fragments_for_line = []

        for row_index, row in enumerate(rows_):
            fragments: StyleAndTextTuples = []
            middle_row = row_index == len(rows_) // 2

            # Draw left arrow if we have hidden completions on the left.
            if render_left_arrow:
                fragments.append(('class:scrollbar', '<' if middle_row else ' '))
            elif render_right_arrow:
                # Reserve one column empty space. (If there is a right
                # arrow right now, there can be a left arrow as well.)
                fragments.append(('', ' '))

            # Draw row content.
            for column_index, c in enumerate(row[self.scroll:][:visible_columns]):
                if c is not None:
                    fragments += _get_menu_item_fragments(
                        c, is_current_completion(c), column_width, space_after=False)

                    # Remember render position for mouse click handler.
                    for x in range(column_width):
                        self._render_pos_to_completion[(column_index * column_width + x, row_index)] = c
                else:
                    fragments.append(('class:completion', ' ' * column_width))

            # Draw trailing padding for this row.
            # (_get_menu_item_fragments only returns padding on the left.)
            if render_left_arrow or render_right_arrow:
                fragments.append(('class:completion', ' '))

            # Draw right arrow if we have hidden completions on the right.
            if render_right_arrow:
                fragments.append(('class:scrollbar', '>' if middle_row else ' '))
            elif render_left_arrow:
                fragments.append(('class:completion', ' '))

            # Add line.
            fragments_for_line.append(to_formatted_text(
                fragments, style='class:completion-menu'))

        self._rendered_rows = height
        self._rendered_columns = visible_columns
        self._total_columns = len(columns_)
        self._render_left_arrow = render_left_arrow
        self._render_right_arrow = render_right_arrow
        self._render_width = column_width * visible_columns + render_left_arrow + render_right_arrow + 1

        def get_line(i: int) -> StyleAndTextTuples:
            return fragments_for_line[i]

        return UIContent(get_line=get_line, line_count=len(rows_))
Beispiel #27
0
    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
Beispiel #28
0
 def display_meta(self) -> StyleAndTextTuples:
     " Return meta-text. (This is lazy when using a callable). "
     from prompt_toolkit_dev.formatted_text import to_formatted_text
     return to_formatted_text(self._display_meta or '')
 def to_text(val: Any) -> StyleAndTextTuples:
     # Normal lists which are not instances of `FormattedText` are
     # considered plain text.
     if isinstance(val, list) and not isinstance(val, FormattedText):
         return to_formatted_text('{0}'.format(val))
     return to_formatted_text(val, auto_convert=True)
    def create_content(self, width: int, height: int) -> UIContent:
        all_fragments: StyleAndTextTuples = []

        complete_state = get_app().current_buffer.complete_state
        if complete_state:
            completions = complete_state.completions
            index = complete_state.complete_index  # Can be None!

            # Width of the completions without the left/right arrows in the margins.
            content_width = width - 6

            # Booleans indicating whether we stripped from the left/right
            cut_left = False
            cut_right = False

            # Create Menu content.
            fragments: StyleAndTextTuples = []

            for i, c in enumerate(completions):
                # When there is no more place for the next completion
                if fragment_list_len(fragments) + len(
                        c.display_text) >= content_width:
                    # If the current one was not yet displayed, page to the next sequence.
                    if i <= (index or 0):
                        fragments = []
                        cut_left = True
                    # If the current one is visible, stop here.
                    else:
                        cut_right = True
                        break

                fragments.extend(
                    to_formatted_text(
                        c.display_text,
                        style=('class:completion-toolbar.completion.current'
                               if i == index else
                               'class:completion-toolbar.completion')))
                fragments.append(('', ' '))

            # Extend/strip until the content width.
            fragments.append(
                ('', ' ' * (content_width - fragment_list_len(fragments))))
            fragments = fragments[:content_width]

            # Return fragments
            all_fragments.append(('', ' '))
            all_fragments.append(
                ('class:completion-toolbar.arrow', '<' if cut_left else ' '))
            all_fragments.append(('', ' '))

            all_fragments.extend(fragments)

            all_fragments.append(('', ' '))
            all_fragments.append(
                ('class:completion-toolbar.arrow', '>' if cut_right else ' '))
            all_fragments.append(('', ' '))

        def get_line(i: int) -> StyleAndTextTuples:
            return all_fragments

        return UIContent(get_line=get_line, line_count=1)