Coverage for /run/media/veganeco/water/status600.com/pypi/reptilian_climates/modules_series_2/ships/fields/gardens_pip/rich/control.py: 58%

1 import sys

2 import time

3 from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union

4

5 if sys.version_info >= (3, 8):

6 from typing import Final

7 else:

8 from typing_extensions import Final # pragma: no cover

9

10 from .segment import ControlCode, ControlType, Segment

11

12 if TYPE_CHECKING:

13 from .console import Console, ConsoleOptions, RenderResult

14

15 STRIP_CONTROL_CODES: Final = [

16 7, # Bell

17 8, # Backspace

18 11, # Vertical tab

19 12, # Form feed

20 13, # Carriage return

21]

22 _CONTROL_STRIP_TRANSLATE: Final = {

23 _codepoint: None for _codepoint in STRIP_CONTROL_CODES

24}

25

26 CONTROL_ESCAPE: Final = {

27 7: "\\a",

28 8: "\\b",

29 11: "\\v",

30 12: "\\f",

31 13: "\\r",

32}

33

34 CONTROL_CODES_FORMAT: Dict[int, Callable[..., str]] = {

35 ControlType.BELL: lambda: "\x07",

36 ControlType.CARRIAGE_RETURN: lambda: "\r",

37 ControlType.HOME: lambda: "\x1b[H",

38 ControlType.CLEAR: lambda: "\x1b[2J",

39 ControlType.ENABLE_ALT_SCREEN: lambda: "\x1b[?1049h",

40 ControlType.DISABLE_ALT_SCREEN: lambda: "\x1b[?1049l",

41 ControlType.SHOW_CURSOR: lambda: "\x1b[?25h",

42 ControlType.HIDE_CURSOR: lambda: "\x1b[?25l",

43 ControlType.CURSOR_UP: lambda param: f"\x1b[{param}A",

44 ControlType.CURSOR_DOWN: lambda param: f"\x1b[{param}B",

45 ControlType.CURSOR_FORWARD: lambda param: f"\x1b[{param}C",

46 ControlType.CURSOR_BACKWARD: lambda param: f"\x1b[{param}D",

47 ControlType.CURSOR_MOVE_TO_COLUMN: lambda param: f"\x1b[{param+1}G",

48 ControlType.ERASE_IN_LINE: lambda param: f"\x1b[{param}K",

49 ControlType.CURSOR_MOVE_TO: lambda x, y: f"\x1b[{y+1};{x+1}H",

50 ControlType.SET_WINDOW_TITLE: lambda title: f"\x1b]0;{title}\x07",

51}

52

53

54 class Control:

55 """A renderable that inserts a control code (non printable but may move cursor).

56

57 Args:

58 *codes (str): Positional arguments are either a :class:`~rich.segment.ControlType` enum or a

59 tuple of ControlType and an integer parameter

60 """

61

62 __slots__ = ["segment"]

63

64 def __init__(self, *codes: Union[ControlType, ControlCode]) -> None:

65 control_codes: List[ControlCode] = [

66 (code,) if isinstance(code, ControlType) else code for code in codes

67 ]

68 _format_map = CONTROL_CODES_FORMAT

69 rendered_codes = "".join(

70 _format_map[code](*parameters) for code, *parameters in control_codes

71 )

72 self.segment = Segment(rendered_codes, None, control_codes)

73

74 @classmethod

75 def bell(cls) -> "Control":

76 """Ring the 'bell'."""

77 return cls(ControlType.BELL)

78

79 @classmethod

80 def home(cls) -> "Control":

81 """Move cursor to 'home' position."""

82 return cls(ControlType.HOME)

83

84 @classmethod

85 def move(cls, x: int = 0, y: int = 0) -> "Control":

86 """Move cursor relative to current position.

87

88 Args:

89 x (int): X offset.

90 y (int): Y offset.

91

92 Returns:

93 ~Control: Control object.

94

95 """

96

97 def get_codes() -> Iterable[ControlCode]:

98 control = ControlType

99 if x:

100 yield (

101 control.CURSOR_FORWARD if x > 0 else control.CURSOR_BACKWARD,

102 abs(x),

103 )

104 if y:

105 yield (

106 control.CURSOR_DOWN if y > 0 else control.CURSOR_UP,

107 abs(y),

108 )

109

110 control = cls(*get_codes())

111 return control

112

113 @classmethod

114 def move_to_column(cls, x: int, y: int = 0) -> "Control":

115 """Move to the given column, optionally add offset to row.

116

117 Returns:

118 x (int): absolute x (column)

119 y (int): optional y offset (row)

120

121 Returns:

122 ~Control: Control object.

123 """

124

125 return (

126 cls(

127 (ControlType.CURSOR_MOVE_TO_COLUMN, x),

128 (

129 ControlType.CURSOR_DOWN if y > 0 else ControlType.CURSOR_UP,

130 abs(y),

131 ),

132 )

133 if y

134 else cls((ControlType.CURSOR_MOVE_TO_COLUMN, x))

135 )

136

137 @classmethod

138 def move_to(cls, x: int, y: int) -> "Control":

139 """Move cursor to absolute position.

140

141 Args:

142 x (int): x offset (column)

143 y (int): y offset (row)

144

145 Returns:

146 ~Control: Control object.

147 """

148 return cls((ControlType.CURSOR_MOVE_TO, x, y))

149

150 @classmethod

151 def clear(cls) -> "Control":

152 """Clear the screen."""

153 return cls(ControlType.CLEAR)

154

155 @classmethod

156 def show_cursor(cls, show: bool) -> "Control":

157 """Show or hide the cursor."""

158 return cls(ControlType.SHOW_CURSOR if show else ControlType.HIDE_CURSOR)

159

160 @classmethod

161 def alt_screen(cls, enable: bool) -> "Control":

162 """Enable or disable alt screen."""

163 if enable:

164 return cls(ControlType.ENABLE_ALT_SCREEN, ControlType.HOME)

165 else:

166 return cls(ControlType.DISABLE_ALT_SCREEN)

167

168 @classmethod

169 def title(cls, title: str) -> "Control":

170 """Set the terminal window title

171

172 Args:

173 title (str): The new terminal window title

174 """

175 return cls((ControlType.SET_WINDOW_TITLE, title))

176

177 def __str__(self) -> str:

178 return self.segment.text

179

180 def __rich_console__(

181 self, console: "Console", options: "ConsoleOptions"

182 ) -> "RenderResult":

183 if self.segment.text:

184 yield self.segment

185

186

187 def strip_control_codes(

188 text: str, _translate_table: Dict[int, None] = _CONTROL_STRIP_TRANSLATE

189) -> str:

190 """Remove control codes from text.

191

192 Args:

193 text (str): A string possibly contain control codes.

194

195 Returns:

196 str: String with control codes removed.

197 """

198 return text.translate(_translate_table)

199

200

201 def escape_control_codes(

202 text: str,

203 _translate_table: Dict[int, str] = CONTROL_ESCAPE,

204) -> str:

205 """Replace control codes with their "escaped" equivalent in the given text.

206 (e.g. "\b" becomes "\\b")

207

208 Args:

209 text (str): A string possibly containing control codes.

210

211 Returns:

212 str: String with control codes replaced with their escaped version.

213 """

214 return text.translate(_translate_table)

215

216

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

218 from rich.console import Console

219

220 console = Console()

221 console.print("Look at the title of your terminal window ^")

222 # console.print(Control((ControlType.SET_WINDOW_TITLE, "Hello, world!")))

223 for i in range(10):

224 console.set_window_title("🚀 Loading" + "." * i)

225 time.sleep(0.5)