Merge pull request #2716 from xonsh/ptk_2.0

Ptk 2.0 Try Again
This commit is contained in:
Morten Enemark Lund 2018-07-16 18:15:27 +02:00 committed by GitHub
commit b69fb9ab4b
Failed to generate hash of commit
69 changed files with 1198 additions and 289 deletions

View file

@ -40,10 +40,8 @@ before_install:
install:
- if [[ $BUILD_DOCS = true ]]; then
python setup.py install;
pip install -r requirements-docs.txt;
pip install pygments prompt_toolkit<2 ply psutil ipykernel matplotlib;
pip install doctr;
python setup.py install;
else
pip install -r requirements-tests.txt;
fi

View file

@ -435,7 +435,7 @@ def rewrite_imports(name, pkg, order, imps):
for start, stop, s in replacements[::-1]:
lines[start] = s
for i in range(stop - start - 1):
del lines[start+1]
del lines[start + 1]
return ''.join(lines)
@ -524,8 +524,8 @@ def rewrite_init(pkg, order, debug='DEBUG'):
if start + 1 == stop:
lines.insert(stop, s)
else:
lines[start+1] = s
lines = lines[:start+2] + lines[stop:]
lines[start + 1] = s
lines = lines[:start + 2] + lines[stop:]
init = '\n'.join(lines) + '\n'
with open(fname, 'w', encoding='utf-8', errors='surrogateescape') as f:
f.write(init)

View file

@ -1,20 +1,22 @@
name: py34-xonsh-test
channels:
- conda-forge
- defaults
dependencies:
- python=3.4
- pygments
- prompt_toolkit<2.0
- prompt_toolkit
- ply
- pytest
- pytest-timeout
- numpy
- psutil
- matplotlib=1.5.3
- matplotlib
- flake8
- coverage
- pyflakes
- pytest-cov
- codecov
# conda forge doesn't have the following for Python v3.4
- pip:
- pytest-flake8
- codecov

View file

@ -1,20 +1,20 @@
name: py35-xonsh-test
channels:
- conda-forge
- defaults
dependencies:
- python=3.5
- pygments
- prompt_toolkit<2.0
- prompt_toolkit
- ply
- pytest
- pytest-timeout
- numpy
- psutil
- matplotlib=1.5.3
- matplotlib
- flake8
- coverage
- pyflakes
- pytest-cov
- pip:
- pytest-flake8
- codecov
- pytest-flake8
- codecov

View file

@ -1,20 +1,20 @@
name: py36-xonsh-test
channels:
- conda-forge
- defaults
dependencies:
- python=3.6
- pygments
- prompt_toolkit<2.0
- prompt_toolkit
- ply
- pytest
- pytest-timeout
- numpy
- psutil
- matplotlib=1.5.3
- matplotlib
- flake8
- coverage
- pyflakes
- pytest-cov
- pip:
- pytest-flake8
- codecov
- pytest-flake8
- codecov

View file

@ -25,7 +25,11 @@ spec = importlib.util.find_spec('prompt_toolkit')
if spec is not None:
# hacky runaround to import PTK-specific events
builtins.__xonsh_env__ = Env()
from xonsh.ptk.shell import events
from xonsh.platform import ptk_version_info
if ptk_version_info()[0] < 2:
from xonsh.ptk.shell import events
else:
from xonsh.ptk2.shell import events
else:
from xonsh.events import events

17
news/ptk_2.0.rst Normal file
View file

@ -0,0 +1,17 @@
**Added:**
* Support for ``prompt_toolkit 2.0``
* The ``--shell-type`` (``$SHELL_TYPE``) may now be specified using
shortcuts, such as ``rl`` for ``readline`` and ``ptk2`` for
``prompt_toolkit2``. See ``xonsh --help`` for a full listing
of available aliases.
**Changed:** None
**Deprecated:** None
**Removed:** None
**Fixed:** None
**Security:** None

View file

@ -1,4 +1,10 @@
cloud_sptheme
numpydoc==0.5
Sphinx==1.5.6
prompt_toolkit<2
numpydoc
Sphinx
prompt_toolkit
pygments
ply
psutil
ipykernel
matplotlib
doctr

View file

@ -1,12 +1,12 @@
ply==3.8
py==1.4.31
pyflakes==1.2.3
pytest==2.9.2
pytest-flake8==0.5
pytest-cov==2.3.0
pytest-timeout==1.0.0
prompt-toolkit<2
pygments==2.1.3
codecov==2.0.5
flake8==2.6.2
coverage==4.2
ply
py
pytest
pytest-flake8
pytest-cov
pytest-timeout
prompt-toolkit
pygments
codecov
flake8
pyflakes<1.7.0,>=1.5.0
coverage

View file

