mirror of
https://github.com/Textualize/rich.git
synced 2026-02-06 10:58:48 +00:00
overflow
This commit is contained in:
parent
f7318cea1b
commit
47bb80ff67
@ -8,12 +8,14 @@
|
||||
|
||||
# Rich
|
||||
|
||||
Rich is a Python library for rendering _rich_ text and beautiful formatting to the terminal.
|
||||
Rich is a Python library for _rich_ text and beautiful formatting in the terminal.
|
||||
|
||||
The [Rich API](https://rich.readthedocs.io/en/latest/) makes it easy to add colorful text (up to 16.7 million colors) with styles (bold, italic, underline etc.) to your script or application. Rich can also render pretty tables, progress bars, markdown, syntax highlighted source code, and tracebacks -- out of the box.
|
||||
|
||||

|
||||
|
||||
For a video introduction to Rich see [calmcode.io](https://calmcode.io/rich/introduction.html).
|
||||
|
||||
## Compatibility
|
||||
|
||||
Rich works with Linux, OSX, and Windows. True color / emoji works with new Windows Terminal, classic terminal is limited to 8 colors.
|
||||
|
||||
11
examples/overflow.py
Normal file
11
examples/overflow.py
Normal file
@ -0,0 +1,11 @@
|
||||
from typing import List
|
||||
from rich.console import Console, OverflowMethod
|
||||
from rich.text import Text
|
||||
|
||||
console = Console()
|
||||
supercali = "supercalifragilisticexpialidocious"
|
||||
|
||||
overflow_methods: List[OverflowMethod] = ["fold", "crop", "ellipsis"]
|
||||
for overflow in overflow_methods:
|
||||
console.rule(overflow)
|
||||
console.print(supercali, overflow=overflow, width=10)
|
||||
@ -48,7 +48,7 @@ class LogRender:
|
||||
output.add_column(style="log.time")
|
||||
if self.show_level:
|
||||
output.add_column(style="log.level", width=8)
|
||||
output.add_column(ratio=1, style="log.message", justify=None)
|
||||
output.add_column(ratio=1, style="log.message")
|
||||
if self.show_path and path:
|
||||
output.add_column(style="log.path")
|
||||
row: List["RenderableType"] = []
|
||||
|
||||
@ -3,34 +3,6 @@ from math import ceil
|
||||
from typing import List
|
||||
|
||||
|
||||
def ratio_distribute(total: int, ratios: List[int]) -> List[int]:
|
||||
"""Divide an integer total in to parts based on ratios.
|
||||
|
||||
Args:
|
||||
total (int): The total to divide.
|
||||
ratios (List[int]): A list of integer ratios.
|
||||
minimums (List[int]): List of minimum values for each slot.
|
||||
|
||||
Returns:
|
||||
List[int]: A list of integers garanteed to sum to total.
|
||||
"""
|
||||
total_ratio = sum(ratios)
|
||||
assert total_ratio > 0, "Sum of ratios must be > 0"
|
||||
|
||||
total_remaining = total
|
||||
distributed_total: List[int] = []
|
||||
append = distributed_total.append
|
||||
|
||||
for ratio in ratios:
|
||||
if total_ratio > 0:
|
||||
distributed = int(ceil(ratio * total_remaining / total_ratio))
|
||||
else:
|
||||
distributed = total_remaining
|
||||
append(distributed)
|
||||
total_ratio -= ratio
|
||||
total_remaining -= distributed
|
||||
return distributed_total
|
||||
|
||||
|
||||
def ratio_reduce(
|
||||
total: int, ratios: List[int], maximums: List[int], values: List[int]
|
||||
|
||||
@ -58,8 +58,8 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
WINDOWS = platform.system() == "Windows"
|
||||
|
||||
HighlighterType = Callable[[Union[str, "Text"]], "Text"]
|
||||
JustifyValues = Optional[Literal["left", "center", "right", "full"]]
|
||||
OverflowValues = Literal["crop", "fold", "ellipsis"]
|
||||
JustifyMethod = Literal["default", "left", "center", "right", "full"]
|
||||
OverflowMethod = Literal["crop", "fold", "ellipsis"]
|
||||
|
||||
|
||||
CONSOLE_HTML_FORMAT = """\
|
||||
@ -91,8 +91,8 @@ class ConsoleOptions:
|
||||
max_width: int
|
||||
is_terminal: bool
|
||||
encoding: str
|
||||
justify: Optional[JustifyValues] = None
|
||||
overflow: Optional[OverflowValues] = None
|
||||
justify: Optional[JustifyMethod] = None
|
||||
overflow: Optional[OverflowMethod] = None
|
||||
no_wrap: bool = False
|
||||
|
||||
def update(
|
||||
@ -100,8 +100,8 @@ class ConsoleOptions:
|
||||
width: int = None,
|
||||
min_width: int = None,
|
||||
max_width: int = None,
|
||||
justify: JustifyValues = None,
|
||||
overflow: OverflowValues = None,
|
||||
justify: JustifyMethod = None,
|
||||
overflow: OverflowMethod = None,
|
||||
no_wrap: bool = None,
|
||||
) -> "ConsoleOptions":
|
||||
"""Update values, return a copy."""
|
||||
@ -557,7 +557,8 @@ class Console:
|
||||
self,
|
||||
text: str,
|
||||
style: Union[str, Style] = "",
|
||||
justify: JustifyValues = None,
|
||||
justify: JustifyMethod = None,
|
||||
overflow: OverflowMethod = None,
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlighter: HighlighterType = None,
|
||||
@ -568,7 +569,8 @@ class Console:
|
||||
Args:
|
||||
text (str): Text to render.
|
||||
style (Union[str, Style], optional): Style to apply to rendered text.
|
||||
justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``.
|
||||
justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to ``None``.
|
||||
overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to ``None``.
|
||||
emoji (Optional[bool], optional): Enable emoji, or ``None`` to use Console default.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use Console default.
|
||||
highlighter (HighlighterType, optional): Optional highlighter to apply.
|
||||
@ -581,10 +583,13 @@ class Console:
|
||||
|
||||
if markup_enabled:
|
||||
rich_text = render_markup(text, style=style, emoji=emoji_enabled)
|
||||
rich_text.justify = justify
|
||||
rich_text.overflow = overflow
|
||||
else:
|
||||
rich_text = Text(
|
||||
_emoji_replace(text) if emoji_enabled else text,
|
||||
justify=justify,
|
||||
overflow=overflow,
|
||||
style=style,
|
||||
)
|
||||
|
||||
@ -626,7 +631,7 @@ class Console:
|
||||
objects: Iterable[Any],
|
||||
sep: str,
|
||||
end: str,
|
||||
justify: JustifyValues = None,
|
||||
justify: JustifyMethod = None,
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlight: bool = None,
|
||||
@ -732,10 +737,12 @@ class Console:
|
||||
sep=" ",
|
||||
end="\n",
|
||||
style: Union[str, Style] = None,
|
||||
justify: JustifyValues = None,
|
||||
justify: JustifyMethod = None,
|
||||
overflow: OverflowMethod = None,
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlight: bool = None,
|
||||
width: int = None,
|
||||
) -> None:
|
||||
r"""Print to the console.
|
||||
|
||||
@ -744,10 +751,12 @@ class Console:
|
||||
sep (str, optional): String to write between print data. Defaults to " ".
|
||||
end (str, optional): String to write at end of print data. Defaults to "\n".
|
||||
style (Union[str, Style], optional): A style to apply to output. Defaults to None.
|
||||
justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``.
|
||||
emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None
|
||||
highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None.
|
||||
justify (str, optional): Overflowmethod: "left", "right", "center", or "full". Defaults to ``None``.
|
||||
overflow (str, optional): Overflow method: "crop", "fold", or "ellipisis". Defaults to None.
|
||||
emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to ``None``.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to ``None``.
|
||||
highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to ``None``.
|
||||
width (Optional[int], optional): Width of output, or ``None`` to auto-detect. Defaults to ``None``.
|
||||
"""
|
||||
if not objects:
|
||||
self.line()
|
||||
@ -763,7 +772,9 @@ class Console:
|
||||
markup=markup,
|
||||
highlight=highlight,
|
||||
)
|
||||
render_options = self.options
|
||||
render_options = self.options.update(
|
||||
justify=justify, overflow=overflow, width=width
|
||||
)
|
||||
extend = self._buffer.extend
|
||||
render = self.render
|
||||
if style is None:
|
||||
@ -804,7 +815,7 @@ class Console:
|
||||
*objects: Any,
|
||||
sep=" ",
|
||||
end="\n",
|
||||
justify: JustifyValues = None,
|
||||
justify: JustifyMethod = None,
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlight: bool = None,
|
||||
|
||||
@ -19,7 +19,8 @@ if TYPE_CHECKING:
|
||||
Console,
|
||||
ConsoleOptions,
|
||||
ConsoleRenderable,
|
||||
OverflowValues,
|
||||
JustifyMethod,
|
||||
OverflowMethod,
|
||||
RenderResult,
|
||||
RenderableType,
|
||||
)
|
||||
@ -110,32 +111,35 @@ class Lines:
|
||||
self,
|
||||
console: "Console",
|
||||
width: int,
|
||||
align: Literal["none", "left", "center", "right", "full"] = "left",
|
||||
overflow: Optional["OverflowValues"] = None,
|
||||
justify: "JustifyMethod" = "left",
|
||||
overflow: "OverflowMethod" = "fold",
|
||||
) -> None:
|
||||
"""Pad each line with spaces to a given width.
|
||||
"""Justify and overflow text to a given width.
|
||||
|
||||
Args:
|
||||
console (Console): Console instance.
|
||||
width (int): Number of characters per line.
|
||||
justify (str, optional): Default justify method for text: "left", "center", "full" or "right". Defaults to "left".
|
||||
overflow (str, optional): Default overflow for text: "crop", "fold", or "ellipisis". Defaults to "fold".
|
||||
|
||||
"""
|
||||
from .text import Text
|
||||
|
||||
if align == "left":
|
||||
if justify == "left":
|
||||
for line in self._lines:
|
||||
line.truncate(width, overflow=overflow, pad=True)
|
||||
elif align == "center":
|
||||
elif justify == "center":
|
||||
for line in self._lines:
|
||||
line.rstrip()
|
||||
line.truncate(width, overflow=overflow)
|
||||
line.pad_right((width - cell_len(line.plain)) // 2)
|
||||
line.pad_left(width - cell_len(line.plain))
|
||||
elif align == "right":
|
||||
line.pad_left((width - cell_len(line.plain)) // 2)
|
||||
line.pad_right(width - cell_len(line.plain))
|
||||
elif justify == "right":
|
||||
for line in self._lines:
|
||||
line.rstrip()
|
||||
line.truncate(width, overflow=overflow)
|
||||
line.pad_left(width - cell_len(line.plain))
|
||||
elif align == "full":
|
||||
elif justify == "full":
|
||||
for line_index, line in enumerate(self._lines):
|
||||
if line_index == len(self._lines) - 1:
|
||||
break
|
||||
|
||||
@ -10,7 +10,7 @@ from .console import (
|
||||
Console,
|
||||
ConsoleOptions,
|
||||
ConsoleRenderable,
|
||||
JustifyValues,
|
||||
JustifyMethod,
|
||||
RenderResult,
|
||||
Segment,
|
||||
)
|
||||
@ -113,13 +113,13 @@ class Paragraph(TextElement):
|
||||
"""A Paragraph."""
|
||||
|
||||
style_name = "markdown.paragraph"
|
||||
justify: JustifyValues
|
||||
justify: JustifyMethod
|
||||
|
||||
@classmethod
|
||||
def create(cls, markdown: "Markdown", node) -> "Paragraph":
|
||||
return cls(justify=markdown.justify or "left")
|
||||
|
||||
def __init__(self, justify: JustifyValues) -> None:
|
||||
def __init__(self, justify: JustifyMethod) -> None:
|
||||
self.justify = justify
|
||||
|
||||
def __rich_console__(
|
||||
@ -382,7 +382,7 @@ class Markdown(JupyterMixin):
|
||||
Args:
|
||||
markup (str): A string containing markdown.
|
||||
code_theme (str, optional): Pygments theme for code blocks. Defaults to "monokai".
|
||||
justify (JustifyValues, optional): Justify value for paragraphs. Defaults to None.
|
||||
justify (JustifyMethod, optional): Justify value for paragraphs. Defaults to None.
|
||||
style (Union[str, Style], optional): Optional style to apply to markdown.
|
||||
hyperlinks (bool, optional): Enable hyperlinks. Defaults to ``True``.
|
||||
"""
|
||||
@ -403,7 +403,7 @@ class Markdown(JupyterMixin):
|
||||
self,
|
||||
markup: str,
|
||||
code_theme: str = "monokai",
|
||||
justify: JustifyValues = None,
|
||||
justify: JustifyMethod = None,
|
||||
style: Union[str, Style] = "none",
|
||||
hyperlinks: bool = True,
|
||||
) -> None:
|
||||
|
||||
@ -27,7 +27,7 @@ from typing import (
|
||||
|
||||
from . import get_console
|
||||
from .bar import Bar
|
||||
from .console import Console, JustifyValues, RenderGroup, RenderableType
|
||||
from .console import Console, JustifyMethod, RenderGroup, RenderableType
|
||||
from .highlighter import Highlighter
|
||||
from . import filesize
|
||||
from .live_render import LiveRender
|
||||
@ -129,7 +129,7 @@ class TextColumn(ProgressColumn):
|
||||
self,
|
||||
text_format: str,
|
||||
style: StyleType = "none",
|
||||
justify: JustifyValues = "left",
|
||||
justify: JustifyMethod = "left",
|
||||
markup: bool = True,
|
||||
highlighter: Highlighter = None,
|
||||
) -> None:
|
||||
@ -688,7 +688,7 @@ class Progress:
|
||||
sep=" ",
|
||||
end="\n",
|
||||
style: Union[str, Style] = None,
|
||||
justify: JustifyValues = None,
|
||||
justify: JustifyMethod = None,
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlight: bool = None,
|
||||
@ -716,7 +716,7 @@ class Progress:
|
||||
*objects: Any,
|
||||
sep=" ",
|
||||
end="\n",
|
||||
justify: JustifyValues = None,
|
||||
justify: JustifyMethod = None,
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlight: bool = None,
|
||||
|
||||
@ -29,7 +29,8 @@ if TYPE_CHECKING:
|
||||
from .console import (
|
||||
Console,
|
||||
ConsoleOptions,
|
||||
JustifyValues,
|
||||
JustifyMethod,
|
||||
OverflowMethod,
|
||||
RenderableType,
|
||||
RenderResult,
|
||||
)
|
||||
@ -58,10 +59,10 @@ class Column:
|
||||
style: StyleType = "none"
|
||||
"""StyleType: The style of the column."""
|
||||
|
||||
justify: "JustifyValues" = "left"
|
||||
justify: "JustifyMethod" = "left"
|
||||
"""str: How to justify text within the column ("left", "center", "right", or "full")"""
|
||||
|
||||
overflow: "OverflowValues" = "ellipsis"
|
||||
overflow: "OverflowMethod" = "ellipsis"
|
||||
|
||||
width: Optional[int] = None
|
||||
"""Optional[int]: Width of the column, or ``None`` (default) to auto calculate width."""
|
||||
@ -230,8 +231,12 @@ class Table(JupyterMixin):
|
||||
table_width = (
|
||||
sum(self._calculate_column_widths(console, max_width)) + extra_width
|
||||
)
|
||||
|
||||
return Measurement(1, table_width)
|
||||
_measure_column = self._measure_column
|
||||
minimum_width = max(
|
||||
_measure_column(console, column, max_width).minimum
|
||||
for column in self.columns
|
||||
)
|
||||
return Measurement(minimum_width, table_width)
|
||||
|
||||
@property
|
||||
def padding(self) -> Tuple[int, int, int, int]:
|
||||
@ -251,8 +256,8 @@ class Table(JupyterMixin):
|
||||
header_style: StyleType = None,
|
||||
footer_style: StyleType = None,
|
||||
style: StyleType = None,
|
||||
justify: "JustifyValues" = "left",
|
||||
overflow: "OverflowValues" = "ellipsis",
|
||||
justify: "JustifyMethod" = "left",
|
||||
overflow: "OverflowMethod" = "ellipsis",
|
||||
width: int = None,
|
||||
ratio: int = None,
|
||||
no_wrap: bool = False,
|
||||
@ -267,7 +272,7 @@ class Table(JupyterMixin):
|
||||
header_style (Union[str, Style], optional): Style for the header. Defaults to "none".
|
||||
footer_style (Union[str, Style], optional): Style for the header. Defaults to "none".
|
||||
style (Union[str, Style], optional): Style for the column cells. Defaults to "none".
|
||||
justify (JustifyValues, optional): Alignment for cells. Defaults to "left".
|
||||
justify (JustifyMethod, optional): Alignment for cells. Defaults to "left".
|
||||
width (int, optional): A minimum width in characters. Defaults to None.
|
||||
ratio (int, optional): Flexible ratio for the column. Defaults to None.
|
||||
no_wrap (bool, optional): Set to ``True`` to disable wrapping of this column.
|
||||
@ -398,6 +403,7 @@ class Table(JupyterMixin):
|
||||
widths[index] = fixed_widths[index] + next(iter_flex_widths)
|
||||
table_width = sum(widths)
|
||||
|
||||
# Reduce rows that not no_wrap
|
||||
if table_width > max_width:
|
||||
excess_width = table_width - max_width
|
||||
widths = ratio_reduce(
|
||||
@ -408,6 +414,7 @@ class Table(JupyterMixin):
|
||||
)
|
||||
table_width = sum(widths)
|
||||
|
||||
# Reduce rows that are no_wrap
|
||||
if table_width > max_width:
|
||||
excess_width = table_width - max_width
|
||||
widths = ratio_reduce(
|
||||
@ -425,54 +432,6 @@ class Table(JupyterMixin):
|
||||
)
|
||||
table_width = sum(widths)
|
||||
|
||||
# flex_widths = [_range.span for _range in width_ranges]
|
||||
# excess_width = table_width - max_width
|
||||
|
||||
# widths = [
|
||||
# max(width_range.minimum + 2, width - excess_width)
|
||||
# for width_range, width, excess_width in zip(
|
||||
# width_ranges, widths, ratio_divide(excess_width, flex_widths)
|
||||
# )
|
||||
# ]
|
||||
|
||||
# flex_widths = [
|
||||
# 0 if column.no_wrap else 1
|
||||
# for width_range, column in zip(width_ranges, columns)
|
||||
# ]
|
||||
# if any(flex_widths):
|
||||
# widths = [
|
||||
# max(width_range.minimum, width - excess_width)
|
||||
# for width_range, width, excess_width in zip(
|
||||
# width_ranges,
|
||||
# widths,
|
||||
# ratio_divide(excess_width, flex_widths),
|
||||
# )
|
||||
# ]
|
||||
table_width = sum(widths)
|
||||
# if table_width > max_width:
|
||||
# excess_width = table_width - max_width
|
||||
# flex_widths = [0 if column.no_wrap else 1 for column in columns]
|
||||
|
||||
# if any(flex_widths):
|
||||
# widths = [
|
||||
# max(width_range.minimum, width - excess_width)
|
||||
# for width_range, width, excess_width in zip(
|
||||
# width_ranges,
|
||||
# widths,
|
||||
# ratio_divide(excess_width, flex_widths),
|
||||
# )
|
||||
# ]
|
||||
# table_width = sum(widths)
|
||||
# if table_width > max_width:
|
||||
# flex_widths = [1 for column in columns]
|
||||
# excess_width = table_width - max_width
|
||||
# widths = [
|
||||
# width - excess_width
|
||||
# for width_range, width, excess_width in zip(
|
||||
# width_ranges, widths, ratio_divide(excess_width, flex_widths),
|
||||
# )
|
||||
# ]
|
||||
|
||||
elif table_width < max_width and self.expand:
|
||||
pad_widths = ratio_divide(max_width - table_width, widths)
|
||||
widths = [_width + pad for _width, pad in zip(widths, pad_widths)]
|
||||
|
||||
134
rich/text.py
134
rich/text.py
@ -30,13 +30,17 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
from .console import (
|
||||
Console,
|
||||
ConsoleOptions,
|
||||
JustifyValues,
|
||||
OverflowValues,
|
||||
JustifyMethod,
|
||||
OverflowMethod,
|
||||
RenderResult,
|
||||
RenderableType,
|
||||
)
|
||||
|
||||
DEFAULT_OVERFLOW = "fold"
|
||||
DEFAULT_JUSTIFY: "JustifyMethod" = "default"
|
||||
DEFAULT_OVERFLOW: "OverflowMethod" = "fold"
|
||||
|
||||
|
||||
_re_whitespace = re.compile(r"\s+$")
|
||||
|
||||
|
||||
class Span(NamedTuple):
|
||||
@ -98,7 +102,8 @@ class Text(JupyterMixin):
|
||||
Args:
|
||||
text (str, optional): Default unstyled text. Defaults to "".
|
||||
style (Union[str, Style], optional): Base style for text. Defaults to "".
|
||||
justify (str, optional): Default alignment for text, "left", "center", "full" or "right". Defaults to None.
|
||||
justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
|
||||
overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
|
||||
end (str, optional): Character to end text with. Defaults to "\n".
|
||||
tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8.
|
||||
"""
|
||||
@ -107,8 +112,8 @@ class Text(JupyterMixin):
|
||||
self,
|
||||
text: str = "",
|
||||
style: Union[str, Style] = "",
|
||||
justify: "JustifyValues" = None,
|
||||
overflow: "OverflowValues" = None,
|
||||
justify: "JustifyMethod" = None,
|
||||
overflow: "OverflowMethod" = None,
|
||||
no_wrap: bool = False,
|
||||
end: str = "\n",
|
||||
tab_size: Optional[int] = 8,
|
||||
@ -162,14 +167,16 @@ class Text(JupyterMixin):
|
||||
text: str,
|
||||
style: Union[str, Style] = "",
|
||||
emoji: bool = True,
|
||||
justify: "JustifyValues" = None,
|
||||
justify: "JustifyMethod" = None,
|
||||
overflow: "OverflowMethod" = None,
|
||||
) -> "Text":
|
||||
"""Create Text instance from markup.
|
||||
|
||||
Args:
|
||||
text (str): A string containing console markup.
|
||||
emoji (bool, optional): Also render emoji code. Defaults to True.
|
||||
justify (str, optional): Default alignment for text, "left", "center", "full" or "right". Defaults to None.
|
||||
justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
|
||||
overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
|
||||
|
||||
Returns:
|
||||
Text: A Text instance with markup rendered.
|
||||
@ -178,6 +185,7 @@ class Text(JupyterMixin):
|
||||
|
||||
rendered_text = render(text, style, emoji=emoji)
|
||||
rendered_text.justify = justify
|
||||
rendered_text.overflow = overflow
|
||||
return rendered_text
|
||||
|
||||
@classmethod
|
||||
@ -185,7 +193,8 @@ class Text(JupyterMixin):
|
||||
cls,
|
||||
*parts: Union[str, "Text", Tuple[str, StyleType]],
|
||||
style: Union[str, Style] = "",
|
||||
justify: "JustifyValues" = None,
|
||||
justify: "JustifyMethod" = None,
|
||||
overflow: "OverflowMethod" = None,
|
||||
end: str = "\n",
|
||||
tab_size: int = 8,
|
||||
) -> "Text":
|
||||
@ -194,14 +203,17 @@ class Text(JupyterMixin):
|
||||
|
||||
Args:
|
||||
style (Union[str, Style], optional): Base style for text. Defaults to "".
|
||||
justify (str, optional): Default alignment for text, "left", "center", "full" or "right". Defaults to None.
|
||||
justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
|
||||
overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
|
||||
end (str, optional): Character to end text with. Defaults to "\n".
|
||||
tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8.
|
||||
|
||||
Returns:
|
||||
Text: A new text instance.
|
||||
"""
|
||||
text = cls(style=style, justify=justify, end=end, tab_size=tab_size)
|
||||
text = cls(
|
||||
style=style, justify=justify, overflow=overflow, end=end, tab_size=tab_size
|
||||
)
|
||||
append = text.append
|
||||
for part in parts:
|
||||
if isinstance(part, (Text, str)):
|
||||
@ -221,11 +233,12 @@ class Text(JupyterMixin):
|
||||
@plain.setter
|
||||
def plain(self, new_text: str) -> None:
|
||||
"""Set the text to a new value."""
|
||||
self._text[:] = [new_text]
|
||||
old_length = self._length
|
||||
self._length = len(new_text)
|
||||
if old_length > self._length:
|
||||
self._trim_spans()
|
||||
if new_text != self.plain:
|
||||
self._text[:] = [new_text]
|
||||
old_length = self._length
|
||||
self._length = len(new_text)
|
||||
if old_length > self._length:
|
||||
self._trim_spans()
|
||||
|
||||
@property
|
||||
def spans(self) -> List[Span]:
|
||||
@ -253,7 +266,7 @@ class Text(JupyterMixin):
|
||||
"""Return a copy of this instance."""
|
||||
copy_self = Text(
|
||||
self.plain,
|
||||
style=self.style.copy() if isinstance(self.style, Style) else self.style,
|
||||
style=self.style,
|
||||
justify=self.justify,
|
||||
overflow=self.overflow,
|
||||
no_wrap=self.no_wrap,
|
||||
@ -369,6 +382,20 @@ class Text(JupyterMixin):
|
||||
"""Trip whitespace from end of text."""
|
||||
self.plain = self.plain.rstrip()
|
||||
|
||||
def rstrip_end(self, size: int):
|
||||
"""Remove whitespace beyond a certain width at the end of the text.
|
||||
|
||||
Args:
|
||||
size (int): The desired size of the text.
|
||||
"""
|
||||
text_length = len(self)
|
||||
if text_length > size:
|
||||
excess = text_length - size
|
||||
whitespace_match = _re_whitespace.search(self.plain)
|
||||
if whitespace_match is not None:
|
||||
whitespace_count = len(whitespace_match.group(0))
|
||||
self.plain = self.plain[: -min(whitespace_count, excess)]
|
||||
|
||||
def set_length(self, new_length: int) -> None:
|
||||
"""Set new length of the text, clipping or padding is required."""
|
||||
length = len(self)
|
||||
@ -389,10 +416,13 @@ class Text(JupyterMixin):
|
||||
self, console: "Console", options: "ConsoleOptions"
|
||||
) -> Iterable[Segment]:
|
||||
tab_size: int = console.tab_size or self.tab_size or 8 # type: ignore
|
||||
overflow = cast(
|
||||
"OverflowValues", self.overflow or options.overflow or DEFAULT_OVERFLOW
|
||||
justify = cast(
|
||||
"JustifyMethod", self.justify or options.justify or DEFAULT_OVERFLOW
|
||||
)
|
||||
justify = self.justify or options.justify
|
||||
overflow = cast(
|
||||
"OverflowMethod", self.overflow or options.overflow or DEFAULT_OVERFLOW
|
||||
)
|
||||
|
||||
if self.no_wrap or options.no_wrap:
|
||||
render_text = self
|
||||
if overflow in ("crop", "ellipsis"):
|
||||
@ -401,7 +431,7 @@ class Text(JupyterMixin):
|
||||
if justify:
|
||||
lines = Lines([render_text])
|
||||
lines.justify(
|
||||
console, options.max_width, align=justify, overflow=overflow
|
||||
console, options.max_width, justify=justify, overflow=overflow
|
||||
)
|
||||
render_text = lines[0]
|
||||
yield from render_text.render(console, end=self.end)
|
||||
@ -413,13 +443,6 @@ class Text(JupyterMixin):
|
||||
overflow=overflow,
|
||||
tab_size=tab_size or 8,
|
||||
)
|
||||
# new_line = Segment.line()
|
||||
# for last, line in loop_last(lines):
|
||||
# yield from line.render(console)
|
||||
# if not last:
|
||||
# yield new_line
|
||||
# else:
|
||||
# yield Segment(self.end)
|
||||
all_lines = Text("\n").join(lines)
|
||||
yield from all_lines.render(console, end=self.end)
|
||||
|
||||
@ -544,20 +567,20 @@ class Text(JupyterMixin):
|
||||
def truncate(
|
||||
self,
|
||||
max_width: int,
|
||||
overflow: Optional["OverflowValues"] = None,
|
||||
overflow: Optional["OverflowMethod"] = None,
|
||||
pad: bool = False,
|
||||
) -> None:
|
||||
"""Truncate text if it is longer that a given width.
|
||||
|
||||
Args:
|
||||
max_width (int): Maximum number of characters in text.
|
||||
ellipsis (bool): Replace last character with ellipsis if truncated.
|
||||
overflow (str, optional): Overflow method: "crop", "fold", or "ellipisis". Defaults to None, to use self.overflow.
|
||||
pad (bool, optional): Pad with spaces if the length is less than max_width. Defaults to False.
|
||||
"""
|
||||
|
||||
length = cell_len(self.plain)
|
||||
_overflow = overflow or self.overflow or DEFAULT_OVERFLOW
|
||||
if length > max_width:
|
||||
overflow = overflow or self.overflow or DEFAULT_OVERFLOW
|
||||
if overflow == "ellipsis":
|
||||
if _overflow == "ellipsis":
|
||||
self.plain = set_cell_size(self.plain, max_width - 1).rstrip() + "…"
|
||||
else:
|
||||
self.plain = set_cell_size(self.plain, max_width)
|
||||
@ -571,13 +594,18 @@ class Text(JupyterMixin):
|
||||
new_length = self._length
|
||||
spans: List[Span] = []
|
||||
append = spans.append
|
||||
_Span = Span
|
||||
for span in self._spans:
|
||||
if span.end < new_length:
|
||||
append(span)
|
||||
continue
|
||||
if span.start >= new_length:
|
||||
continue
|
||||
append(span.right_crop(new_length))
|
||||
if span.end > new_length:
|
||||
start, end, style = span
|
||||
append(_Span(start, min(new_length, end), style))
|
||||
else:
|
||||
append(span)
|
||||
self._spans[:] = spans
|
||||
|
||||
def pad_left(self, count: int, character: str = " ") -> None:
|
||||
@ -741,7 +769,7 @@ class Text(JupyterMixin):
|
||||
if new_span is None:
|
||||
break
|
||||
span = new_span
|
||||
line_index += 1
|
||||
line_index = (line_index + 1) % len(line_ranges)
|
||||
line_start, line_end = line_ranges[line_index]
|
||||
|
||||
return new_lines
|
||||
@ -754,21 +782,26 @@ class Text(JupyterMixin):
|
||||
self,
|
||||
console: "Console",
|
||||
width: int,
|
||||
justify: Optional["JustifyValues"] = "left",
|
||||
overflow: Optional["OverflowValues"] = None,
|
||||
justify: "JustifyMethod" = None,
|
||||
overflow: "OverflowMethod" = None,
|
||||
tab_size: int = 8,
|
||||
) -> Lines:
|
||||
"""Word wrap the text.
|
||||
|
||||
Args:
|
||||
console (Console): Console instance.
|
||||
width (int): Number of characters per line.
|
||||
justify (bool, optional): True to pad lines with spaces. Defaults to False.
|
||||
emoji (bool, optional): Also render emoji code. Defaults to True.
|
||||
justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to "left".
|
||||
overflow (str, optional): Overflow method: "crop", "fold", or "ellipisis". Defaults to None.
|
||||
tab_size (int, optional): Default tab size. Defaults to 8.
|
||||
|
||||
Returns:
|
||||
Lines: Number of lines.
|
||||
"""
|
||||
wrap_overflow: "OverflowValues" = cast(
|
||||
"OverflowValues", overflow or self.overflow or DEFAULT_OVERFLOW
|
||||
wrap_justify = cast("JustifyMethod", justify or self.justify or DEFAULT_JUSTIFY)
|
||||
wrap_overflow = cast(
|
||||
"OverflowMethod", overflow or self.overflow or DEFAULT_OVERFLOW
|
||||
)
|
||||
lines: Lines = Lines()
|
||||
for line in self.split():
|
||||
@ -776,13 +809,16 @@ class Text(JupyterMixin):
|
||||
line = line.tabs_to_spaces(tab_size)
|
||||
offsets = divide_line(str(line), width, fold=wrap_overflow == "fold")
|
||||
new_lines = line.divide(offsets)
|
||||
if justify:
|
||||
new_lines.justify(console, width, align=justify, overflow=overflow)
|
||||
for line in new_lines:
|
||||
line.rstrip_end(width)
|
||||
if wrap_justify:
|
||||
new_lines.justify(
|
||||
console, width, justify=wrap_justify, overflow=wrap_overflow
|
||||
)
|
||||
lines.extend(new_lines)
|
||||
|
||||
if wrap_overflow in ("crop", "ellipsis"):
|
||||
for line in lines:
|
||||
line.truncate(width, wrap_overflow)
|
||||
for line in lines:
|
||||
line.truncate(width, wrap_overflow)
|
||||
|
||||
return lines
|
||||
|
||||
@ -801,3 +837,11 @@ class Text(JupyterMixin):
|
||||
line.set_length(width)
|
||||
append(line)
|
||||
return lines
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
t = Text("foo bar", justify="left")
|
||||
print(repr(t.wrap(console, 4)))
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -23,10 +23,11 @@ def render_log():
|
||||
|
||||
|
||||
def test_log():
|
||||
expected = "\n\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mHello from \x1b[1m<\x1b[0m\x1b[1;38;5;13mconsole\x1b[0m\x1b[39m \x1b[0m\x1b[3;33mwidth\x1b[0m\x1b[39m=\x1b[0m\x1b[1;34m80\x1b[0m\x1b[39m ColorSystem.TRUECOLOR\x1b[0m\x1b[1m>\x1b[0m ! \x1b[2mtest_log.py:20\x1b[0m\x1b[2m \x1b[0m\n\x1b[2;36m \x1b[0m\x1b[2;36m \x1b[0m\x1b[1m[\x1b[0m\x1b[1;34m1\x1b[0m, \x1b[1;34m2\x1b[0m, \x1b[1;34m3\x1b[0m\x1b[1m]\x1b[0m \x1b[2mtest_log.py:21\x1b[0m\x1b[2m \x1b[0m\n \x1b[3m Locals \x1b[0m \n \x1b[34m╭─────────┬────────────────────────────────────────╮\x1b[0m \n \x1b[34m│\x1b[0m\x1b[32m'console'\x1b[0m\x1b[34m│\x1b[0m\x1b[1m<\x1b[0m\x1b[1;38;5;13mconsole\x1b[0m\x1b[39m \x1b[0m\x1b[3;33mwidth\x1b[0m\x1b[39m=\x1b[0m\x1b[1;34m80\x1b[0m\x1b[39m ColorSystem.TRUECOLOR\x1b[0m\x1b[1m>\x1b[0m\x1b[34m│\x1b[0m \n \x1b[34m╰─────────┴────────────────────────────────────────╯\x1b[0m \n"
|
||||
expected = "\n\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mHello from \x1b[1m<\x1b[0m\x1b[1;38;5;13mconsole\x1b[0m\x1b[39m \x1b[0m\x1b[3;33mwidth\x1b[0m\x1b[39m=\x1b[0m\x1b[1;34m80\x1b[0m\x1b[39m ColorSystem.TRUECOLOR\x1b[0m\x1b[1m>\x1b[0m ! \x1b[2mtest_log.py:20\x1b[0m\n\x1b[2;36m \x1b[0m\x1b[2;36m \x1b[0m\x1b[1m[\x1b[0m\x1b[1;34m1\x1b[0m, \x1b[1;34m2\x1b[0m, \x1b[1;34m3\x1b[0m\x1b[1m]\x1b[0m \x1b[2mtest_log.py:21\x1b[0m\n \x1b[3m Locals \x1b[0m \n \x1b[34m╭─────────┬────────────────────────────────────────╮\x1b[0m \n \x1b[34m│\x1b[0m\x1b[32m'console'\x1b[0m\x1b[34m│\x1b[0m\x1b[1m<\x1b[0m\x1b[1;38;5;13mconsole\x1b[0m\x1b[39m \x1b[0m\x1b[3;33mwidth\x1b[0m\x1b[39m=\x1b[0m\x1b[1;34m80\x1b[0m\x1b[39m ColorSystem.TRUECOLOR\x1b[0m\x1b[1m>\x1b[0m\x1b[34m│\x1b[0m \n \x1b[34m╰─────────┴────────────────────────────────────────╯\x1b[0m \n"
|
||||
assert render_log() == expected
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(render_log())
|
||||
print(repr(render_log()))
|
||||
render = render_log()
|
||||
print(render)
|
||||
print(repr(render))
|
||||
|
||||
@ -21,7 +21,7 @@ def make_log():
|
||||
|
||||
def test_log():
|
||||
render = make_log()
|
||||
expected = "\x1b[2;36m[DATE]\x1b[0m\x1b[2;36m \x1b[0m\x1b[32mDEBUG\x1b[0m foo \x1b[2mtest_logging.py:17\x1b[0m\x1b[2m \x1b[0m\n"
|
||||
expected = "\x1b[2;36m[DATE]\x1b[0m\x1b[2;36m \x1b[0m\x1b[32mDEBUG\x1b[0m foo \x1b[2mtest_logging.py:17\x1b[0m\n"
|
||||
assert render == expected
|
||||
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ def test_track() -> None:
|
||||
assert value == next(expected_values)
|
||||
result = console.file.getvalue()
|
||||
print(repr(result))
|
||||
expected = "test \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[35m 0%\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[?25l\r\x1b[2Ktest \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[35m 0%\x1b[0m \x1b[36m-:--:--\x1b[0m \r\x1b[2Ktest \x1b[38;2;249;38;114m━━━━━━━\x1b[0m\x1b[38;5;237m╺\x1b[0m\x1b[38;5;237m━━━━━━━━━━━━━\x1b[0m \x1b[35m 33%\x1b[0m \x1b[36m-:--:--\x1b[0m \r\x1b[2Ktest \x1b[38;2;249;38;114m━━━━━━━━━━━━━━\x1b[0m\x1b[38;5;237m╺\x1b[0m\x1b[38;5;237m━━━━━━\x1b[0m \x1b[35m 67%\x1b[0m \x1b[36m0:00:06\x1b[0m \r\x1b[2Ktest \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[35m100%\x1b[0m \x1b[36m0:00:00\x1b[0m \r\x1b[2Ktest \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[35m100%\x1b[0m \x1b[36m0:00:00\x1b[0m \n\x1b[?25h"
|
||||
expected = " \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-…\x1b[0m \x1b[?25l\r\x1b[2K \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-…\x1b[0m \r\x1b[2K\x1b[38;2;249;38;114m━━━━━━━━━━━\x1b[0m\x1b[38;2;249;38;114m╸\x1b[0m\x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:…\x1b[0m \r\x1b[2K\x1b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\x1b[38;5;237m╺\x1b[0m\x1b[38;5;237m━━━━━━━━━━━\x1b[0m \x1b[36m0:…\x1b[0m \r\x1b[2K\x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:…\x1b[0m \r\x1b[2K\x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:…\x1b[0m \n\x1b[?25h"
|
||||
assert result == expected
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
@ -192,7 +192,7 @@ def test_columns() -> None:
|
||||
progress.refresh()
|
||||
result = console.file.getvalue()
|
||||
print(repr(result))
|
||||
expected = 'test foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \r\x1b[2Ktest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \x1b[?25l\r\x1b[1A\x1b[2Ktest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2K\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mhello \x1b[2mtest_progress.py:190\x1b[0m\x1b[2m \x1b[0m\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2Kworld\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m \ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m \r\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m \ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m \n\x1b[?25h'
|
||||
expected = "test foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \r\x1b[2Ktest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \x1b[?25l\r\x1b[1A\x1b[2Ktest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2K\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mhello \x1b[2mtest_progress.py:190\x1b[0m\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2Kworld\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m \ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m \r\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m \ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m \r\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m \ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m \n\x1b[?25h"
|
||||
assert result == expected
|
||||
|
||||
|
||||
|
||||
@ -313,8 +313,18 @@ def test_right_crop():
|
||||
assert test._spans == [Span(0, 3, "red")]
|
||||
|
||||
|
||||
def test_wrap_4():
|
||||
def test_wrap_3():
|
||||
test = Text("foo bar baz")
|
||||
lines = test.wrap(Console(), 3)
|
||||
print(repr(lines))
|
||||
assert len(lines) == 3
|
||||
assert lines[0] == Text("foo")
|
||||
assert lines[1] == Text("bar")
|
||||
assert lines[2] == Text("baz")
|
||||
|
||||
|
||||
def test_wrap_4():
|
||||
test = Text("foo bar baz", justify="left")
|
||||
lines = test.wrap(Console(), 4)
|
||||
assert len(lines) == 3
|
||||
assert lines[0] == Text("foo ")
|
||||
@ -322,17 +332,8 @@ def test_wrap_4():
|
||||
assert lines[2] == Text("baz ")
|
||||
|
||||
|
||||
def test_wrap_3():
|
||||
test = Text("foo bar baz")
|
||||
lines = test.wrap(Console(), 3)
|
||||
assert len(lines) == 3
|
||||
assert lines[0] == Text("foo")
|
||||
assert lines[1] == Text("bar")
|
||||
assert lines[2] == Text("baz")
|
||||
|
||||
|
||||
def test_wrap_long():
|
||||
test = Text("abracadabra")
|
||||
test = Text("abracadabra", justify="left")
|
||||
lines = test.wrap(Console(), 4)
|
||||
assert len(lines) == 3
|
||||
assert lines[0] == Text("abra")
|
||||
@ -341,7 +342,7 @@ def test_wrap_long():
|
||||
|
||||
|
||||
def test_wrap_long_words():
|
||||
test = Text("X 123456789")
|
||||
test = Text("X 123456789", justify="left")
|
||||
lines = test.wrap(Console(), 4)
|
||||
|
||||
assert len(lines) == 3
|
||||
@ -358,7 +359,7 @@ def test_fit():
|
||||
|
||||
|
||||
def test_wrap_tabs():
|
||||
test = Text("foo\tbar",)
|
||||
test = Text("foo\tbar", justify="left")
|
||||
lines = test.wrap(Console(), 4)
|
||||
assert len(lines) == 2
|
||||
assert str(lines[0]) == "foo "
|
||||
|
||||
Loading…
Reference in New Issue
Block a user