This page gives an overview of how Rich represents, styles, and renders text internally. It covers the two main subsystems: the Text/Span model (the in-memory representation of styled text) and the markup parser (which converts BBCode-style markup strings into Text instances). For a detailed API reference of the Text class see Text and Spans. For the full markup syntax and theming system, see Markup and Formatting. For the Style and Color classes that are referenced throughout this page, see Styles and Colors.
The Text class is the primary bridge between human-readable content and the Segment stream that the Console ultimately writes to the terminal. Most Rich renderables (tables, panels, markdown blocks, log records) produce Text objects internally, and Text itself implements __rich_console__ so it participates in the standard rendering protocol described in Rendering Pipeline.
Data flow from string to terminal:
Sources: rich/text.py689-705 rich/markup.py106-231 rich/containers.py66-110
Span — the atomic style regionSpan is a NamedTuple defined in rich/text.py with three fields:
| Field | Type | Description |
|---|---|---|
start | int | Inclusive character offset into the plain text |
end | int | Exclusive character offset |
style | str | Style | The style to apply over <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/start, end) |
Text — styled string containerText lives in rich/text.py and inherits from JupyterMixin. Its internal storage uses a list of strings (_text) that behave as a rope — they are concatenated lazily via the plain property. This avoids repeated string copying on each append() call.
Key slots:
| Slot | Purpose |
|---|---|
_text | List[str] — rope of string segments |
_spans | List[Span] — all active style regions |
_length | int — cached total character count |
style | Base style applied to the whole object |
justify | "left", "center", "right", "full", or None |
overflow | "crop", "fold", "ellipsis", or None |
no_wrap | bool | None |
end | Terminator appended after render (default "\n") |
tab_size | Spaces per tab character |
Sources: rich/text.py118-165
The diagram below maps the two main subsystems to the code entities that implement them.
Sources: rich/text.py1-50 rich/markup.py1-50 rich/theme.py1-116 rich/containers.py66-110
Text InstancesThere are four class-method constructors in addition to the plain __init__:
| Constructor | Source | Description |
|---|---|---|
Text(plain, style=...) | __init__ | Direct construction from an unstyled string |
Text.from_markup(markup) | rich/text.py259-291 | Parses markup tags; delegates to markup.render() |
Text.from_ansi(text) | rich/text.py293-329 | Parses ANSI escape codes; delegates to AnsiDecoder |
Text.styled(text, style) | rich/text.py331-354 | Wraps the whole string in a single span; base style left empty so padding is unstyled |
Text.assemble(*parts) | rich/text.py356-400 | Combines str, Text, or (str, style) tuples into one Text |
Sources: rich/text.py259-400
The markup system in rich/markup.py converts BBCode-style tag strings into Text+Span trees.
Parsing flow:
Key rules enforced by the parser:
#, /, or @ — strings like [1], [True] are not treated as tags.[/name] must match an open [name] tag (raises MarkupError on mismatch).[/] closes the most recently opened tag implicitly.@ are treated as event metadata spans, not visual styles (used by the Textual framework).\[bold] renders as literal [bold].The escape() function in rich/markup.py safely escapes user content so it is not interpreted as markup tags.
Sources: rich/markup.py48-231 rich/markup.py12-17
TextAfter construction, styles can be added to any character range:
| Method | Description |
|---|---|
stylize(style, start, end) | Appends a Span covering <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/start, end) |
Before a Text object reaches the Segment stage it often goes through layout operations:
| Method | File | Description |
|---|---|---|
wrap(console, width, ...) | rich/text.py1050-1117 | Splits text into Lines, respecting word boundaries and CJK double-width characters |
divide(offsets) | rich/text.py1118-1170 | Cuts text at exact character offsets; correctly splits Span objects that cross a cut point |
split(separator) | rich/text.py1172-1220 | Splits on a separator string; analogous to str.split but span-aware |
truncate(max_width, overflow) | rich/text.py859-884 | Clips or ellipsis-crops to a cell width |
expand_tabs(tab_size) | rich/text.py817-857 | Converts \t to spaces; adjusts all affected Span offsets |
align(align, width) | rich/text.py944-962 | Pads left/right to a target width |
Word-boundary logic is implemented in rich/_wrap.py by divide_line(), which accounts for Unicode cell width via cell_len() from rich/cells.py.
Sources: rich/text.py817-1220 rich/_wrap.py1-79 rich/containers.py111-168
Text to SegmentsText.__rich_console__ is the entry point when a Console renders a Text object. Internally it calls Text.render().
Text.render() algorithm:
(offset, is_closing, span_id) tuples.stack) of span IDs.Style.combine() and yield a Segment(text[offset:next_offset], combined_style).Segment(end) for the terminator.A style_cache dict keyed on the sorted tuple of active style IDs avoids redundant Style.combine() calls for the same combination.
Sources: rich/text.py719-776
Named styles such as "bold red" or "repr.number" inside Span objects are resolved at render time by Console.get_style(), not at parse time. The Console holds a ThemeStack (from rich/theme.py) that maps names to Style objects. This means that a Text object is independent of any specific theme and can be rendered on consoles with different themes.
| Class | File | Role |
|---|---|---|
Theme | rich/theme.py8-73 | Holds a Dict[str, Style]; can be constructed from a dict or loaded from an INI file |
ThemeStack | rich/theme.py80-110 | A stack of merged theme dicts; push_theme() / pop_theme() enable local theme overrides |
Sources: rich/theme.py1-116
| Symbol | File | Role in Text System |
|---|---|---|
Span | rich/text.py | NamedTuple marking a styled region [start, end) |
Text | rich/text.py | Styled string container; implements __rich_console__ |
Lines | rich/containers.py | List[Text] with justify support; produced by Text.wrap() |
Tag | rich/markup.py | NamedTuple representing one parsed markup tag |
_parse() | rich/markup.py | Tokenises a markup string into (pos, text, Tag) tuples |
render() | rich/markup.py | Converts a markup string to a Text instance |
escape() | rich/markup.py | Escapes a plain string so it is safe to embed in markup |
divide_line() | rich/_wrap.py | Computes line-break offsets for word wrapping |
Theme | rich/theme.py | Maps style name strings to Style objects |
ThemeStack | rich/theme.py | Runtime stack of Theme dicts held by Console |
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.