Skip to content
Open
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- Added `CURSOR_PREV_LINE` (`\e[nF`) and `CURSOR_PREV_LINE` (`\e[nG`) to `ControlType`.
- Changed `Console.render_lines` to include `ControlType.ERASE_IN_LINE` at the end of each line.
- Changed `Console.render_lines` to include a `ControlType.CLEAR` sequence at the end of the last line, if present.
- Changed `LiveRender.position_cursor`'s cursor-moving logic and removed line clearing from there.


To achieve this, I had to introduce `\e[nF` (move to the start of n lines up) in `ControlType` and I also included the not-used `\e[nG` (move to the start of n lines down) for completeness. I also had to modify `ControlType.CLEAR` to accept a parameter and backfixed any cases where it was already used.

## [14.2.0] - 2025-10-09

### Changed
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,4 @@ The following people have contributed to the development of Rich:
- [Jonathan Helmus](https://github.com/jjhelmus)
- [Brandon Capener](https://github.com/bcapener)
- [Alex Zheng](https://github.com/alexzheng111)
- [Dimitris Krestos](https://github.com/dimitriskres)
10 changes: 8 additions & 2 deletions rich/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
from .region import Region
from .scope import render_scope
from .screen import Screen
from .segment import Segment
from .segment import Segment, ControlType
from .style import Style, StyleType
from .styled import Styled
from .terminal_theme import DEFAULT_TERMINAL_THEME, SVG_EXPORT_THEME, TerminalTheme
Expand Down Expand Up @@ -1395,6 +1395,11 @@ def render_lines(
render_height,
)
)

for line in lines:
erase_control = Control((ControlType.ERASE_IN_LINE, 0))
line.append(erase_control.segment)

if render_options.height is not None:
extra_lines = render_options.height - len(lines)
if extra_lines > 0:
Expand Down Expand Up @@ -2043,6 +2048,8 @@ def _write_buffer(self) -> None:
"""Write the buffer to the output file."""

with self._lock:
self._buffer.append(Control((ControlType.CLEAR, 0)).segment)

if self.record and not self._buffer_index:
with self._record_buffer_lock:
self._record_buffer.extend(self._buffer[:])
Expand All @@ -2062,7 +2069,6 @@ def _write_buffer(self) -> None:
use_legacy_windows_render = (
fileno in _STD_STREAMS_OUTPUT
)

if use_legacy_windows_render:
from rich._win32_console import LegacyWindowsTerm
from rich._windows_renderer import legacy_windows_render
Expand Down
6 changes: 4 additions & 2 deletions rich/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
ControlType.BELL: lambda: "\x07",
ControlType.CARRIAGE_RETURN: lambda: "\r",
ControlType.HOME: lambda: "\x1b[H",
ControlType.CLEAR: lambda: "\x1b[2J",
ControlType.CLEAR: lambda param: f"\x1b[{param}J",
ControlType.ENABLE_ALT_SCREEN: lambda: "\x1b[?1049h",
ControlType.DISABLE_ALT_SCREEN: lambda: "\x1b[?1049l",
ControlType.SHOW_CURSOR: lambda: "\x1b[?25h",
ControlType.HIDE_CURSOR: lambda: "\x1b[?25l",
ControlType.CURSOR_UP: lambda param: f"\x1b[{param}A",
ControlType.CURSOR_DOWN: lambda param: f"\x1b[{param}B",
ControlType.CURSOR_PREV_LINE: lambda param: f"\x1b[{param}F",
ControlType.CURSOR_NEXT_LINE: lambda param: f"\x1b[{param}E",
ControlType.CURSOR_FORWARD: lambda param: f"\x1b[{param}C",
ControlType.CURSOR_BACKWARD: lambda param: f"\x1b[{param}D",
ControlType.CURSOR_MOVE_TO_COLUMN: lambda param: f"\x1b[{param+1}G",
Expand Down Expand Up @@ -144,7 +146,7 @@ def move_to(cls, x: int, y: int) -> "Control":
@classmethod
def clear(cls) -> "Control":
"""Clear the screen."""
return cls(ControlType.CLEAR)
return cls((ControlType.CLEAR, 2))

@classmethod
def show_cursor(cls, show: bool) -> "Control":
Expand Down
14 changes: 3 additions & 11 deletions rich/live_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,9 @@ def position_cursor(self) -> Control:
"""
if self._shape is not None:
_, height = self._shape
return Control(
ControlType.CARRIAGE_RETURN,
(ControlType.ERASE_IN_LINE, 2),
*(
(
(ControlType.CURSOR_UP, 1),
(ControlType.ERASE_IN_LINE, 2),
)
* (height - 1)
)
)
if height > 1:
return Control((ControlType.CURSOR_PREV_LINE, height - 1))
return Control(ControlType.CARRIAGE_RETURN)
return Control()

def restore_cursor(self) -> Control:
Expand Down
2 changes: 2 additions & 0 deletions rich/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class ControlType(IntEnum):
CURSOR_MOVE_TO = 14
ERASE_IN_LINE = 15
SET_WINDOW_TITLE = 16
CURSOR_PREV_LINE = 17
CURSOR_NEXT_LINE = 18


ControlCode = Union[
Expand Down