Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/rich/cells.py: 19%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

77 statements  

1from __future__ import annotations 

2 

3import re 

4from functools import lru_cache 

5from typing import Callable 

6 

7from ._cell_widths import CELL_WIDTHS 

8 

9# Regex to match sequence of the most common character ranges 

10_is_single_cell_widths = re.compile("^[\u0020-\u006f\u00a0\u02ff\u0370-\u0482]*$").match 

11 

12 

13@lru_cache(4096) 

14def cached_cell_len(text: str) -> int: 

15 """Get the number of cells required to display text. 

16 

17 This method always caches, which may use up a lot of memory. It is recommended to use 

18 `cell_len` over this method. 

19 

20 Args: 

21 text (str): Text to display. 

22 

23 Returns: 

24 int: Get the number of cells required to display text. 

25 """ 

26 _get_size = get_character_cell_size 

27 total_size = sum(_get_size(character) for character in text) 

28 return total_size 

29 

30 

31def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> int: 

32 """Get the number of cells required to display text. 

33 

34 Args: 

35 text (str): Text to display. 

36 

37 Returns: 

38 int: Get the number of cells required to display text. 

39 """ 

40 if len(text) < 512: 

41 return _cell_len(text) 

42 _get_size = get_character_cell_size 

43 total_size = sum(_get_size(character) for character in text) 

44 return total_size 

45 

46 

47@lru_cache(maxsize=4096) 

48def get_character_cell_size(character: str) -> int: 

49 """Get the cell size of a character. 

50 

51 Args: 

52 character (str): A single character. 

53 

54 Returns: 

55 int: Number of cells (0, 1 or 2) occupied by that character. 

56 """ 

57 return _get_codepoint_cell_size(ord(character)) 

58 

59 

60@lru_cache(maxsize=4096) 

61def _get_codepoint_cell_size(codepoint: int) -> int: 

62 """Get the cell size of a character. 

63 

64 Args: 

65 codepoint (int): Codepoint of a character. 

66 

67 Returns: 

68 int: Number of cells (0, 1 or 2) occupied by that character. 

69 """ 

70 

71 _table = CELL_WIDTHS 

72 lower_bound = 0 

73 upper_bound = len(_table) - 1 

74 index = (lower_bound + upper_bound) // 2 

75 while True: 

76 start, end, width = _table[index] 

77 if codepoint < start: 

78 upper_bound = index - 1 

79 elif codepoint > end: 

80 lower_bound = index + 1 

81 else: 

82 return 0 if width == -1 else width 

83 if upper_bound < lower_bound: 

84 break 

85 index = (lower_bound + upper_bound) // 2 

86 return 1 

87 

88 

89def set_cell_size(text: str, total: int) -> str: 

90 """Set the length of a string to fit within given number of cells.""" 

91 

92 if _is_single_cell_widths(text): 

93 size = len(text) 

94 if size < total: 

95 return text + " " * (total - size) 

96 return text[:total] 

97 

98 if total <= 0: 

99 return "" 

100 cell_size = cell_len(text) 

101 if cell_size == total: 

102 return text 

103 if cell_size < total: 

104 return text + " " * (total - cell_size) 

105 

106 start = 0 

107 end = len(text) 

108 

109 # Binary search until we find the right size 

110 while True: 

111 pos = (start + end) // 2 

112 before = text[: pos + 1] 

113 before_len = cell_len(before) 

114 if before_len == total + 1 and cell_len(before[-1]) == 2: 

115 return before[:-1] + " " 

116 if before_len == total: 

117 return before 

118 if before_len > total: 

119 end = pos 

120 else: 

121 start = pos 

122 

123 

124def chop_cells( 

125 text: str, 

126 width: int, 

127) -> list[str]: 

128 """Split text into lines such that each line fits within the available (cell) width. 

129 

130 Args: 

131 text: The text to fold such that it fits in the given width. 

132 width: The width available (number of cells). 

133 

134 Returns: 

135 A list of strings such that each string in the list has cell width 

136 less than or equal to the available width. 

137 """ 

138 _get_character_cell_size = get_character_cell_size 

139 lines: list[list[str]] = [[]] 

140 

141 append_new_line = lines.append 

142 append_to_last_line = lines[-1].append 

143 

144 total_width = 0 

145 

146 for character in text: 

147 cell_width = _get_character_cell_size(character) 

148 char_doesnt_fit = total_width + cell_width > width 

149 

150 if char_doesnt_fit: 

151 append_new_line([character]) 

152 append_to_last_line = lines[-1].append 

153 total_width = cell_width 

154 else: 

155 append_to_last_line(character) 

156 total_width += cell_width 

157 

158 return ["".join(line) for line in lines] 

159 

160 

161if __name__ == "__main__": # pragma: no cover 

162 print(get_character_cell_size("😽")) 

163 for line in chop_cells("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", 8): 

164 print(line) 

165 for n in range(80, 1, -1): 

166 print(set_cell_size("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", n) + "|") 

167 print("x" * n)