1818
1919
2020class _ThemeSyntax (Protocol ):
21+ """Protocol for theme objects that map tag names to SGR escape strings."""
2122 def __getitem__ (self , key : str , / ) -> str : ...
2223
2324
2425@dataclass (frozen = True , slots = True )
2526class RenderCell :
27+ """One terminal cell: a character, its column width, and SGR style.
28+
29+ A screen row like ``>>> def`` is a sequence of cells::
30+
31+ > > > d e f
32+ ╰─╯╰─╯╰─╯╰─╯╰─╯╰─╯╰─╯
33+ """
34+
2635 text : str
2736 width : int
2837 style : StyleRef = field (default_factory = StyleRef )
@@ -94,6 +103,12 @@ def append_plain_text(segment: str) -> None:
94103
95104@dataclass (frozen = True , slots = True )
96105class RenderLine :
106+ """One physical screen row as a tuple of :class:`RenderCell` objects.
107+
108+ ``text`` is the pre-rendered terminal string (characters + SGR escapes);
109+ ``width`` is the total visible column count.
110+ """
111+
97112 cells : tuple [RenderCell , ...]
98113 text : str
99114 width : int
@@ -139,10 +154,16 @@ def from_rendered_text(cls, text: str) -> Self:
139154class ScreenOverlay :
140155 """An overlay that replaces or inserts lines at a screen position.
141156
142- If insert is True, lines are spliced in (shifting content down);
143- if False (default), lines replace existing content at y .
157+ If * insert* is True, lines are spliced in (shifting content down);
158+ if False (default), lines replace existing content at *y* .
144159
145160 Overlays are used to display tab completion menus and status messages.
161+ For example, a tab-completion menu inserted below the input::
162+
163+ >>> os.path.j ← line 0 (base content)
164+ join ← ScreenOverlay(y=1, insert=True)
165+ junction ← (pushes remaining lines down)
166+ ... ← line 1 (shifted down by 2)
146167 """
147168 y : int
148169 lines : tuple [RenderLine , ...]
@@ -151,6 +172,19 @@ class ScreenOverlay:
151172
152173@dataclass (frozen = True , slots = True )
153174class RenderedScreen :
175+ """The complete screen state: content lines, cursor, and overlays.
176+
177+ ``lines`` holds the base content; ``composed_lines`` is the final
178+ result after overlays (completion menus, messages) are applied::
179+
180+ lines: composed_lines:
181+ ┌──────────────────┐ ┌──────────────────┐
182+ │>>> os.path.j │ │>>> os.path.j │
183+ │... │ ──► │ join │ ← overlay
184+ └──────────────────┘ │... │
185+ └──────────────────┘
186+ """
187+
154188 lines : tuple [RenderLine , ...]
155189 cursor : CursorXY
156190 overlays : tuple [ScreenOverlay , ...] = ()
@@ -173,9 +207,11 @@ def _compose(self) -> tuple[RenderLine, ...]:
173207 "overlays must be sorted by ascending y"
174208 )
175209 if overlay .insert :
210+ # Splice overlay lines in, pushing existing content down.
176211 lines [adjusted_y :adjusted_y ] = overlay .lines
177212 y_offset += len (overlay .lines )
178213 else :
214+ # Replace existing lines at the overlay position.
179215 target_len = adjusted_y + len (overlay .lines )
180216 if len (lines ) < target_len :
181217 lines .extend ([EMPTY_RENDER_LINE ] * (target_len - len (lines )))
@@ -217,6 +253,17 @@ def screen_lines(self) -> tuple[str, ...]:
217253
218254@dataclass (frozen = True , slots = True )
219255class LineDiff :
256+ """The changed region between an old and new version of one screen row.
257+
258+ When the user types ``e`` so the row changes from
259+ ``>>> nam`` to ``>>> name``::
260+
261+ >>> n a m old
262+ >>> n a m e new
263+ ╰─╯
264+ start_cell=7, new_cells=("m","e"), old_cells=("m",)
265+ """
266+
220267 start_cell : int
221268 start_x : int
222269 old_cells : tuple [RenderCell , ...]
@@ -250,10 +297,13 @@ class LineUpdate:
250297 y : int
251298 start_cell : int
252299 start_x : int
300+ """Screen x-coordinate where the update begins. Used for cursor positioning."""
253301 cells : tuple [RenderCell , ...]
254302 char_width : int = 0
255303 clear_eol : bool = False
256304 reset_to_margin : bool = False
305+ """If True, the console must resync the cursor position after writing
306+ (needed when cells contain non-SGR escape sequences that may move the cursor)."""
257307 text : str = field (init = False , default = "" )
258308
259309 def __post_init__ (self ) -> None :
@@ -273,6 +323,14 @@ def render_cells(
273323 cells : Sequence [RenderCell ],
274324 visual_style : str | None = None ,
275325) -> str :
326+ """Render a sequence of cells into a terminal string with SGR escapes.
327+
328+ Tracks the active SGR state to emit resets only when the style
329+ actually changes, minimizing output bytes.
330+
331+ If *visual_style* is given (used by redraw visualization), it is appended
332+ to every cell's style.
333+ """
276334 rendered : list [str ] = []
277335 active_escape = ""
278336 for cell in cells :
@@ -305,6 +363,8 @@ def diff_render_lines(old: RenderLine, new: RenderLine) -> LineDiff | None:
305363 start_x = 0
306364 max_prefix = min (len (old .cells ), len (new .cells ))
307365 while prefix < max_prefix and old .cells [prefix ] == new .cells [prefix ]:
366+ # Stop at any cell with non-SGR controls, since those might affect
367+ # cursor position and must be re-emitted.
308368 if old .cells [prefix ].controls :
309369 break
310370 start_x += old .cells [prefix ].width
0 commit comments