77
88from __future__ import annotations
99
10+ from functools import lru_cache
1011from typing import Any , Iterable , Iterator , Sequence
1112
1213import rich .repr
1314from rich .cells import cell_len , set_cell_size
15+ from rich .color import ColorSystem
1416from rich .console import Console , ConsoleOptions , RenderResult
1517from rich .measure import Measurement
1618from rich .segment import Segment
2224from textual .css .types import AlignHorizontal , AlignVertical
2325from textual .filter import LineFilter
2426
27+ SGR_STYLES = ["1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "21" , "51" , "52" , "53" ]
28+
2529
2630def 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