@ -304,8 +304,8 @@ def main():
url='https://github.com/xonsh/xonsh',
platforms='Cross Platform',
classifiers=['Programming Language :: Python :: 3'],
packages=['xonsh', 'xonsh.ply.ply', 'xonsh.ptk', 'xonsh.parsers',
'xonsh.xoreutils', 'xontrib',
packages=['xonsh', 'xonsh.ply.ply', 'xonsh.ptk', 'xonsh.ptk2',
'xonsh.parsers', 'xonsh.xoreutils', 'xontrib',
'xonsh.completers', 'xonsh.history', 'xonsh.prompt',
'xonsh.lib'],
package_dir={'xonsh': 'xonsh', 'xontrib': 'xontrib', 'xonsh.lib': 'xonsh/lib'},
@ -332,7 +332,7 @@ def main():
}
skw['cmdclass']['develop'] = xdevelop
skw['extras_require'] = {
'ptk': ['prompt-toolkit<2.0.0'],
'ptk': ['prompt-toolkit'],
'pygments': ['pygments'],
'win': ['win_unicode_console'],
'mac': ['gnureadline'],

View file

@ -21,6 +21,7 @@ def Shell(*args, **kwargs):
@pytest.fixture
def shell(xonsh_builtins, xonsh_execer, monkeypatch):
"""Xonsh Shell Mock"""
Shell.shell_type_aliases = {'rl': 'readline'}
monkeypatch.setattr(xonsh.main, 'Shell', Shell)

View file

@ -5,18 +5,21 @@ try:
except ImportError:
pytest.mark.skip(msg='prompt_toolkit is not available')
from xonsh.ptk.history import PromptToolkitHistory
from xonsh.ptk2.history import PromptToolkitHistory
from tools import skip_if_lt_ptk2
@pytest.fixture
def history_obj():
"""Instantiate `PromptToolkitHistory` and append a line string"""
hist = PromptToolkitHistory(load_prev=False)
hist.append('line10')
hist.append_string('line10')
return hist
@skip_if_lt_ptk2
def test_obj(history_obj):
assert ['line10'] == history_obj.strings
assert ['line10'] == history_obj.get_strings()
assert len(history_obj) == 1
assert ['line10'] == [x for x in history_obj]

View file

@ -5,13 +5,13 @@ from collections import namedtuple
from unittest.mock import MagicMock, patch
import pytest
from prompt_toolkit.interface import CommandLineInterface
from prompt_toolkit.application import Application
from prompt_toolkit.document import Document
from prompt_toolkit.buffer import Buffer, AcceptAction
from prompt_toolkit.buffer import Buffer
from xonsh.tools import ON_WINDOWS
from tools import DummyEnv
from tools import DummyEnv, skip_if_lt_ptk2
Context = namedtuple('Context', ['indent', 'buffer', 'accept', 'cli', 'cr'])
@ -22,10 +22,10 @@ def ctx():
"""Context in which the ptk multiline functionality will be tested."""
builtins.__xonsh_env__ = DummyEnv()
builtins.__xonsh_env__['INDENT'] = ' '
from xonsh.ptk.key_bindings import carriage_return
from xonsh.ptk2.key_bindings import carriage_return
ptk_buffer = Buffer()
ptk_buffer.accept_action = MagicMock(name='accept', spec=AcceptAction)
cli = MagicMock(name='cli', spec=CommandLineInterface)
ptk_buffer.accept_action = MagicMock(name='accept')
cli = MagicMock(name='cli', spec=Application)
yield Context(indent=' ',
buffer=ptk_buffer,
accept=ptk_buffer.accept_action,
@ -34,6 +34,7 @@ def ctx():
del builtins.__xonsh_env__
@skip_if_lt_ptk2
def test_colon_indent(ctx):
document = Document('for i in range(5):')
ctx.buffer.set_document(document)
@ -41,6 +42,7 @@ def test_colon_indent(ctx):
assert ctx.buffer.document.current_line == ctx.indent
@skip_if_lt_ptk2
def test_dedent(ctx):
document = Document('\n'+ctx.indent+'pass')
ctx.buffer.set_document(document)
@ -53,23 +55,25 @@ def test_dedent(ctx):
assert ctx.buffer.document.current_line == ctx.indent
@skip_if_lt_ptk2
def test_nodedent(ctx):
'''don't dedent if first line of ctx.buffer'''
mock = MagicMock(return_value=True)
with patch('xonsh.ptk.key_bindings.can_compile', mock):
with patch('xonsh.ptk2.key_bindings.can_compile', mock):
document = Document('pass')
ctx.buffer.set_document(document)
ctx.cr(ctx.buffer, ctx.cli)
assert ctx.accept.mock_calls is not None
mock = MagicMock(return_value=True)
with patch('xonsh.ptk.key_bindings.can_compile', mock):
with patch('xonsh.ptk2.key_bindings.can_compile', mock):
document = Document(ctx.indent+'pass')
ctx.buffer.set_document(document)
ctx.cr(ctx.buffer, ctx.cli)
assert ctx.accept.mock_calls is not None
@skip_if_lt_ptk2
def test_continuation_line(ctx):
document = Document('\nsecond line')
ctx.buffer.set_document(document)
@ -77,9 +81,10 @@ def test_continuation_line(ctx):
assert ctx.buffer.document.current_line == ''
@skip_if_lt_ptk2
def test_trailing_slash(ctx):
mock = MagicMock(return_value=True)
with patch('xonsh.ptk.key_bindings.can_compile', mock):
with patch('xonsh.ptk2.key_bindings.can_compile', mock):
document = Document('this line will \\')
ctx.buffer.set_document(document)
ctx.cr(ctx.buffer, ctx.cli)
@ -89,18 +94,20 @@ def test_trailing_slash(ctx):
assert ctx.accept.mock_calls is not None
@skip_if_lt_ptk2
def test_cant_compile_newline(ctx):
mock = MagicMock(return_value=False)
with patch('xonsh.ptk.key_bindings.can_compile', mock):
with patch('xonsh.ptk2.key_bindings.can_compile', mock):
document = Document('for i in (1, 2, ')
ctx.buffer.set_document(document)
ctx.cr(ctx.buffer, ctx.cli)
assert ctx.buffer.document.current_line == ''
@skip_if_lt_ptk2
def test_can_compile_and_executes(ctx):
mock = MagicMock(return_value=True)
with patch('xonsh.ptk.key_bindings.can_compile', mock):
with patch('xonsh.ptk2.key_bindings.can_compile', mock):
document = Document('ls')
ctx.buffer.set_document(document)
ctx.cr(ctx.buffer, ctx.cli)

View file

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
"""Tests the xonsh replay functionality."""
from __future__ import unicode_literals, print_function
import os
import builtins
@ -21,7 +20,7 @@ def ctx():
"""Create a global Shell instance to use in all the test."""
ctx = {'PATH': []}
execer = Execer(xonsh_ctx=ctx)
builtins.__xonsh_shell__ = Shell(execer=execer, ctx=ctx)
builtins.__xonsh_shell__ = Shell(execer=execer, ctx=ctx, shell_type='none')
yield
del builtins.__xonsh_shell__

View file

@ -15,6 +15,7 @@ import pytest
from xonsh.environ import Env
from xonsh.base_shell import BaseShell
from xonsh.platform import ptk_version_info
VER_3_4 = (3, 4)
@ -48,6 +49,9 @@ skip_if_on_darwin = pytest.mark.skipif(ON_DARWIN, reason='not Mac friendly')
skip_if_on_travis = pytest.mark.skipif(ON_TRAVIS, reason='not Travis CI friendly')
skip_if_lt_ptk2 = pytest.mark.skipif(ptk_version_info()[0] < 2,
reason="prompt-tollkit <2")
def sp(cmd):
return subprocess.check_output(cmd, universal_newlines=True)

View file

@ -96,7 +96,7 @@ class Aliases(cabc.MutableMapping):
cabc.Sequence):
word_idx = line.find(word)
expansion = ' '.join(self.get(word))
line = line[:word_idx] + expansion + line[word_idx+len(word):]
line = line[:word_idx] + expansion + line[word_idx + len(word):]
return line
#
@ -298,7 +298,7 @@ def source_alias(args, stdin=None):
src += '\n'
ctx = builtins.__xonsh_ctx__
updates = {'__file__': fpath, '__name__': os.path.abspath(fpath)}
with env.swap(ARGS=args[i+1:]), swap_values(ctx, updates):
with env.swap(ARGS=args[i + 1:]), swap_values(ctx, updates):
try:
builtins.execx(src, 'exec', ctx, filename=fpath)
except Exception:
@ -455,7 +455,7 @@ def make_default_aliases():
'xexec': xexec,
'source': source_alias,
'source-zsh': ['source-foreign', 'zsh', '--sourcer=source'],
'source-bash': ['source-foreign', 'bash', '--sourcer=source'],
'source-bash': ['source-foreign', 'bash', '--sourcer=source'],
'source-cmd': source_cmd,
'source-foreign': source_foreign,
'history': xhm.history_main,

View file

@ -113,15 +113,15 @@ def _ansi_expand_style(cmap):
if key == 'NO_COLOR':
continue
elif len(val) == 0:
cmap['BOLD_'+key] = '1'
cmap['UNDERLINE_'+key] = '4'
cmap['BOLD_UNDERLINE_'+key] = '1;4'
cmap['BACKGROUND_'+key] = val
cmap['BOLD_' + key] = '1'
cmap['UNDERLINE_' + key] = '4'
cmap['BOLD_UNDERLINE_' + key] = '1;4'
cmap['BACKGROUND_' + key] = val
else:
cmap['BOLD_'+key] = '1;' + val
cmap['UNDERLINE_'+key] = '4;' + val
cmap['BOLD_UNDERLINE_'+key] = '1;4;' + val
cmap['BACKGROUND_'+key] = val.replace('38', '48', 1)
cmap['BOLD_' + key] = '1;' + val
cmap['UNDERLINE_' + key] = '4;' + val
cmap['BOLD_UNDERLINE_' + key] = '1;4;' + val
cmap['BACKGROUND_' + key] = val.replace('38', '48', 1)
def _bw_style():
@ -143,7 +143,7 @@ def _bw_style():
'RED': '',
'WHITE': '',
'YELLOW': '',
}
}
_ansi_expand_style(style)
return style
@ -242,7 +242,7 @@ def _default_style():
'BACKGROUND_INTENSE_PURPLE': '0;105', # PURPLE
'BACKGROUND_INTENSE_CYAN': '0;106', # CYAN
'BACKGROUND_INTENSE_WHITE': '0;107', # WHITE
}
}
return style
@ -265,7 +265,7 @@ def _monokai_style():
'INTENSE_RED': '38;5;197',
'INTENSE_WHITE': '38;5;15',
'INTENSE_YELLOW': '38;5;186',
}
}
_ansi_expand_style(style)
return style
@ -293,7 +293,7 @@ def _algol_style():
'RED': '38;5;09',
'WHITE': '38;5;102',
'YELLOW': '38;5;09',
}
}
_ansi_expand_style(style)
return style
@ -317,7 +317,7 @@ def _algol_nu_style():
'RED': '38;5;09',
'WHITE': '38;5;102',
'YELLOW': '38;5;09',
}
}
_ansi_expand_style(style)
return style
@ -365,7 +365,7 @@ def _borland_style():
'RED': '38;5;124',
'WHITE': '38;5;145',
'YELLOW': '38;5;124',
}
}
_ansi_expand_style(style)
return style
@ -461,7 +461,7 @@ def _fruity_style():
'RED': '38;5;09',
'WHITE': '38;5;187',
'YELLOW': '38;5;202',
}
}
_ansi_expand_style(style)
return style
@ -485,7 +485,7 @@ def _igor_style():
'RED': '38;5;166',
'WHITE': '38;5;163',
'YELLOW': '38;5;166',
}
}
_ansi_expand_style(style)
return style
@ -509,7 +509,7 @@ def _lovelace_style():
'RED': '38;5;124',
'WHITE': '38;5;102',
'YELLOW': '38;5;130',
}
}
_ansi_expand_style(style)
return style
@ -533,7 +533,7 @@ def _manni_style():
'RED': '38;5;124',
'WHITE': '38;5;145',
'YELLOW': '38;5;166',
}
}
_ansi_expand_style(style)
return style
@ -557,7 +557,7 @@ def _murphy_style():
'RED': '38;5;124',
'WHITE': '38;5;145',
'YELLOW': '38;5;166',
}
}
_ansi_expand_style(style)
return style
@ -581,7 +581,7 @@ def _native_style():
'RED': '38;5;124',
'WHITE': '38;5;145',
'YELLOW': '38;5;124',
}
}
_ansi_expand_style(style)
return style
@ -605,7 +605,7 @@ def _paraiso_dark_style():
'RED': '38;5;203',
'WHITE': '38;5;79',
'YELLOW': '38;5;214',
}
}
_ansi_expand_style(style)
return style
@ -629,7 +629,7 @@ def _paraiso_light_style():
'RED': '38;5;16',
'WHITE': '38;5;102',
'YELLOW': '38;5;214',
}
}
_ansi_expand_style(style)
return style
@ -653,7 +653,7 @@ def _pastie_style():
'RED': '38;5;124',
'WHITE': '38;5;145',
'YELLOW': '38;5;130',
}
}
_ansi_expand_style(style)
return style
@ -677,7 +677,7 @@ def _perldoc_style():
'RED': '38;5;124',
'WHITE': '38;5;145',
'YELLOW': '38;5;166',
}
}
_ansi_expand_style(style)
return style
@ -701,7 +701,7 @@ def _rrt_style():
'RED': '38;5;09',
'WHITE': '38;5;117',
'YELLOW': '38;5;09',
}
}
_ansi_expand_style(style)
return style
@ -725,7 +725,7 @@ def _tango_style():
'RED': '38;5;124',
'WHITE': '38;5;15',
'YELLOW': '38;5;94',
}
}
_ansi_expand_style(style)
return style
@ -749,7 +749,7 @@ def _trac_style():
'RED': '38;5;124',
'WHITE': '38;5;145',
'YELLOW': '38;5;100',
}
}
_ansi_expand_style(style)
return style
@ -773,7 +773,7 @@ def _vim_style():
'RED': '38;5;160',
'WHITE': '38;5;188',
'YELLOW': '38;5;160',
}
}
_ansi_expand_style(style)
return style
@ -797,7 +797,7 @@ def _vs_style():
'RED': '38;5;124',
'WHITE': '38;5;31',
'YELLOW': '38;5;124',
}
}
_ansi_expand_style(style)
return style
@ -821,7 +821,7 @@ def _xcode_style():
'RED': '38;5;160',
'WHITE': '38;5;60',
'YELLOW': '38;5;94',
}
}
_ansi_expand_style(style)
return style
@ -853,13 +853,13 @@ ANSI_STYLES = LazyDict({
'vim': _vim_style,
'vs': _vs_style,
'xcode': _xcode_style,
}, globals(), 'ANSI_STYLES')
}, globals(), 'ANSI_STYLES')
del (_algol_style, _algol_nu_style, _autumn_style, _borland_style, _bw_style,
_colorful_style, _default_style, _emacs_style, _friendly_style,
_fruity_style, _igor_style, _lovelace_style, _manni_style,
_monokai_style, _murphy_style, _native_style, _paraiso_dark_style,
_paraiso_light_style, _pastie_style, _perldoc_style, _rrt_style,
_paraiso_light_style, _pastie_style, _perldoc_style, _rrt_style,
_tango_style, _trac_style, _vim_style, _vs_style, _xcode_style)
@ -872,13 +872,13 @@ def make_ansi_style(palette):
for name, t in BASE_XONSH_COLORS.items():
closest = find_closest_color(t, palette)
if len(closest) == 3:
closest = ''.join([a*2 for a in closest])
closest = ''.join([a * 2 for a in closest])
short = rgb2short(closest)[0]
style[name] = '38;5;' + short
style['BOLD_'+name] = '1;38;5;' + short
style['UNDERLINE_'+name] = '4;38;5;' + short
style['BOLD_UNDERLINE_'+name] = '1;4;38;5;' + short
style['BACKGROUND_'+name] = '48;5;' + short
style['BOLD_' + name] = '1;38;5;' + short
style['UNDERLINE_' + name] = '4;38;5;' + short
style['BOLD_UNDERLINE_' + name] = '1;4;38;5;' + short
style['BACKGROUND_' + name] = '48;5;' + short
return style

View file

@ -478,9 +478,9 @@ def pdump(s, **kwargs):
closer = closers[openers.find(s[i])]
j = s.rfind(closer)
if j == -1 or j <= i:
return s[:i+1] + '\n' + textwrap.indent(pdump(s[i+1:]), ' ')
pre = s[:i+1] + '\n'
mid = s[i+1:j]
return s[:i + 1] + '\n' + textwrap.indent(pdump(s[i + 1:]), ' ')
pre = s[:i + 1] + '\n'
mid = s[i + 1:j]
post = '\n' + s[j:]
mid = textwrap.indent(pdump(mid), ' ')
if '(' in post or '[' in post or '{' in post:

View file

@ -375,7 +375,7 @@ class BaseShell(object):
rtn=info['rtn'],
out=info.get('out', None),
ts=info['ts']
)
)
if hist is not None:
hist.append(info)
hist.last_cmd_rtn = hist.last_cmd_out = None
@ -436,7 +436,7 @@ class BaseShell(object):
self.reset_buffer()
return src, code
lincont = get_line_continuation()
if src.endswith(lincont+'\n'):
if src.endswith(lincont + '\n'):
self.need_more_lines = True
return src, None
try:

View file

@ -22,7 +22,7 @@ def _splitpath(path, sofar=[]):
@lazyobject
def _CHARACTER_MAP():
cmap = {chr(o): '_%s' % chr(o+32) for o in range(65, 91)}
cmap = {chr(o): '_%s' % chr(o + 32) for o in range(65, 91)}
cmap.update({'.': '_.', '_': '__'})
return cmap

View file

@ -303,7 +303,7 @@ def CLUT():
('253', 'dadada'),
('254', 'e4e4e4'),
('255', 'eeeeee'),
]
]
def _str2hex(hexstr):
@ -358,8 +358,8 @@ def rgb_to_256(rgb):
res = []
for part in parts:
i = 0
while i < len(incs)-1:
s, b = incs[i], incs[i+1] # smaller, bigger
while i < len(incs) - 1:
s, b = incs[i], incs[i + 1] # smaller, bigger
if s <= part <= b:
s1 = abs(s - part)
b1 = abs(b - part)
@ -391,11 +391,11 @@ def rgb_to_ints(rgb):
if len(rgb) == 6:
return tuple([int(h, 16) for h in RE_RGB6.split(rgb)[1:4]])
else:
return tuple([int(h*2, 16) for h in RE_RGB3.split(rgb)[1:4]])
return tuple([int(h * 2, 16) for h in RE_RGB3.split(rgb)[1:4]])
def color_dist(x, y):
return math.sqrt((x[0]-y[0])**2 + (x[1]-y[1])**2 + (x[2]-y[2])**2)
return math.sqrt((x[0] - y[0])**2 + (x[1] - y[1])**2 + (x[2] - y[2])**2)
def find_closest_color(x, palette):

View file

@ -41,6 +41,7 @@ class Completer(object):
res = out
lprefix = len(prefix)
if res is not None and len(res) != 0:
def sortkey(s): return s.lstrip(''''"''').lower()
def sortkey(s):
return s.lstrip(''''"''').lower()
return tuple(sorted(res, key=sortkey)), lprefix
return set(), lprefix

View file

@ -31,4 +31,4 @@ def default_completers():
('import', complete_import),
('python', complete_python),
('path', complete_path),
])
])

View file

@ -110,9 +110,9 @@ def _dots(prefix):
if slash == '\\':
slash = ''
if prefix in {'', '.'}:
return ('.'+slash, '..'+slash)
return ('.' + slash, '..' + slash)
elif prefix == '..':
return ('..'+slash,)
return ('..' + slash,)
else:
return ()

View file

