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
6 changes: 3 additions & 3 deletions rich/cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

from functools import lru_cache
from operator import itemgetter
from typing import Callable, NamedTuple, Sequence, Tuple
from typing import Callable, NamedTuple, Sequence

from rich._unicode_data import load as load_cell_table

CellSpan = Tuple[int, int, int]
CellSpan = tuple[int, int, int]

_span_get_cell_len = itemgetter(2)

Expand Down Expand Up @@ -160,7 +160,7 @@ def _cell_len(text: str, unicode_version: str) -> int:

def split_graphemes(
text: str, unicode_version: str = "auto"
) -> "tuple[list[CellSpan], int]":
) -> tuple[list[CellSpan], int]:
"""Divide text into spans that define a single grapheme, and additionally return the cell length of the whole string.

The returned spans will cover every index in the string, with no gaps. It is possible for some graphemes to have a cell length of zero.
Expand Down
29 changes: 29 additions & 0 deletions tests/test_cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
cell_len,
chop_cells,
get_character_cell_size,
set_cell_size,
split_graphemes,
split_text,
)
Expand Down Expand Up @@ -252,3 +253,31 @@ def test_non_printable():
for ordinal in range(31):
character = chr(ordinal)
assert cell_len(character) == 0


def test_cell_len_edge_cases() -> None:
"""cell_len for empty strings, ZWJ sequences, and mixed CJK."""
assert cell_len("") == 0
assert cell_len("👩\u200d🔧") == 2
assert cell_len("👩\u200d👩\u200d👧\u200d👧") == 2
assert cell_len("あ1り2") == 6


def test_set_cell_size_edge_cases() -> None:
"""set_cell_size for empty strings and exact-boundary cases."""
assert set_cell_size("", 0) == ""
assert set_cell_size("", 3) == " "
assert set_cell_size("foo", 3) == "foo"
assert set_cell_size("あい", 4) == "あい"
assert set_cell_size("あ1り2", 6) == "あ1り2"
assert set_cell_size("👩\u200d🔧", 2) == "👩\u200d🔧"
assert set_cell_size("👩\u200d🔧", 1) == " "


def test_chop_cells_edge_cases() -> None:
"""chop_cells for empty strings, ZWJ sequences, and mixed CJK boundaries."""
assert chop_cells("", 3) == []
assert chop_cells("👩\u200d🔧", 2) == ["👩\u200d🔧"]
assert chop_cells("👩\u200d🔧👩\u200d🔧", 2) == ["👩\u200d🔧", "👩\u200d🔧"]
assert chop_cells("あ1り2", 3) == ["あ1", "り2"]
assert chop_cells("あ1り2", 6) == ["あ1り2"]