xmyshell 0.2.11__tar.gz → 0.2.12__tar.gz
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.
- {xmyshell-0.2.11/xmyshell.egg-info → xmyshell-0.2.12}/PKG-INFO +7 -1
- {xmyshell-0.2.11 → xmyshell-0.2.12}/README.md +6 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/pyproject.toml +1 -1
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/completer.py +47 -8
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/environment.py +3 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/kernel.py +50 -38
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/keybindings.py +21 -10
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/lexer.py +41 -1
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/main.py +3 -2
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/meta.py +2 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/stubs/__init__.pyi +2 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12/xmyshell.egg-info}/PKG-INFO +7 -1
- {xmyshell-0.2.11 → xmyshell-0.2.12}/LICENSE +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/setup.cfg +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/__init__.py +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/__main__.py +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/data/commands.json +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/init.py +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/stubs/__init__.py +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/stubs/py.typed +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/themes.py +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/update.py +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell/utils.py +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell.egg-info/SOURCES.txt +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell.egg-info/dependency_links.txt +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell.egg-info/entry_points.txt +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell.egg-info/requires.txt +0 -0
- {xmyshell-0.2.11 → xmyshell-0.2.12}/xmyshell.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xmyshell
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
4
4
|
Summary: XmyShell is a shell-like tool built on top of prompt_toolkit which provides a persistent Python runtime.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -163,6 +163,12 @@ The exit code of the last command.
|
|
|
163
163
|
- `exec_duration`
|
|
164
164
|
The duration which last command took.
|
|
165
165
|
|
|
166
|
+
- `SHOW_WELCOME`
|
|
167
|
+
Toggle whether to show welcome message when staring.
|
|
168
|
+
|
|
169
|
+
- `SHELL_CMD`
|
|
170
|
+
Shell command line prefix, defaults to `["cmd", "/c"]` on Windows and `["bin/sh", "-c"]` on other OS.
|
|
171
|
+
|
|
166
172
|
- `HOME_DIR`
|
|
167
173
|
The home directory, similar to `~`.
|
|
168
174
|
|
|
@@ -153,6 +153,12 @@ The exit code of the last command.
|
|
|
153
153
|
- `exec_duration`
|
|
154
154
|
The duration which last command took.
|
|
155
155
|
|
|
156
|
+
- `SHOW_WELCOME`
|
|
157
|
+
Toggle whether to show welcome message when staring.
|
|
158
|
+
|
|
159
|
+
- `SHELL_CMD`
|
|
160
|
+
Shell command line prefix, defaults to `["cmd", "/c"]` on Windows and `["bin/sh", "-c"]` on other OS.
|
|
161
|
+
|
|
156
162
|
- `HOME_DIR`
|
|
157
163
|
The home directory, similar to `~`.
|
|
158
164
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from collections.abc import Generator, Iterable
|
|
2
2
|
import os
|
|
3
|
+
import shlex
|
|
3
4
|
import sys
|
|
4
5
|
import re
|
|
5
6
|
import time
|
|
@@ -118,16 +119,51 @@ class ShellCompleter(Completer):
|
|
|
118
119
|
others = sorted(c for c in commands if c not in COMMON and c not in BUILTINS)
|
|
119
120
|
return builtins + common + others
|
|
120
121
|
|
|
121
|
-
def _path_completions(self, text: str, max_results: int = 100):
|
|
122
|
+
def _path_completions(self, text: str, max_results: int = 100, in_string: bool=False):
|
|
122
123
|
if text and text[-1] in whitespace: return
|
|
123
|
-
|
|
124
|
+
|
|
125
|
+
def last_token_raw(text: str) -> str:
|
|
126
|
+
quote = None
|
|
127
|
+
start = 0
|
|
128
|
+
i = 0
|
|
129
|
+
while i < len(text):
|
|
130
|
+
ch = text[i]
|
|
131
|
+
if quote:
|
|
132
|
+
if ch == quote:
|
|
133
|
+
quote = None
|
|
134
|
+
elif ch == "\\" and quote == '"':
|
|
135
|
+
i += 1
|
|
136
|
+
else:
|
|
137
|
+
if ch in ("'", '"'):
|
|
138
|
+
quote = ch
|
|
139
|
+
elif ch in " \t":
|
|
140
|
+
start = i + 1
|
|
141
|
+
elif ch == "\\":
|
|
142
|
+
i += 1
|
|
143
|
+
i += 1
|
|
144
|
+
return text[start:]
|
|
145
|
+
|
|
146
|
+
def start_pos(text: str, prefix: str) -> int:
|
|
147
|
+
original_len = len(text)
|
|
148
|
+
if text and text[-1] in ("'", '"') and not text.endswith(prefix):
|
|
149
|
+
text = text[:-1]
|
|
150
|
+
if not prefix or not text.endswith(prefix):
|
|
151
|
+
return -len(prefix)
|
|
152
|
+
start = len(text) - len(prefix)
|
|
153
|
+
if start > 0 and text[start - 1] in ("'", '"'):
|
|
154
|
+
start -= 1
|
|
155
|
+
return start - original_len
|
|
156
|
+
|
|
157
|
+
last = last_token_raw(text)
|
|
158
|
+
in_string = in_string or last.startswith('"') or last.startswith("'")
|
|
159
|
+
last = last.lstrip('"\'')
|
|
124
160
|
if last == '~': return # don't remove this
|
|
125
161
|
path = os.path.expanduser(last)
|
|
126
162
|
dirname = os.path.dirname(path) or "."
|
|
127
|
-
prefix = os.path.basename(path)
|
|
163
|
+
prefix = os.path.basename(path).strip('"\'')
|
|
128
164
|
|
|
129
165
|
try:
|
|
130
|
-
matches = []
|
|
166
|
+
matches: list[str] = []
|
|
131
167
|
for item in os.listdir(dirname):
|
|
132
168
|
if item.startswith(prefix):
|
|
133
169
|
matches.append(item)
|
|
@@ -137,11 +173,14 @@ class ShellCompleter(Completer):
|
|
|
137
173
|
for item in sorted(matches):
|
|
138
174
|
full = os.path.join(dirname, item)
|
|
139
175
|
is_dir = os.path.isdir(full)
|
|
140
|
-
|
|
176
|
+
if ' ' in item and not in_string:
|
|
177
|
+
name = f'"{item}"'
|
|
178
|
+
else:
|
|
179
|
+
name = item
|
|
141
180
|
yield Completion(
|
|
142
181
|
name,
|
|
143
|
-
display=truncate(
|
|
144
|
-
start_position
|
|
182
|
+
display=truncate(item, MAX_DISPLAY_LEN),
|
|
183
|
+
start_position=start_pos(text, prefix),
|
|
145
184
|
display_meta="directory" if is_dir else "file",
|
|
146
185
|
)
|
|
147
186
|
except OSError:
|
|
@@ -207,7 +246,7 @@ class ShellCompleter(Completer):
|
|
|
207
246
|
|
|
208
247
|
string = _in_string(text)
|
|
209
248
|
if string:
|
|
210
|
-
yield from self._path_completions(string)
|
|
249
|
+
yield from self._path_completions(string, in_string=True)
|
|
211
250
|
return
|
|
212
251
|
|
|
213
252
|
if text and text[-1].isspace():
|
|
@@ -10,6 +10,7 @@ from .utils import XMYSHELL_MAGIC, getcwd, getlogin, HOME_DIR, xmyshell_cat
|
|
|
10
10
|
namespace: dict[str, Any] = {}
|
|
11
11
|
aliases: dict[str, str] = {}
|
|
12
12
|
init_environ: dict[str, str] = dict(os.environ)
|
|
13
|
+
SHELL_CMD = ["cmd", "/c"] if os.name == "nt" else ["/bin/sh", "-c"]
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def xmyshell_alias(altname: str, target: str) -> None:
|
|
@@ -34,6 +35,8 @@ _init_namespace: dict[str, Any] = {
|
|
|
34
35
|
"cat": xmyshell_cat,
|
|
35
36
|
"alias": xmyshell_alias,
|
|
36
37
|
"unalias": xmyshell_unalias,
|
|
38
|
+
"SHOW_WELCOME": True,
|
|
39
|
+
"SHELL_CMD": SHELL_CMD,
|
|
37
40
|
"HOME_DIR": HOME_DIR,
|
|
38
41
|
"PROFILE": f"{HOME_DIR}/.xmyshell/config.py",
|
|
39
42
|
"exit_code": 0,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from collections.abc import Generator, Sequence
|
|
2
|
+
from contextlib import contextmanager
|
|
2
3
|
import os
|
|
3
4
|
import sys
|
|
4
5
|
import runpy
|
|
5
6
|
import subprocess
|
|
6
7
|
import signal
|
|
7
8
|
import shlex
|
|
8
|
-
from .environment import namespace, aliases, xmyshell_alias, xmyshell_unalias
|
|
9
|
+
from .environment import namespace, aliases, xmyshell_alias, xmyshell_unalias, SHELL_CMD
|
|
9
10
|
from .utils import pywarning, pyerror, getcwd
|
|
10
11
|
from .meta import HELP_MESSAGE
|
|
11
12
|
|
|
@@ -14,8 +15,20 @@ def _reload() -> None:
|
|
|
14
15
|
from .init import xmyshell_reload
|
|
15
16
|
xmyshell_reload()
|
|
16
17
|
|
|
18
|
+
def _reset_sigint():
|
|
19
|
+
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
_kwargs_preexec = {}
|
|
22
|
+
if os.name == "posix":
|
|
23
|
+
_kwargs_preexec["preexec_fn"] = _reset_sigint
|
|
24
|
+
|
|
25
|
+
@contextmanager
|
|
26
|
+
def _no_interrupt():
|
|
27
|
+
old = signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
28
|
+
yield
|
|
29
|
+
signal.signal(signal.SIGINT, old)
|
|
30
|
+
|
|
31
|
+
def _run(cmd_line: list[str] | str, **kwargs) -> subprocess.CompletedProcess:
|
|
19
32
|
# Block SIGINT in the parent while the child runs, so that a Ctrl-C
|
|
20
33
|
# delivered to the console/process group is handled solely by the child.
|
|
21
34
|
#
|
|
@@ -26,17 +39,15 @@ def _run(*args, **kwargs) -> subprocess.CompletedProcess:
|
|
|
26
39
|
# KeyboardInterrupt handler only fires for Ctrl-C at the prompt.
|
|
27
40
|
#
|
|
28
41
|
# Must run on the main thread: signal.signal() is not allowed elsewhere.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
signal.signal(signal.SIGINT, old)
|
|
39
|
-
|
|
42
|
+
with _no_interrupt():
|
|
43
|
+
if isinstance(cmd_line, list):
|
|
44
|
+
return subprocess.run(cmd_line, **_kwargs_preexec, **kwargs)
|
|
45
|
+
else:
|
|
46
|
+
return subprocess.run(
|
|
47
|
+
namespace.get("SHELL_CMD", SHELL_CMD) + [cmd_line],
|
|
48
|
+
**_kwargs_preexec,
|
|
49
|
+
**kwargs,
|
|
50
|
+
)
|
|
40
51
|
|
|
41
52
|
def xmyshell_source(path: str) -> None:
|
|
42
53
|
_updated = runpy.run_path(
|
|
@@ -250,29 +261,30 @@ def xmyshell(cmd_line: str) -> int:
|
|
|
250
261
|
cmd_line, sh_cmd = cmd_line.split("|>", 1)
|
|
251
262
|
|
|
252
263
|
if sh_cmd:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
264
|
+
with _no_interrupt():
|
|
265
|
+
proc = subprocess.Popen(
|
|
266
|
+
namespace.get("SHELL_CMD", SHELL_CMD) + [sh_cmd],
|
|
267
|
+
env=os.environ,
|
|
268
|
+
stdin=subprocess.PIPE,
|
|
269
|
+
stdout=subprocess.PIPE,
|
|
270
|
+
text=True,
|
|
271
|
+
**_kwargs_preexec,
|
|
272
|
+
)
|
|
273
|
+
old_stdout = sys.stdout
|
|
274
|
+
sys.stdout = proc.stdin
|
|
275
|
+
return_code = xmyshell_raw_command(cmd_line)
|
|
276
|
+
if return_code is None:
|
|
277
|
+
pyerror(f"'{cmd_line}' cannot be used in a pipeline with '|>'")
|
|
278
|
+
return -1
|
|
279
|
+
assert proc.stdin
|
|
280
|
+
proc.stdin.close()
|
|
281
|
+
sys.stdout = old_stdout
|
|
282
|
+
if target_var:
|
|
283
|
+
result, _ = proc.communicate()
|
|
284
|
+
namespace[target_var] = result
|
|
285
|
+
return proc.returncode
|
|
286
|
+
else:
|
|
287
|
+
return proc.wait()
|
|
276
288
|
|
|
277
289
|
if target_var:
|
|
278
290
|
from io import StringIO
|
|
@@ -342,10 +354,10 @@ def xmyshell(cmd_line: str) -> int:
|
|
|
342
354
|
cmd_line = _fix_first_word(cmd_line)
|
|
343
355
|
if target_var:
|
|
344
356
|
result = _run(
|
|
345
|
-
cmd_line,
|
|
357
|
+
cmd_line, env=os.environ, capture_output=True, text=True
|
|
346
358
|
)
|
|
347
359
|
namespace[target_var] = result.stdout
|
|
348
360
|
else:
|
|
349
|
-
result = _run(cmd_line,
|
|
361
|
+
result = _run(cmd_line, env=os.environ)
|
|
350
362
|
|
|
351
363
|
return result.returncode
|
|
@@ -3,7 +3,6 @@ from prompt_toolkit.buffer import Buffer
|
|
|
3
3
|
from prompt_toolkit.key_binding import KeyBindings, KeyPressEvent
|
|
4
4
|
from prompt_toolkit.selection import SelectionState
|
|
5
5
|
from prompt_toolkit.keys import Keys
|
|
6
|
-
from prompt_toolkit.key_binding.bindings.auto_suggest import load_auto_suggest_bindings
|
|
7
6
|
from dataclasses import dataclass
|
|
8
7
|
|
|
9
8
|
@dataclass
|
|
@@ -200,15 +199,12 @@ def _(event: KeyPressEvent) -> None:
|
|
|
200
199
|
stack.pop()
|
|
201
200
|
_auto_editing = False
|
|
202
201
|
return
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
210
|
-
or buffer.cursor_position == 0
|
|
211
|
-
):
|
|
202
|
+
pos = buffer.cursor_position
|
|
203
|
+
text = buffer.text
|
|
204
|
+
before_ok = pos == 0 or (pos > 0 and (text[pos - 1].isspace()
|
|
205
|
+
or text[pos - 1] in set(r"!@#$%^&*:;,./?|+-=~()[]{}<>_")))
|
|
206
|
+
not_on_quote = pos >= len(text) or text[pos] != '"'
|
|
207
|
+
if should_completion(buffer) and before_ok and not_on_quote:
|
|
212
208
|
_auto_editing = True
|
|
213
209
|
buffer.insert_text('""')
|
|
214
210
|
buffer.cursor_left()
|
|
@@ -359,3 +355,18 @@ def _(event: KeyPressEvent) -> None:
|
|
|
359
355
|
_auto_editing = False
|
|
360
356
|
_last_info = BufferInfo.from_buffer(buffer)
|
|
361
357
|
_reset_undo_merge()
|
|
358
|
+
|
|
359
|
+
@bindings.add(Keys.Tab)
|
|
360
|
+
def _(event: KeyPressEvent) -> None:
|
|
361
|
+
buffer = event.current_buffer
|
|
362
|
+
if buffer.complete_state:
|
|
363
|
+
if buffer.complete_state.current_completion is None:
|
|
364
|
+
buffer.complete_next()
|
|
365
|
+
if buffer.complete_state.current_completion:
|
|
366
|
+
# Trailing space bypasses prompt_toolkit's `completion_does_nothing`
|
|
367
|
+
# filter (which hides completions identical to the replaced text) and
|
|
368
|
+
# doubles as the shell argument separator. Invisible characters would
|
|
369
|
+
# also bypass the filter, but would corrupt command parsing.
|
|
370
|
+
text = buffer.complete_state.current_completion.text
|
|
371
|
+
buffer.complete_state.current_completion.text = text.rstrip()
|
|
372
|
+
buffer.apply_completion(buffer.complete_state.current_completion)
|
|
@@ -14,6 +14,46 @@ def _is_number_literal(s: str) -> bool:
|
|
|
14
14
|
except (ValueError, SyntaxError):
|
|
15
15
|
return False
|
|
16
16
|
|
|
17
|
+
def _shell_split_keep_sep(text: str) -> list[str]:
|
|
18
|
+
parts: list[str] = []
|
|
19
|
+
quote: str | None = None
|
|
20
|
+
i = 0
|
|
21
|
+
n = len(text)
|
|
22
|
+
seg_start = 0
|
|
23
|
+
|
|
24
|
+
def flush(end: int) -> None:
|
|
25
|
+
nonlocal seg_start
|
|
26
|
+
if end > seg_start:
|
|
27
|
+
parts.append(text[seg_start:end])
|
|
28
|
+
seg_start = end
|
|
29
|
+
|
|
30
|
+
while i < n:
|
|
31
|
+
ch = text[i]
|
|
32
|
+
if quote is None:
|
|
33
|
+
if ch in " \t":
|
|
34
|
+
flush(i)
|
|
35
|
+
j = i
|
|
36
|
+
while j < n and text[j] in " \t":
|
|
37
|
+
j += 1
|
|
38
|
+
parts.append(text[i:j])
|
|
39
|
+
seg_start = j
|
|
40
|
+
i = j
|
|
41
|
+
continue
|
|
42
|
+
elif ch == "\\" and i + 1 < n:
|
|
43
|
+
i += 2
|
|
44
|
+
continue
|
|
45
|
+
elif ch in ("'", '"'):
|
|
46
|
+
quote = ch
|
|
47
|
+
else:
|
|
48
|
+
if ch == quote:
|
|
49
|
+
quote = None
|
|
50
|
+
elif ch == "\\" and quote == '"' and i + 1 < n:
|
|
51
|
+
i += 2
|
|
52
|
+
continue
|
|
53
|
+
i += 1
|
|
54
|
+
flush(n)
|
|
55
|
+
return parts
|
|
56
|
+
|
|
17
57
|
def _split_with_braces_and_pipes(line: str) -> list[str]:
|
|
18
58
|
parts = []
|
|
19
59
|
outer_buf = []
|
|
@@ -86,7 +126,7 @@ def _split_with_braces_and_pipes(line: str) -> list[str]:
|
|
|
86
126
|
return parts
|
|
87
127
|
|
|
88
128
|
def _lex_cmd(cmd: str, first_word: bool = True) -> list[tuple[str, str]]:
|
|
89
|
-
parts: list[str] =
|
|
129
|
+
parts: list[str] = _shell_split_keep_sep(cmd)
|
|
90
130
|
result: list[tuple[str, str]] = []
|
|
91
131
|
for part in parts:
|
|
92
132
|
if part == "":
|
|
@@ -17,8 +17,9 @@ from .lexer import XmyShellLexer
|
|
|
17
17
|
def xmyshell_main() -> None:
|
|
18
18
|
xmyshell_init()
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if namespace.get("SHOW_WELCOME"):
|
|
21
|
+
print(WELCOME_MESSAGE)
|
|
22
|
+
print(f"Initialized with {(time_ns() - _start_time) // 1_000_000}ms.")
|
|
22
23
|
|
|
23
24
|
session = PromptSession(
|
|
24
25
|
history=FileHistory(os.path.expanduser("~/.xmyshell/history.txt")),
|
|
@@ -59,6 +59,8 @@ Built-in variables:
|
|
|
59
59
|
exec_duration - The duration which last command took.
|
|
60
60
|
HOME_DIR - The home directory, similar to "~".
|
|
61
61
|
PROFILE - The path of config file.
|
|
62
|
+
SHOW_WELCOME - Toggle whether to show welcome message.
|
|
63
|
+
SHELL_CMD - Shell command line prefix, e.g. ["cmd", "/c"]
|
|
62
64
|
|
|
63
65
|
Keyboard Shortcuts:
|
|
64
66
|
↑↓ - Navigate through command history.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xmyshell
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
4
4
|
Summary: XmyShell is a shell-like tool built on top of prompt_toolkit which provides a persistent Python runtime.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -163,6 +163,12 @@ The exit code of the last command.
|
|
|
163
163
|
- `exec_duration`
|
|
164
164
|
The duration which last command took.
|
|
165
165
|
|
|
166
|
+
- `SHOW_WELCOME`
|
|
167
|
+
Toggle whether to show welcome message when staring.
|
|
168
|
+
|
|
169
|
+
- `SHELL_CMD`
|
|
170
|
+
Shell command line prefix, defaults to `["cmd", "/c"]` on Windows and `["bin/sh", "-c"]` on other OS.
|
|
171
|
+
|
|
166
172
|
- `HOME_DIR`
|
|
167
173
|
The home directory, similar to `~`.
|
|
168
174
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|