Aluode/PerceptionLabPortable
0
1from itertools import zip_longest2from typing import (3 TYPE_CHECKING,4 Iterable,5 Iterator,6 List,7 Optional,8 TypeVar,9 Union,10 overload,11)12 13if TYPE_CHECKING:14 from .console import (15 Console,16 ConsoleOptions,17 JustifyMethod,18 OverflowMethod,19 RenderResult,20 RenderableType,21 )22 from .text import Text23 24from .cells import cell_len25from .measure import Measurement26 27T = TypeVar("T")28 29 30class Renderables:31 """A list subclass which renders its contents to the console."""32 33 def __init__(34 self, renderables: Optional[Iterable["RenderableType"]] = None35 ) -> None:36 self._renderables: List["RenderableType"] = (37 list(renderables) if renderables is not None else []38 )39 40 def __rich_console__(41 self, console: "Console", options: "ConsoleOptions"42 ) -> "RenderResult":43 """Console render method to insert line-breaks."""44 yield from self._renderables45 46 def __rich_measure__(47 self, console: "Console", options: "ConsoleOptions"48 ) -> "Measurement":49 dimensions = [50 Measurement.get(console, options, renderable)51 for renderable in self._renderables52 ]53 if not dimensions:54 return Measurement(1, 1)55 _min = max(dimension.minimum for dimension in dimensions)56 _max = max(dimension.maximum for dimension in dimensions)57 return Measurement(_min, _max)58 59 def append(self, renderable: "RenderableType") -> None:60 self._renderables.append(renderable)61 62 def __iter__(self) -> Iterable["RenderableType"]:63 return iter(self._renderables)64 65 66class Lines:67 """A list subclass which can render to the console."""68 69 def __init__(self, lines: Iterable["Text"] = ()) -> None:70 self._lines: List["Text"] = list(lines)71 72 def __repr__(self) -> str:73 return f"Lines({self._lines!r})"74 75 def __iter__(self) -> Iterator["Text"]:76 return iter(self._lines)77 78 @overload79 def __getitem__(self, index: int) -> "Text":80 ...81 82 @overload83 def __getitem__(self, index: slice) -> List["Text"]:84 ...85 86 def __getitem__(self, index: Union[slice, int]) -> Union["Text", List["Text"]]:87 return self._lines[index]88 89 def __setitem__(self, index: int, value: "Text") -> "Lines":90 self._lines[index] = value91 return self92 93 def __len__(self) -> int:94 return self._lines.__len__()95 96 def __rich_console__(97 self, console: "Console", options: "ConsoleOptions"98 ) -> "RenderResult":99 """Console render method to insert line-breaks."""100 yield from self._lines101 102 def append(self, line: "Text") -> None:103 self._lines.append(line)104 105 def extend(self, lines: Iterable["Text"]) -> None:106 self._lines.extend(lines)107 108 def pop(self, index: int = -1) -> "Text":109 return self._lines.pop(index)110 111 def justify(112 self,113 console: "Console",114 width: int,115 justify: "JustifyMethod" = "left",116 overflow: "OverflowMethod" = "fold",117 ) -> None:118 """Justify and overflow text to a given width.119 120 Args:121 console (Console): Console instance.122 width (int): Number of cells available per line.123 justify (str, optional): Default justify method for text: "left", "center", "full" or "right". Defaults to "left".124 overflow (str, optional): Default overflow for text: "crop", "fold", or "ellipsis". Defaults to "fold".125 126 """127 from .text import Text128 129 if justify == "left":130 for line in self._lines:131 line.truncate(width, overflow=overflow, pad=True)132 elif justify == "center":133 for line in self._lines:134 line.rstrip()135 line.truncate(width, overflow=overflow)136 line.pad_left((width - cell_len(line.plain)) // 2)137 line.pad_right(width - cell_len(line.plain))138 elif justify == "right":139 for line in self._lines:140 line.rstrip()141 line.truncate(width, overflow=overflow)142 line.pad_left(width - cell_len(line.plain))143 elif justify == "full":144 for line_index, line in enumerate(self._lines):145 if line_index == len(self._lines) - 1:146 break147 words = line.split(" ")148 words_size = sum(cell_len(word.plain) for word in words)149 num_spaces = len(words) - 1150 spaces = [1 for _ in range(num_spaces)]151 index = 0152 if spaces:153 while words_size + num_spaces < width:154 spaces[len(spaces) - index - 1] += 1155 num_spaces += 1156 index = (index + 1) % len(spaces)157 tokens: List[Text] = []158 for index, (word, next_word) in enumerate(159 zip_longest(words, words[1:])160 ):161 tokens.append(word)162 if index < len(spaces):163 style = word.get_style_at_offset(console, -1)164 next_style = next_word.get_style_at_offset(console, 0)165 space_style = style if style == next_style else line.style166 tokens.append(Text(" " * spaces[index], style=space_style))167 self[line_index] = Text("").join(tokens)168 