@ -24,7 +24,7 @@ def XONSH_EXPR_TOKENS():
'+', '-', '/', '//', '%', '**', '|', '&', '~', '^', '>>', '<<', '<',
'<=', '>', '>=', '==', '!=', ',', '?', '??', '$(',
'${', '$[', '...', '![', '!(', '@(', '@$(', '@',
}
}
@xl.lazyobject
@ -36,7 +36,7 @@ def XONSH_STMT_TOKENS():
'yield ', '-', '/', '//', '%', '**', '|', '&', '~', '^', '>>', '<<',
'<', '<=', '->', '=', '+=', '-=', '*=', '/=', '%=', '**=',
'>>=', '<<=', '&=', '^=', '|=', '//=', ';', ':', '..',
}
}
@xl.lazyobject
@ -95,7 +95,7 @@ def complete_python_mode(prefix, line, start, end, ctx):
if not (prefix.startswith('@(') or prefix.startswith('${')):
return set()
prefix_start = prefix[:2]
python_matches = complete_python(prefix[2:], line, start-2, end-2, ctx)
python_matches = complete_python(prefix[2:], line, start - 2, end - 2, ctx)
if isinstance(python_matches, cabc.Sequence):
python_matches = python_matches[0]
return set(prefix_start + i for i in python_matches)

View file

@ -29,5 +29,5 @@ def justify(s, max_length, left_pad=0):
characters long, padding all lines but the first on the left with the
string left_pad.
"""
txt = textwrap.wrap(s, width=max_length, subsequent_indent=' '*left_pad)
txt = textwrap.wrap(s, width=max_length, subsequent_indent=' ' * left_pad)
return '\n'.join(txt)

View file

@ -405,7 +405,7 @@ def DEFAULT_DOCS():
'AUTO_SUGGEST_IN_COMPLETIONS': VarDocs(
'Places the auto-suggest result as the first option in the completions. '
'This enables you to tab complete the auto-suggestion.'
),
),
'BASH_COMPLETIONS': VarDocs(
'This is a list (or tuple) of strings that specifies where the '
'``bash_completion`` script may be found. '
@ -614,7 +614,7 @@ def DEFAULT_DOCS():
'Completions display is evaluated and presented whenever a key is '
'pressed. This avoids the need to press TAB, except to cycle through '
'the possibilities. This currently only affects the prompt-toolkit shell.'
),
),
'UPDATE_OS_ENVIRON': VarDocs(
"If True ``os_environ`` will always be updated "
"when the xonsh environment changes. The environment can be reset to "

View file

@ -255,7 +255,7 @@ class EventManager:
def _mkevent(name, species=Event, doc=None):
# NOTE: Also used in `xonsh_events` test fixture
# (A little bit of magic to enable docstrings to work right)
return type(name, (species,), {'__doc__': doc, '__module__': 'xonsh.events', '__qualname__': 'events.'+name})()
return type(name, (species,), {'__doc__': doc, '__module__': 'xonsh.events', '__qualname__': 'events.' + name})()
def transmogrify(self, name, species):
"""

View file

@ -194,9 +194,9 @@ class Execer(object):
input = '\n'.join(lines)
continue
if last_error_line > 1 and lines[idx-1].rstrip()[-1:] == ':':
if last_error_line > 1 and lines[idx - 1].rstrip()[-1:] == ':':
# catch non-indented blocks and raise error.
prev_indent = len(lines[idx-1]) - len(lines[idx-1].lstrip())
prev_indent = len(lines[idx - 1]) - len(lines[idx - 1].lstrip())
curr_indent = len(lines[idx]) - len(lines[idx].lstrip())
if prev_indent == curr_indent:
raise original_error

View file

@ -49,8 +49,8 @@ except ImportError:
raise TypeError("expected __fspath__() to return str or bytes, "
"not " + type(path).__name__)
raise TypeError("expected str, bytes or os.PathLike object, not "
+ path_type.__name__)
raise TypeError("expected str, bytes or os.PathLike object, not " +
path_type.__name__)
def _fscodec():
encoding = sys.getfilesystemencoding()

View file

@ -35,7 +35,7 @@ _object_init_docstring = LazyObject(lambda: object.__init__.__doc__,
_builtin_type_docstrings = LazyObject(lambda: {
t.__doc__
for t in (types.ModuleType, types.MethodType, types.FunctionType)
}, globals(), '_builtin_type_docstrings')
}, globals(), '_builtin_type_docstrings')
_builtin_func_type = LazyObject(lambda: type(all), globals(),
'_builtin_func_type')
@ -50,7 +50,7 @@ info_fields = LazyObject(lambda: [
# These won't be printed but will be used to determine how to
# format the object
'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
], globals(), 'info_fields')
], globals(), 'info_fields')
def object_info(**kw):

View file

@ -73,7 +73,7 @@ class XonshKernel(Kernel):
if n == 0:
return
lower = range(0, n, chunksize)
upper = range(chunksize, n+chunksize, chunksize)
upper = range(chunksize, n + chunksize, chunksize)
for l, u in zip(lower, upper):
response = {'name': name, 'text': s[l:u], }
self.send_response(self.iopub_socket, 'stream', response)

View file

@ -42,7 +42,7 @@ class LazyObject(object):
'load': load,
'ctx': ctx,
'name': name,
}
}
def _lazy_obj(self):
d = self._lasdo
@ -258,7 +258,7 @@ class BackgroundModuleProxy(types.ModuleType):
self.__dct__ = {
'loaded': False,
'modname': modname,
}
}
def __getattribute__(self, name):
passthrough = frozenset({'__dct__', '__class__', '__spec__'})

View file

@ -242,7 +242,7 @@ def special_handlers():
(ERRORTOKEN, ' '): handle_error_space,
(ERRORTOKEN, '\\\n'): handle_error_linecont,
(ERRORTOKEN, '\\\r\n'): handle_error_linecont,
}
}
_make_matcher_handler('(', 'LPAREN', True, ')', sh)
_make_matcher_handler('[', 'LBRACKET', True, ']', sh)
_make_matcher_handler('{', 'LBRACE', True, '}', sh)
@ -426,6 +426,6 @@ class Lexer(object):
'DOLLAR_LBRACKET', # $[
'ATDOLLAR_LPAREN', # @$(
'ERRORTOKEN', # whoops!
) + tuple(i.upper() for i in kwmod.kwlist)
) + tuple(i.upper() for i in kwmod.kwlist)
self._tokens = t
return self._tokens

View file

@ -167,7 +167,7 @@ def parser():
'Possible options: readline, prompt_toolkit, random. '
'Warning! If set this overrides $SHELL_TYPE variable.',
dest='shell_type',
choices=('readline', 'prompt_toolkit', 'best', 'random'),
choices=tuple(Shell.shell_type_aliases.keys()),
default=None)
p.add_argument('--timings',
help='Prints timing information before the prompt is shown. '

View file

@ -2197,7 +2197,7 @@ class BaseParser(object):
pass
elif isinstance(p1, ast.Slice) or \
any([isinstance(x, ast.Slice) for x in p2]):
p1 = ast.ExtSlice(dims=[p1]+p2)
p1 = ast.ExtSlice(dims=[p1] + p2)
else:
p1.value = ast.Tuple(elts=[p1.value] + [x.value for x in p2],
ctx=ast.Load(), lineno=p1.lineno,

View file

@ -146,6 +146,15 @@ def ptk_above_min_supported():
return ptk_version_info()[:2] >= minimum_required_ptk_version
@functools.lru_cache(1)
def ptk_shell_type():
"""Returns the prompt_toolkit shell type based on the installed version."""
if ptk_version_info()[:2] < (2, 0):
return 'prompt_toolkit1'
else:
return 'prompt_toolkit2'
@functools.lru_cache(1)
def ptk_below_max_supported():
ptk_max_version_cutoff = (2, 0)
@ -181,7 +190,7 @@ def pathsplit(p):
without a drive.
"""
n = len(p)
while n and p[n-1] not in seps:
while n and p[n - 1] not in seps:
n -= 1
pre = p[:n]
pre = pre.rstrip(seps) or pre
@ -274,14 +283,14 @@ if PYTHON_VERSION_INFO < (3, 5, 0):
return self.__path__.is_dir()
else:
return not self.__path__.is_symlink() \
and self.__path__.is_dir()
and self.__path__.is_dir()
def is_file(self, *, follow_symlinks=True):
if follow_symlinks:
return self.__path__.is_file()
else:
return not self.__path__.is_symlink() \
and self.__path__.is_file()
and self.__path__.is_file()
def stat(self, *, follow_symlinks=True):
return os.stat(self.path, follow_symlinks=follow_symlinks)

View file

@ -802,8 +802,7 @@ def for_type_by_name(type_module, type_name, func, dtp=None):
#: printers for the default singletons
_singleton_pprinters = LazyObject(lambda: dict.fromkeys(
map(id, [None, True, False, Ellipsis,
NotImplemented]), _repr_pprint),
globals(), '_singleton_pprinters')
NotImplemented]), _repr_pprint), globals(), '_singleton_pprinters')
def _defaultdict_pprint(obj, p, cycle):

View file

@ -96,9 +96,9 @@ class QueueReader:
"""Returns whether or not the queue is fully read and the reader is
closed.
"""
return (self.closed
and (self.thread is None or not self.thread.is_alive())
and self.queue.empty())
return (self.closed and
(self.thread is None or not self.thread.is_alive()) and
self.queue.empty())
def read_queue(self):
"""Reads a single chunk from the queue. This is blocking if
@ -272,7 +272,7 @@ def _expand_console_buffer(cols, max_offset, expandsize, orig_posize, fd):
# expand it so that we can read from it successfully.
if cols == 0:
return orig_posize[-1], max_offset, orig_posize
rows = ((max_offset + expandsize)//cols) + 1
rows = ((max_offset + expandsize) // cols) + 1
winutils.set_console_screen_buffer_size(cols, rows, fd=fd)
orig_posize = orig_posize[:3] + (rows,)
max_offset = (rows - 1) * cols
@ -328,7 +328,7 @@ def populate_console(reader, fd, buffer, chunksize, queue, expandsize=None):
x, y, cols, rows = posize = winutils.get_position_size(fd)
pre_x = pre_y = -1
orig_posize = posize
offset = (cols*y) + x
offset = (cols * y) + x
max_offset = (rows - 1) * cols
# I believe that there is a bug in PTK that if we reset the
# cursor position, the cursor on the next prompt is accidentally on
@ -340,7 +340,7 @@ def populate_console(reader, fd, buffer, chunksize, queue, expandsize=None):
# winutils.set_console_cursor_position(x, y, fd=fd)
while True:
posize = winutils.get_position_size(fd)
offset = (cols*y) + x
offset = (cols * y) + x
if ((posize[1], posize[0]) <= (y, x) and posize[2:] == (cols, rows)) or \
(pre_x == x and pre_y == y):
# already at or ahead of the current cursor position.
@ -382,11 +382,11 @@ def populate_console(reader, fd, buffer, chunksize, queue, expandsize=None):
time.sleep(reader.timeout)
continue
cur_x, cur_y = posize[0], posize[1]
cur_offset = (cols*cur_y) + cur_x
beg_offset = (cols*y) + x
cur_offset = (cols * cur_y) + cur_x
beg_offset = (cols * y) + x
end_offset = beg_offset + nread
if end_offset > cur_offset and cur_offset != max_offset:
buf = buf[:cur_offset-end_offset]
buf = buf[:cur_offset - end_offset]
# convert to lines
xshift = cols - x
yshift = (nread // cols) + (1 if nread % cols > 0 else 0)
@ -895,7 +895,7 @@ class PopenThread(threading.Thread):
if s is None:
rtn = self.returncode
if rtn is not None and rtn != 0:
s = (-1*rtn, rtn < 0 if ON_WINDOWS else os.WCOREDUMP(rtn))
s = (-1 * rtn, rtn < 0 if ON_WINDOWS else os.WCOREDUMP(rtn))
return s
@signal.setter
@ -1345,15 +1345,14 @@ class ProcProxyThread(threading.Thread):
# run the function itself
try:
with STDOUT_DISPATCHER.register(sp_stdout), \
STDERR_DISPATCHER.register(sp_stderr), \
redirect_stdout(STDOUT_DISPATCHER), \
redirect_stderr(STDERR_DISPATCHER):
STDERR_DISPATCHER.register(sp_stderr), \
redirect_stdout(STDOUT_DISPATCHER), \
redirect_stderr(STDERR_DISPATCHER):
r = self.f(self.args, sp_stdin, sp_stdout, sp_stderr, spec)
except SystemExit as e:
r = e.code if isinstance(e.code, int) else int(bool(e.code))
except OSError as e:
status = still_writable(self.c2pwrite) and \
still_writable(self.errwrite)
status = still_writable(self.c2pwrite) and still_writable(self.errwrite)
if status:
# stdout and stderr are still writable, so error must
# come from function itself.
@ -1671,13 +1670,13 @@ def SIGNAL_MESSAGES():
signal.SIGILL: 'Illegal instructions',
signal.SIGTERM: 'Terminated',
signal.SIGSEGV: 'Segmentation fault',
}
}
if ON_POSIX:
sm.update({
signal.SIGQUIT: 'Quit',
signal.SIGHUP: 'Hangup',
signal.SIGKILL: 'Killed',
})
})
return sm
@ -1811,8 +1810,8 @@ class CommandPipeline:
timeout = builtins.__xonsh_env__.get('XONSH_PROC_FREQUENCY')
# get the correct stdout
stdout = proc.stdout
if ((stdout is None or spec.stdout is None or not safe_readable(stdout))
and spec.captured_stdout is not None):
if ((stdout is None or spec.stdout is None or not safe_readable(stdout)) and
spec.captured_stdout is not None):
stdout = spec.captured_stdout
if hasattr(stdout, 'buffer'):
stdout = stdout.buffer
@ -1841,8 +1840,8 @@ class CommandPipeline:
return
# get the correct stderr
stderr = proc.stderr
if ((stderr is None or spec.stderr is None or not safe_readable(stderr))
and spec.captured_stderr is not None):
if ((stderr is None or spec.stderr is None or not safe_readable(stderr)) and
spec.captured_stderr is not None):
stderr = spec.captured_stderr
if hasattr(stderr, 'buffer'):
stderr = stderr.buffer

View file

@ -66,7 +66,7 @@ def _dynamically_collapsed_pwd():
max(1, remaining_space_for_text // (len(pwd) - i))))
remaining_space_for_text -= part_len
if len(part) > part_len:
reduced_part = part[0:part_len-len(elision_char)] + elision_char
reduced_part = part[0:part_len - len(elision_char)] + elision_char
parts.append(reduced_part)
else:
parts.append(part)

View file

@ -86,13 +86,13 @@ def _get_stash(gitdir):
def _gitoperation(gitdir):
files = (
('rebase-merge', 'REBASE'),
('rebase-apply', 'AM/REBASE'),
('MERGE_HEAD', 'MERGING'),
('CHERRY_PICK_HEAD', 'CHERRY-PICKING'),
('REVERT_HEAD', 'REVERTING'),
('BISECT_LOG', 'BISECTING'),
)
('rebase-merge', 'REBASE'),
('rebase-apply', 'AM/REBASE'),
('MERGE_HEAD', 'MERGING'),
('CHERRY_PICK_HEAD', 'CHERRY-PICKING'),
('REVERT_HEAD', 'REVERTING'),
('BISECT_LOG', 'BISECTING'),
)
return [f[1] for f in files
if os.path.exists(os.path.join(gitdir, f[0]))]

View file

@ -29,7 +29,7 @@ class PromptToolkitCompleter(Completer):
should_complete = (
complete_event.completion_requested or
env.get('UPDATE_COMPLETIONS_ON_KEYPRESS')
)
)
# Only generate completions when the user hits tab.
if not should_complete or self.completer is None:
return

