Skip to content

Commit 61e39f1

Browse files
committed
Preserve style metadata in _pyrepl cells
Thread StyleRef and styled content fragments through render and reader paths. Keep semantic color information attached to cells during redraw diffs.
1 parent 1b1375a commit 61e39f1

File tree

8 files changed

+264
-263
lines changed

8 files changed

+264
-263
lines changed

Lib/_pyrepl/content.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
from dataclasses import dataclass
44

5-
from .utils import ColorSpan, disp_str, unbracket, wlen
5+
from .render import StyleRef
6+
from .utils import ColorSpan, THEME, iter_display_chars, unbracket, wlen
67

78

89
@dataclass(frozen=True, slots=True)
910
class ContentFragment:
1011
text: str
1112
width: int
13+
style: StyleRef = StyleRef()
1214

1315

1416
@dataclass(frozen=True, slots=True)
@@ -58,8 +60,14 @@ def build_body_fragments(
5860
colors: list[ColorSpan] | None,
5961
start_index: int,
6062
) -> tuple[ContentFragment, ...]:
61-
chars, char_widths = disp_str(buffer, colors, start_index)
63+
theme = THEME()
6264
return tuple(
63-
ContentFragment(text, width)
64-
for text, width in zip(chars, char_widths)
65+
ContentFragment(
66+
styled_char.text,
67+
styled_char.width,
68+
StyleRef.from_tag(styled_char.tag, theme[styled_char.tag])
69+
if styled_char.tag else
70+
StyleRef(),
71+
)
72+
for styled_char in iter_display_chars(buffer, colors, start_index)
6573
)

Lib/_pyrepl/reader.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@
2929

3030
from . import commands, console, input
3131
from .content import (
32+
ContentFragment,
3233
ContentLine,
33-
PromptContent,
3434
SourceLine,
3535
build_body_fragments,
3636
process_prompt as build_prompt_content,
3737
)
3838
from .layout import LayoutMap, LayoutResult, LayoutRow, WrappedRow, layout_content_lines
39-
from .render import RenderCell, RenderLine, RenderedScreen
40-
from .utils import wlen, gen_colors, THEME
39+
from .render import RenderCell, RenderLine, RenderedScreen, StyleRef
40+
from .utils import ANSI_ESCAPE_SEQUENCE, wlen, gen_colors
4141
from .trace import trace
4242

4343

@@ -431,8 +431,7 @@ def _render_wrapped_rows(
431431
return [
432432
self._render_line(
433433
row.prompt_text,
434-
[fragment.text for fragment in row.fragments],
435-
[fragment.width for fragment in row.fragments],
434+
list(row.fragments),
436435
row.suffix,
437436
)
438437
for row in wrapped_rows
@@ -446,22 +445,33 @@ def _render_message_lines(self) -> list[RenderLine]:
446445
for message_line in self.msg.split("\n")
447446
]
448447

449-
@staticmethod
450448
def _render_line(
449+
self,
451450
prefix: str,
452-
chars: list[str],
453-
char_widths: list[int],
451+
fragments: list[ContentFragment],
454452
suffix: str = "",
455453
) -> RenderLine:
456454
cells: list[RenderCell] = []
457455
if prefix:
458-
cells.extend(RenderLine.from_rendered_text(prefix).cells)
456+
prompt_cells = list(RenderLine.from_rendered_text(prefix).cells)
457+
if self.can_colorize and prompt_cells and not ANSI_ESCAPE_SEQUENCE.search(prefix):
458+
prompt_style = StyleRef.from_tag("prompt")
459+
prompt_cells = [
460+
RenderCell(
461+
cell.text,
462+
cell.width,
463+
style=prompt_style if cell.text else cell.style,
464+
controls=cell.controls,
465+
)
466+
for cell in prompt_cells
467+
]
468+
cells.extend(prompt_cells)
459469
cells.extend(
460-
RenderCell.from_rendered_text(text, width)
461-
for text, width in zip(chars, char_widths)
470+
RenderCell(fragment.text, fragment.width, style=fragment.style)
471+
for fragment in fragments
462472
)
463473
if suffix:
464-
cells.append(RenderCell.from_rendered_text(suffix, wlen(suffix)))
474+
cells.extend(RenderLine.from_rendered_text(suffix).cells)
465475
return RenderLine.from_cells(cells)
466476

467477
@staticmethod
@@ -564,10 +574,6 @@ def get_prompt(self, lineno: int, cursor_on_line: bool) -> str:
564574
prompt = self.ps3
565575
else:
566576
prompt = self.ps1
567-
568-
if self.can_colorize:
569-
t = THEME()
570-
prompt = f"{t.prompt}{prompt}{t.reset}"
571577
return prompt
572578

573579
def push_input_trans(self, itrans: input.KeymapTranslator) -> None:

0 commit comments

Comments
 (0)