mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
Merge branch 'master' into environment_fixes
This commit is contained in:
commit
06986fc0d4
11 changed files with 218 additions and 30 deletions
|
@ -55,9 +55,9 @@ line is ``#!/usr/bin/env xonsh``.
|
|||
- ``_.rtn``
|
||||
- Returns the exit code, or status, of the previous command.
|
||||
* - ``N=V command``
|
||||
- ``with ${...}.swap(N=V): command``
|
||||
- Set temporary environment variable(s) and execute for command.
|
||||
Use an indented block to execute many commands in the same context.
|
||||
- ``$N=V command`` or ``with ${...}.swap(N=V): command``
|
||||
- Set temporary environment variable(s) and execute the command.
|
||||
Use the second notation with an indented block to execute many commands in the same context.
|
||||
* - ``!$``
|
||||
- ``__xonsh__.history[-1, -1]``
|
||||
- Get the last argument of the last command
|
||||
|
|
23
news/argcomplete.rst
Normal file
23
news/argcomplete.rst
Normal file
|
@ -0,0 +1,23 @@
|
|||
**Added:**
|
||||
|
||||
* Added xontrib-argcomplete to support kislyuk/argcomplete - tab completion for argparse.
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
23
news/shift-arrow.rst
Normal file
23
news/shift-arrow.rst
Normal file
|
@ -0,0 +1,23 @@
|
|||
**Added:**
|
||||
|
||||
* Borrow shift-selection from prompt-toolkit. Shift-arrow (selects a letter) and control-shift-arrow (selects a word) should now be supported.
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
24
news/simple-variables.rst
Normal file
24
news/simple-variables.rst
Normal file
|
@ -0,0 +1,24 @@
|
|||
**Added:**
|
||||
|
||||
* Xonsh now supports bash-style variable assignments preceding
|
||||
subprocess commands (e.g. ``$FOO = "bar" bash -c r"echo $FOO"``).
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -499,6 +499,7 @@ def test_script_stderr(case):
|
|||
("pwd", None, lambda: os.getcwd() + "\n"),
|
||||
("echo WORKING", None, "WORKING\n"),
|
||||
("ls -f", lambda out: out.splitlines().sort(), os.listdir().sort()),
|
||||
("$FOO='foo' $BAR=2 xonsh -c r'echo -n $FOO$BAR'", None, "foo2",),
|
||||
],
|
||||
)
|
||||
def test_single_command_no_windows(cmd, fmt, exp):
|
||||
|
|
|
@ -2501,6 +2501,10 @@ def test_ls_quotes_3_space():
|
|||
check_xonsh_ast({}, '$[ls "wakka jawaka baraka"]', False)
|
||||
|
||||
|
||||
def test_leading_envvar_assignment():
|
||||
check_xonsh_ast({}, "![$FOO= 'foo' $BAR =2 echo r'$BAR']", False)
|
||||
|
||||
|
||||
def test_echo_comma():
|
||||
check_xonsh_ast({}, "![echo ,]", False)
|
||||
|
||||
|
|
|
@ -390,6 +390,7 @@ class SubprocSpec:
|
|||
universal_newlines=False,
|
||||
close_fds=False,
|
||||
captured=False,
|
||||
env=None,
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
|
@ -413,6 +414,8 @@ class SubprocSpec:
|
|||
The flag for if the subprocess is captured, may be one of:
|
||||
False for $[], 'stdout' for $(), 'hiddenobject' for ![], or
|
||||
'object' for !().
|
||||
env : dict
|
||||
Replacement environment to run the subporcess in.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
@ -451,6 +454,13 @@ class SubprocSpec:
|
|||
self.universal_newlines = universal_newlines
|
||||
self.close_fds = close_fds
|
||||
self.captured = captured
|
||||
if env is not None:
|
||||
self.env = {
|
||||
k: v if not (isinstance(v, list)) or len(v) > 1 else v[0]
|
||||
for (k, v) in env.items()
|
||||
}
|
||||
else:
|
||||
self.env = None
|
||||
# pure attrs
|
||||
self.args = list(cmd)
|
||||
self.alias = None
|
||||
|
@ -579,7 +589,8 @@ class SubprocSpec:
|
|||
|
||||
def prep_env(self, kwargs):
|
||||
"""Prepares the environment to use in the subprocess."""
|
||||
denv = builtins.__xonsh__.env.detype()
|
||||
with builtins.__xonsh__.env.swap(self.env) as env:
|
||||
denv = env.detype()
|
||||
if ON_WINDOWS:
|
||||
# Over write prompt variable as xonsh's $PROMPT does
|
||||
# not make much sense for other subprocs
|
||||
|
@ -878,7 +889,7 @@ def _update_last_spec(last):
|
|||
last.captured_stderr = last.captured_stdout
|
||||
|
||||
|
||||
def cmds_to_specs(cmds, captured=False):
|
||||
def cmds_to_specs(cmds, captured=False, envs=None):
|
||||
"""Converts a list of cmds to a list of SubprocSpec objects that are
|
||||
ready to be executed.
|
||||
"""
|
||||
|
@ -886,11 +897,12 @@ def cmds_to_specs(cmds, captured=False):
|
|||
i = 0
|
||||
specs = []
|
||||
redirects = []
|
||||
for cmd in cmds:
|
||||
for i, cmd in enumerate(cmds):
|
||||
if isinstance(cmd, str):
|
||||
redirects.append(cmd)
|
||||
else:
|
||||
spec = SubprocSpec.build(cmd, captured=captured)
|
||||
env = envs[i] if envs is not None else None
|
||||
spec = SubprocSpec.build(cmd, captured=captured, env=env)
|
||||
spec.pipeline_index = i
|
||||
specs.append(spec)
|
||||
i += 1
|
||||
|
@ -921,7 +933,7 @@ def _should_set_title(captured=False):
|
|||
)
|
||||
|
||||
|
||||
def run_subproc(cmds, captured=False):
|
||||
def run_subproc(cmds, captured=False, envs=None):
|
||||
"""Runs a subprocess, in its many forms. This takes a list of 'commands,'
|
||||
which may be a list of command line arguments or a string, representing
|
||||
a special connecting character. For example::
|
||||
|
@ -937,7 +949,7 @@ def run_subproc(cmds, captured=False):
|
|||
if builtins.__xonsh__.env.get("XONSH_TRACE_SUBPROC"):
|
||||
print("TRACE SUBPROC: %s" % str(cmds), file=sys.stderr)
|
||||
|
||||
specs = cmds_to_specs(cmds, captured=captured)
|
||||
specs = cmds_to_specs(cmds, captured=captured, envs=envs)
|
||||
captured = specs[-1].captured
|
||||
if captured == "hiddenobject":
|
||||
command = HiddenCommandPipeline(specs)
|
||||
|
@ -982,20 +994,20 @@ def run_subproc(cmds, captured=False):
|
|||
return
|
||||
|
||||
|
||||
def subproc_captured_stdout(*cmds):
|
||||
def subproc_captured_stdout(*cmds, envs=None):
|
||||
"""Runs a subprocess, capturing the output. Returns the stdout
|
||||
that was produced as a str.
|
||||
"""
|
||||
return run_subproc(cmds, captured="stdout")
|
||||
return run_subproc(cmds, captured="stdout", envs=envs)
|
||||
|
||||
|
||||
def subproc_captured_inject(*cmds):
|
||||
def subproc_captured_inject(*cmds, envs=None):
|
||||
"""Runs a subprocess, capturing the output. Returns a list of
|
||||
whitespace-separated strings of the stdout that was produced.
|
||||
The string is split using xonsh's lexer, rather than Python's str.split()
|
||||
or shlex.split().
|
||||
"""
|
||||
o = run_subproc(cmds, captured="object")
|
||||
o = run_subproc(cmds, captured="object", envs=envs)
|
||||
o.end()
|
||||
toks = []
|
||||
for line in o:
|
||||
|
@ -1004,26 +1016,26 @@ def subproc_captured_inject(*cmds):
|
|||
return toks
|
||||
|
||||
|
||||
def subproc_captured_object(*cmds):
|
||||
def subproc_captured_object(*cmds, envs=None):
|
||||
"""
|
||||
Runs a subprocess, capturing the output. Returns an instance of
|
||||
CommandPipeline representing the completed command.
|
||||
"""
|
||||
return run_subproc(cmds, captured="object")
|
||||
return run_subproc(cmds, captured="object", envs=envs)
|
||||
|
||||
|
||||
def subproc_captured_hiddenobject(*cmds):
|
||||
def subproc_captured_hiddenobject(*cmds, envs=None):
|
||||
"""Runs a subprocess, capturing the output. Returns an instance of
|
||||
HiddenCommandPipeline representing the completed command.
|
||||
"""
|
||||
return run_subproc(cmds, captured="hiddenobject")
|
||||
return run_subproc(cmds, captured="hiddenobject", envs=envs)
|
||||
|
||||
|
||||
def subproc_uncaptured(*cmds):
|
||||
def subproc_uncaptured(*cmds, envs=None):
|
||||
"""Runs a subprocess, without capturing the output. Returns the stdout
|
||||
that was produced as a str.
|
||||
"""
|
||||
return run_subproc(cmds, captured=False)
|
||||
return run_subproc(cmds, captured=False, envs=envs)
|
||||
|
||||
|
||||
def ensure_list_of_strs(x):
|
||||
|
|
|
@ -2885,6 +2885,26 @@ class BaseParser(object):
|
|||
# subprocess
|
||||
#
|
||||
|
||||
def _get_envvars(self, p, lineno, col):
|
||||
"""Get replacement environment from subproc_atoms, return None or
|
||||
ast.List containing ast.Dict for each subproc_atom in the pipeline.
|
||||
"""
|
||||
if not isinstance(p, list):
|
||||
return None
|
||||
has_env = False
|
||||
envs = empty_list(lineno=lineno, col=col)
|
||||
for subproc in p:
|
||||
if hasattr(subproc, "_xenvvars"):
|
||||
has_env = True
|
||||
envs.elts.append(subproc._xenvvars)
|
||||
else:
|
||||
envs.elts.append(
|
||||
ast.Constant(
|
||||
value=None, lineno=subproc.lineno, col_offset=subproc.col_offset
|
||||
)
|
||||
)
|
||||
return envs if has_env else None
|
||||
|
||||
def _dollar_rules(self, p):
|
||||
"""These handle the special xonsh $ shell atoms by looking up
|
||||
in a special __xonsh__.env dictionary injected in the __builtin__.
|
||||
|
@ -2920,6 +2940,11 @@ class BaseParser(object):
|
|||
p0 = xonsh_call("__xonsh__.subproc_uncaptured", p2, lineno=lineno, col=col)
|
||||
else:
|
||||
assert False
|
||||
|
||||
envs = self._get_envvars(p2, lineno, col)
|
||||
if envs is not None:
|
||||
p0.keywords.append(ast.keyword(arg="envs", value=envs))
|
||||
|
||||
return p0
|
||||
|
||||
def _envvar_getter_by_name(self, var, lineno=None, col=None):
|
||||
|
@ -2974,6 +2999,8 @@ class BaseParser(object):
|
|||
else:
|
||||
raise ValueError("action not understood: " + action)
|
||||
del arg._cliarg_action
|
||||
if hasattr(args[0], "_xenvvars"):
|
||||
setattr(cliargs, "_xenvvars", args[0]._xenvvars)
|
||||
return cliargs
|
||||
|
||||
def p_pipe(self, p):
|
||||
|
@ -3043,6 +3070,18 @@ class BaseParser(object):
|
|||
arg._cliarg_action = "append"
|
||||
p[0] = p0
|
||||
|
||||
def p_envvar_assign_subproc_atoms(self, p):
|
||||
"""subproc_atoms : envvar_assign subproc_atoms
|
||||
| envvar_assign subproc_atoms WS
|
||||
"""
|
||||
p1, p20 = p[1], p[2][0]
|
||||
if hasattr(p20, "_xenvvars"):
|
||||
p20._xenvvars.keys.append(p1.keys[0])
|
||||
p20._xenvvars.values.append(p1.values[0])
|
||||
else:
|
||||
setattr(p20, "_xenvvars", p1)
|
||||
p[0] = p[2]
|
||||
|
||||
#
|
||||
# Subproc atom rules
|
||||
#
|
||||
|
@ -3142,11 +3181,14 @@ class BaseParser(object):
|
|||
"""subproc_atom : atdollar_lparen_tok subproc RPAREN
|
||||
subproc_arg_part : atdollar_lparen_tok subproc RPAREN
|
||||
"""
|
||||
p1 = p[1]
|
||||
p1, p2 = p[1], p[2]
|
||||
p0 = xonsh_call(
|
||||
"__xonsh__.subproc_captured_inject", p[2], lineno=p1.lineno, col=p1.lexpos
|
||||
"__xonsh__.subproc_captured_inject", p2, lineno=p1.lineno, col=p1.lexpos
|
||||
)
|
||||
p0._cliarg_action = "extend"
|
||||
envs = self._get_envvars(p2, lineno=p2[0].lineno, col=p2[0].col_offset)
|
||||
if envs is not None:
|
||||
p0.keywords.append(ast.keyword(arg="envs", value=envs))
|
||||
p[0] = p0
|
||||
|
||||
def p_subproc_atom_subproc_inject_bang_empty(self, p):
|
||||
|
@ -3282,6 +3324,28 @@ class BaseParser(object):
|
|||
p1 = p[1]
|
||||
p[0] = ast.Str(s=p1.value, lineno=p1.lineno, col_offset=p1.lexpos)
|
||||
|
||||
def p_envvar_assign_left(self, p):
|
||||
"""envvar_assign_left : dollar_name_tok EQUALS
|
||||
| dollar_name_tok WS EQUALS
|
||||
| dollar_name_tok EQUALS WS
|
||||
| dollar_name_tok WS EQUALS WS
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_envvar_assign(self, p):
|
||||
"""envvar_assign : envvar_assign_left test WS
|
||||
| envvar_assign_left subproc_atom WS
|
||||
"""
|
||||
p1, p2 = p[1], p[2]
|
||||
p[0] = ast.Dict(
|
||||
keys=[
|
||||
ast.Constant(value=p1.value[1:], lineno=p1.lineno, col_offset=p1.lexpos)
|
||||
],
|
||||
values=[p2],
|
||||
lineno=p1.lineno,
|
||||
col_offset=p1.lexpos,
|
||||
)
|
||||
|
||||
#
|
||||
# Helpers
|
||||
#
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import builtins
|
||||
|
||||
from prompt_toolkit import search
|
||||
from prompt_toolkit.application.current import get_app
|
||||
from prompt_toolkit.enums import DEFAULT_BUFFER
|
||||
from prompt_toolkit.filters import (
|
||||
Condition,
|
||||
|
@ -13,7 +14,8 @@ from prompt_toolkit.filters import (
|
|||
IsSearching,
|
||||
)
|
||||
from prompt_toolkit.keys import Keys
|
||||
from prompt_toolkit.application.current import get_app
|
||||
from prompt_toolkit.key_binding.key_bindings import KeyBindings, KeyBindingsBase
|
||||
from prompt_toolkit.key_binding.bindings.named_commands import get_by_name
|
||||
|
||||
from xonsh.aliases import xonsh_exit
|
||||
from xonsh.tools import check_for_partial_string, get_line_continuation
|
||||
|
@ -165,7 +167,7 @@ def autopair_condition():
|
|||
@Condition
|
||||
def whitespace_or_bracket_before():
|
||||
"""Check if there is whitespace or an opening
|
||||
bracket to the left of the cursor"""
|
||||
bracket to the left of the cursor"""
|
||||
d = get_app().current_buffer.document
|
||||
return bool(
|
||||
d.cursor_position == 0
|
||||
|
@ -177,7 +179,7 @@ def whitespace_or_bracket_before():
|
|||
@Condition
|
||||
def whitespace_or_bracket_after():
|
||||
"""Check if there is whitespace or a closing
|
||||
bracket to the right of the cursor"""
|
||||
bracket to the right of the cursor"""
|
||||
d = get_app().current_buffer.document
|
||||
return bool(
|
||||
d.is_cursor_at_the_end_of_line
|
||||
|
@ -186,10 +188,11 @@ def whitespace_or_bracket_after():
|
|||
)
|
||||
|
||||
|
||||
def load_xonsh_bindings(key_bindings):
|
||||
def load_xonsh_bindings() -> KeyBindingsBase:
|
||||
"""
|
||||
Load custom key bindings.
|
||||
"""
|
||||
key_bindings = KeyBindings()
|
||||
handle = key_bindings.add
|
||||
has_selection = HasSelection()
|
||||
insert_mode = ViInsertMode() | EmacsInsertMode()
|
||||
|
@ -357,3 +360,25 @@ def load_xonsh_bindings(key_bindings):
|
|||
during the previous command.
|
||||
"""
|
||||
pass
|
||||
|
||||
@handle(Keys.ControlX, Keys.ControlX, filter=has_selection)
|
||||
def _cut(event):
|
||||
""" Cut selected text. """
|
||||
data = event.current_buffer.cut_selection()
|
||||
event.app.clipboard.set_data(data)
|
||||
|
||||
@handle(Keys.ControlX, Keys.ControlC, filter=has_selection)
|
||||
def _copy(event):
|
||||
""" Copy selected text. """
|
||||
data = event.current_buffer.copy_selection()
|
||||
event.app.clipboard.set_data(data)
|
||||
|
||||
@handle(Keys.ControlV, filter=insert_mode | has_selection)
|
||||
def _yank(event):
|
||||
""" Paste selected text. """
|
||||
buff = event.current_buffer
|
||||
if buff.selection_state:
|
||||
buff.cut_selection()
|
||||
get_by_name("yank").call(event)
|
||||
|
||||
return key_bindings
|
||||
|
|
|
@ -21,7 +21,10 @@ from prompt_toolkit import ANSI
|
|||
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
||||
from prompt_toolkit.lexers import PygmentsLexer
|
||||
from prompt_toolkit.enums import EditingMode
|
||||
from prompt_toolkit.key_binding import KeyBindings
|
||||
from prompt_toolkit.key_binding.bindings.emacs import (
|
||||
load_emacs_shift_selection_bindings,
|
||||
)
|
||||
from prompt_toolkit.key_binding.key_bindings import merge_key_bindings
|
||||
from prompt_toolkit.history import ThreadedHistory
|
||||
from prompt_toolkit.shortcuts import print_formatted_text as ptk_print
|
||||
from prompt_toolkit.shortcuts import CompleteStyle
|
||||
|
@ -91,8 +94,13 @@ class PromptToolkitShell(BaseShell):
|
|||
self.history = ThreadedHistory(PromptToolkitHistory())
|
||||
self.prompter = PromptSession(history=self.history)
|
||||
self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx, self)
|
||||
self.key_bindings = KeyBindings()
|
||||
load_xonsh_bindings(self.key_bindings)
|
||||
self.key_bindings = merge_key_bindings(
|
||||
[
|
||||
load_xonsh_bindings(),
|
||||
load_emacs_shift_selection_bindings(),
|
||||
]
|
||||
)
|
||||
|
||||
# Store original `_history_matches` in case we need to restore it
|
||||
self._history_matches_orig = self.prompter.default_buffer._history_matches
|
||||
# This assumes that PromptToolkitShell is a singleton
|
||||
|
@ -282,8 +290,7 @@ class PromptToolkitShell(BaseShell):
|
|||
|
||||
@property
|
||||
def bottom_toolbar_tokens(self):
|
||||
"""Returns self._bottom_toolbar_tokens if it would yield a result
|
||||
"""
|
||||
"""Returns self._bottom_toolbar_tokens if it would yield a result"""
|
||||
if builtins.__xonsh__.env.get("BOTTOM_TOOLBAR"):
|
||||
return self._bottom_toolbar_tokens
|
||||
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
"url": "https://github.com/DangerOnTheRanger/xonsh-apt-tabcomplete",
|
||||
"description": ["Adds tabcomplete functionality to apt-get/apt-cache inside of xonsh."]
|
||||
},
|
||||
{"name": "argcomplete",
|
||||
"package": "xontrib-argcomplete",
|
||||
"url": "https://github.com/anki-code/xontrib-argcomplete",
|
||||
"description": ["Adding support of kislyuk/argcomplete to xonsh."]
|
||||
},
|
||||
{"name": "autojump",
|
||||
"package": "xontrib-autojump",
|
||||
"url": "https://github.com/sagartewari01/autojump-xonsh",
|
||||
|
|
Loading…
Add table
Reference in a new issue