View file

@ -149,9 +149,9 @@ def whitespace_or_bracket_before(cli):
"""Check if there is whitespace or an opening
bracket to the left of the cursor"""
d = cli.current_buffer.document
return bool(d.cursor_position == 0
or d.char_before_cursor.isspace()
or d.char_before_cursor in '([{')
return bool(d.cursor_position == 0 or
d.char_before_cursor.isspace() or
d.char_before_cursor in '([{')
@Condition
@ -159,9 +159,9 @@ def whitespace_or_bracket_after(cli):
"""Check if there is whitespace or a closing
bracket to the right of the cursor"""
d = cli.current_buffer.document
return bool(d.is_cursor_at_the_end_of_line
or d.current_char.isspace()
or d.current_char in ')]}')
return bool(d.is_cursor_at_the_end_of_line or
d.current_char.isspace() or
d.current_char in ')]}')
def load_xonsh_bindings(key_bindings_manager):

View file

@ -46,7 +46,7 @@ class PromptToolkitShell(BaseShell):
'enable_auto_suggest_bindings': True,
'enable_search': True,
'enable_abort_and_exit_bindings': True,
}
}
self.key_bindings_manager = KeyBindingManager(**key_bindings_manager_args)
load_xonsh_bindings(self.key_bindings_manager)
# This assumes that PromptToolkitShell is a singleton
@ -218,11 +218,11 @@ class PromptToolkitShell(BaseShell):
dots = builtins.__xonsh_env__.get('MULTILINE_PROMPT')
dots = dots() if callable(dots) else dots
if dots is None:
return [(Token, ' '*(width + 1))]
return [(Token, ' ' * (width + 1))]
basetoks = self.format_color(dots)
baselen = sum(len(t[1]) for t in basetoks)
if baselen == 0:
return [(Token, ' '*(width + 1))]
return [(Token, ' ' * (width + 1))]
toks = basetoks * (width // baselen)
n = width % baselen
count = 0
@ -234,7 +234,7 @@ class PromptToolkitShell(BaseShell):
elif newcount <= n:
toks.append(tok)
else:
toks.append((tok[0], tok[1][:n-count]))
toks.append((tok[0], tok[1][:n - count]))
count = newcount
if n <= count:
break

5
xonsh/ptk2/__init__.py Normal file
View file

@ -0,0 +1,5 @@
# must come before ptk / pygments imports
from xonsh.lazyasd import load_module_in_background
load_module_in_background('pkg_resources', debug='XONSH_DEBUG',
replacements={'pygments.plugin': 'pkg_resources'})

110
xonsh/ptk2/completer.py Normal file
View file

@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
"""Completer implementation to use with prompt_toolkit."""
import os
import builtins
from prompt_toolkit.layout.dimension import LayoutDimension
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
class PromptToolkitCompleter(Completer):
"""Simple prompt_toolkit Completer object.
It just redirects requests to normal Xonsh completer.
"""
def __init__(self, completer, ctx, shell):
"""Takes instance of xonsh.completer.Completer, the xonsh execution
context, and the shell instance itself.
"""
self.completer = completer
self.ctx = ctx
self.shell = shell
self.hist_suggester = AutoSuggestFromHistory()
def get_completions(self, document, complete_event):
"""Returns a generator for list of completions."""
env = builtins.__xonsh_env__
should_complete = (
complete_event.completion_requested or
env.get('UPDATE_COMPLETIONS_ON_KEYPRESS')
)
# Only generate completions when the user hits tab.
if not should_complete or self.completer is None:
return
# generate actual completions
line = document.current_line.lstrip()
line_ex = builtins.aliases.expand_alias(line)
endidx = document.cursor_position_col
begidx = (line[:endidx].rfind(' ') + 1
if line[:endidx].rfind(' ') >= 0 else 0)
prefix = line[begidx:endidx]
expand_offset = len(line_ex) - len(line)
# get normal completions
completions, l = self.completer.complete(prefix, line_ex,
begidx + expand_offset,
endidx + expand_offset,
self.ctx)
# completions from auto suggest
sug_comp = None
if env.get('AUTO_SUGGEST') and env.get('AUTO_SUGGEST_IN_COMPLETIONS'):
sug_comp = self.suggestion_completion(document, line)
if sug_comp is None:
pass
elif len(completions) == 0:
completions = (sug_comp,)
else:
completions = set(completions)
completions.discard(sug_comp)
completions = (sug_comp,) + tuple(sorted(completions))
# reserve space, if needed.
if len(completions) <= 1:
pass
elif len(os.path.commonprefix(completions)) <= len(prefix):
self.reserve_space()
# Find common prefix (strip quoting)
c_prefix = os.path.commonprefix([a.strip('\'"') for a in completions])
# Find last split symbol, do not trim the last part
while c_prefix:
if c_prefix[-1] in r'/\.:@,':
break
c_prefix = c_prefix[:-1]
# yield completions
if sug_comp is None:
pre = min(document.cursor_position_col - begidx, len(c_prefix))
else:
pre = len(c_prefix)
for comp in completions:
# do not display quote
disp = comp[pre:].strip('\'"')
yield Completion(comp, -l, display=disp)
def suggestion_completion(self, document, line):
"""Provides a completion based on the current auto-suggestion."""
app = self.shell.prompter.app
sug = self.hist_suggester.get_suggestion(app.current_buffer, document)
if sug is None:
return None
comp, _, _ = sug.text.partition(' ')
_, _, prev = line.rpartition(' ')
return prev + comp
def reserve_space(self):
app = builtins.__xonsh_shell__.shell.prompter.app
window = app.layout.container.children[0].content.children[1].content
if window and window.render_info:
h = window.render_info.content_height
r = builtins.__xonsh_env__.get('COMPLETIONS_MENU_ROWS')
size = h + r
def comp_height(cli):
# If there is an autocompletion menu to be shown, make sure that o
# layout has at least a minimal height in order to display it.
if not cli.is_done:
return LayoutDimension(min=size)
else:
return LayoutDimension()
window._height = comp_height

41
xonsh/ptk2/history.py Normal file
View file

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
"""History object for use with prompt_toolkit."""
import builtins
import prompt_toolkit.history
class PromptToolkitHistory(prompt_toolkit.history.History):
"""History class that implements the prompt-toolkit history interface
with the xonsh backend.
"""
def __init__(self, load_prev=True, *args, **kwargs):
"""Initialize history object."""
super().__init__()
self.load_prev = load_prev
def store_string(self, entry):
pass
def load_history_strings(self):
"""Loads synchronous history strings"""
if not self.load_prev:
return
hist = builtins.__xonsh_history__
if hist is None:
return
for cmd in hist.all_items():
line = cmd['inp'].rstrip()
strs = self.get_strings()
if len(strs) == 0 or line != strs[-1]:
yield line
def __getitem__(self, index):
return self.get_strings()[index]
def __len__(self):
return len(self.get_strings())
def __iter__(self):
return iter(self.get_strings())

371
xonsh/ptk2/key_bindings.py Normal file
View file

@ -0,0 +1,371 @@
# -*- coding: utf-8 -*-
"""Key bindings for prompt_toolkit xonsh shell."""
import builtins
from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.filters import (Condition, IsMultiline, HasSelection,
EmacsInsertMode, ViInsertMode)
from prompt_toolkit.keys import Keys
from prompt_toolkit.application.current import get_app
from xonsh.aliases import xonsh_exit
from xonsh.tools import check_for_partial_string, get_line_continuation
from xonsh.shell import transform_command
env = builtins.__xonsh_env__
DEDENT_TOKENS = frozenset(['raise', 'return', 'pass', 'break', 'continue'])
def carriage_return(b, cli, *, autoindent=True):
"""Preliminary parser to determine if 'Enter' key should send command to the
xonsh parser for execution or should insert a newline for continued input.
Current 'triggers' for inserting a newline are:
- Not on first line of buffer and line is non-empty
- Previous character is a colon (covers if, for, etc...)
- User is in an open paren-block
- Line ends with backslash
- Any text exists below cursor position (relevant when editing previous
multiline blocks)
"""
doc = b.document
at_end_of_line = _is_blank(doc.current_line_after_cursor)
current_line_blank = _is_blank(doc.current_line)
indent = env.get('INDENT') if autoindent else ''
partial_string_info = check_for_partial_string(doc.text)
in_partial_string = (partial_string_info[0] is not None and
partial_string_info[1] is None)
# indent after a colon
if (doc.current_line_before_cursor.strip().endswith(':') and
at_end_of_line):
b.newline(copy_margin=autoindent)
b.insert_text(indent, fire_event=False)
# if current line isn't blank, check dedent tokens
elif (not current_line_blank and
doc.current_line.split(maxsplit=1)[0] in DEDENT_TOKENS and
doc.line_count > 1):
b.newline(copy_margin=autoindent)
b.delete_before_cursor(count=len(indent))
elif (not doc.on_first_line and not current_line_blank):
b.newline(copy_margin=autoindent)
elif (doc.current_line.endswith(get_line_continuation())):
b.newline(copy_margin=autoindent)
elif (doc.find_next_word_beginning() is not None and
(any(not _is_blank(i) for i in doc.lines_from_current[1:]))):
b.newline(copy_margin=autoindent)
elif not current_line_blank and not can_compile(doc.text):
b.newline(copy_margin=autoindent)
elif current_line_blank and in_partial_string:
b.newline(copy_margin=autoindent)
else:
try:
b.accept_action.validate_and_handle(cli, b)
except AttributeError: # PTK 2.0
b.validate_and_handle()
def _is_blank(l):
return len(l.strip()) == 0
def can_compile(src):
"""Returns whether the code can be compiled, i.e. it is valid xonsh."""
src = src if src.endswith('\n') else src + '\n'
src = transform_command(src, show_diff=False)
src = src.lstrip()
try:
builtins.__xonsh_execer__.compile(src, mode='single', glbs=None,
locs=builtins.__xonsh_ctx__)
rtn = True
except SyntaxError:
rtn = False
except Exception:
rtn = True
return rtn
@Condition
def tab_insert_indent():
"""Check if <Tab> should insert indent instead of starting autocompletion.
Checks if there are only whitespaces before the cursor - if so indent
should be inserted, otherwise autocompletion.
"""
before_cursor = get_app().current_buffer.document.current_line_before_cursor
return bool(before_cursor.isspace())
@Condition
def beginning_of_line():
"""Check if cursor is at beginning of a line other than the first line in a
multiline document
"""
app = get_app()
before_cursor = app.current_buffer.document.current_line_before_cursor
return bool(len(before_cursor) == 0 and
not app.current_buffer.document.on_first_line)
@Condition
def end_of_line():
"""Check if cursor is at the end of a line other than the last line in a
multiline document
"""
d = get_app().current_buffer.document
at_end = d.is_cursor_at_the_end_of_line
last_line = d.is_cursor_at_the_end
return bool(at_end and not last_line)
@Condition
def should_confirm_completion():
"""Check if completion needs confirmation"""
return (builtins.__xonsh_env__.get('COMPLETIONS_CONFIRM') and
get_app().current_buffer.complete_state)
# Copied from prompt-toolkit's key_binding/bindings/basic.py
@Condition
def ctrl_d_condition():
"""Ctrl-D binding is only active when the default buffer is selected and
empty.
"""
if builtins.__xonsh_env__.get("IGNOREEOF"):
raise EOFError
else:
app = get_app()
try:
buffer_name = app.current_buffer_name
except AttributeError: # PTK 2.0
buffer_name = app.current_buffer.name
return buffer_name == DEFAULT_BUFFER and not app.current_buffer.text
@Condition
def autopair_condition():
"""Check if XONSH_AUTOPAIR is set"""
return builtins.__xonsh_env__.get("XONSH_AUTOPAIR", False)
@Condition
def whitespace_or_bracket_before():
"""Check if there is whitespace or an opening
bracket to the left of the cursor"""
d = get_app().current_buffer.document
return bool(d.cursor_position == 0 or
d.char_before_cursor.isspace() or
d.char_before_cursor in '([{')
@Condition
def whitespace_or_bracket_after():
"""Check if there is whitespace or a closing
bracket to the right of the cursor"""
d = get_app().current_buffer.document
return bool(d.is_cursor_at_the_end_of_line or
d.current_char.isspace() or
d.current_char in ')]}')
def load_xonsh_bindings(key_bindings):
"""
Load custom key bindings.
"""
handle = key_bindings.add
has_selection = HasSelection()
insert_mode = ViInsertMode() | EmacsInsertMode()
@handle(Keys.Tab, filter=tab_insert_indent)
def insert_indent(event):
"""
If there are only whitespaces before current cursor position insert
indent instead of autocompleting.
"""
event.cli.current_buffer.insert_text(env.get('INDENT'))
@handle(Keys.ControlX, Keys.ControlE, filter=~has_selection)
def open_editor(event):
""" Open current buffer in editor """
event.current_buffer.open_in_editor(event.cli)
@handle(Keys.BackTab, filter=insert_mode)
def insert_literal_tab(event):
""" Insert literal tab on Shift+Tab instead of autocompleting """
b = event.current_buffer
if b.complete_state:
b.complete_previous()
else:
event.cli.current_buffer.insert_text(env.get('INDENT'))
@handle('(', filter=autopair_condition & whitespace_or_bracket_after)
def insert_right_parens(event):
event.cli.current_buffer.insert_text('(')
event.cli.current_buffer.insert_text(')', move_cursor=False)
@handle(')', filter=autopair_condition)
def overwrite_right_parens(event):
buffer = event.cli.current_buffer
if buffer.document.current_char == ')':
buffer.cursor_position += 1
else:
buffer.insert_text(')')
@handle('[', filter=autopair_condition & whitespace_or_bracket_after)
def insert_right_bracket(event):
event.cli.current_buffer.insert_text('[')
event.cli.current_buffer.insert_text(']', move_cursor=False)
@handle(']', filter=autopair_condition)
def overwrite_right_bracket(event):
buffer = event.cli.current_buffer
if buffer.document.current_char == ']':
buffer.cursor_position += 1
else:
buffer.insert_text(']')
@handle('{', filter=autopair_condition & whitespace_or_bracket_after)
def insert_right_brace(event):
event.cli.current_buffer.insert_text('{')
event.cli.current_buffer.insert_text('}', move_cursor=False)
@handle('}', filter=autopair_condition)
def overwrite_right_brace(event):
buffer = event.cli.current_buffer
if buffer.document.current_char == '}':
buffer.cursor_position += 1
else:
buffer.insert_text('}')
@handle('\'', filter=autopair_condition)
def insert_right_quote(event):
buffer = event.cli.current_buffer
if buffer.document.current_char == '\'':
buffer.cursor_position += 1
elif whitespace_or_bracket_before(event.cli)\
and whitespace_or_bracket_after(event.cli):
buffer.insert_text('\'')
buffer.insert_text('\'', move_cursor=False)
else:
buffer.insert_text('\'')
@handle('"', filter=autopair_condition)
def insert_right_double_quote(event):
buffer = event.cli.current_buffer
if buffer.document.current_char == '"':
buffer.cursor_position += 1
elif whitespace_or_bracket_before(event.cli)\
and whitespace_or_bracket_after(event.cli):
buffer.insert_text('"')
buffer.insert_text('"', move_cursor=False)
else:
buffer.insert_text('"')
@handle(Keys.Backspace, filter=autopair_condition)
def delete_brackets_or_quotes(event):
"""Delete empty pair of brackets or quotes"""
buffer = event.cli.current_buffer
before = buffer.document.char_before_cursor
after = buffer.document.current_char
if any([before == b and after == a
for (b, a) in ['()', '[]', '{}', "''", '""']]):
buffer.delete(1)
buffer.delete_before_cursor(1)
@handle(Keys.ControlD, filter=ctrl_d_condition)
def call_exit_alias(event):
"""Use xonsh exit function"""
b = event.cli.current_buffer
try:
b.accept_action.validate_and_handle(event.cli, b)
except AttributeError: # PTK 2.0
b.validate_and_handle()
xonsh_exit([])
@handle(Keys.ControlJ, filter=IsMultiline())
@handle(Keys.ControlM, filter=IsMultiline())
def multiline_carriage_return(event):
""" Wrapper around carriage_return multiline parser """
b = event.cli.current_buffer
carriage_return(b, event.cli)
@handle(Keys.ControlJ, filter=should_confirm_completion)
@handle(Keys.ControlM, filter=should_confirm_completion)
def enter_confirm_completion(event):
"""Ignore <enter> (confirm completion)"""
event.current_buffer.complete_state = None
@handle(Keys.Escape, filter=should_confirm_completion)
def esc_cancel_completion(event):
"""Use <ESC> to cancel completion"""
event.cli.current_buffer.cancel_completion()
@handle(Keys.Escape, Keys.ControlJ)
def execute_block_now(event):
"""Execute a block of text irrespective of cursor position"""
b = event.cli.current_buffer
try:
b.accept_action.validate_and_handle(event.cli, b)
except AttributeError: # PTK 2.0
b.validate_and_handle()
@handle(Keys.Left, filter=beginning_of_line)
def wrap_cursor_back(event):
"""Move cursor to end of previous line unless at beginning of
document
"""
b = event.cli.current_buffer
b.cursor_up(count=1)
relative_end_index = b.document.get_end_of_line_position()
b.cursor_right(count=relative_end_index)
@handle(Keys.Right, filter=end_of_line)
def wrap_cursor_forward(event):
"""Move cursor to beginning of next line unless at end of document"""
b = event.cli.current_buffer
relative_begin_index = b.document.get_start_of_line_position()
b.cursor_left(count=abs(relative_begin_index))
b.cursor_down(count=1)
@handle(Keys.ControlI, filter=insert_mode)
def generate_completions(event):
"""
Tab-completion: where the first tab completes the common suffix and the
second tab lists all the completions.
Notes
-----
This method was forked from the mainline prompt-toolkit repo.
Copyright (c) 2014, Jonathan Slenders, All rights reserved.
"""
b = event.current_buffer
try:
start_completion = event.cli.start_completion
except AttributeError: # PTK 2.0
start_completion = event.current_buffer.start_completion
def second_tab():
if b.complete_state:
b.complete_next()
else:
start_completion(select_first=False)
# On the second tab-press, or when already navigating through
# completions.
if event.is_repeat or b.complete_state:
second_tab()
else:
start_completion(insert_common_part=True, select_first=False)

321
xonsh/ptk2/shell.py Normal file
View file

@ -0,0 +1,321 @@
# -*- coding: utf-8 -*-
"""The prompt_toolkit based xonsh shell."""
import sys
import builtins
from xonsh.events import events
from xonsh.base_shell import BaseShell
from xonsh.shell import transform_command
from xonsh.tools import print_exception, carriage_return
from xonsh.platform import HAS_PYGMENTS, ON_WINDOWS
from xonsh.style_tools import partial_color_tokenize, _TokenType, DEFAULT_STYLE_DICT
from xonsh.lazyimps import pygments, pyghooks, winutils
from xonsh.ptk2.history import PromptToolkitHistory
from xonsh.ptk2.completer import PromptToolkitCompleter
from xonsh.ptk2.key_bindings import load_xonsh_bindings
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.history import ThreadedHistory
from prompt_toolkit.shortcuts import print_formatted_text as ptk_print
from prompt_toolkit.shortcuts import CompleteStyle
from prompt_toolkit.shortcuts.prompt import PromptSession
from prompt_toolkit.formatted_text import PygmentsTokens
from prompt_toolkit.styles.pygments import (style_from_pygments_cls,
style_from_pygments_dict, pygments_token_to_classname)
Token = _TokenType()
events.transmogrify('on_ptk_create', 'LoadEvent')
events.doc('on_ptk_create', """
on_ptk_create(prompter: PromptSession, history: PromptToolkitHistory, completer: PromptToolkitCompleter, bindings: KeyBindings) ->
Fired after prompt toolkit has been initialized
""")
class PromptToolkit2Shell(BaseShell):
"""The xonsh shell for prompt_toolkit v2."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
if ON_WINDOWS:
winutils.enable_virtual_terminal_processing()
self._first_prompt = True
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)
# This assumes that PromptToolkit2Shell is a singleton
events.on_ptk_create.fire(
prompter=self.prompter,
history=self.history,
completer=self.pt_completer,
bindings=self.key_bindings,
)
def singleline(self, auto_suggest=None, enable_history_search=True,
multiline=True, **kwargs):
"""Reads a single line of input from the shell. The store_in_history
kwarg flags whether the input should be stored in PTK's in-memory
history.
"""
events.on_pre_prompt.fire()
env = builtins.__xonsh_env__
mouse_support = env.get('MOUSE_SUPPORT')
auto_suggest = auto_suggest if env.get('AUTO_SUGGEST') else None
completions_display = env.get('COMPLETIONS_DISPLAY')
if completions_display == 'multi':
complete_style = CompleteStyle.MULTI_COLUMN
complete_while_typing = env.get('UPDATE_COMPLETIONS_ON_KEYPRESS')
if complete_while_typing:
# PTK requires history search to be none when completing while typing
enable_history_search = False
if HAS_PYGMENTS:
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
completer = None if completions_display == 'none' else self.pt_completer
if env.get('UPDATE_PROMPT_ON_KEYPRESS'):
get_prompt_tokens = self.prompt_tokens
get_rprompt_tokens = self.rprompt_tokens
get_bottom_toolbar_tokens = self.bottom_toolbar_tokens
else:
get_prompt_tokens = self.prompt_tokens()
get_rprompt_tokens = self.rprompt_tokens()
get_bottom_toolbar_tokens = self.bottom_toolbar_tokens()
if env.get('VI_MODE'):
editing_mode = EditingMode.VI
else:
editing_mode = EditingMode.EMACS
prompt_args = {
'mouse_support': mouse_support,
'auto_suggest': auto_suggest,
'message': get_prompt_tokens,
'rprompt': get_rprompt_tokens,
'bottom_toolbar': get_bottom_toolbar_tokens,
'completer': completer,
'multiline': multiline,
'editing_mode': editing_mode,
'prompt_continuation': self.continuation_tokens,
'enable_history_search': enable_history_search,
'reserve_space_for_menu': 0,
'key_bindings': self.key_bindings,
'complete_style': complete_style,
'complete_while_typing': complete_while_typing,
}
if builtins.__xonsh_env__.get('COLOR_INPUT'):
if HAS_PYGMENTS:
prompt_args['lexer'] = PygmentsLexer(pyghooks.XonshLexer)
style = style_from_pygments_cls(
pyghooks.xonsh_style_proxy(self.styler))
else:
style_dict = {
pygments_token_to_classname(key.__name__): value
for key, value in DEFAULT_STYLE_DICT
}
style = style_from_pygments_dict(style_dict)
prompt_args['style'] = style
line = self.prompter.prompt(**prompt_args)
events.on_post_prompt.fire()
return line
def _push(self, line):
"""Pushes a line onto the buffer and compiles the code in a way that
enables multiline input.
"""
code = None
self.buffer.append(line)
if self.need_more_lines:
return None, code
src = ''.join(self.buffer)
src = transform_command(src)
try:
code = self.execer.compile(src,
mode='single',
glbs=self.ctx,
locs=None)
self.reset_buffer()
except Exception: # pylint: disable=broad-except
self.reset_buffer()
print_exception()
return src, None
return src, code
def cmdloop(self, intro=None):
"""Enters a loop that reads and execute input from user."""
if intro:
print(intro)
auto_suggest = AutoSuggestFromHistory()
self.push = self._push
while not builtins.__xonsh_exit__:
try:
line = self.singleline(auto_suggest=auto_suggest)
if not line:
self.emptyline()
else:
line = self.precmd(line)
self.default(line)
except (KeyboardInterrupt, SystemExit):
self.reset_buffer()
except EOFError:
if builtins.__xonsh_env__.get("IGNOREEOF"):
print('Use "exit" to leave the shell.', file=sys.stderr)
else:
break
def prompt_tokens(self):
"""Returns a list of (token, str) tuples for the current prompt."""
p = builtins.__xonsh_env__.get('PROMPT')
try:
p = self.prompt_formatter(p)
except Exception: # pylint: disable=broad-except
print_exception()
toks = partial_color_tokenize(p)
if self._first_prompt:
carriage_return()
self._first_prompt = False
self.settitle()
return PygmentsTokens(toks)
def rprompt_tokens(self):
"""Returns a list of (token, str) tuples for the current right
prompt.
"""
p = builtins.__xonsh_env__.get('RIGHT_PROMPT')
# self.prompt_formatter does handle empty strings properly,
# but this avoids descending into it in the common case of
# $RIGHT_PROMPT == ''.
if isinstance(p, str) and len(p) == 0:
return []
try:
p = self.prompt_formatter(p)
except Exception: # pylint: disable=broad-except
print_exception()
toks = partial_color_tokenize(p)
return PygmentsTokens(toks)
def bottom_toolbar_tokens(self):
"""Returns a list of (token, str) tuples for the current bottom
toolbar.
"""
p = builtins.__xonsh_env__.get('BOTTOM_TOOLBAR')
if not p:
return
try:
p = self.prompt_formatter(p)
except Exception: # pylint: disable=broad-except
print_exception()
toks = partial_color_tokenize(p)
return PygmentsTokens(toks)
def continuation_tokens(self, width, line_number, is_soft_wrap=False):
"""Displays dots in multiline prompt"""
if is_soft_wrap:
return ''
width = width - 1
dots = builtins.__xonsh_env__.get('MULTILINE_PROMPT')
dots = dots() if callable(dots) else dots
if dots is None:
return [(Token, ' ' * (width + 1))]
basetoks = self.format_color(dots)
baselen = sum(len(t[1]) for t in basetoks)
if baselen == 0:
return [(Token, ' ' * (width + 1))]
toks = basetoks * (width // baselen)
n = width % baselen
count = 0
for tok in basetoks:
slen = len(tok[1])
newcount = slen + count
if slen == 0:
continue
elif newcount <= n:
toks.append(tok)
else:
toks.append((tok[0], tok[1][:n - count]))
count = newcount
if n <= count:
break
toks.append((Token, ' ')) # final space
return PygmentsTokens(toks)
def format_color(self, string, hide=False, force_string=False, **kwargs):
"""Formats a color string using Pygments. This, therefore, returns
a list of (Token, str) tuples. If force_string is set to true, though,
this will return a color formatted string.
"""
tokens = partial_color_tokenize(string)
if force_string and HAS_PYGMENTS:
env = builtins.__xonsh_env__
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
proxy_style = pyghooks.xonsh_style_proxy(self.styler)
formatter = pyghooks.XonshTerminal256Formatter(style=proxy_style)
s = pygments.format(tokens, formatter)
return s
elif force_string:
print("To force colorization of string, install Pygments")
return tokens
else:
return tokens
def print_color(self, string, end='\n', **kwargs):
"""Prints a color string using prompt-toolkit color management."""
if isinstance(string, str):
tokens = partial_color_tokenize(string + end)
else:
# assume this is a list of (Token, str) tuples and just print
tokens = string
tokens = PygmentsTokens(tokens)
if HAS_PYGMENTS:
env = builtins.__xonsh_env__
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
proxy_style = style_from_pygments_cls(pyghooks.xonsh_style_proxy(self.styler))
else:
proxy_style = style_from_pygments_dict(DEFAULT_STYLE_DICT)
ptk_print(tokens, style=proxy_style)
def color_style_names(self):
"""Returns an iterable of all available style names."""
if not HAS_PYGMENTS:
return ['For other xonsh styles, please install pygments']
return pygments.styles.get_all_styles()
def color_style(self):
"""Returns the current color map."""
if not HAS_PYGMENTS:
return DEFAULT_STYLE_DICT
env = builtins.__xonsh_env__
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
return self.styler.styles
def restore_tty_sanity(self):
"""An interface for resetting the TTY stdin mode. This is highly
dependent on the shell backend. Also it is mostly optional since
it only affects ^Z backgrounding behaviour.
"""
# PTK does not seem to need any specialization here. However,
# if it does for some reason in the future...
# The following writes an ANSI escape sequence that sends the cursor
# to the end of the line. This has the effect of restoring ECHO mode.
# See http://unix.stackexchange.com/a/108014/129048 for more details.
# This line can also be replaced by os.system("stty sane"), as per
# http://stackoverflow.com/questions/19777129/interactive-python-interpreter-run-in-background#comment29421919_19778355
# However, it is important to note that not termios-based solution
# seems to work. My guess is that this is because termios restoration
# needs to be performed by the subprocess itself. This fix is important
# when subprocesses don't properly restore the terminal attributes,
# like Python in interactive mode. Also note that the sequences "\033M"
# and "\033E" seem to work too, but these are technically VT100 codes.
# I used the more primitive ANSI sequence to maximize compatibility.
# -scopatz 2017-01-28
# if not ON_POSIX:
# return
# sys.stdout.write('\033[9999999C\n')

View file

@ -273,7 +273,7 @@ def code_by_name(name, styles):
pass
elif 'hex' in fg:
for p in fg.split('_'):
codes.append('#'+p[3:] if p.startswith('hex') else p)
codes.append('#' + p[3:] if p.startswith('hex') else p)
else:
fgtok = getattr(Color, fg.upper())
if fgtok in styles:
@ -284,7 +284,7 @@ def code_by_name(name, styles):
if len(bg) == 0:
pass
elif bg.startswith('background_hex'):
codes.append('bg:#'+bg[14:])
codes.append('bg:#' + bg[14:])
else:
bgtok = getattr(Color, bg.upper())
if bgtok in styles:
@ -540,7 +540,7 @@ if hasattr(pygments.style, 'ansicolors'):
Generic.Output: '#ansidarkblue',
Generic.Traceback: '#ansidarkblue',
Error: '#ansired',
}, globals(), 'XONSH_BASE_STYLE')
}, globals(), 'XONSH_BASE_STYLE')
else:
XONSH_BASE_STYLE = LazyObject(lambda: {
Whitespace: "#bbbbbb",
@ -548,9 +548,9 @@ else:
Comment.Preproc: "noitalic #BC7A00",
Keyword: "bold #008000",
Keyword.Pseudo: "nobold",
Keyword.Type: "nobold #B00040",
Operator: "#666666",
Operator.Word: "bold #AA22FF",
Keyword.Type: "nobold #B00040",
Operator: "#666666",
Operator.Word: "bold #AA22FF",
Name.Builtin: "#008000",
Name.Function: "#0000FF",
Name.Class: "bold #0000FF",
@ -667,7 +667,7 @@ KNOWN_COLORS = LazyObject(lambda: frozenset([
'UNDERLINE_YELLOW',
'WHITE',
'YELLOW',
]), globals(), 'KNOWN_COLORS')
]), globals(), 'KNOWN_COLORS')
def _expand_style(cmap):
@ -676,13 +676,13 @@ def _expand_style(cmap):
if key is Color.NO_COLOR:
continue
_, _, key = str(key).rpartition('.')
cmap[getattr(Color, 'BOLD_'+key)] = 'bold ' + val
cmap[getattr(Color, 'UNDERLINE_'+key)] = 'underline ' + val
cmap[getattr(Color, 'BOLD_UNDERLINE_'+key)] = 'bold underline ' + val
cmap[getattr(Color, 'BOLD_' + key)] = 'bold ' + val
cmap[getattr(Color, 'UNDERLINE_' + key)] = 'underline ' + val
cmap[getattr(Color, 'BOLD_UNDERLINE_' + key)] = 'bold underline ' + val
if val == 'noinherit':
cmap[getattr(Color, 'BACKGROUND_'+key)] = val
cmap[getattr(Color, 'BACKGROUND_' + key)] = val
else:
cmap[getattr(Color, 'BACKGROUND_'+key)] = 'bg:' + val
cmap[getattr(Color, 'BACKGROUND_' + key)] = 'bg:' + val
def _bw_style():
@ -704,7 +704,7 @@ def _bw_style():
Color.RED: 'noinherit',
Color.WHITE: 'noinherit',
Color.YELLOW: 'noinherit',
}
}
_expand_style(style)
return style
@ -823,7 +823,7 @@ def _algol_style():
Color.RED: '#FF0000',
Color.WHITE: '#888',
Color.YELLOW: '#FF0000',
}
}
_expand_style(style)
return style
@ -847,7 +847,7 @@ def _algol_nu_style():
Color.RED: '#FF0000',
Color.WHITE: '#888',
Color.YELLOW: '#FF0000',
}
}
_expand_style(style)
return style
@ -871,7 +871,7 @@ def _autumn_style():
Color.RED: '#aa0000',
Color.WHITE: '#aaaaaa',
Color.YELLOW: '#aa5500',
}
}
_expand_style(style)
return style
@ -895,7 +895,7 @@ def _borland_style():
Color.RED: '#aa0000',
Color.WHITE: '#aaaaaa',
Color.YELLOW: '#a61717',
}
}
_expand_style(style)
return style
@ -919,7 +919,7 @@ def _colorful_style():
Color.RED: '#A00000',
Color.WHITE: '#bbbbbb',
Color.YELLOW: '#A60',
}
}
_expand_style(style)
return style
@ -943,7 +943,7 @@ def _emacs_style():
Color.RED: '#A00000',
Color.WHITE: '#bbbbbb',
Color.YELLOW: '#BB6622',
}
}
_expand_style(style)
return style
@ -967,7 +967,7 @@ def _friendly_style():
Color.RED: '#A00000',
Color.WHITE: '#bbbbbb',
Color.YELLOW: '#c65d09',
}
}
_expand_style(style)
return style
@ -991,7 +991,7 @@ def _fruity_style():
Color.RED: '#ff0007',
Color.WHITE: '#cdcaa9',
Color.YELLOW: '#fb660a',
}
}
_expand_style(style)
return style
@ -1015,7 +1015,7 @@ def _igor_style():
Color.RED: '#C34E00',
Color.WHITE: '#CC00A3',
Color.YELLOW: '#C34E00',
}
}
_expand_style(style)
return style
@ -1039,7 +1039,7 @@ def _lovelace_style():
Color.RED: '#c02828',
Color.WHITE: '#888888',
Color.YELLOW: '#b85820',
}
}
_expand_style(style)
return style
@ -1063,7 +1063,7 @@ def _manni_style():
Color.RED: '#AA0000',
Color.WHITE: '#AAAAAA',
Color.YELLOW: '#CC3300',
}
}
_expand_style(style)
return style
@ -1087,7 +1087,7 @@ def _murphy_style():
Color.RED: '#A00000',
Color.WHITE: '#bbbbbb',
Color.YELLOW: '#c65d09',
}
}
_expand_style(style)
return style
@ -1111,7 +1111,7 @@ def _native_style():
Color.RED: '#a61717',
Color.WHITE: '#aaaaaa',
Color.YELLOW: '#a61717',
}
}
_expand_style(style)
return style
@ -1135,7 +1135,7 @@ def _paraiso_dark_style():
Color.RED: '#ef6155',
Color.WHITE: '#5bc4bf',
Color.YELLOW: '#f99b15',
}
}
_expand_style(style)
return style
@ -1159,7 +1159,7 @@ def _paraiso_light_style():
Color.RED: '#2f1e2e',
Color.WHITE: '#8d8687',
Color.YELLOW: '#f99b15',
}
}
_expand_style(style)
return style
@ -1183,7 +1183,7 @@ def _pastie_style():
Color.RED: '#aa0000',
Color.WHITE: '#bbbbbb',
Color.YELLOW: '#aa6600',
}
}
_expand_style(style)
return style
@ -1207,7 +1207,7 @@ def _perldoc_style():
Color.RED: '#aa0000',
Color.WHITE: '#a7a7a7',
Color.YELLOW: '#cb6c20',
}
}
_expand_style(style)
return style
@ -1231,7 +1231,7 @@ def _rrt_style():
Color.RED: '#ff0000',
Color.WHITE: '#87ceeb',
Color.YELLOW: '#ff0000',
}
}
_expand_style(style)
return style
@ -1255,7 +1255,7 @@ def _tango_style():
Color.RED: '#a40000',
Color.WHITE: '#f8f8f8',
Color.YELLOW: '#8f5902',
}
}
_expand_style(style)
return style
@ -1279,7 +1279,7 @@ def _trac_style():
Color.RED: '#aa0000',
Color.WHITE: '#aaaaaa',
Color.YELLOW: '#808000',
}
}
_expand_style(style)
return style
@ -1303,7 +1303,7 @@ def _vim_style():
Color.RED: '#cd0000',
Color.WHITE: '#cccccc',
Color.YELLOW: '#cd0000',
}
}
_expand_style(style)
return style
@ -1327,7 +1327,7 @@ def _vs_style():
Color.RED: '#a31515',
Color.WHITE: '#2b91af',
Color.YELLOW: '#a31515',
}
}
_expand_style(style)
return style
@ -1351,7 +1351,7 @@ def _xcode_style():
Color.RED: '#C41A16',
Color.WHITE: '#3F6E75',
Color.YELLOW: '#836C28',
}
}
_expand_style(style)
return style
@ -1383,7 +1383,7 @@ STYLES = LazyDict({
'vim': _vim_style,
'vs': _vs_style,
'xcode': _xcode_style,
}, globals(), 'STYLES')
}, globals(), 'STYLES')
del (_algol_style, _algol_nu_style, _autumn_style, _borland_style, _bw_style,
_colorful_style, _default_style, _emacs_style, _friendly_style,
@ -1401,10 +1401,10 @@ def make_pygments_style(palette):
for name, t in BASE_XONSH_COLORS.items():
color = find_closest_color(t, palette)
style[getattr(Color, name)] = '#' + color
style[getattr(Color, 'BOLD_'+name)] = 'bold #' + color
style[getattr(Color, 'UNDERLINE_'+name)] = 'underline #' + color
style[getattr(Color, 'BOLD_UNDERLINE_'+name)] = 'bold underline #' + color
style[getattr(Color, 'BACKGROUND_'+name)] = 'bg:#' + color
style[getattr(Color, 'BOLD_' + name)] = 'bold #' + color
style[getattr(Color, 'UNDERLINE_' + name)] = 'underline #' + color
style[getattr(Color, 'BOLD_UNDERLINE_' + name)] = 'bold underline #' + color
style[getattr(Color, 'BACKGROUND_' + name)] = 'bg:#' + color
return style

View file

@ -94,7 +94,7 @@ def _discover_lexers():
'.sql': ('pygments.lexers.sql', 'SqlLexer'),
'.txt': ('pygments.lexers.special', 'TextLexer'),
'.html': ('pygments.lexers.html', 'HtmlLexer'),
}
}
exts = {}
lexers = {'exts': exts}
if DEBUG:
@ -109,8 +109,8 @@ def _discover_lexers():
filename = filename[1:]
if '*' in filename:
continue
if (DEBUG and filename in exts and exts[filename] != val
and filename not in default_exts):
if (DEBUG and filename in exts and exts[filename] != val and
filename not in default_exts):
duplicates[filename].add(val)
duplicates[filename].add(exts[filename])
exts[filename] = val
@ -144,16 +144,16 @@ def _discover_formatters():
filename = filename[1:]
if '*' in filename:
continue
if (DEBUG and filename in exts and exts[filename] != val
and filename not in default_exts):
if (DEBUG and filename in exts and exts[filename] != val and
filename not in default_exts):
duplicates[filename].add(val)
duplicates[filename].add(exts[filename])
exts[filename] = val
# add names and aliases
names[cls.name] = val
for alias in cls.aliases:
if (DEBUG and alias in names and names[alias] != val
and alias not in default_names):
if (DEBUG and alias in names and names[alias] != val and
alias not in default_names):
duplicates[alias].add(val)
duplicates[alias].add(names[alias])
names[alias] = val
@ -180,8 +180,8 @@ def _discover_styles():
cls = get_style_by_name(name)
mod = inspect.getmodule(cls)
val = (mod.__name__, cls.__name__)
if (DEBUG and name in names and names[name] != val
and name not in default_names):
if (DEBUG and name in names and names[name] != val and
name not in default_names):
duplicates[name].add(val)
duplicates[name].add(names[name])
names[name] = val
@ -208,8 +208,8 @@ def _discover_filters():
cls = type(filter)
mod = inspect.getmodule(cls)
val = (mod.__name__, cls.__name__)
if (DEBUG and name in names and names[name] != val
and name not in default_names):
if (DEBUG and name in names and names[name] != val and
name not in default_names):
duplicates[name].add(val)
duplicates[name].add(names[name])
names[name] = val

View file

@ -24,7 +24,7 @@ def _limited_traceback(excinfo):
tb = extract_tb(excinfo.tb)
try:
idx = [__file__ in e for e in tb].index(True)
return format_list(tb[idx+1:])
return format_list(tb[idx + 1:])
except ValueError:
return format_list(tb)

View file

@ -305,7 +305,7 @@ class ReadlineShell(BaseShell, cmd.Cmd):
(False, False, False, True, False): False,
(False, False, False, False, True): False,
(False, False, False, False, False): False,
}
}
self.cmdqueue = collections.deque()
def __del__(self):
@ -360,8 +360,8 @@ class ReadlineShell(BaseShell, cmd.Cmd):
'{PURPLE}({NO_COLOR}q{PURPLE}){NO_COLOR}uit '
'{YELLOW}==={NO_COLOR}')
while len(lines) > h - 1:
print(''.join(lines[:h-1]), end='', flush=True, file=sys.stderr)
lines = lines[h-1:]
print(''.join(lines[:h - 1]), end='', flush=True, file=sys.stderr)
lines = lines[h - 1:]
print(more_msg, end='', flush=True, file=sys.stderr)
q = sys.stdin.read(1).lower()
print(flush=True, file=sys.stderr)

View file

@ -8,7 +8,7 @@ import builtins
import warnings
from xonsh.platform import (best_shell_type, has_prompt_toolkit,
ptk_above_min_supported, ptk_below_max_supported)
ptk_above_min_supported, ptk_shell_type)
from xonsh.tools import XonshError, print_exception
from xonsh.events import events
import xonsh.history.main as xhm
@ -93,6 +93,22 @@ class Shell(object):
readline version of shell should be used.
"""
shell_type_aliases = {
'b': 'best',
'best': 'best',
'ptk': 'prompt_toolkit',
'ptk1': 'prompt_toolkit1',
'ptk2': 'prompt_toolkit2',
'prompt-toolkit': 'prompt_toolkit',
'prompt_toolkit': 'prompt_toolkit',
'prompt-toolkit1': 'prompt_toolkit1',
'prompt-toolkit2': 'prompt_toolkit2',
'rand': 'random',
'random': 'random',
'rl': 'readline',
'readline': 'readline',
}
def __init__(self, execer, ctx=None, shell_type=None, **kwargs):
"""
Parameters
@ -105,7 +121,7 @@ class Shell(object):
this no additional context is computed and this is used
directly.
shell_type : str, optional
The shell type to start, such as 'readline', 'prompt_toolkit',
The shell type to start, such as 'readline', 'prompt_toolkit1',
or 'random'.
"""
self.execer = execer
@ -123,6 +139,7 @@ class Shell(object):
# This bricks interactive xonsh
# Can happen from the use of .xinitrc, .xsession, etc
shell_type = 'best'
shell_type = self.shell_type_aliases.get(shell_type, shell_type)
if shell_type == 'best' or shell_type is None:
shell_type = best_shell_type()
elif shell_type == 'random':
@ -137,19 +154,15 @@ class Shell(object):
'supported. Please update prompt-toolkit. Using '
'readline instead.')
shell_type = 'readline'
elif not ptk_below_max_supported():
warnings.warn('prompt-toolkit version 2.0 is not yet '
'supported. Please see Github PR #2570 for '
'latest status. To use prompt-toolkit now you '
'can downgrade to version 1.x with\n'
'xpip install "prompt_toolkit<2"\n'
'Starting xonsh with readline shell instead.')
shell_type = 'readline'
else:
shell_type = ptk_shell_type()
self.shell_type = env['SHELL_TYPE'] = shell_type
# actually make the shell
if shell_type == 'none':
from xonsh.base_shell import BaseShell as shell_class
elif shell_type == 'prompt_toolkit':
elif shell_type == 'prompt_toolkit2':
from xonsh.ptk2.shell import PromptToolkit2Shell as shell_class
elif shell_type == 'prompt_toolkit1':
from xonsh.ptk.shell import PromptToolkitShell as shell_class
elif shell_type == 'readline':
from xonsh.readline_shell import ReadlineShell as shell_class

