Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed extraneous blank line on non-interactive disabled `Progress` https://github.com/Textualize/rich/pull/3905
- Fixed extra padding on first cell in columns https://github.com/Textualize/rich/pull/3935
- Fixed trailing whitespace removed when soft_wrap=True https://github.com/Textualize/rich/pull/3937
- Fixed style new-lines when soft_wrap = True and a print style is set https://github.com/Textualize/rich/pull/3938

### Added

- Added support for some multi-codepopint glyphs (will fix alignment issues for these characters) https://github.com/Textualize/rich/pull/3930
- Added support for `UNICODE_VERSION` environment variable https://github.com/Textualize/rich/pull/3930
- Added `last_render_height` property to LiveRender https://github.com/Textualize/rich/pull/3934
- Expose locals_max_depth and locals_overflow in traceback.install https://github.com/Textualize/rich/pull/3906/
- Added `Segment.split_lines_terminator` https://github.com/Textualize/rich/pull/3938

### Changed

Expand Down
14 changes: 9 additions & 5 deletions rich/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -1723,12 +1723,16 @@ def print(
for renderable in renderables:
extend(render(renderable, render_options))
else:
render_style = self.get_style(style)
new_line = Segment.line()
for renderable in renderables:
extend(
Segment.apply_style(
render(renderable, render_options), self.get_style(style)
)
)
for line, add_new_line in Segment.split_lines_terminator(
render(renderable, render_options)
):
extend(Segment.apply_style(line, render_style))
if add_new_line:
new_segments.append(new_line)

if new_line_start:
if (
len("".join(segment.text for segment in new_segments).splitlines())
Expand Down
31 changes: 31 additions & 0 deletions rich/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,37 @@ def split_lines(cls, segments: Iterable["Segment"]) -> Iterable[List["Segment"]]
if line:
yield line

@classmethod
def split_lines_terminator(
cls, segments: Iterable["Segment"]
) -> Iterable[Tuple[List["Segment"], bool]]:
"""Split a sequence of segments in to a list of lines and a boolean to indicate if there was a new line.

Args:
segments (Iterable[Segment]): Segments potentially containing line feeds.

Yields:
Iterable[List[Segment]]: Iterable of segment lists, one per line.
"""
line: List[Segment] = []
append = line.append

for segment in segments:
if "\n" in segment.text and not segment.control:
text, style, _ = segment
while text:
_text, new_line, text = text.partition("\n")
if _text:
append(cls(_text, style))
if new_line:
yield (line, True)
line = []
append = line.append
else:
append(segment)
if line:
yield (line, False)

@classmethod
def split_and_crop_lines(
cls,
Expand Down
15 changes: 15 additions & 0 deletions tests/test_segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ def test_split_lines():
assert list(Segment.split_lines(lines)) == [[Segment("Hello")], [Segment("World")]]


def test_split_lines_terminator():
lines = [Segment("Hello\nWorld")]
assert list(Segment.split_lines_terminator(lines)) == [
([Segment("Hello")], True),
([Segment("World")], False),
]


def test_split_lines_terminator_single_line():
lines = [Segment("Hello")]
assert list(Segment.split_lines_terminator(lines)) == [
([Segment("Hello")], False),
]


def test_split_and_crop_lines():
assert list(
Segment.split_and_crop_lines([Segment("Hello\nWorld!\n"), Segment("foo")], 4)
Expand Down
17 changes: 17 additions & 0 deletions tests/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -1110,3 +1110,20 @@ def test_soft_wrap() -> None:
print(repr(output))
expected = "\x1b[37;44m Hello World \x1b[0m\n"
assert output == expected


def test_soft_wrap_styled() -> None:
"""Regression test for https://github.com/Textualize/rich/issues/3838

If soft_wrap is True and a style is set, we don't want to style the new lines.
"""
console = Console(color_system="standard", width=80, force_terminal=True)
with console.capture() as capture:
console.print("soft wrap is on", style="blue on white", soft_wrap=True)
console.print("Next line")

output = capture.get()
print(repr(output))
# Background is reset before \n
expected = "\x1b[34;47msoft wrap is on\x1b[0m\nNext line\n"
assert output == expected
Loading