xonsh/xontrib/bashisms.py

164 lines
4.3 KiB
Python
Raw Normal View History

2022-05-05 00:32:20 +05:30
"""Bash-like interface extensions for xonsh.
Enables additional Bash-like syntax while at the command prompt.
For example, the ``!!`` syntax for running the previous command is now usable.
Note that these features are implemented as precommand events and
these additions do not affect the xonsh language when run as script.
That said, you might find them useful if you have strong muscle memory.
**Warning:** This xontrib may modify user command line input to implement its behavior.
To see the modifications as they are applied (in unified diffformat), please set ``$XONSH_DEBUG`` to ``2`` or higher.
The xontrib also adds commands: ``alias``, ``export``, ``unset``, ``set``, ``shopt``, ``complete``.
"""
2022-01-31 21:26:34 +05:30
import re
import shlex
import sys
2022-01-31 21:26:34 +05:30
from xonsh.built_ins import XSH
__all__ = ()
2016-11-20 18:49:26 -05:00
def _warn_not_supported(msg: str):
print(
f"""Not supported ``{msg}`` in xontrib bashisms.
PRs are welcome - https://github.com/xonsh/xonsh/blob/main/xontrib/bashisms.py""",
file=sys.stderr,
)
@XSH.builtins.events.on_transform_command
2017-01-14 18:13:27 -05:00
def bash_preproc(cmd, **kw):
bang_previous = {
2019-04-26 11:11:11 -04:00
"!": lambda x: x,
"$": lambda x: shlex.split(x)[-1],
"^": lambda x: shlex.split(x)[0],
"*": lambda x: " ".join(shlex.split(x)[1:]),
}
def replace_bang(m):
arg = m.group(1)
inputs = XSH.history.inps
# Dissect the previous command.
if arg in bang_previous:
try:
return bang_previous[arg](inputs[-1])
except IndexError:
print(f"xonsh: no history for '!{arg}'")
2019-04-26 11:11:11 -04:00
return ""
# Look back in history for a matching command.
else:
try:
return next(x for x in reversed(inputs) if x.startswith(arg))
except StopIteration:
print(f"xonsh: no previous commands match '!{arg}'")
2019-04-26 11:11:11 -04:00
return ""
return re.sub(r"!([!$^*]|[\w]+)", replace_bang, cmd)
def alias(args, stdin=None):
ret = 0
if args:
for arg in args:
2019-04-26 11:11:11 -04:00
if "=" in arg:
# shlex.split to remove quotes, e.g. "foo='echo hey'" into
# "foo=echo hey"
2019-04-26 11:11:11 -04:00
name, cmd = shlex.split(arg)[0].split("=", 1)
XSH.aliases[name] = shlex.split(cmd)
elif arg in XSH.aliases:
print(f"{arg}={XSH.aliases[arg]}")
else:
print(f"alias: {arg}: not found", file=sys.stderr)
ret = 1
else:
for alias, cmd in XSH.aliases.items():
print(f"{alias}={cmd}")
return ret
XSH.aliases["alias"] = alias
XSH.env["THREAD_SUBPROCS"] = False
2020-09-24 23:13:20 +03:00
def _unset(args):
if not args:
2020-09-24 23:28:41 +03:00
print("Usage: unset ENV_VARIABLE", file=sys.stderr)
2020-09-24 23:13:20 +03:00
for v in args:
try:
XSH.env.pop(v)
2020-09-25 00:00:56 +03:00
except KeyError:
2020-09-24 23:28:41 +03:00
print(f"{v} not found", file=sys.stderr)
XSH.aliases["unset"] = _unset
2020-09-24 23:13:20 +03:00
def _export(args):
if not args:
2020-09-24 23:28:41 +03:00
print("Usage: export ENV_VARIABLE=VALUE", file=sys.stderr)
2020-09-24 23:13:20 +03:00
for eq in args:
if "=" in eq:
name, val = shlex.split(eq)[0].split("=", 1)
XSH.env[name] = val
2020-09-24 23:13:20 +03:00
else:
2020-09-24 23:28:41 +03:00
print(f"{eq} equal sign not found", file=sys.stderr)
2020-09-24 23:13:20 +03:00
XSH.aliases["export"] = _export
2020-09-24 23:13:20 +03:00
def _set(args):
arg = args[0]
2020-09-24 23:28:41 +03:00
if arg == "-e":
XSH.env["RAISE_SUBPROC_ERROR"] = True
2020-09-24 23:28:41 +03:00
elif arg == "+e":
XSH.env["RAISE_SUBPROC_ERROR"] = False
2020-09-24 23:28:41 +03:00
elif arg == "-x":
XSH.env["XONSH_TRACE_SUBPROC"] = True
2020-09-24 23:28:41 +03:00
elif arg == "+x":
XSH.env["XONSH_TRACE_SUBPROC"] = False
2020-09-24 23:13:20 +03:00
else:
_warn_not_supported(f"set {arg}")
2020-09-24 23:28:41 +03:00
2020-09-24 23:13:20 +03:00
XSH.aliases["set"] = _set
2020-09-25 09:57:36 +03:00
def _shopt(args):
supported_shopt = ["DOTGLOB"]
args_len = len(args)
if args_len == 0:
for so in supported_shopt:
onoff = "on" if so in XSH.env and XSH.env[so] else "off"
2020-09-25 09:57:36 +03:00
print(f"dotglob\t{onoff}")
return
elif args_len < 2 or args[0] in ["-h", "--help"]:
print(f'Usage: shopt <-s|-u> <{"|".join(supported_shopt).lower()}>')
return
opt = args[0]
optname = args[1]
if opt == "-s" and optname == "dotglob":
XSH.env["DOTGLOB"] = True
2020-09-25 09:57:36 +03:00
elif opt == "-u" and optname == "dotglob":
XSH.env["DOTGLOB"] = False
2020-09-25 09:57:36 +03:00
else:
_warn_not_supported(f"shopt {args}")
2020-09-25 09:57:36 +03:00
XSH.aliases["shopt"] = _shopt
2020-09-25 09:57:36 +03:00
XSH.aliases["complete"] = "completer list"