View file

@ -250,7 +250,7 @@ KNOWN_COLORS = LazyObject(lambda: frozenset([
'UNDERLINE_YELLOW',
'WHITE',
'YELLOW',
]), globals(), 'KNOWN_COLORS')
]), globals(), 'KNOWN_COLORS')
DEFAULT_STYLE_DICT = LazyObject(lambda: {
Token: '',
@ -419,5 +419,5 @@ DEFAULT_STYLE_DICT = LazyObject(lambda: {
Token.Scrollbar.Arrow: 'bg:#ansiblack #ansiwhite bold',
Token.Scrollbar.Button: 'bg:#ansiblack',
Token.Text: '',
Token.Text.Whitespace: '#ansilightgray'},
globals(), 'DEFAULT_STYLE_DICT')
Token.Text.Whitespace: '#ansilightgray',
}, globals(), 'DEFAULT_STYLE_DICT')

View file

@ -301,7 +301,7 @@ def setup_timings():
width = max(len(s) for s, _ in times) + 2
header_format = '|{{:<{}}}|{{:^11}}|{{:^11}}|'.format(width)
entry_format = '|{{:<{}}}|{{:^11.3f}}|{{:^11.3f}}|'.format(width)
sepline = '|{}|{}|{}|'.format('-'*width, '-'*11, '-'*11)
sepline = '|{}|{}|{}|'.format('-' * width, '-' * 11, '-' * 11)
# Print result table
print(' Debug level: {}'.format(os.getenv('XONSH_DEBUG', 'Off')))
print(sepline)

