Translate ANSI code in to styled Text.
| 118 | |
| 119 | |
| 120 | class AnsiDecoder: |
| 121 | """Translate ANSI code in to styled Text.""" |
| 122 | |
| 123 | def __init__(self) -> None: |
| 124 | self.style = Style.null() |
| 125 | |
| 126 | def decode(self, terminal_text: str) -> Iterable[Text]: |
| 127 | """Decode ANSI codes in an iterable of lines. |
| 128 | |
| 129 | Args: |
| 130 | terminal_text: Output potentially containing ANSI escape sequences. |
| 131 | |
| 132 | Yields: |
| 133 | Text: Marked up Text. |
| 134 | """ |
| 135 | for line in re.split(r"(?<=\n)", terminal_text): |
| 136 | yield self.decode_line(line.rstrip("\n")) |
| 137 | |
| 138 | def decode_line(self, line: str) -> Text: |
| 139 | """Decode a line containing ansi codes. |
| 140 | |
| 141 | Args: |
| 142 | line (str): A line of terminal output. |
| 143 | |
| 144 | Returns: |
| 145 | Text: A Text instance marked up according to ansi codes. |
| 146 | """ |
| 147 | from_ansi = Color.from_ansi |
| 148 | from_rgb = Color.from_rgb |
| 149 | _Style = Style |
| 150 | text = Text() |
| 151 | append = text.append |
| 152 | line = line.rsplit("\r", 1)[-1] |
| 153 | for plain_text, sgr, osc in _ansi_tokenize(line): |
| 154 | if plain_text: |
| 155 | append(plain_text, self.style or None) |
| 156 | elif osc is not None: |
| 157 | if osc.startswith("8;"): |
| 158 | _params, semicolon, link = osc[2:].partition(";") |
| 159 | if semicolon: |
| 160 | self.style = self.style.update_link(link or None) |
| 161 | elif sgr is not None: |
| 162 | # Translate in to semi-colon separated codes |
| 163 | # Ignore invalid codes, because we want to be lenient |
| 164 | codes = [ |
| 165 | min(255, int(_code) if _code else 0) |
| 166 | for _code in sgr.split(";") |
| 167 | if _code.isdigit() or _code == "" |
| 168 | ] |
| 169 | iter_codes = iter(codes) |
| 170 | for code in iter_codes: |
| 171 | if code == 0: |
| 172 | # reset |
| 173 | self.style = _Style.null() |
| 174 | elif code in SGR_STYLE_MAP: |
| 175 | # styles |
| 176 | self.style += _Style.parse(SGR_STYLE_MAP[code]) |
| 177 | elif code == 38: |
no outgoing calls
searching dependent graphs…