Skip to content

Commit 9195904

Browse files
authored
Merge pull request #6110 from Textualize/render-style-optimization
render ansi
2 parents 11580e9 + cd18c0a commit 9195904

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

src/textual/strip.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77

88
from __future__ import annotations
99

10+
from functools import lru_cache
1011
from typing import Any, Iterable, Iterator, Sequence
1112

1213
import rich.repr
1314
from rich.cells import cell_len, set_cell_size
15+
from rich.color import ColorSystem
1416
from rich.console import Console, ConsoleOptions, RenderResult
1517
from rich.measure import Measurement
1618
from rich.segment import Segment
@@ -22,6 +24,8 @@
2224
from textual.css.types import AlignHorizontal, AlignVertical
2325
from textual.filter import LineFilter
2426

27+
SGR_STYLES = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "21", "51", "52", "53"]
28+
2529

2630
def get_line_length(segments: Iterable[Segment]) -> int:
2731
"""Get the line length (total length of all segments).
@@ -645,6 +649,55 @@ def _apply_link_style(self, link_style: Style) -> Strip:
645649
]
646650
return Strip(segments, self._cell_length)
647651

652+
@classmethod
653+
@lru_cache(maxsize=16384)
654+
def render_ansi(cls, style: Style, color_system: ColorSystem) -> str:
655+
"""Render ANSI codes for a give style.
656+
657+
Args:
658+
style: A Rich style.
659+
color_system: Color system enumeration.
660+
661+
Returns:
662+
A string of ANSI escape sequences to render the style.
663+
"""
664+
sgr: list[str]
665+
if attributes := style._attributes & style._set_attributes:
666+
_style_map = SGR_STYLES
667+
sgr = [
668+
_style_map[bit_offset]
669+
for bit_offset in range(attributes.bit_length())
670+
if attributes & (1 << bit_offset)
671+
]
672+
else:
673+
sgr = []
674+
if (color := style._color) is not None:
675+
sgr.extend(color.downgrade(color_system).get_ansi_codes())
676+
if (bgcolor := style._bgcolor) is not None:
677+
sgr.extend(bgcolor.downgrade(color_system).get_ansi_codes(False))
678+
ansi = style._ansi = ";".join(sgr)
679+
return ansi
680+
681+
@classmethod
682+
def render_style(cls, style: Style, text: str, color_system: ColorSystem) -> str:
683+
"""Render a Rich style and text.
684+
685+
Args:
686+
style: Style to render.
687+
text: Content string.
688+
color_system: Color system enumeration.
689+
690+
Returns:
691+
Text with ANSI escape sequences.
692+
"""
693+
ansi = style._ansi or cls.render_ansi(style, color_system)
694+
output = f"\x1b[{ansi}m{text}\x1b[0m" if ansi else text
695+
if style._link:
696+
output = (
697+
f"\x1b]8;id={style._link_id};{style._link}\x1b\\{output}\x1b]8;;\x1b\\"
698+
)
699+
return output
700+
648701
def render(self, console: Console) -> str:
649702
"""Render the strip into terminal sequences.
650703
@@ -655,8 +708,8 @@ def render(self, console: Console) -> str:
655708
Rendered sequences.
656709
"""
657710
if self._render_cache is None:
658-
color_system = console._color_system
659-
render = Style.render
711+
color_system = console._color_system or ColorSystem.TRUECOLOR
712+
render = self.render_style
660713
self._render_cache = "".join(
661714
[
662715
(

0 commit comments

Comments
 (0)