View file

@ -238,7 +238,7 @@ _redir_map = (
# stdout to stderr
'out>err', 'out>&2', '1>err', 'out>e', 'out>2', 'o>err', 'o>&2',
'1>&2', 'o>e', '1>e', 'o>2', '1>2',
)
)
IORedirect = group(group(*_redir_map), '{}>>?'.format(group(*_redir_names)))
_redir_check = set(_redir_map)
_redir_check = {'{}>'.format(i) for i in _redir_names}.union(_redir_check)

View file

@ -165,8 +165,8 @@ class EnvPath(collections.MutableSequence):
# make TypeError's message as informative as possible
# when given an invalid initialization sequence
raise TypeError(
"EnvPath's initialization sequence should only "
"contain str, bytes and pathlib.Path entries")
"EnvPath's initialization sequence should only "
"contain str, bytes and pathlib.Path entries")
self._l = args
else:
raise TypeError('EnvPath cannot be initialized with items '
@ -272,9 +272,8 @@ class DefaultNotGivenType(object):
DefaultNotGiven = DefaultNotGivenType()
BEG_TOK_SKIPS = LazyObject(
lambda: frozenset(['WS', 'INDENT', 'NOT', 'LPAREN']),
globals(), 'BEG_TOK_SKIPS')
BEG_TOK_SKIPS = LazyObject(lambda: frozenset(['WS', 'INDENT', 'NOT', 'LPAREN']),
globals(), 'BEG_TOK_SKIPS')
END_TOK_TYPES = LazyObject(lambda: frozenset(['SEMI', 'AND', 'OR', 'RPAREN']),
globals(), 'END_TOK_TYPES')
RE_END_TOKS = LazyObject(lambda: re.compile('(;|and|\&\&|or|\|\||\))'),
@ -499,7 +498,7 @@ def get_logical_line(lines, idx):
n = 1
nlines = len(lines)
linecont = get_line_continuation()
while idx > 0 and lines[idx-1].endswith(linecont):
while idx > 0 and lines[idx - 1].endswith(linecont):
idx -= 1
start = idx
line = lines[idx]
@ -524,9 +523,9 @@ def replace_logical_line(lines, logical, idx, n):
lines[idx] = logical
return
space = ' '
for i in range(idx, idx+n-1):
for i in range(idx, idx + n - 1):
a = len(lines[i])
b = logical.find(space, a-1)
b = logical.find(space, a - 1)
if b < 0:
# no space found
lines[i] = logical
@ -535,7 +534,7 @@ def replace_logical_line(lines, logical, idx, n):
# found space to split on
lines[i] = logical[:b] + linecont
logical = logical[b:]
lines[idx+n-1] = logical
lines[idx + n - 1] = logical
def is_balanced(expr, ltok, rtok):
@ -1468,7 +1467,7 @@ HISTORY_UNITS = LazyObject(lambda: {
'tb': ('b', _tb_to_b),
'terabyte': ('b', _tb_to_b),
'terabytes': ('b', _tb_to_b),
}, globals(), 'HISTORY_UNITS')
}, globals(), 'HISTORY_UNITS')
"""Maps lowercase unit names to canonical name and conversion utilities."""
@ -1708,7 +1707,7 @@ RE_STRING_CONT = LazyDict({
"'": lambda: re.compile(r"((\\(.|\n))|([^'\\]))*"),
'"""': lambda: re.compile(r'((\\(.|\n))|([^"\\])|("(?!""))|\n)*'),
"'''": lambda: re.compile(r"((\\(.|\n))|([^'\\])|('(?!''))|\n)*"),
}, globals(), 'RE_STRING_CONT')
}, globals(), 'RE_STRING_CONT')
"""Dictionary mapping starting quote sequences to regular expressions that
match the contents of a string beginning with those quotes (not including the
terminating quotes)"""
@ -1975,7 +1974,7 @@ def columnize(elems, width=80, newline='\n'):
# we might be able to fit another column.
ncols += 1
nrows = nelem // ncols
columns = [sizes[i*nrows:(i+1)*nrows] for i in range(ncols)]
columns = [sizes[i * nrows:(i + 1) * nrows] for i in range(ncols)]
last_longest_row = longest_row
else:
# we can't fit another column
@ -1984,7 +1983,7 @@ def columnize(elems, width=80, newline='\n'):
break
pad = (width - last_longest_row + ncols) // ncols
pad = pad if pad > 1 else 1
data = [elems[i*nrows:(i+1)*nrows] for i in range(ncols)]
data = [elems[i * nrows:(i + 1) * nrows] for i in range(ncols)]
colwidths = [max(map(len, d)) + pad for d in data]
colwidths[-1] -= pad
row_t = ''.join(['{{row[{i}]: <{{w[{i}]}}}}'.format(i=i) for i in range(ncols)])

