xulbux 1.5.5__py3-none-any.whl → 1.5.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of xulbux might be problematic. Click here for more details.
- xulbux/__init__.py +20 -10
- xulbux/{__help__.py → _cli_.py} +15 -37
- xulbux/_consts_.py +108 -111
- xulbux/xx_cmd.py +245 -104
- xulbux/xx_code.py +28 -25
- xulbux/xx_color.py +330 -182
- xulbux/xx_data.py +214 -90
- xulbux/xx_env_vars.py +36 -23
- xulbux/xx_file.py +20 -14
- xulbux/xx_format_codes.py +154 -88
- xulbux/xx_json.py +36 -16
- xulbux/xx_path.py +38 -23
- xulbux/xx_regex.py +44 -27
- xulbux/xx_string.py +75 -47
- xulbux/xx_system.py +37 -26
- {xulbux-1.5.5.dist-info → xulbux-1.5.7.dist-info}/METADATA +14 -10
- xulbux-1.5.7.dist-info/RECORD +20 -0
- {xulbux-1.5.5.dist-info → xulbux-1.5.7.dist-info}/WHEEL +1 -1
- xulbux-1.5.7.dist-info/entry_points.txt +3 -0
- xulbux-1.5.5.dist-info/RECORD +0 -20
- xulbux-1.5.5.dist-info/entry_points.txt +0 -2
- {xulbux-1.5.5.dist-info → xulbux-1.5.7.dist-info}/licenses/LICENSE +0 -0
xulbux/xx_cmd.py
CHANGED
|
@@ -20,14 +20,12 @@ You can also use special formatting codes directly inside the log message to cha
|
|
|
20
20
|
For more detailed information about formatting codes, see the the `xx_format_codes` description.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
|
|
24
23
|
from ._consts_ import DEFAULT, CHARS
|
|
25
24
|
from .xx_format_codes import *
|
|
26
25
|
from .xx_string import *
|
|
27
26
|
from .xx_color import *
|
|
28
27
|
|
|
29
28
|
from contextlib import suppress
|
|
30
|
-
import subprocess as _subprocess
|
|
31
29
|
import pyperclip as _pyperclip
|
|
32
30
|
import keyboard as _keyboard
|
|
33
31
|
import getpass as _getpass
|
|
@@ -38,12 +36,10 @@ import sys as _sys
|
|
|
38
36
|
import os as _os
|
|
39
37
|
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
43
39
|
class Cmd:
|
|
44
40
|
|
|
45
41
|
@staticmethod
|
|
46
|
-
def get_args(find_args:dict) -> dict:
|
|
42
|
+
def get_args(find_args: dict) -> dict[str, dict[str, any]]:
|
|
47
43
|
args = _sys.argv[1:]
|
|
48
44
|
results = {}
|
|
49
45
|
for arg_key, arg_group in find_args.items():
|
|
@@ -53,47 +49,71 @@ class Cmd:
|
|
|
53
49
|
if arg in args:
|
|
54
50
|
exists = True
|
|
55
51
|
arg_index = args.index(arg)
|
|
56
|
-
if arg_index + 1 < len(args) and not args[arg_index + 1].startswith(
|
|
52
|
+
if arg_index + 1 < len(args) and not args[arg_index + 1].startswith("-"):
|
|
57
53
|
value = String.to_type(args[arg_index + 1])
|
|
58
54
|
break
|
|
59
|
-
results[arg_key] = {
|
|
55
|
+
results[arg_key] = {"exists": exists, "value": value}
|
|
60
56
|
return results
|
|
61
57
|
|
|
62
|
-
def w() -> int:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def
|
|
58
|
+
def w() -> int:
|
|
59
|
+
return getattr(_shutil.get_terminal_size(), "columns", 80)
|
|
60
|
+
|
|
61
|
+
def h() -> int:
|
|
62
|
+
return getattr(_shutil.get_terminal_size(), "lines", 24)
|
|
63
|
+
|
|
64
|
+
def wh() -> tuple[int, int]:
|
|
65
|
+
return Cmd.w(), Cmd.h()
|
|
66
|
+
|
|
67
|
+
def user() -> str:
|
|
68
|
+
return _os.getenv("USER") or _os.getenv("USERNAME") or _getpass.getuser()
|
|
66
69
|
|
|
67
|
-
@staticmethod
|
|
68
70
|
def is_admin() -> bool:
|
|
69
71
|
try:
|
|
70
|
-
if _os.name ==
|
|
72
|
+
if _os.name == "nt":
|
|
71
73
|
return _ctypes.windll.shell32.IsUserAnAdmin() != 0
|
|
72
|
-
elif _os.name ==
|
|
74
|
+
elif _os.name == "posix":
|
|
73
75
|
return _os.geteuid() == 0
|
|
74
76
|
else:
|
|
75
77
|
return False
|
|
76
|
-
except:
|
|
78
|
+
except Exception:
|
|
77
79
|
return False
|
|
78
80
|
|
|
79
81
|
@staticmethod
|
|
80
|
-
def pause_exit(
|
|
82
|
+
def pause_exit(
|
|
83
|
+
pause: bool = False,
|
|
84
|
+
exit: bool = False,
|
|
85
|
+
prompt: object = "",
|
|
86
|
+
exit_code: int = 0,
|
|
87
|
+
reset_ansi: bool = False,
|
|
88
|
+
) -> None:
|
|
81
89
|
"""Will print the `last_prompt` and then pause the program if `pause` is set<br>
|
|
82
|
-
to `True` and after the pause, exit the program if `exit` is set to `True`.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if
|
|
86
|
-
|
|
90
|
+
to `True` and after the pause, exit the program if `exit` is set to `True`.
|
|
91
|
+
"""
|
|
92
|
+
print(prompt, end="", flush=True)
|
|
93
|
+
if reset_ansi:
|
|
94
|
+
FormatCodes.print("[_]", end="")
|
|
95
|
+
if pause:
|
|
96
|
+
_keyboard.read_event()
|
|
97
|
+
if exit:
|
|
98
|
+
_sys.exit(exit_code)
|
|
87
99
|
|
|
88
|
-
@staticmethod
|
|
89
100
|
def cls() -> None:
|
|
90
101
|
"""Will clear the console in addition to completely resetting the ANSI formats."""
|
|
91
|
-
if _shutil.which(
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
if _shutil.which("cls"):
|
|
103
|
+
_os.system("cls")
|
|
104
|
+
elif _shutil.which("clear"):
|
|
105
|
+
_os.system("clear")
|
|
106
|
+
print("\033[0m", end="", flush=True)
|
|
94
107
|
|
|
95
108
|
@staticmethod
|
|
96
|
-
def log(
|
|
109
|
+
def log(
|
|
110
|
+
title: str,
|
|
111
|
+
prompt: object = "",
|
|
112
|
+
start: str = "",
|
|
113
|
+
end: str = "\n",
|
|
114
|
+
title_bg_color: hexa | rgba = None,
|
|
115
|
+
default_color: hexa | rgba = None,
|
|
116
|
+
) -> None:
|
|
97
117
|
"""Will print a formatted log message:<br>
|
|
98
118
|
`title` -⠀the title of the log message (e.g. `DEBUG`, `WARN`, `FAIL`, etc.)<br>
|
|
99
119
|
`prompt` -⠀the log message<br>
|
|
@@ -103,138 +123,259 @@ class Cmd:
|
|
|
103
123
|
`default_color` -⠀the default text color of the `prompt`\n
|
|
104
124
|
--------------------------------------------------------------------------------
|
|
105
125
|
The log message supports special formatting codes. For more detailed<br>
|
|
106
|
-
information about formatting codes, see `xx_format_codes` class description.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
126
|
+
information about formatting codes, see `xx_format_codes` class description.
|
|
127
|
+
"""
|
|
128
|
+
title_color = "_color" if not title_bg_color else Color.text_color_for_on_bg(title_bg_color)
|
|
129
|
+
if title:
|
|
130
|
+
FormatCodes.print(
|
|
131
|
+
f'{start} [bold][{title_color}]{f"[BG:{title_bg_color}]" if title_bg_color else ""} {title.upper()}: [_]\t{f"[{default_color}]" if default_color else ""}{str(prompt)}[_]',
|
|
132
|
+
default_color=default_color,
|
|
133
|
+
end=end,
|
|
134
|
+
)
|
|
135
|
+
else:
|
|
136
|
+
FormatCodes.print(
|
|
137
|
+
f'{start} {f"[{default_color}]" if default_color else ""}{str(prompt)}[_]',
|
|
138
|
+
default_color=default_color,
|
|
139
|
+
end=end,
|
|
140
|
+
)
|
|
110
141
|
|
|
111
142
|
@staticmethod
|
|
112
|
-
def debug(
|
|
143
|
+
def debug(
|
|
144
|
+
prompt: object = "Point in program reached.",
|
|
145
|
+
active: bool = True,
|
|
146
|
+
start: str = "\n",
|
|
147
|
+
end: str = "\n\n",
|
|
148
|
+
title_bg_color: hexa | rgba = DEFAULT.color["yellow"],
|
|
149
|
+
default_color: hexa | rgba = DEFAULT.text_color,
|
|
150
|
+
pause: bool = False,
|
|
151
|
+
exit: bool = False,
|
|
152
|
+
) -> None:
|
|
113
153
|
"""A preset for `log()`: `DEBUG` log message with the options to pause<br>
|
|
114
154
|
at the message and exit the program after the message was printed."""
|
|
115
155
|
if active:
|
|
116
|
-
Cmd.log(
|
|
156
|
+
Cmd.log("DEBUG", prompt, start, end, title_bg_color, default_color)
|
|
117
157
|
Cmd.pause_exit(pause, exit)
|
|
118
158
|
|
|
119
159
|
@staticmethod
|
|
120
|
-
def info(
|
|
160
|
+
def info(
|
|
161
|
+
prompt: object = "Program running.",
|
|
162
|
+
start: str = "\n",
|
|
163
|
+
end: str = "\n\n",
|
|
164
|
+
title_bg_color: hexa | rgba = DEFAULT.color["blue"],
|
|
165
|
+
default_color: hexa | rgba = DEFAULT.text_color,
|
|
166
|
+
pause: bool = False,
|
|
167
|
+
exit: bool = False,
|
|
168
|
+
) -> None:
|
|
121
169
|
"""A preset for `log()`: `INFO` log message with the options to pause<br>
|
|
122
170
|
at the message and exit the program after the message was printed."""
|
|
123
|
-
Cmd.log(
|
|
171
|
+
Cmd.log("INFO", prompt, start, end, title_bg_color, default_color)
|
|
124
172
|
Cmd.pause_exit(pause, exit)
|
|
125
173
|
|
|
126
174
|
@staticmethod
|
|
127
|
-
def done(
|
|
175
|
+
def done(
|
|
176
|
+
prompt: object = "Program finished.",
|
|
177
|
+
start: str = "\n",
|
|
178
|
+
end: str = "\n\n",
|
|
179
|
+
title_bg_color: hexa | rgba = DEFAULT.color["teal"],
|
|
180
|
+
default_color: hexa | rgba = DEFAULT.text_color,
|
|
181
|
+
pause: bool = False,
|
|
182
|
+
exit: bool = False,
|
|
183
|
+
) -> None:
|
|
128
184
|
"""A preset for `log()`: `DONE` log message with the options to pause<br>
|
|
129
185
|
at the message and exit the program after the message was printed."""
|
|
130
|
-
Cmd.log(
|
|
186
|
+
Cmd.log("DONE", prompt, start, end, title_bg_color, default_color)
|
|
131
187
|
Cmd.pause_exit(pause, exit)
|
|
132
188
|
|
|
133
189
|
@staticmethod
|
|
134
|
-
def warn(
|
|
190
|
+
def warn(
|
|
191
|
+
prompt: object = "Important message.",
|
|
192
|
+
start: str = "\n",
|
|
193
|
+
end: str = "\n\n",
|
|
194
|
+
title_bg_color: hexa | rgba = DEFAULT.color["orange"],
|
|
195
|
+
default_color: hexa | rgba = DEFAULT.text_color,
|
|
196
|
+
pause: bool = False,
|
|
197
|
+
exit: bool = False,
|
|
198
|
+
) -> None:
|
|
135
199
|
"""A preset for `log()`: `WARN` log message with the options to pause<br>
|
|
136
200
|
at the message and exit the program after the message was printed."""
|
|
137
|
-
Cmd.log(
|
|
201
|
+
Cmd.log("WARN", prompt, start, end, title_bg_color, default_color)
|
|
138
202
|
Cmd.pause_exit(pause, exit)
|
|
139
203
|
|
|
140
204
|
@staticmethod
|
|
141
|
-
def fail(
|
|
205
|
+
def fail(
|
|
206
|
+
prompt: object = "Program error.",
|
|
207
|
+
start: str = "\n",
|
|
208
|
+
end: str = "\n\n",
|
|
209
|
+
title_bg_color: hexa | rgba = DEFAULT.color["red"],
|
|
210
|
+
default_color: hexa | rgba = DEFAULT.text_color,
|
|
211
|
+
pause: bool = False,
|
|
212
|
+
exit: bool = True,
|
|
213
|
+
reset_ansi=True,
|
|
214
|
+
) -> None:
|
|
142
215
|
"""A preset for `log()`: `FAIL` log message with the options to pause<br>
|
|
143
216
|
at the message and exit the program after the message was printed."""
|
|
144
|
-
Cmd.log(
|
|
217
|
+
Cmd.log("FAIL", prompt, start, end, title_bg_color, default_color)
|
|
145
218
|
Cmd.pause_exit(pause, exit, reset_ansi=reset_ansi)
|
|
146
219
|
|
|
147
220
|
@staticmethod
|
|
148
|
-
def exit(
|
|
221
|
+
def exit(
|
|
222
|
+
prompt: object = "Program ended.",
|
|
223
|
+
start: str = "\n",
|
|
224
|
+
end: str = "\n\n",
|
|
225
|
+
title_bg_color: hexa | rgba = DEFAULT.color["magenta"],
|
|
226
|
+
default_color: hexa | rgba = DEFAULT.text_color,
|
|
227
|
+
pause: bool = False,
|
|
228
|
+
exit: bool = True,
|
|
229
|
+
reset_ansi=True,
|
|
230
|
+
) -> None:
|
|
149
231
|
"""A preset for `log()`: `EXIT` log message with the options to pause<br>
|
|
150
232
|
at the message and exit the program after the message was printed."""
|
|
151
|
-
Cmd.log(
|
|
233
|
+
Cmd.log("EXIT", prompt, start, end, title_bg_color, default_color)
|
|
152
234
|
Cmd.pause_exit(pause, exit, reset_ansi=reset_ansi)
|
|
153
235
|
|
|
154
236
|
@staticmethod
|
|
155
|
-
def
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
237
|
+
def confirm(
|
|
238
|
+
prompt: object = "Do you want to continue?",
|
|
239
|
+
start="\n",
|
|
240
|
+
end="\n",
|
|
241
|
+
default_color: hexa | rgba = DEFAULT.color["cyan"],
|
|
242
|
+
default_is_yes: bool = True,
|
|
243
|
+
) -> bool:
|
|
162
244
|
"""Ask a yes/no question.\n
|
|
163
245
|
-----------------------------------------------------------------------------------
|
|
164
246
|
The question can be formatted with special formatting codes. For more detailed<br>
|
|
165
|
-
information about formatting codes, see the `xx_format_codes` description.
|
|
166
|
-
|
|
167
|
-
|
|
247
|
+
information about formatting codes, see the `xx_format_codes` description.
|
|
248
|
+
"""
|
|
249
|
+
confirmed = input(
|
|
250
|
+
FormatCodes.to_ansi(
|
|
251
|
+
f'{start} {str(prompt)} [_|dim](({"Y" if default_is_yes else "y"}/{"n" if default_is_yes else "N"}): )',
|
|
252
|
+
default_color,
|
|
253
|
+
)
|
|
254
|
+
).strip().lower() in (("", "y", "yes") if default_is_yes else ("y", "yes"))
|
|
255
|
+
if end:
|
|
256
|
+
Cmd.log("", end, end="")
|
|
168
257
|
return confirmed
|
|
169
258
|
|
|
170
259
|
@staticmethod
|
|
171
|
-
def restricted_input(
|
|
260
|
+
def restricted_input(
|
|
261
|
+
prompt: object = "",
|
|
262
|
+
allowed_chars: str = CHARS.all,
|
|
263
|
+
min_len: int = None,
|
|
264
|
+
max_len: int = None,
|
|
265
|
+
mask_char: str = None,
|
|
266
|
+
reset_ansi: bool = True,
|
|
267
|
+
) -> str | None:
|
|
172
268
|
"""Acts like a standard Python `input()` with the advantage, that you can specify:
|
|
173
269
|
- what text characters the user is allowed to type and
|
|
174
270
|
- the minimum and/or maximum length of the users input
|
|
175
|
-
- optional mask character (hide user input, e.g. for passwords)
|
|
271
|
+
- optional mask character (hide user input, e.g. for passwords)
|
|
272
|
+
- reset the ANSI formatting codes after the user continues\n
|
|
176
273
|
-----------------------------------------------------------------------------------
|
|
177
274
|
The input can be formatted with special formatting codes. For more detailed<br>
|
|
178
|
-
information about formatting codes, see the `xx_format_codes` description.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
275
|
+
information about formatting codes, see the `xx_format_codes` description.
|
|
276
|
+
"""
|
|
277
|
+
FormatCodes.print(prompt, end="", flush=True)
|
|
278
|
+
result = ""
|
|
279
|
+
select_all = False
|
|
280
|
+
last_line_count = 1
|
|
281
|
+
last_console_width = 0
|
|
282
|
+
|
|
283
|
+
def update_display(console_width: int) -> None:
|
|
185
284
|
nonlocal select_all, last_line_count, last_console_width
|
|
186
|
-
lines = String.
|
|
285
|
+
lines = String.split_count(
|
|
286
|
+
str(prompt) + (mask_char * len(result) if mask_char else result),
|
|
287
|
+
console_width,
|
|
288
|
+
)
|
|
187
289
|
line_count = len(lines)
|
|
188
290
|
if (line_count > 1 or line_count < last_line_count) and not last_line_count == 1:
|
|
189
|
-
if last_console_width > console_width:
|
|
190
|
-
|
|
191
|
-
|
|
291
|
+
if last_console_width > console_width:
|
|
292
|
+
line_count *= 2
|
|
293
|
+
for _ in range(
|
|
294
|
+
line_count
|
|
295
|
+
if line_count < last_line_count and not line_count > last_line_count
|
|
296
|
+
else (line_count - 2 if line_count > last_line_count else line_count - 1)
|
|
297
|
+
):
|
|
298
|
+
_sys.stdout.write("\033[2K\r\033[A")
|
|
192
299
|
prompt_len = len(str(prompt)) if prompt else 0
|
|
193
|
-
prompt_str, input_str = lines[0][:prompt_len],
|
|
194
|
-
|
|
300
|
+
prompt_str, input_str = lines[0][:prompt_len], (
|
|
301
|
+
lines[0][prompt_len:] if len(lines) == 1 else "\n".join([lines[0][prompt_len:]] + lines[1:])
|
|
302
|
+
) # SEPARATE THE PROMPT AND THE INPUT
|
|
303
|
+
_sys.stdout.write(
|
|
304
|
+
"\033[2K\r" + FormatCodes.to_ansi(prompt_str) + ("\033[7m" if select_all else "") + input_str + "\033[27m"
|
|
305
|
+
)
|
|
195
306
|
last_line_count, last_console_width = line_count, console_width
|
|
307
|
+
|
|
308
|
+
def handle_enter():
|
|
309
|
+
if min_len is not None and len(result) < min_len:
|
|
310
|
+
return False
|
|
311
|
+
FormatCodes.print("[_]" if reset_ansi else "", flush=True)
|
|
312
|
+
return True
|
|
313
|
+
|
|
314
|
+
def handle_backspace_delete():
|
|
315
|
+
nonlocal result, select_all
|
|
316
|
+
if select_all:
|
|
317
|
+
result, select_all = "", False
|
|
318
|
+
elif result and event.name == "backspace":
|
|
319
|
+
result = result[:-1]
|
|
320
|
+
update_display(Cmd.w())
|
|
321
|
+
|
|
322
|
+
def handle_paste():
|
|
323
|
+
nonlocal result, select_all
|
|
324
|
+
if select_all:
|
|
325
|
+
result, select_all = "", False
|
|
326
|
+
filtered_text = "".join(char for char in _pyperclip.paste() if allowed_chars == CHARS.all or char in allowed_chars)
|
|
327
|
+
if max_len is None or len(result) + len(filtered_text) <= max_len:
|
|
328
|
+
result += filtered_text
|
|
329
|
+
update_display(Cmd.w())
|
|
330
|
+
|
|
331
|
+
def handle_select_all():
|
|
332
|
+
nonlocal select_all
|
|
333
|
+
select_all = True
|
|
334
|
+
update_display(Cmd.w())
|
|
335
|
+
|
|
336
|
+
def handle_copy():
|
|
337
|
+
nonlocal select_all
|
|
338
|
+
with suppress(KeyboardInterrupt):
|
|
339
|
+
select_all = False
|
|
340
|
+
update_display(Cmd.w())
|
|
341
|
+
_pyperclip.copy(result)
|
|
342
|
+
|
|
343
|
+
def handle_character_input():
|
|
344
|
+
nonlocal result
|
|
345
|
+
if (allowed_chars == CHARS.all or event.name in allowed_chars) and (max_len is None or len(result) < max_len):
|
|
346
|
+
result += event.name
|
|
347
|
+
update_display(Cmd.w())
|
|
348
|
+
|
|
196
349
|
while True:
|
|
197
350
|
event = _keyboard.read_event()
|
|
198
|
-
if event.event_type ==
|
|
199
|
-
if event.name ==
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
elif
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
result += filtered_text
|
|
214
|
-
update_display(Cmd.w())
|
|
215
|
-
elif event.name == 'a' and _keyboard.is_pressed('ctrl'):
|
|
216
|
-
select_all = True
|
|
217
|
-
update_display(Cmd.w())
|
|
218
|
-
elif event.name == 'c' and _keyboard.is_pressed('ctrl') and select_all:
|
|
219
|
-
with suppress(KeyboardInterrupt): # PREVENT CTRL+C FROM RAISING A `KeyboardInterrupt` EXCEPTION
|
|
220
|
-
select_all = False
|
|
221
|
-
update_display(Cmd.w())
|
|
222
|
-
_pyperclip.copy(result)
|
|
223
|
-
elif event.name == 'esc':
|
|
224
|
-
return
|
|
225
|
-
elif event.name == 'space':
|
|
226
|
-
if (allowed_chars == CHARS.all or ' ' in allowed_chars) and (max_length is None or len(result) < max_length):
|
|
227
|
-
result += ' '
|
|
228
|
-
update_display(Cmd.w())
|
|
351
|
+
if event.event_type == "down":
|
|
352
|
+
if event.name == "enter" and handle_enter():
|
|
353
|
+
return result.rstrip("\n")
|
|
354
|
+
elif event.name in ("backspace", "delete", "entf"):
|
|
355
|
+
handle_backspace_delete()
|
|
356
|
+
elif (event.name == "v" and _keyboard.is_pressed("ctrl")) or _mouse.is_pressed("right"):
|
|
357
|
+
handle_paste()
|
|
358
|
+
elif event.name == "a" and _keyboard.is_pressed("ctrl"):
|
|
359
|
+
handle_select_all()
|
|
360
|
+
elif event.name == "c" and _keyboard.is_pressed("ctrl") and select_all:
|
|
361
|
+
handle_copy()
|
|
362
|
+
elif event.name == "esc":
|
|
363
|
+
return None
|
|
364
|
+
elif event.name == "space":
|
|
365
|
+
handle_character_input()
|
|
229
366
|
elif len(event.name) == 1:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
update_display(Cmd.w())
|
|
233
|
-
else: # ANY DISALLOWED OR NON-DEFINED KEY PRESSED
|
|
367
|
+
handle_character_input()
|
|
368
|
+
else:
|
|
234
369
|
select_all = False
|
|
235
370
|
update_display(Cmd.w())
|
|
236
371
|
|
|
237
372
|
@staticmethod
|
|
238
|
-
def pwd_input(
|
|
373
|
+
def pwd_input(
|
|
374
|
+
prompt: object = "Password: ",
|
|
375
|
+
allowed_chars: str = CHARS.standard_ascii,
|
|
376
|
+
min_len: int = None,
|
|
377
|
+
max_len: int = None,
|
|
378
|
+
_reset_ansi: bool = True,
|
|
379
|
+
) -> str:
|
|
239
380
|
"""Password input that masks the entered characters with asterisks."""
|
|
240
|
-
return Cmd.restricted_input(prompt, allowed_chars,
|
|
381
|
+
return Cmd.restricted_input(prompt, allowed_chars, min_len, max_len, "*", _reset_ansi)
|
xulbux/xx_code.py
CHANGED
|
@@ -5,24 +5,16 @@ from .xx_data import *
|
|
|
5
5
|
import regex as _rx
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
8
|
class Code:
|
|
11
9
|
|
|
12
10
|
@staticmethod
|
|
13
|
-
def
|
|
14
|
-
"""Replaces all special space characters with normal spaces.<br>
|
|
15
|
-
Also replaces tab characters with `tab_spaces` spaces."""
|
|
16
|
-
return string.replace('\t', ' ' * tab_spaces).replace('\u2000', ' ').replace('\u2001', ' ').replace('\u2002', ' ').replace('\u2003', ' ').replace('\u2004', ' ').replace('\u2005', ' ').replace('\u2006', ' ').replace('\u2007', ' ').replace('\u2008', ' ').replace('\u2009', ' ').replace('\u200A', ' ')
|
|
17
|
-
|
|
18
|
-
@staticmethod
|
|
19
|
-
def add_indent(code:str, indent:int) -> str:
|
|
11
|
+
def add_indent(code: str, indent: int) -> str:
|
|
20
12
|
"""Adds `indent` spaces at the beginning of each line."""
|
|
21
|
-
indented_lines = [
|
|
22
|
-
return
|
|
13
|
+
indented_lines = [" " * indent + line for line in code.splitlines()]
|
|
14
|
+
return "\n".join(indented_lines)
|
|
23
15
|
|
|
24
16
|
@staticmethod
|
|
25
|
-
def get_tab_spaces(code:str) -> int:
|
|
17
|
+
def get_tab_spaces(code: str) -> int:
|
|
26
18
|
"""Will try to get the amount of spaces used for indentation."""
|
|
27
19
|
code_lines = String.get_string_lines(code, remove_empty_lines=True)
|
|
28
20
|
indents = [len(line) - len(line.lstrip()) for line in code_lines]
|
|
@@ -30,42 +22,50 @@ class Code:
|
|
|
30
22
|
return min(non_zero_indents) if non_zero_indents else 0
|
|
31
23
|
|
|
32
24
|
@staticmethod
|
|
33
|
-
def change_tab_size(code:str, new_tab_size:int, remove_empty_lines:bool = False) -> str:
|
|
25
|
+
def change_tab_size(code: str, new_tab_size: int, remove_empty_lines: bool = False) -> str:
|
|
34
26
|
"""Replaces all tabs with `new_tab_size` spaces.<br>
|
|
35
|
-
If `remove_empty_lines` is `True`, empty lines will be removed in the process.
|
|
27
|
+
If `remove_empty_lines` is `True`, empty lines will be removed in the process.
|
|
28
|
+
"""
|
|
36
29
|
code_lines = String.get_string_lines(code, remove_empty_lines=True)
|
|
37
30
|
lines = code_lines if remove_empty_lines else String.get_string_lines(code)
|
|
38
31
|
tab_spaces = Code.get_tab_spaces(code)
|
|
39
32
|
if (tab_spaces == new_tab_size) or tab_spaces == 0:
|
|
40
33
|
if remove_empty_lines:
|
|
41
|
-
return
|
|
34
|
+
return "\n".join(code_lines)
|
|
42
35
|
return code
|
|
43
36
|
result = []
|
|
44
37
|
for line in lines:
|
|
45
38
|
stripped = line.lstrip()
|
|
46
39
|
indent_level = (len(line) - len(stripped)) // tab_spaces
|
|
47
|
-
new_indent =
|
|
40
|
+
new_indent = " " * (indent_level * new_tab_size)
|
|
48
41
|
result.append(new_indent + stripped)
|
|
49
|
-
return
|
|
42
|
+
return "\n".join(result)
|
|
50
43
|
|
|
51
44
|
@staticmethod
|
|
52
|
-
def get_func_calls(code:str) -> list:
|
|
45
|
+
def get_func_calls(code: str) -> list:
|
|
53
46
|
"""Will try to get all function calls and return them as a list."""
|
|
54
|
-
funcs
|
|
47
|
+
funcs = _rx.findall(r"(?i)" + Regex.func_call(), code)
|
|
48
|
+
nested_func_calls = []
|
|
55
49
|
for _, func_attrs in funcs:
|
|
56
|
-
nested_calls = _rx.findall(r
|
|
50
|
+
nested_calls = _rx.findall(r"(?i)" + Regex.func_call(), func_attrs)
|
|
57
51
|
if nested_calls:
|
|
58
52
|
nested_func_calls.extend(nested_calls)
|
|
59
53
|
return Data.remove_duplicates(funcs + nested_func_calls)
|
|
60
54
|
|
|
61
55
|
@staticmethod
|
|
62
|
-
def is_js(code:str, funcs:list = [
|
|
56
|
+
def is_js(code: str, funcs: list = ["__", "$t", "$lang"]) -> bool:
|
|
63
57
|
"""Will check if the code is likely to be JavaScript."""
|
|
64
|
-
funcs =
|
|
65
|
-
js_pattern = _rx.compile(
|
|
58
|
+
funcs = "|".join(funcs)
|
|
59
|
+
js_pattern = _rx.compile(
|
|
60
|
+
Regex.outside_strings(
|
|
61
|
+
r"""^(?:
|
|
66
62
|
(\$[\w_]+)\s* # JQUERY-STYLE VARIABLES
|
|
67
63
|
|(\$[\w_]+\s*\() # JQUERY-STYLE FUNCTION CALLS
|
|
68
|
-
|((
|
|
64
|
+
|(("""
|
|
65
|
+
+ funcs
|
|
66
|
+
+ r")"
|
|
67
|
+
+ Regex.brackets("()")
|
|
68
|
+
+ r"""\s*) # PREDEFINED FUNCTION CALLS
|
|
69
69
|
|(\bfunction\s*\() # FUNCTION DECLARATIONS
|
|
70
70
|
|(\b(var|let|const)\s+[\w_]+\s*=) # VARIABLE DECLARATIONS
|
|
71
71
|
|(\b(if|for|while|switch)\s*\() # CONTROL STRUCTURES
|
|
@@ -98,5 +98,8 @@ class Code:
|
|
|
98
98
|
|(\bdelete\b) # DELETE OPERATOR
|
|
99
99
|
|(\btypeof\b) # TYPEOF OPERATOR
|
|
100
100
|
|(\bvoid\b) # VOID OPERATOR
|
|
101
|
-
)[\s\S]*$
|
|
101
|
+
)[\s\S]*$"""
|
|
102
|
+
),
|
|
103
|
+
_rx.VERBOSE | _rx.IGNORECASE,
|
|
104
|
+
)
|
|
102
105
|
return bool(js_pattern.fullmatch(code))
|