1
1
from __future__ import annotations
2
2
3
- import re
4
3
from functools import lru_cache
5
4
from typing import Callable
6
5
7
6
from ._cell_widths import CELL_WIDTHS
8
7
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
13
31
14
32
15
33
@lru_cache (4096 )
@@ -25,9 +43,9 @@ def cached_cell_len(text: str) -> int:
25
43
Returns:
26
44
int: Get the number of cells required to display text.
27
45
"""
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 ))
31
49
32
50
33
51
def 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
41
59
"""
42
60
if len (text ) < 512 :
43
61
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 ))
47
65
48
66
49
67
@lru_cache (maxsize = 4096 )
@@ -56,20 +74,7 @@ def get_character_cell_size(character: str) -> int:
56
74
Returns:
57
75
int: Number of cells (0, 1 or 2) occupied by that character.
58
76
"""
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 )
73
78
_table = CELL_WIDTHS
74
79
lower_bound = 0
75
80
upper_bound = len (_table ) - 1
0 commit comments