View file

@ -17,8 +17,7 @@ from xonsh.proc import STDOUT_CAPTURE_KINDS
import xonsh.prompt.cwd as prompt
terminal = LazyObject(lambda: importlib.import_module(
'pygments.formatters.terminal'),
globals(), 'terminal')
'pygments.formatters.terminal'), globals(), 'terminal')
class TracerType(object):
@ -203,7 +202,7 @@ _TRACER_MAIN_ACTIONS = {
'del': _off,
'stop': _off,
'color': _color,
}
}
def tracermain(args=None, stdin=None, stdout=None, stderr=None, spec=None):

View file

@ -395,7 +395,7 @@ def CONSOLE_SCREEN_BUFFER_INFO():
("wAttributes", WORD),
("srWindow", SMALL_RECT),
("dwMaximumWindowSize", COORD),
]
]
return _CONSOLE_SCREEN_BUFFER_INFO
@ -408,7 +408,7 @@ def GetConsoleScreenBufferInfo():
gcsbi.argtypes = (
HANDLE,
POINTER(CONSOLE_SCREEN_BUFFER_INFO),
)
)
gcsbi.restype = BOOL
return gcsbi
@ -469,7 +469,7 @@ def SetConsoleScreenBufferSize():
scsbs.argtypes = (
HANDLE, # _In_ HANDLE hConsoleOutput
COORD, # _In_ COORD dwSize
)
)
scsbs.restype = BOOL
return scsbs
@ -503,7 +503,7 @@ def SetConsoleCursorPosition():
sccp.argtypes = (
HANDLE, # _In_ HANDLE hConsoleOutput
COORD, # _In_ COORD dwCursorPosition
)
)
sccp.restype = BOOL
return sccp

