diff --git a/CHANGELOG.md b/CHANGELOG.md index 13ef9279ab..b54b6e3811 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `cells.cell_len` now has a `unicode_version` parameter (that you probably should never change) https://github.com/Textualize/rich/pull/3930 +- Live will not write a new line if there was nothing rendered ## [14.2.0] - 2025-10-09 diff --git a/rich/live.py b/rich/live.py index cc3a39bd57..2fd893b008 100644 --- a/rich/live.py +++ b/rich/live.py @@ -166,7 +166,11 @@ def stop(self) -> None: finally: self._disable_redirect_io() self.console.pop_render_hook() - if not self._alt_screen and self.console.is_terminal: + if ( + not self._alt_screen + and self.console.is_terminal + and self._live_render.last_render_height + ): self.console.line() self.console.show_cursor(True) if self._alt_screen: diff --git a/rich/live_render.py b/rich/live_render.py index d3da5111d8..e7ec970a78 100644 --- a/rich/live_render.py +++ b/rich/live_render.py @@ -1,5 +1,4 @@ -from typing import Optional, Tuple, Literal - +from typing import Literal, Optional, Tuple from ._loop import loop_last from .console import Console, ConsoleOptions, RenderableType, RenderResult @@ -30,6 +29,17 @@ def __init__( self.vertical_overflow = vertical_overflow self._shape: Optional[Tuple[int, int]] = None + @property + def last_render_height(self) -> int: + """The number of lines in the last render (may be 0 if nothing was rendered). + + Returns: + Height in lines + """ + if self._shape is None: + return 0 + return self._shape[1] + def set_renderable(self, renderable: RenderableType) -> None: """Set a new renderable. diff --git a/tests/test_live.py b/tests/test_live.py index 16658b44fa..15a4e43996 100644 --- a/tests/test_live.py +++ b/tests/test_live.py @@ -167,3 +167,21 @@ def test_live_screen() -> None: print(repr(result)) expected = "\x1b[?1049h\x1b[H\x1b[?25l\x1b[Hfoo \n \n \n \n \x1b[Hfoo \n \n \n \n \x1b[?25h\x1b[?1049l" assert result == expected + + +def test_live_empty() -> None: + """Regression test for https://github.com/Textualize/rich/issues/3796 + + No NL should be written if there was nothing rendered. + """ + + from rich.console import Group + + console = create_capture_console(width=20, height=5) + console.begin_capture() + with Live(Group(), console=console, transient=True): + pass + result = console.end_capture() + print(repr(result)) + assert "\n" not in result + assert result == "\x1b[?25l\r\x1b[2K\x1b[?25h\r"