11from __future__ import annotations
22
3- import re
43from functools import lru_cache
54from typing import Callable
65
76from ._cell_widths import CELL_WIDTHS
87
9- # Regex to match sequence of the most common character ranges
10- _is_single_cell_widths = re .compile (
11- "^[\u0020 -\u007e \u00a0 -\u02ff \u0370 -\u0482 \u2500 -\u25FF ]*$"
12- ).match
8+ # Ranges of unicode ordinals that produce a 1-cell wide character
9+ # This is non-exhaustive, but covers most common Western characters
10+ _SINGLE_CELL_UNICODE_RANGES : list [tuple [int , int ]] = [
11+ (0x20 , 0x7E ), # Latin (excluding non-printable)
12+ (0xA0 , 0xAC ),
13+ (0xAE , 0x002FF ),
14+ (0x00370 , 0x00482 ), # Greek / Cyrillic
15+ (0x02500 , 0x025FC ), # Box drawing, box elements, geometric shapes
16+ (0x02800 , 0x028FF ), # Braille
17+ ]
18+
19+ # A set of characters that are a single cell wide
20+ _SINGLE_CELLS = frozenset (
21+ [
22+ character
23+ for _start , _end in _SINGLE_CELL_UNICODE_RANGES
24+ for character in map (chr , range (_start , _end + 1 ))
25+ ]
26+ )
27+
28+ # When called with a string this will return True if all
29+ # characters are single-cell, otherwise False
30+ _is_single_cell_widths : Callable [[str ], bool ] = _SINGLE_CELLS .issuperset
1331
1432
1533@lru_cache (4096 )
@@ -25,9 +43,9 @@ def cached_cell_len(text: str) -> int:
2543 Returns:
2644 int: Get the number of cells required to display text.
2745 """
28- _get_size = get_character_cell_size
29- total_size = sum ( _get_size ( character ) for character in text )
30- return total_size
46+ if _is_single_cell_widths ( text ):
47+ return len ( text )
48+ return sum ( map ( get_character_cell_size , text ))
3149
3250
3351def cell_len (text : str , _cell_len : Callable [[str ], int ] = cached_cell_len ) -> int :
@@ -41,9 +59,9 @@ def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> in
4159 """
4260 if len (text ) < 512 :
4361 return _cell_len (text )
44- _get_size = get_character_cell_size
45- total_size = sum ( _get_size ( character ) for character in text )
46- return total_size
62+ if _is_single_cell_widths ( text ):
63+ return len ( text )
64+ return sum ( map ( get_character_cell_size , text ))
4765
4866
4967@lru_cache (maxsize = 4096 )
@@ -56,20 +74,7 @@ def get_character_cell_size(character: str) -> int:
5674 Returns:
5775 int: Number of cells (0, 1 or 2) occupied by that character.
5876 """
59- return _get_codepoint_cell_size (ord (character ))
60-
61-
62- @lru_cache (maxsize = 4096 )
63- def _get_codepoint_cell_size (codepoint : int ) -> int :
64- """Get the cell size of a character.
65-
66- Args:
67- codepoint (int): Codepoint of a character.
68-
69- Returns:
70- int: Number of cells (0, 1 or 2) occupied by that character.
71- """
72-
77+ codepoint = ord (character )
7378 _table = CELL_WIDTHS
7479 lower_bound = 0
7580 upper_bound = len (_table ) - 1
0 commit comments