View file

@ -481,7 +481,7 @@ class PrettyFormatter(Visitor):
s += '\n'
t = sorted(node.responses.items())
t = ['{0!r}: {1}'.format(k, self.visit(v)) for k, v in t]
s += textwrap.indent(',\n'.join(t), 2*self.indent)
s += textwrap.indent(',\n'.join(t), 2 * self.indent)
s += '\n' + self.indent + '}'
if node.converter is not None:
s += ',\n' + self.indent + 'converter={0!r}'.format(node.converter)
@ -620,7 +620,7 @@ class StateVisitor(Visitor):
p = path[-1]
if isinstance(p, int) and abs(p) + (p >= 0) > len(loc):
i = abs(p) + (p >= 0) - len(loc)
ex = [None]*i
ex = [None] * i
loc.extend(ex)
loc[p] = val

View file

@ -117,7 +117,7 @@ _XONFIG_SOURCE_FOREIGN_SHELL_COMMAND = collections.defaultdict(
bash='source-bash',
cmd='source-cmd',
zsh='source-zsh',
)
)
def _dump_xonfig_foreign_shell(path, value):
@ -608,13 +608,13 @@ def STRIP_COLOR_RE():
def _align_string(string, align='<', fill=' ', width=80):
""" Align and pad a color formatted string """
linelen = len(STRIP_COLOR_RE.sub('', string))
padlen = max(width-linelen, 0)
padlen = max(width - linelen, 0)
if align == '^':
return fill*(padlen//2) + string + fill*(padlen//2 + padlen % 2)
return fill * (padlen // 2) + string + fill * (padlen // 2 + padlen % 2)
elif align == '>':
return fill*padlen + string
return fill * padlen + string
elif align == '<':
return string + fill*padlen
return string + fill * padlen
else:
return string

View file

@ -118,7 +118,7 @@ def _list(ns):
for d in data:
name = d['name']
lname = len(name)
s += "{PURPLE}" + name + "{NO_COLOR} " + " "*(nname - lname)
s += "{PURPLE}" + name + "{NO_COLOR} " + " " * (nname - lname)
if d['installed']:
s += '{GREEN}installed{NO_COLOR} '
else:
@ -154,7 +154,7 @@ def _create_xontrib_parser():
_MAIN_XONTRIB_ACTIONS = {
'load': _load,
'list': _list,
}
}
@unthreadable

View file

@ -36,7 +36,7 @@ def _uptime_osx():
bt = struct.unpack_from('@qq', bt)
else:
raise ValueError('length of boot time not understood: ' + repr(bt))
bt = bt[0] + bt[1]*1e-6
bt = bt[0] + bt[1] * 1e-6
if bt == 0.0:
return None
_BOOTTIME = bt

View file

@ -19,11 +19,11 @@ def bash_preproc(cmd, **kw):
@events.on_ptk_create
def custom_keybindings(bindings, **kw):
handler = bindings.registry.add_binding
handler = bindings.add
insert_mode = ViInsertMode() | EmacsInsertMode()
@Condition
def last_command_exists(cli):
def last_command_exists():
return len(__xonsh_history__) > 0
@handler(Keys.Escape, '.', filter=last_command_exists &

View file

@ -8,6 +8,7 @@ import matplotlib.pyplot as plt
from xonsh.tools import print_color, ON_WINDOWS
try:
# Use iterm2_tools as an indicator for the iterm2 terminal emulator
from iterm2_tools.images import display_image_bytes
@ -79,19 +80,19 @@ def figure_to_tight_array(fig, width, height, minimal=True):
# perform reversible operations to produce an optimally tight layout
dpi = dpi_fig
subplotpars = {
k: getattr(fig.subplotpars, k)
for k in ['wspace', 'hspace', 'bottom', 'top', 'left', 'right']
}
k: getattr(fig.subplotpars, k)
for k in ['wspace', 'hspace', 'bottom', 'top', 'left', 'right']
}
# set the figure dimensions to the terminal size
fig.set_size_inches(width/dpi, height/dpi, forward=True)
fig.set_size_inches(width / dpi, height / dpi, forward=True)
width, height = fig.canvas.get_width_height()
# remove all space between subplots
fig.subplots_adjust(wspace=0, hspace=0)
# move all subplots to take the entirety of space in the figure
# leave only one line for top and bottom
fig.subplots_adjust(bottom=1/height, top=1-1/height, left=0, right=1)
fig.subplots_adjust(bottom=1 / height, top=1 - 1 / height, left=0, right=1)
# reduce font size in order to reduce text impact on the image
font_size = matplotlib.rcParams['font.size']
@ -110,7 +111,7 @@ def figure_to_tight_array(fig, width, height, minimal=True):
matplotlib.rcParams.update({'font.size': font_size})
# reset the axis positions and figure dimensions
fig.set_size_inches(w/dpi, h/dpi, forward=True)
fig.set_size_inches(w / dpi, h / dpi, forward=True)
fig.subplots_adjust(**subplotpars)
else:
fig.dpi = dpi_fig

View file

@ -18,7 +18,7 @@ class VoxHandler:
create = subparsers.add_parser(
'new', aliases=['create'],
help='Create a new virtual environment'
)
)
create.add_argument('name', metavar='ENV',
help='The environments to create')
@ -48,7 +48,7 @@ class VoxHandler:
activate = subparsers.add_parser(
'activate', aliases=['workon', 'enter'],
help='Activate virtual environment'
)
)
activate.add_argument('name', metavar='ENV',
help='The environment to activate')
subparsers.add_parser('deactivate', aliases=['exit'], help='Deactivate current virtual environment')
@ -83,7 +83,7 @@ class VoxHandler:
if cmd is None:
self.parser.print_usage()
else:
getattr(self, 'cmd_'+cmd)(args, stdin)
getattr(self, 'cmd_' + cmd)(args, stdin)
def cmd_new(self, args, stdin=None):
"""Create a virtual environment in $VIRTUALENV_HOME with python3's ``venv``.

View file

@ -236,7 +236,7 @@ class Vox(collections.abc.Mapping):
bin_, lib, inc = _subdir_names()
for dirpath, dirnames, _ in os.walk(self.venvdir):
if bin_ in dirnames and lib in dirnames:
yield dirpath[len(self.venvdir)+1:] # +1 is to remove the separator
yield dirpath[len(self.venvdir) + 1:] # +1 is to remove the separator
# Don't recurse in to the special dirs
dirnames.remove(bin_)
dirnames.remove(lib) # This one in particular is likely to be quite large.

View file

@ -15,7 +15,7 @@ def custom_keybindings(bindings, **kw):
# Alt+Left and Alt+Right still jump over smaller word segments.
# See https://github.com/xonsh/xonsh/issues/2403
handler = bindings.registry.add_binding
handler = bindings.add
@handler(Keys.ControlLeft)
def ctrl_left(event):