mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
Add ptk3 (#3521)
* delete package ptk; rename ptk2 to ptk_shell.; leave ptk2 as alias for ptk_shell. * SHELL_TYPE "prompt_toolkit" only; remove ptk1 specific behavior. * Doc updates: eliminate reference to prompt-toolkit < 2.0 * update requirements files ptk>=2; test shell_style="none" * fix ptk2 stub per code review * Add ptk2 to list of packages to install.
This commit is contained in:
parent
3a232c6782
commit
53bfe8c99c
46 changed files with 524 additions and 1484 deletions
|
@ -157,6 +157,7 @@ v0.9.14
|
|||
|
||||
- `Before <https://i.imgur.com/EMhPdgU.png>`_
|
||||
- `After <https://i.imgur.com/sJiqgsb.png>`_
|
||||
|
||||
* The autovox xontrib now preserves activated environment on cd
|
||||
* setup.cfg -- duplicated flake8 config so interactive use and test runs enforce same rules. (Implementation is arguably a regression.)
|
||||
* Pressing ``Ctrl+Z`` no longer deadlocks the terminal,
|
||||
|
|
|
@ -44,10 +44,10 @@ For those of you who want the gritty details.
|
|||
shell
|
||||
base_shell
|
||||
readline_shell
|
||||
ptk2/shell
|
||||
ptk2/history
|
||||
ptk2/completer
|
||||
ptk2/key_bindings
|
||||
ptk_shell/shell
|
||||
ptk_shell/history
|
||||
ptk_shell/completer
|
||||
ptk_shell/key_bindings
|
||||
pretty
|
||||
replay
|
||||
diff_history
|
||||
|
@ -88,13 +88,3 @@ For those of you who want the gritty details.
|
|||
|
||||
mplhooks
|
||||
vox
|
||||
|
||||
**Pending Deprecation:**
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ptk/shell
|
||||
ptk/history
|
||||
ptk/completer
|
||||
ptk/key_bindings
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
.. _xonsh_ptk_completer:
|
||||
|
||||
*************************************************************
|
||||
Prompt Toolkit Completer (``xonsh.ptk.completer``)
|
||||
*************************************************************
|
||||
|
||||
.. warning:: This module is pending deprecation.
|
|
@ -1,7 +0,0 @@
|
|||
.. _xonsh_ptk_history:
|
||||
|
||||
****************************************************************
|
||||
Prompt Toolkit History Object (``xonsh.ptk.history``)
|
||||
****************************************************************
|
||||
|
||||
.. warning:: This module is pending deprecation.
|
|
@ -1,8 +0,0 @@
|
|||
.. _xonsh_ptk_key_bindings:
|
||||
|
||||
**********************************************************
|
||||
Prompt Toolkit Key Bindings (``xonsh.ptk.key_bindings``)
|
||||
**********************************************************
|
||||
|
||||
.. warning:: This module is pending deprecation.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
.. _xonsh_ptk_shell:
|
||||
|
||||
******************************************************
|
||||
Prompt Toolkit Shell (``xonsh.ptk.shell``)
|
||||
******************************************************
|
||||
|
||||
.. warning:: This module is pending deprecation.
|
|
@ -1,10 +0,0 @@
|
|||
.. _xonsh_ptk2_key_bindings:
|
||||
|
||||
***********************************************************
|
||||
Prompt Toolkit 2 Key Bindings (``xonsh.ptk2.key_bindings``)
|
||||
***********************************************************
|
||||
|
||||
.. automodule:: xonsh.ptk2.key_bindings
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
|
@ -1,10 +1,10 @@
|
|||
.. _xonsh_ptk2_completer:
|
||||
|
||||
*************************************************************
|
||||
Prompt Toolkit 2 Completer (``xonsh.ptk2.completer``)
|
||||
Prompt Toolkit 2+ Completer (``xonsh.ptk_shell.completer``)
|
||||
*************************************************************
|
||||
|
||||
.. automodule:: xonsh.ptk2.completer
|
||||
.. automodule:: xonsh.ptk_shell.completer
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
|
@ -1,10 +1,10 @@
|
|||
.. _xonsh_ptk2_history:
|
||||
|
||||
****************************************************************
|
||||
Prompt Toolkit 2 History Object (``xonsh.ptk2.history``)
|
||||
Prompt Toolkit 2+ History Object (``xonsh.ptk_shell.history``)
|
||||
****************************************************************
|
||||
|
||||
.. automodule:: xonsh.ptk2.history
|
||||
.. automodule:: xonsh.ptk_shell.history
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
10
docs/api/ptk_shell/key_bindings.rst
Normal file
10
docs/api/ptk_shell/key_bindings.rst
Normal file
|
@ -0,0 +1,10 @@
|
|||
.. _xonsh_ptk_shell_key_bindings:
|
||||
|
||||
*****************************************************************
|
||||
Prompt Toolkit 2+ Key Bindings (``xonsh.ptk_shell.key_bindings``)
|
||||
*****************************************************************
|
||||
|
||||
.. automodule:: xonsh.ptk_shell.key_bindings
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
|
@ -1,10 +1,10 @@
|
|||
.. _xonsh_ptk2_shell:
|
||||
|
||||
******************************************************
|
||||
Prompt Toolkit 2 Shell (``xonsh.ptk2.shell``)
|
||||
Prompt Toolkit 2+ Shell (``xonsh.ptk_shell.shell``)
|
||||
******************************************************
|
||||
|
||||
.. automodule:: xonsh.ptk2.shell
|
||||
.. automodule:: xonsh.ptk_shell.shell
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
|
@ -32,12 +32,7 @@ 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.platform import ptk_version_info
|
||||
|
||||
if ptk_version_info()[0] < 2:
|
||||
from xonsh.ptk.shell import events
|
||||
else:
|
||||
from xonsh.ptk2.shell import events
|
||||
from xonsh.ptk_shell.shell import events
|
||||
else:
|
||||
from xonsh.events import events
|
||||
|
||||
|
|
|
@ -4,16 +4,15 @@ Xonsh currently has the following external dependencies,
|
|||
|
||||
*Run Time:*
|
||||
|
||||
#. Python v3.4+
|
||||
#. Python v3.5+
|
||||
|
||||
Pip supports "extra" dependencies in the form of ``xonsh[ptk,linux]``, where
|
||||
the list in the brackets identify the optional features
|
||||
|
||||
Xonsh currently has the following extras
|
||||
|
||||
#. ``ptk``: prompt-toolkit:
|
||||
*advanced readline library, line-editing*
|
||||
#. ``pygments>=2.2``: *syntax-highlighting*
|
||||
#. ``ptk``: prompt-toolkit >= 2.0: *advanced readline library, line-editing*
|
||||
#. ``pygments``: pygments >=2.2: *syntax-highlighting*
|
||||
#. ``proctitle``: setproctitle: *change the title of terminal to reflect the current subprocess*
|
||||
#. ``linux``: distro: *linux specific platform information*
|
||||
#. ``mac``: gnureadline: *GNU's featureful version of readline*
|
||||
|
|
|
@ -72,26 +72,21 @@ Custom keyload function
|
|||
|
||||
We need our additional keybindings to load after the shell is initialized, so we
|
||||
define a function that contains all of the custom keybindings and decorate it
|
||||
with the appropriate event, in this case ``on_ptk_create``. Please note that
|
||||
the method of ``handler`` differs depending on the version of ``prompt_toolkit``.
|
||||
with the appropriate event, in this case ``on_ptk_create``.
|
||||
|
||||
We'll start with a toy example that just inserts the text "hi" into the current line of the prompt::
|
||||
|
||||
@events.on_ptk_create
|
||||
def custom_keybindings(bindings, **kw):
|
||||
# prompt_toolkit 1.x
|
||||
# handler = bindings.registry.add_binding
|
||||
# prompt_toolkit 2.x
|
||||
handler = bindings.add
|
||||
|
||||
@handler(Keys.ControlW)
|
||||
|
||||
@bindings.add(Keys.ControlW)
|
||||
def say_hi(event):
|
||||
event.current_buffer.insert_text('hi')
|
||||
|
||||
Put that in your `xonshrc <xonshrc.html>`_, restart xonsh and then see if
|
||||
pressing ``Ctrl-w`` does anything (it should!)
|
||||
|
||||
.. note:: From prompt_toolkit 2.x it is also possible to write ``Keys.ControlW`` like ``c-w``.
|
||||
.. note:: It is also possible to write ``Keys.ControlW`` like ``c-w``.
|
||||
|
||||
|
||||
What commands can keybindings run?
|
||||
|
|
24
news/add_ptk3.rst
Normal file
24
news/add_ptk3.rst
Normal file
|
@ -0,0 +1,24 @@
|
|||
**Added:**
|
||||
|
||||
* Support package prompt-toolkit V3 as well as V2 in prompt_toolkit shell.
|
||||
|
||||
**Changed:**
|
||||
|
||||
* $SHELL_TYPE "prompt_toolkit" with any suffix creates the "prompt_toolkit" shell, requires package prompt-toolkit >= 2.0
|
||||
* Moved code from package xonsh.ptk2 to xonsh.ptk_shell (because it's the only one now); package xonsh.ptk2 redirects thence.
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* prompt-toolkit versions before 2.0
|
||||
|
||||
**Removed:**
|
||||
|
||||
* package xonsh.ptk
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -1,7 +1,7 @@
|
|||
cloud_sptheme
|
||||
numpydoc
|
||||
Sphinx==2.4.4
|
||||
prompt_toolkit
|
||||
prompt_toolkit>=2.0
|
||||
pygments>=2.2
|
||||
psutil
|
||||
pyzmq
|
||||
|
|
|
@ -4,7 +4,7 @@ flake8
|
|||
pytest-flake8
|
||||
pytest-cov
|
||||
pytest-timeout
|
||||
prompt-toolkit
|
||||
prompt-toolkit>=2.0
|
||||
pygments>=2.2
|
||||
codecov
|
||||
coverage
|
||||
|
|
|
@ -4,7 +4,7 @@ flake8
|
|||
pytest-flake8
|
||||
pytest-cov
|
||||
pytest-timeout
|
||||
prompt-toolkit
|
||||
prompt-toolkit>=2.0
|
||||
pygments>=2.2
|
||||
codecov
|
||||
coverage
|
||||
|
|
4
setup.py
4
setup.py
|
@ -357,7 +357,7 @@ def main():
|
|||
packages=[
|
||||
"xonsh",
|
||||
"xonsh.ply.ply",
|
||||
"xonsh.ptk",
|
||||
"xonsh.ptk_shell",
|
||||
"xonsh.ptk2",
|
||||
"xonsh.parsers",
|
||||
"xonsh.xoreutils",
|
||||
|
@ -408,7 +408,7 @@ def main():
|
|||
}
|
||||
skw["cmdclass"]["develop"] = xdevelop
|
||||
skw["extras_require"] = {
|
||||
"ptk": ["prompt-toolkit"],
|
||||
"ptk": ["prompt-toolkit>=2.0"],
|
||||
"pygments": ["pygments>=2.2"],
|
||||
"mac": ["gnureadline"],
|
||||
"linux": ["distro"],
|
||||
|
|
|
@ -149,23 +149,25 @@ def test_nested():
|
|||
def events_fxt():
|
||||
return EventManager()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def xonsh_builtins_ls_colors(xonsh_builtins, events_fxt):
|
||||
x = xonsh_builtins.__xonsh__
|
||||
xonsh_builtins.__xonsh__.shell = DummyShell() # because load_command_cache zaps it.
|
||||
xonsh_builtins.__xonsh__.shell.shell_type = "prompt_toolkit2"
|
||||
xonsh_builtins.__xonsh__.shell.shell_type = "prompt_toolkit"
|
||||
lsc = LsColors(LsColors.default_settings)
|
||||
xonsh_builtins.__xonsh__.env["LS_COLORS"] = lsc # establish LS_COLORS before style.
|
||||
xonsh_builtins.__xonsh__.shell.shell.styler = XonshStyle() # default style
|
||||
|
||||
events.on_lscolors_change(on_lscolors_change)
|
||||
|
||||
|
||||
yield xonsh_builtins
|
||||
xonsh_builtins.__xonsh__ = x
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_path(tmpdir, xonsh_builtins_ls_colors):
|
||||
|
||||
|
||||
test_dir = str(tmpdir.mkdir("xonsh-test-highlight-path"))
|
||||
check_token(
|
||||
"cd {}".format(test_dir), [(Name.Builtin, "cd"), (Color.BOLD_BLUE, test_dir)]
|
||||
|
@ -184,7 +186,7 @@ def test_path(tmpdir, xonsh_builtins_ls_colors):
|
|||
def test_color_on_lscolors_change(tmpdir, xonsh_builtins_ls_colors):
|
||||
"""Verify colorizer returns Token.Text if file type not defined in LS_COLORS"""
|
||||
|
||||
lsc = xonsh_builtins_ls_colors.__xonsh__.env["LS_COLORS"]
|
||||
lsc = xonsh_builtins_ls_colors.__xonsh__.env["LS_COLORS"]
|
||||
test_dir = str(tmpdir.mkdir("xonsh-test-highlight-path"))
|
||||
|
||||
lsc['di'] = ('GREEN',)
|
||||
|
@ -194,11 +196,12 @@ def test_color_on_lscolors_change(tmpdir, xonsh_builtins_ls_colors):
|
|||
)
|
||||
|
||||
del lsc['di']
|
||||
|
||||
|
||||
check_token(
|
||||
"cd {}".format(test_dir), [(Name.Builtin, "cd"), (Text, test_dir)]
|
||||
)
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_subproc_args():
|
||||
check_token("cd 192.168.0.1", [(Text, "192.168.0.1")])
|
||||
|
|
|
@ -5,21 +5,54 @@ try:
|
|||
except ImportError:
|
||||
pytest.mark.skip(msg="prompt_toolkit is not available")
|
||||
|
||||
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"""
|
||||
from xonsh.ptk_shell.history import PromptToolkitHistory
|
||||
hist = PromptToolkitHistory(load_prev=False)
|
||||
hist.append_string("line10")
|
||||
return hist
|
||||
|
||||
|
||||
@skip_if_lt_ptk2
|
||||
def test_obj(history_obj):
|
||||
assert ["line10"] == history_obj.get_strings()
|
||||
assert len(history_obj) == 1
|
||||
assert ["line10"] == [x for x in history_obj]
|
||||
|
||||
|
||||
def test_ptk2_backcompat():
|
||||
"""
|
||||
Test that legacy code (ahem, xontribs) can still reference xonsh.ptk2 (for a while)
|
||||
"""
|
||||
|
||||
import xonsh.ptk_shell.shell as imports_new
|
||||
import xonsh.ptk2.shell as imports_legacy
|
||||
# defining the ptk2 package this way leaves out the internal global names (which all start with '_')
|
||||
|
||||
s_new = set(dir(imports_new))
|
||||
s_legacy = set(dir(imports_legacy))
|
||||
extra_names = s_new - s_legacy
|
||||
|
||||
for name in extra_names:
|
||||
assert name.startswith('_')
|
||||
|
||||
assert s_legacy.issubset(s_new)
|
||||
|
||||
# prove that legacy API is usable
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def history_obj_legacy():
|
||||
"""Instantiate `PromptToolkitHistory` via legacy alias and append a line string"""
|
||||
from xonsh.ptk2.history import PromptToolkitHistory
|
||||
hist = PromptToolkitHistory(load_prev=False)
|
||||
hist.append_string("line10")
|
||||
return hist
|
||||
|
||||
|
||||
def test_obj_legacy(history_obj_legacy):
|
||||
history_obj = history_obj_legacy
|
||||
assert ["line10"] == history_obj.get_strings()
|
||||
assert len(history_obj) == 1
|
||||
assert ["line10"] == [x for x in history_obj]
|
||||
|
|
|
@ -12,9 +12,7 @@ from prompt_toolkit.buffer import Buffer
|
|||
from xonsh.tools import ON_WINDOWS
|
||||
from xonsh.built_ins import XonshSession
|
||||
|
||||
from tools import DummyEnv, skip_if_lt_ptk2
|
||||
|
||||
|
||||
from tools import DummyEnv
|
||||
Context = namedtuple("Context", ["indent", "buffer", "accept", "cli", "cr"])
|
||||
|
||||
|
||||
|
@ -24,7 +22,7 @@ def ctx():
|
|||
builtins.__xonsh__ = XonshSession()
|
||||
builtins.__xonsh__.env = DummyEnv()
|
||||
builtins.__xonsh__.env["INDENT"] = " "
|
||||
from xonsh.ptk2.key_bindings import carriage_return
|
||||
from xonsh.ptk_shell.key_bindings import carriage_return
|
||||
|
||||
ptk_buffer = Buffer()
|
||||
ptk_buffer.accept_action = MagicMock(name="accept")
|
||||
|
@ -40,7 +38,6 @@ def ctx():
|
|||
del builtins.__xonsh__
|
||||
|
||||
|
||||
@skip_if_lt_ptk2
|
||||
def test_colon_indent(ctx):
|
||||
document = Document("for i in range(5):")
|
||||
ctx.buffer.set_document(document)
|
||||
|
@ -48,7 +45,6 @@ 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)
|
||||
|
@ -61,25 +57,23 @@ 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.ptk2.key_bindings.can_compile", mock):
|
||||
with patch("xonsh.ptk_shell.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.ptk2.key_bindings.can_compile", mock):
|
||||
with patch("xonsh.ptk_shell.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)
|
||||
|
@ -87,10 +81,9 @@ 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.ptk2.key_bindings.can_compile", mock):
|
||||
with patch("xonsh.ptk_shell.key_bindings.can_compile", mock):
|
||||
document = Document("this line will \\")
|
||||
ctx.buffer.set_document(document)
|
||||
ctx.cr(ctx.buffer, ctx.cli)
|
||||
|
@ -100,20 +93,18 @@ 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.ptk2.key_bindings.can_compile", mock):
|
||||
with patch("xonsh.ptk_shell.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.ptk2.key_bindings.can_compile", mock):
|
||||
with patch("xonsh.ptk_shell.key_bindings.can_compile", mock):
|
||||
document = Document("ls")
|
||||
ctx.buffer.set_document(document)
|
||||
ctx.cr(ctx.buffer, ctx.cli)
|
||||
|
|
70
tests/test_ptk_shell.py
Normal file
70
tests/test_ptk_shell.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Test initialization of prompt_toolkit shell"""
|
||||
|
||||
import os
|
||||
import gc
|
||||
import builtins
|
||||
|
||||
import pytest
|
||||
|
||||
from tools import skip_if_on_windows
|
||||
|
||||
from xonsh.platform import ON_WINDOWS, minimum_required_ptk_version
|
||||
|
||||
from xonsh.ptk_shell.shell import PromptToolkitShell
|
||||
|
||||
# verify error if ptk not installed or below min
|
||||
|
||||
import warnings
|
||||
import xonsh.platform
|
||||
from xonsh.shell import Shell
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ptk_ver, ini_shell_type, exp_shell_type, warn_snip",
|
||||
[
|
||||
(None, "prompt_toolkit", "readline", "prompt_toolkit is not available"),
|
||||
((0, 5, 7), "prompt_toolkit", "readline", "is not supported"),
|
||||
((1, 0, 0), "prompt_toolkit", "readline", "is not supported"),
|
||||
((2, 0, 0), "prompt_toolkit", "prompt_toolkit", None),
|
||||
((2, 0, 0), "best", "prompt_toolkit", None),
|
||||
((2, 0, 0), "readline", "readline", None),
|
||||
((3, 0, 0), "prompt_toolkit", "prompt_toolkit", None),
|
||||
((3, 0, 0), "best", "prompt_toolkit", None),
|
||||
((3, 0, 0), "readline", "readline", None),
|
||||
((4, 0, 0), "prompt_toolkit", "prompt_toolkit", None),
|
||||
],
|
||||
)
|
||||
def test_prompt_toolkit_version_checks(ptk_ver, ini_shell_type, exp_shell_type, warn_snip, monkeypatch, xonsh_builtins):
|
||||
|
||||
mocked_warn = ""
|
||||
|
||||
def mock_warning(msg):
|
||||
nonlocal mocked_warn
|
||||
mocked_warn = msg
|
||||
return
|
||||
|
||||
def mock_ptk_above_min_supported():
|
||||
nonlocal ptk_ver
|
||||
return ptk_ver and (ptk_ver[:2] >= minimum_required_ptk_version)
|
||||
|
||||
def mock_has_prompt_toolkit():
|
||||
nonlocal ptk_ver
|
||||
return ptk_ver is not None
|
||||
|
||||
monkeypatch.setattr("xonsh.shell.warnings.warn", mock_warning) # hardwon: patch the caller!
|
||||
monkeypatch.setattr("xonsh.shell.ptk_above_min_supported", mock_ptk_above_min_supported) # have to patch both callers
|
||||
monkeypatch.setattr("xonsh.platform.ptk_above_min_supported", mock_ptk_above_min_supported)
|
||||
monkeypatch.setattr("xonsh.shell.has_prompt_toolkit", mock_has_prompt_toolkit)
|
||||
monkeypatch.setattr("xonsh.platform.has_prompt_toolkit", mock_has_prompt_toolkit)
|
||||
|
||||
act_shell_type = Shell.choose_shell_type(ini_shell_type, {})
|
||||
|
||||
assert act_shell_type == exp_shell_type
|
||||
|
||||
if warn_snip:
|
||||
assert warn_snip in mocked_warn
|
||||
|
||||
pass
|
||||
|
||||
# someday: initialize PromptToolkitShell and have it actually do something.
|
|
@ -26,7 +26,7 @@ def xonsh_builtins_LS_COLORS(xonsh_builtins):
|
|||
e = xonsh_builtins.__xonsh__.env
|
||||
lsc = LsColors(LsColors.default_settings)
|
||||
xonsh_builtins.__xonsh__.env["LS_COLORS"] = lsc
|
||||
xonsh_builtins.__xonsh__.shell.shell_type = "prompt_toolkit2"
|
||||
xonsh_builtins.__xonsh__.shell.shell_type = "prompt_toolkit"
|
||||
# styler = XonshStyle() # default style
|
||||
# xonsh_builtins.__xonsh__.shell.shell.styler = styler
|
||||
# can't really instantiate XonshStyle separate from a shell??
|
||||
|
|
|
@ -77,3 +77,4 @@ hello = 'world'
|
|||
|
||||
ctx = xontrib_context("script")
|
||||
assert ctx == {"hello": "world"}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import pytest
|
|||
|
||||
from xonsh.environ import Env
|
||||
from xonsh.base_shell import BaseShell
|
||||
from xonsh.platform import ptk_version_info
|
||||
|
||||
|
||||
VER_3_5 = (3, 5)
|
||||
|
@ -59,10 +58,6 @@ 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-toolkit <2"
|
||||
)
|
||||
|
||||
|
||||
def sp(cmd):
|
||||
return subprocess.check_output(cmd, universal_newlines=True)
|
||||
|
|
|
@ -1021,7 +1021,8 @@ def DEFAULT_DOCS():
|
|||
),
|
||||
"LANG": VarDocs("Fallback locale setting for systems where it matters"),
|
||||
"LS_COLORS": VarDocs(
|
||||
"Color settings for ``ls`` command line utility",
|
||||
"Color settings for ``ls`` command line utility and, "
|
||||
"with ``$SHELL_TYPE='prompt_toolkit'``, file arguments in subprocess mode.",
|
||||
default="``*.7z=1;0;31:*.Z=1;0;31:*.aac=0;36:*.ace=1;0;31:"
|
||||
"*.alz=1;0;31:*.arc=1;0;31:*.arj=1;0;31:*.asf=1;0;35:*.au=0;36:"
|
||||
"*.avi=1;0;35:*.bmp=1;0;35:*.bz=1;0;31:*.bz2=1;0;31:*.cab=1;0;31:"
|
||||
|
|
|
@ -27,8 +27,6 @@ class History:
|
|||
|
||||
History objects should be created via a subclass of History.
|
||||
|
||||
Indexing
|
||||
--------
|
||||
History acts like a sequence that can be indexed to return
|
||||
``HistoryEntry`` objects.
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@ def _pprint_displayhook(value):
|
|||
printed_val = repr(value)
|
||||
if HAS_PYGMENTS and env.get("COLOR_RESULTS"):
|
||||
tokens = list(pygments.lex(printed_val, lexer=pyghooks.XonshLexer()))
|
||||
end = "" if env.get("SHELL_TYPE") == "prompt_toolkit2" else "\n"
|
||||
end = "" if env.get("SHELL_TYPE") == "prompt_toolkit" else "\n"
|
||||
print_color(tokens, end=end)
|
||||
else:
|
||||
print(printed_val) # black & white case
|
||||
|
|
|
@ -169,19 +169,13 @@ def ptk_version_info():
|
|||
return None
|
||||
|
||||
|
||||
minimum_required_ptk_version = (2, 0)
|
||||
"""Minimum version of prompt-toolkit supported by Xonsh"""
|
||||
|
||||
|
||||
@functools.lru_cache(1)
|
||||
def ptk_above_min_supported():
|
||||
minimum_required_ptk_version = (1, 0)
|
||||
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"
|
||||
return ptk_version_info() and ptk_version_info()[:2] >= minimum_required_ptk_version
|
||||
|
||||
|
||||
@functools.lru_cache(1)
|
||||
|
@ -198,7 +192,7 @@ def win_ansi_support():
|
|||
|
||||
@functools.lru_cache(1)
|
||||
def ptk_below_max_supported():
|
||||
ptk_max_version_cutoff = (2, 0)
|
||||
ptk_max_version_cutoff = (99999, 0) # currently, no limit.
|
||||
return ptk_version_info()[:2] < ptk_max_version_cutoff
|
||||
|
||||
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""History object for use with prompt_toolkit."""
|
||||
import builtins
|
||||
from threading import Thread
|
||||
|
||||
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, wait_for_gc=True, *args, **kwargs):
|
||||
"""Initialize history object."""
|
||||
super().__init__()
|
||||
self.strings = []
|
||||
if load_prev:
|
||||
PromptToolkitHistoryAdder(self, wait_for_gc=wait_for_gc)
|
||||
|
||||
def append(self, entry):
|
||||
"""Append new entry to the history."""
|
||||
self.strings.append(entry)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.strings[index]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.strings)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.strings)
|
||||
|
||||
|
||||
class PromptToolkitHistoryAdder(Thread):
|
||||
def __init__(self, ptkhist, wait_for_gc=True, *args, **kwargs):
|
||||
"""Thread responsible for adding inputs from history to the current
|
||||
prompt-toolkit history instance. May wait for the history garbage
|
||||
collector to finish.
|
||||
"""
|
||||
super(PromptToolkitHistoryAdder, self).__init__(*args, **kwargs)
|
||||
self.daemon = True
|
||||
self.ptkhist = ptkhist
|
||||
self.wait_for_gc = wait_for_gc
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
hist = builtins.__xonsh__.history
|
||||
if hist is None:
|
||||
return
|
||||
buf = None
|
||||
ptkhist = self.ptkhist
|
||||
for cmd in hist.all_items():
|
||||
line = cmd["inp"].rstrip()
|
||||
if len(ptkhist) == 0 or line != ptkhist[-1]:
|
||||
ptkhist.append(line)
|
||||
if buf is None:
|
||||
buf = self._buf()
|
||||
if buf is None:
|
||||
continue
|
||||
buf.reset(initial_document=buf.document)
|
||||
|
||||
def _buf(self):
|
||||
# Thread-safe version of
|
||||
# buf = builtins.__xonsh__.shell.shell.prompter.cli.application.buffer
|
||||
path = [
|
||||
"__xonsh__",
|
||||
"shell",
|
||||
"shell",
|
||||
"prompter",
|
||||
"cli",
|
||||
"application",
|
||||
"buffer",
|
||||
]
|
||||
buf = builtins
|
||||
for a in path:
|
||||
buf = getattr(buf, a, None)
|
||||
if buf is None:
|
||||
break
|
||||
return buf
|
|
@ -1,131 +0,0 @@
|
|||
"""A prompt-toolkit inspired shortcut collection."""
|
||||
import builtins
|
||||
import textwrap
|
||||
|
||||
from prompt_toolkit.interface import CommandLineInterface
|
||||
from prompt_toolkit.enums import EditingMode
|
||||
from prompt_toolkit.utils import DummyContext
|
||||
from prompt_toolkit.shortcuts import (
|
||||
create_prompt_application,
|
||||
create_eventloop,
|
||||
create_asyncio_eventloop,
|
||||
create_output,
|
||||
)
|
||||
|
||||
from xonsh.platform import ptk_version_info
|
||||
import xonsh.tools as xt
|
||||
|
||||
|
||||
class Prompter(object):
|
||||
def __init__(self, cli=None, *args, **kwargs):
|
||||
"""Implements a prompt that statefully holds a command-line
|
||||
interface. When used as a context manager, it will return itself
|
||||
on entry and reset itself on exit.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cli : CommandLineInterface or None, optional
|
||||
If this is not a CommandLineInterface object, such an object
|
||||
will be created when the prompt() method is called.
|
||||
"""
|
||||
self.cli = cli
|
||||
self.major_minor = ptk_version_info()[:2]
|
||||
|
||||
def __enter__(self):
|
||||
self.reset()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
pass
|
||||
|
||||
def prompt(self, message="", **kwargs):
|
||||
"""Get input from the user and return it.
|
||||
|
||||
This is a wrapper around a lot of prompt_toolkit functionality and
|
||||
can be a replacement for raw_input. (or GNU readline.) If you want
|
||||
to keep your history across several calls, create one
|
||||
`~prompt_toolkit.history.History instance and pass it every
|
||||
time. This function accepts many keyword arguments. Except for the
|
||||
following. they are a proxy to the arguments of
|
||||
create_prompt_application().
|
||||
|
||||
Parameters
|
||||
----------
|
||||
patch_stdout : file-like, optional
|
||||
Replace ``sys.stdout`` by a proxy that ensures that print
|
||||
statements from other threads won't destroy the prompt. (They
|
||||
will be printed above the prompt instead.)
|
||||
return_asyncio_coroutine : bool, optional
|
||||
When True, return a asyncio coroutine. (Python >3.3)
|
||||
|
||||
Notes
|
||||
-----
|
||||
This method was forked from the mainline prompt-toolkit repo.
|
||||
Copyright (c) 2014, Jonathan Slenders, All rights reserved.
|
||||
"""
|
||||
patch_stdout = kwargs.pop("patch_stdout", False)
|
||||
return_asyncio_coroutine = kwargs.pop("return_asyncio_coroutine", False)
|
||||
if return_asyncio_coroutine:
|
||||
eventloop = create_asyncio_eventloop()
|
||||
else:
|
||||
eventloop = kwargs.pop("eventloop", None) or create_eventloop()
|
||||
|
||||
# Create CommandLineInterface.
|
||||
if self.cli is None:
|
||||
if builtins.__xonsh__.env.get("VI_MODE"):
|
||||
editing_mode = EditingMode.VI
|
||||
else:
|
||||
editing_mode = EditingMode.EMACS
|
||||
kwargs["editing_mode"] = editing_mode
|
||||
cli = CommandLineInterface(
|
||||
application=create_prompt_application(message, **kwargs),
|
||||
eventloop=eventloop,
|
||||
output=create_output(),
|
||||
)
|
||||
self.cli = cli
|
||||
else:
|
||||
cli = self.cli
|
||||
|
||||
# Replace stdout.
|
||||
patch_context = cli.patch_stdout_context() if patch_stdout else DummyContext()
|
||||
|
||||
# Read input and return it.
|
||||
if return_asyncio_coroutine:
|
||||
# Create an asyncio coroutine and call it.
|
||||
exec_context = {"patch_context": patch_context, "cli": cli}
|
||||
exec(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
import asyncio
|
||||
@asyncio.coroutine
|
||||
def prompt_coro():
|
||||
with patch_context:
|
||||
document = yield from cli.run_async(reset_current_buffer=False)
|
||||
if document:
|
||||
return document.text
|
||||
"""
|
||||
),
|
||||
exec_context,
|
||||
)
|
||||
return exec_context["prompt_coro"]()
|
||||
else:
|
||||
# Note: We pass `reset_current_buffer=False`, because that way
|
||||
# it's easy to give DEFAULT_BUFFER a default value, without it
|
||||
# getting erased. We don't have to reset anyway, because this is
|
||||
# the first and only time that this CommandLineInterface will run.
|
||||
try:
|
||||
with patch_context:
|
||||
document = cli.run(reset_current_buffer=False)
|
||||
if document:
|
||||
return document.text
|
||||
except Exception:
|
||||
xt.print_exception()
|
||||
# return something to prevent xonsh crash when any
|
||||
# exceptions raise
|
||||
return ""
|
||||
finally:
|
||||
eventloop.close()
|
||||
|
||||
def reset(self):
|
||||
"""Resets the prompt and cli to a pristine state on this object."""
|
||||
self.cli = None
|
|
@ -0,0 +1 @@
|
|||
from xonsh.ptk_shell import * # noqa: F403 F401
|
|
@ -1,105 +1 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Completer implementation to use with prompt_toolkit."""
|
||||
import os
|
||||
import builtins
|
||||
|
||||
from prompt_toolkit.completion import Completer, Completion
|
||||
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
||||
from prompt_toolkit.application.current import get_app
|
||||
|
||||
|
||||
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):
|
||||
"""Adjust the height for showing autocompletion menu."""
|
||||
app = get_app()
|
||||
render = app.renderer
|
||||
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
|
||||
last_h = render._last_screen.height if render._last_screen else 0
|
||||
last_h = max(render._min_available_height, last_h)
|
||||
if last_h < size:
|
||||
if render._last_screen:
|
||||
render._last_screen.height = size
|
||||
from xonsh.ptk_shell.completer import * # noqa: F403 F401
|
||||
|
|
|
@ -1,53 +1 @@
|
|||
# -*- 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(newest_first=True):
|
||||
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())
|
||||
|
||||
|
||||
def _cust_history_matches(self, i):
|
||||
"""Custom history search method for prompt_toolkit that matches previous
|
||||
commands anywhere on a line, not just at the start.
|
||||
|
||||
This gets monkeypatched into the prompt_toolkit prompter if
|
||||
``XONSH_HISTORY_MATCH_ANYWHERE=True``"""
|
||||
return (
|
||||
self.history_search_text is None
|
||||
or self.history_search_text in self._working_lines[i]
|
||||
)
|
||||
from xonsh.ptk_shell.history import * # noqa: F403 F401
|
||||
|
|
|
@ -1,359 +1 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Key bindings for prompt_toolkit xonsh shell."""
|
||||
import builtins
|
||||
|
||||
from prompt_toolkit import search
|
||||
from prompt_toolkit.enums import DEFAULT_BUFFER
|
||||
from prompt_toolkit.filters import (
|
||||
Condition,
|
||||
IsMultiline,
|
||||
HasSelection,
|
||||
EmacsInsertMode,
|
||||
ViInsertMode,
|
||||
IsSearching,
|
||||
)
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
env = builtins.__xonsh__.env
|
||||
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:
|
||||
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"):
|
||||
return False
|
||||
else:
|
||||
app = get_app()
|
||||
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.
|
||||
"""
|
||||
env = builtins.__xonsh__.env
|
||||
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:
|
||||
env = builtins.__xonsh__.env
|
||||
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() and whitespace_or_bracket_after():
|
||||
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() and whitespace_or_bracket_after():
|
||||
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
|
||||
b.validate_and_handle()
|
||||
xonsh_exit([])
|
||||
|
||||
@handle(Keys.ControlJ, filter=IsMultiline() & insert_mode)
|
||||
@handle(Keys.ControlM, filter=IsMultiline() & insert_mode)
|
||||
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
|
||||
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.ControlM, filter=IsSearching())
|
||||
@handle(Keys.ControlJ, filter=IsSearching())
|
||||
def accept_search(event):
|
||||
search.accept_search()
|
||||
|
||||
@handle(Keys.ControlZ)
|
||||
def skip_control_z(event):
|
||||
"""Prevents the writing of ^Z to the prompt, if Ctrl+Z was pressed
|
||||
during the previous command.
|
||||
"""
|
||||
pass
|
||||
from xonsh.ptk_shell.key_bindings import * # noqa: F403 F401
|
||||
|
|
|
@ -1,375 +1 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""The prompt_toolkit based xonsh shell."""
|
||||
import os
|
||||
import sys
|
||||
import builtins
|
||||
from types import MethodType
|
||||
|
||||
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, ON_POSIX
|
||||
from xonsh.style_tools import partial_color_tokenize, _TokenType, DEFAULT_STYLE_DICT
|
||||
from xonsh.lazyimps import pygments, pyghooks, winutils
|
||||
from xonsh.pygments_cache import get_all_styles
|
||||
from xonsh.ptk2.history import PromptToolkitHistory, _cust_history_matches
|
||||
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 import merge_styles, Style
|
||||
from prompt_toolkit.styles.pygments import (
|
||||
style_from_pygments_cls,
|
||||
style_from_pygments_dict,
|
||||
)
|
||||
|
||||
|
||||
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."""
|
||||
|
||||
completion_displays_to_styles = {
|
||||
"multi": CompleteStyle.MULTI_COLUMN,
|
||||
"single": CompleteStyle.COLUMN,
|
||||
"readline": CompleteStyle.READLINE_LIKE,
|
||||
"none": None,
|
||||
}
|
||||
|
||||
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)
|
||||
# Store original `_history_matches` in case we need to restore it
|
||||
self._history_matches_orig = self.prompter.default_buffer._history_matches
|
||||
# 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
|
||||
refresh_interval = env.get("PROMPT_REFRESH_INTERVAL")
|
||||
refresh_interval = refresh_interval if refresh_interval > 0 else None
|
||||
complete_in_thread = env.get("COMPLETION_IN_THREAD")
|
||||
completions_display = env.get("COMPLETIONS_DISPLAY")
|
||||
complete_style = self.completion_displays_to_styles[completions_display]
|
||||
|
||||
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
|
||||
|
||||
get_bottom_toolbar_tokens = self.bottom_toolbar_tokens
|
||||
|
||||
if env.get("UPDATE_PROMPT_ON_KEYPRESS"):
|
||||
get_prompt_tokens = self.prompt_tokens
|
||||
get_rprompt_tokens = self.rprompt_tokens
|
||||
else:
|
||||
get_prompt_tokens = self.prompt_tokens()
|
||||
get_rprompt_tokens = self.rprompt_tokens()
|
||||
if get_bottom_toolbar_tokens:
|
||||
get_bottom_toolbar_tokens = get_bottom_toolbar_tokens()
|
||||
|
||||
if env.get("VI_MODE"):
|
||||
editing_mode = EditingMode.VI
|
||||
else:
|
||||
editing_mode = EditingMode.EMACS
|
||||
|
||||
if env.get("XONSH_HISTORY_MATCH_ANYWHERE"):
|
||||
self.prompter.default_buffer._history_matches = MethodType(
|
||||
_cust_history_matches, self.prompter.default_buffer
|
||||
)
|
||||
elif (
|
||||
self.prompter.default_buffer._history_matches
|
||||
is not self._history_matches_orig
|
||||
):
|
||||
self.prompter.default_buffer._history_matches = self._history_matches_orig
|
||||
|
||||
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,
|
||||
"include_default_pygments_style": False,
|
||||
"refresh_interval": refresh_interval,
|
||||
"complete_in_thread": complete_in_thread,
|
||||
}
|
||||
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 = style_from_pygments_dict(DEFAULT_STYLE_DICT)
|
||||
|
||||
prompt_args["style"] = style
|
||||
|
||||
style_overrides_env = env.get("PTK_STYLE_OVERRIDES")
|
||||
if style_overrides_env:
|
||||
try:
|
||||
style_overrides = Style.from_dict(style_overrides_env)
|
||||
prompt_args["style"] = merge_styles([style, style_overrides])
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
print_exception()
|
||||
|
||||
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)
|
||||
|
||||
@property
|
||||
def bottom_toolbar_tokens(self):
|
||||
"""Returns self._bottom_toolbar_tokens if it would yield a result
|
||||
"""
|
||||
if builtins.__xonsh__.env.get("BOTTOM_TOOLBAR"):
|
||||
return self._bottom_toolbar_tokens
|
||||
|
||||
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 not dots:
|
||||
return ""
|
||||
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)
|
||||
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, end=end, include_default_pygments_style=False
|
||||
)
|
||||
|
||||
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 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')
|
||||
if not ON_POSIX:
|
||||
return
|
||||
stty, _ = builtins.__xonsh__.commands_cache.lazyget("stty", (None, None))
|
||||
if stty is None:
|
||||
return
|
||||
os.system(stty + " sane")
|
||||
from xonsh.ptk_shell.shell import * # noqa: F403 F401
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
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
|
||||
from prompt_toolkit.application.current import get_app
|
||||
|
||||
|
||||
class PromptToolkitCompleter(Completer):
|
||||
|
@ -80,8 +80,8 @@ class PromptToolkitCompleter(Completer):
|
|||
|
||||
def suggestion_completion(self, document, line):
|
||||
"""Provides a completion based on the current auto-suggestion."""
|
||||
cli = self.shell.prompter.cli
|
||||
sug = self.hist_suggester.get_suggestion(cli, cli.current_buffer, document)
|
||||
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(" ")
|
||||
|
@ -89,20 +89,17 @@ class PromptToolkitCompleter(Completer):
|
|||
return prev + comp
|
||||
|
||||
def reserve_space(self):
|
||||
cli = builtins.__xonsh__.shell.shell.prompter.cli
|
||||
window = cli.application.layout.children[0].content.children[1]
|
||||
"""Adjust the height for showing autocompletion menu."""
|
||||
app = get_app()
|
||||
render = app.renderer
|
||||
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
|
||||
last_h = render._last_screen.height if render._last_screen else 0
|
||||
last_h = max(render._min_available_height, last_h)
|
||||
if last_h < size:
|
||||
if render._last_screen:
|
||||
render._last_screen.height = size
|
53
xonsh/ptk_shell/history.py
Normal file
53
xonsh/ptk_shell/history.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# -*- 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(newest_first=True):
|
||||
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())
|
||||
|
||||
|
||||
def _cust_history_matches(self, i):
|
||||
"""Custom history search method for prompt_toolkit that matches previous
|
||||
commands anywhere on a line, not just at the start.
|
||||
|
||||
This gets monkeypatched into the prompt_toolkit prompter if
|
||||
``XONSH_HISTORY_MATCH_ANYWHERE=True``"""
|
||||
return (
|
||||
self.history_search_text is None
|
||||
or self.history_search_text in self._working_lines[i]
|
||||
)
|
|
@ -2,6 +2,7 @@
|
|||
"""Key bindings for prompt_toolkit xonsh shell."""
|
||||
import builtins
|
||||
|
||||
from prompt_toolkit import search
|
||||
from prompt_toolkit.enums import DEFAULT_BUFFER
|
||||
from prompt_toolkit.filters import (
|
||||
Condition,
|
||||
|
@ -9,14 +10,15 @@ from prompt_toolkit.filters import (
|
|||
HasSelection,
|
||||
EmacsInsertMode,
|
||||
ViInsertMode,
|
||||
IsSearching,
|
||||
)
|
||||
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"])
|
||||
|
||||
|
||||
|
@ -36,6 +38,7 @@ def carriage_return(b, cli, *, autoindent=True):
|
|||
at_end_of_line = _is_blank(doc.current_line_after_cursor)
|
||||
current_line_blank = _is_blank(doc.current_line)
|
||||
|
||||
env = builtins.__xonsh__.env
|
||||
indent = env.get("INDENT") if autoindent else ""
|
||||
|
||||
partial_string_info = check_for_partial_string(doc.text)
|
||||
|
@ -68,7 +71,7 @@ def carriage_return(b, cli, *, autoindent=True):
|
|||
elif current_line_blank and in_partial_string:
|
||||
b.newline(copy_margin=autoindent)
|
||||
else:
|
||||
b.accept_action.validate_and_handle(cli, b)
|
||||
b.validate_and_handle()
|
||||
|
||||
|
||||
def _is_blank(l):
|
||||
|
@ -93,35 +96,36 @@ def can_compile(src):
|
|||
|
||||
|
||||
@Condition
|
||||
def tab_insert_indent(cli):
|
||||
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 = cli.current_buffer.document.current_line_before_cursor
|
||||
before_cursor = get_app().current_buffer.document.current_line_before_cursor
|
||||
|
||||
return bool(before_cursor.isspace())
|
||||
|
||||
|
||||
@Condition
|
||||
def beginning_of_line(cli):
|
||||
def beginning_of_line():
|
||||
"""Check if cursor is at beginning of a line other than the first line in a
|
||||
multiline document
|
||||
"""
|
||||
before_cursor = cli.current_buffer.document.current_line_before_cursor
|
||||
app = get_app()
|
||||
before_cursor = app.current_buffer.document.current_line_before_cursor
|
||||
|
||||
return bool(
|
||||
len(before_cursor) == 0 and not cli.current_buffer.document.on_first_line
|
||||
len(before_cursor) == 0 and not app.current_buffer.document.on_first_line
|
||||
)
|
||||
|
||||
|
||||
@Condition
|
||||
def end_of_line(cli):
|
||||
def end_of_line():
|
||||
"""Check if cursor is at the end of a line other than the last line in a
|
||||
multiline document
|
||||
"""
|
||||
d = cli.current_buffer.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
|
||||
|
||||
|
@ -129,37 +133,40 @@ def end_of_line(cli):
|
|||
|
||||
|
||||
@Condition
|
||||
def should_confirm_completion(cli):
|
||||
def should_confirm_completion():
|
||||
"""Check if completion needs confirmation"""
|
||||
return (
|
||||
builtins.__xonsh__.env.get("COMPLETIONS_CONFIRM")
|
||||
and cli.current_buffer.complete_state
|
||||
and get_app().current_buffer.complete_state
|
||||
)
|
||||
|
||||
|
||||
# Copied from prompt-toolkit's key_binding/bindings/basic.py
|
||||
@Condition
|
||||
def ctrl_d_condition(cli):
|
||||
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
|
||||
return False
|
||||
else:
|
||||
return cli.current_buffer_name == DEFAULT_BUFFER and not cli.current_buffer.text
|
||||
app = get_app()
|
||||
buffer_name = app.current_buffer.name
|
||||
|
||||
return buffer_name == DEFAULT_BUFFER and not app.current_buffer.text
|
||||
|
||||
|
||||
@Condition
|
||||
def autopair_condition(cli):
|
||||
def autopair_condition():
|
||||
"""Check if XONSH_AUTOPAIR is set"""
|
||||
return builtins.__xonsh__.env.get("XONSH_AUTOPAIR", False)
|
||||
|
||||
|
||||
@Condition
|
||||
def whitespace_or_bracket_before(cli):
|
||||
def whitespace_or_bracket_before():
|
||||
"""Check if there is whitespace or an opening
|
||||
bracket to the left of the cursor"""
|
||||
d = cli.current_buffer.document
|
||||
d = get_app().current_buffer.document
|
||||
return bool(
|
||||
d.cursor_position == 0
|
||||
or d.char_before_cursor.isspace()
|
||||
|
@ -168,10 +175,10 @@ def whitespace_or_bracket_before(cli):
|
|||
|
||||
|
||||
@Condition
|
||||
def whitespace_or_bracket_after(cli):
|
||||
def whitespace_or_bracket_after():
|
||||
"""Check if there is whitespace or a closing
|
||||
bracket to the right of the cursor"""
|
||||
d = cli.current_buffer.document
|
||||
d = get_app().current_buffer.document
|
||||
return bool(
|
||||
d.is_cursor_at_the_end_of_line
|
||||
or d.current_char.isspace()
|
||||
|
@ -179,11 +186,11 @@ def whitespace_or_bracket_after(cli):
|
|||
)
|
||||
|
||||
|
||||
def load_xonsh_bindings(key_bindings_manager):
|
||||
def load_xonsh_bindings(key_bindings):
|
||||
"""
|
||||
Load custom key bindings.
|
||||
"""
|
||||
handle = key_bindings_manager.registry.add_binding
|
||||
handle = key_bindings.add
|
||||
has_selection = HasSelection()
|
||||
insert_mode = ViInsertMode() | EmacsInsertMode()
|
||||
|
||||
|
@ -193,6 +200,7 @@ def load_xonsh_bindings(key_bindings_manager):
|
|||
If there are only whitespaces before current cursor position insert
|
||||
indent instead of autocompleting.
|
||||
"""
|
||||
env = builtins.__xonsh__.env
|
||||
event.cli.current_buffer.insert_text(env.get("INDENT"))
|
||||
|
||||
@handle(Keys.ControlX, Keys.ControlE, filter=~has_selection)
|
||||
|
@ -207,6 +215,7 @@ def load_xonsh_bindings(key_bindings_manager):
|
|||
if b.complete_state:
|
||||
b.complete_previous()
|
||||
else:
|
||||
env = builtins.__xonsh__.env
|
||||
event.cli.current_buffer.insert_text(env.get("INDENT"))
|
||||
|
||||
@handle("(", filter=autopair_condition & whitespace_or_bracket_after)
|
||||
|
@ -256,9 +265,7 @@ def load_xonsh_bindings(key_bindings_manager):
|
|||
|
||||
if buffer.document.current_char == "'":
|
||||
buffer.cursor_position += 1
|
||||
elif whitespace_or_bracket_before(event.cli) and whitespace_or_bracket_after(
|
||||
event.cli
|
||||
):
|
||||
elif whitespace_or_bracket_before() and whitespace_or_bracket_after():
|
||||
buffer.insert_text("'")
|
||||
buffer.insert_text("'", move_cursor=False)
|
||||
else:
|
||||
|
@ -270,9 +277,7 @@ def load_xonsh_bindings(key_bindings_manager):
|
|||
|
||||
if buffer.document.current_char == '"':
|
||||
buffer.cursor_position += 1
|
||||
elif whitespace_or_bracket_before(event.cli) and whitespace_or_bracket_after(
|
||||
event.cli
|
||||
):
|
||||
elif whitespace_or_bracket_before() and whitespace_or_bracket_after():
|
||||
buffer.insert_text('"')
|
||||
buffer.insert_text('"', move_cursor=False)
|
||||
else:
|
||||
|
@ -296,16 +301,18 @@ def load_xonsh_bindings(key_bindings_manager):
|
|||
def call_exit_alias(event):
|
||||
"""Use xonsh exit function"""
|
||||
b = event.cli.current_buffer
|
||||
b.accept_action.validate_and_handle(event.cli, b)
|
||||
b.validate_and_handle()
|
||||
xonsh_exit([])
|
||||
|
||||
@handle(Keys.ControlJ, filter=IsMultiline())
|
||||
@handle(Keys.ControlJ, filter=IsMultiline() & insert_mode)
|
||||
@handle(Keys.ControlM, filter=IsMultiline() & insert_mode)
|
||||
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
|
||||
|
@ -319,7 +326,7 @@ def load_xonsh_bindings(key_bindings_manager):
|
|||
def execute_block_now(event):
|
||||
"""Execute a block of text irrespective of cursor position"""
|
||||
b = event.cli.current_buffer
|
||||
b.accept_action.validate_and_handle(event.cli, b)
|
||||
b.validate_and_handle()
|
||||
|
||||
@handle(Keys.Left, filter=beginning_of_line)
|
||||
def wrap_cursor_back(event):
|
||||
|
@ -339,28 +346,14 @@ def load_xonsh_bindings(key_bindings_manager):
|
|||
b.cursor_left(count=abs(relative_begin_index))
|
||||
b.cursor_down(count=1)
|
||||
|
||||
@handle(Keys.ControlI, filter=insert_mode)
|
||||
def generate_completions(event):
|
||||
@handle(Keys.ControlM, filter=IsSearching())
|
||||
@handle(Keys.ControlJ, filter=IsSearching())
|
||||
def accept_search(event):
|
||||
search.accept_search()
|
||||
|
||||
@handle(Keys.ControlZ)
|
||||
def skip_control_z(event):
|
||||
"""Prevents the writing of ^Z to the prompt, if Ctrl+Z was pressed
|
||||
during the previous command.
|
||||
"""
|
||||
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
|
||||
|
||||
def second_tab():
|
||||
if b.complete_state:
|
||||
b.complete_next()
|
||||
else:
|
||||
event.cli.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:
|
||||
event.cli.start_completion(insert_common_part=True, select_first=False)
|
||||
pass
|
|
@ -1,31 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""The prompt_toolkit based xonsh shell."""
|
||||
import os
|
||||
import sys
|
||||
import builtins
|
||||
from types import MethodType
|
||||
|
||||
from prompt_toolkit.key_binding.manager import KeyBindingManager
|
||||
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
||||
from prompt_toolkit.layout.lexers import PygmentsLexer
|
||||
from prompt_toolkit.shortcuts import print_tokens
|
||||
from prompt_toolkit.styles import PygmentsStyle, style_from_dict
|
||||
|
||||
from xonsh.base_shell import BaseShell
|
||||
from xonsh.tools import print_exception, carriage_return, ansicolors_to_ptk1_names
|
||||
from xonsh.ptk.completer import PromptToolkitCompleter
|
||||
from xonsh.ptk.history import PromptToolkitHistory
|
||||
from xonsh.ptk.key_bindings import load_xonsh_bindings
|
||||
from xonsh.ptk.shortcuts import Prompter
|
||||
from xonsh.events import events
|
||||
from xonsh.base_shell import BaseShell
|
||||
from xonsh.shell import transform_command
|
||||
from xonsh.platform import HAS_PYGMENTS, ON_WINDOWS
|
||||
from xonsh.style_tools import (
|
||||
partial_color_tokenize,
|
||||
_TokenType,
|
||||
DEFAULT_STYLE_DICT as _DEFAULT_STYLE_DICT,
|
||||
)
|
||||
from xonsh.tools import print_exception, carriage_return
|
||||
from xonsh.platform import HAS_PYGMENTS, ON_WINDOWS, ON_POSIX
|
||||
from xonsh.style_tools import partial_color_tokenize, _TokenType, DEFAULT_STYLE_DICT
|
||||
from xonsh.lazyimps import pygments, pyghooks, winutils
|
||||
from xonsh.pygments_cache import get_all_styles
|
||||
from xonsh.lazyasd import LazyObject
|
||||
from xonsh.ptk_shell.history import PromptToolkitHistory, _cust_history_matches
|
||||
from xonsh.ptk_shell.completer import PromptToolkitCompleter
|
||||
from xonsh.ptk_shell.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 import merge_styles, Style
|
||||
from prompt_toolkit.styles.pygments import (
|
||||
style_from_pygments_cls,
|
||||
style_from_pygments_dict,
|
||||
)
|
||||
|
||||
|
||||
Token = _TokenType()
|
||||
|
@ -34,54 +39,45 @@ events.transmogrify("on_ptk_create", "LoadEvent")
|
|||
events.doc(
|
||||
"on_ptk_create",
|
||||
"""
|
||||
on_ptk_create(prompter: Prompter, history: PromptToolkitHistory, completer: PromptToolkitCompleter, bindings: KeyBindingManager) ->
|
||||
on_ptk_create(prompter: PromptSession, history: PromptToolkitHistory, completer: PromptToolkitCompleter, bindings: KeyBindings) ->
|
||||
|
||||
Fired after prompt toolkit has been initialized
|
||||
""",
|
||||
)
|
||||
|
||||
# Convert new ansicolor names to names
|
||||
# understood by PTK1
|
||||
DEFAULT_STYLE_DICT = LazyObject(
|
||||
lambda: ansicolors_to_ptk1_names(_DEFAULT_STYLE_DICT),
|
||||
globals(),
|
||||
"DEFAULT_STYLE_DICT",
|
||||
)
|
||||
|
||||
|
||||
class PromptToolkitShell(BaseShell):
|
||||
"""The xonsh shell."""
|
||||
"""The xonsh shell for prompt_toolkit v2 and later."""
|
||||
|
||||
completion_displays_to_styles = {
|
||||
"multi": CompleteStyle.MULTI_COLUMN,
|
||||
"single": CompleteStyle.COLUMN,
|
||||
"readline": CompleteStyle.READLINE_LIKE,
|
||||
"none": None,
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
if ON_WINDOWS:
|
||||
winutils.enable_virtual_terminal_processing()
|
||||
self._first_prompt = True
|
||||
self.prompter = Prompter()
|
||||
self.history = PromptToolkitHistory()
|
||||
self.history = ThreadedHistory(PromptToolkitHistory())
|
||||
self.prompter = PromptSession(history=self.history)
|
||||
self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx, self)
|
||||
key_bindings_manager_args = {
|
||||
"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)
|
||||
self.key_bindings = KeyBindings()
|
||||
load_xonsh_bindings(self.key_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
|
||||
events.on_ptk_create.fire(
|
||||
prompter=self.prompter,
|
||||
history=self.history,
|
||||
completer=self.pt_completer,
|
||||
bindings=self.key_bindings_manager,
|
||||
bindings=self.key_bindings,
|
||||
)
|
||||
|
||||
def singleline(
|
||||
self,
|
||||
store_in_history=True,
|
||||
auto_suggest=None,
|
||||
enable_history_search=True,
|
||||
multiline=True,
|
||||
**kwargs
|
||||
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
|
||||
|
@ -90,14 +86,13 @@ class PromptToolkitShell(BaseShell):
|
|||
events.on_pre_prompt.fire()
|
||||
env = builtins.__xonsh__.env
|
||||
mouse_support = env.get("MOUSE_SUPPORT")
|
||||
if store_in_history:
|
||||
history = self.history
|
||||
else:
|
||||
history = None
|
||||
enable_history_search = False
|
||||
auto_suggest = auto_suggest if env.get("AUTO_SUGGEST") else None
|
||||
refresh_interval = env.get("PROMPT_REFRESH_INTERVAL")
|
||||
refresh_interval = refresh_interval if refresh_interval > 0 else None
|
||||
complete_in_thread = env.get("COMPLETION_IN_THREAD")
|
||||
completions_display = env.get("COMPLETIONS_DISPLAY")
|
||||
multicolumn = completions_display == "multi"
|
||||
complete_style = self.completion_displays_to_styles[completions_display]
|
||||
|
||||
complete_while_typing = env.get("UPDATE_COMPLETIONS_ON_KEYPRESS")
|
||||
if complete_while_typing:
|
||||
# PTK requires history search to be none when completing while typing
|
||||
|
@ -105,45 +100,71 @@ class PromptToolkitShell(BaseShell):
|
|||
if HAS_PYGMENTS:
|
||||
self.styler.style_name = env.get("XONSH_COLOR_STYLE")
|
||||
completer = None if completions_display == "none" else self.pt_completer
|
||||
if not env.get("UPDATE_PROMPT_ON_KEYPRESS"):
|
||||
prompt_tokens_cached = self.prompt_tokens(None)
|
||||
get_prompt_tokens = lambda cli: prompt_tokens_cached
|
||||
rprompt_tokens_cached = self.rprompt_tokens(None)
|
||||
get_rprompt_tokens = lambda cli: rprompt_tokens_cached
|
||||
bottom_toolbar_tokens_cached = self.bottom_toolbar_tokens(None)
|
||||
get_bottom_toolbar_tokens = lambda cli: bottom_toolbar_tokens_cached
|
||||
else:
|
||||
|
||||
get_bottom_toolbar_tokens = self.bottom_toolbar_tokens
|
||||
|
||||
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()
|
||||
if get_bottom_toolbar_tokens:
|
||||
get_bottom_toolbar_tokens = get_bottom_toolbar_tokens()
|
||||
|
||||
with self.prompter:
|
||||
prompt_args = {
|
||||
"mouse_support": mouse_support,
|
||||
"auto_suggest": auto_suggest,
|
||||
"get_prompt_tokens": get_prompt_tokens,
|
||||
"get_rprompt_tokens": get_rprompt_tokens,
|
||||
"get_bottom_toolbar_tokens": get_bottom_toolbar_tokens,
|
||||
"completer": completer,
|
||||
"multiline": multiline,
|
||||
"get_continuation_tokens": self.continuation_tokens,
|
||||
"history": history,
|
||||
"enable_history_search": enable_history_search,
|
||||
"reserve_space_for_menu": 0,
|
||||
"key_bindings_registry": self.key_bindings_manager.registry,
|
||||
"display_completions_in_columns": multicolumn,
|
||||
"complete_while_typing": complete_while_typing,
|
||||
}
|
||||
if builtins.__xonsh__.env.get("COLOR_INPUT"):
|
||||
if HAS_PYGMENTS:
|
||||
prompt_args["lexer"] = PygmentsLexer(pyghooks.XonshLexer)
|
||||
prompt_args["style"] = PygmentsStyle(
|
||||
pyghooks.xonsh_style_proxy(self.styler)
|
||||
)
|
||||
else:
|
||||
prompt_args["style"] = style_from_dict(DEFAULT_STYLE_DICT)
|
||||
line = self.prompter.prompt(**prompt_args)
|
||||
events.on_post_prompt.fire()
|
||||
if env.get("VI_MODE"):
|
||||
editing_mode = EditingMode.VI
|
||||
else:
|
||||
editing_mode = EditingMode.EMACS
|
||||
|
||||
if env.get("XONSH_HISTORY_MATCH_ANYWHERE"):
|
||||
self.prompter.default_buffer._history_matches = MethodType(
|
||||
_cust_history_matches, self.prompter.default_buffer
|
||||
)
|
||||
elif (
|
||||
self.prompter.default_buffer._history_matches
|
||||
is not self._history_matches_orig
|
||||
):
|
||||
self.prompter.default_buffer._history_matches = self._history_matches_orig
|
||||
|
||||
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,
|
||||
"include_default_pygments_style": False,
|
||||
"refresh_interval": refresh_interval,
|
||||
"complete_in_thread": complete_in_thread,
|
||||
}
|
||||
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 = style_from_pygments_dict(DEFAULT_STYLE_DICT)
|
||||
|
||||
prompt_args["style"] = style
|
||||
|
||||
style_overrides_env = env.get("PTK_STYLE_OVERRIDES")
|
||||
if style_overrides_env:
|
||||
try:
|
||||
style_overrides = Style.from_dict(style_overrides_env)
|
||||
prompt_args["style"] = merge_styles([style, style_overrides])
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
print_exception()
|
||||
|
||||
line = self.prompter.prompt(**prompt_args)
|
||||
events.on_post_prompt.fire()
|
||||
return line
|
||||
|
||||
def _push(self, line):
|
||||
|
@ -187,7 +208,7 @@ class PromptToolkitShell(BaseShell):
|
|||
else:
|
||||
break
|
||||
|
||||
def prompt_tokens(self, cli):
|
||||
def prompt_tokens(self):
|
||||
"""Returns a list of (token, str) tuples for the current prompt."""
|
||||
p = builtins.__xonsh__.env.get("PROMPT")
|
||||
try:
|
||||
|
@ -199,9 +220,9 @@ class PromptToolkitShell(BaseShell):
|
|||
carriage_return()
|
||||
self._first_prompt = False
|
||||
self.settitle()
|
||||
return toks
|
||||
return PygmentsTokens(toks)
|
||||
|
||||
def rprompt_tokens(self, cli):
|
||||
def rprompt_tokens(self):
|
||||
"""Returns a list of (token, str) tuples for the current right
|
||||
prompt.
|
||||
"""
|
||||
|
@ -216,32 +237,38 @@ class PromptToolkitShell(BaseShell):
|
|||
except Exception: # pylint: disable=broad-except
|
||||
print_exception()
|
||||
toks = partial_color_tokenize(p)
|
||||
return toks
|
||||
return PygmentsTokens(toks)
|
||||
|
||||
def bottom_toolbar_tokens(self, cli):
|
||||
def _bottom_toolbar_tokens(self):
|
||||
"""Returns a list of (token, str) tuples for the current bottom
|
||||
toolbar.
|
||||
"""
|
||||
p = builtins.__xonsh__.env.get("BOTTOM_TOOLBAR")
|
||||
# self.prompt_formatter does handle empty strings properly,
|
||||
# but this avoids descending into it in the common case of
|
||||
# $TOOLBAR == ''.
|
||||
if isinstance(p, str) and len(p) == 0:
|
||||
return []
|
||||
if not p:
|
||||
return
|
||||
try:
|
||||
p = self.prompt_formatter(p)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
print_exception()
|
||||
toks = partial_color_tokenize(p)
|
||||
return toks
|
||||
return PygmentsTokens(toks)
|
||||
|
||||
def continuation_tokens(self, cli, width):
|
||||
@property
|
||||
def bottom_toolbar_tokens(self):
|
||||
"""Returns self._bottom_toolbar_tokens if it would yield a result
|
||||
"""
|
||||
if builtins.__xonsh__.env.get("BOTTOM_TOOLBAR"):
|
||||
return self._bottom_toolbar_tokens
|
||||
|
||||
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))]
|
||||
if not dots:
|
||||
return ""
|
||||
basetoks = self.format_color(dots)
|
||||
baselen = sum(len(t[1]) for t in basetoks)
|
||||
if baselen == 0:
|
||||
|
@ -262,7 +289,7 @@ class PromptToolkitShell(BaseShell):
|
|||
if n <= count:
|
||||
break
|
||||
toks.append((Token, " ")) # final space
|
||||
return toks
|
||||
return PygmentsTokens(toks)
|
||||
|
||||
def format_color(self, string, hide=False, force_string=False, **kwargs):
|
||||
"""Formats a color string using Pygments. This, therefore, returns
|
||||
|
@ -286,17 +313,22 @@ class PromptToolkitShell(BaseShell):
|
|||
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)
|
||||
tokens = partial_color_tokenize(string)
|
||||
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 = PygmentsStyle(pyghooks.xonsh_style_proxy(self.styler))
|
||||
proxy_style = style_from_pygments_cls(
|
||||
pyghooks.xonsh_style_proxy(self.styler)
|
||||
)
|
||||
else:
|
||||
proxy_style = style_from_dict(DEFAULT_STYLE_DICT)
|
||||
print_tokens(tokens, style=proxy_style)
|
||||
proxy_style = style_from_pygments_dict(DEFAULT_STYLE_DICT)
|
||||
ptk_print(
|
||||
tokens, style=proxy_style, end=end, include_default_pygments_style=False
|
||||
)
|
||||
|
||||
def color_style_names(self):
|
||||
"""Returns an iterable of all available style names."""
|
||||
|
@ -335,3 +367,9 @@ class PromptToolkitShell(BaseShell):
|
|||
# if not ON_POSIX:
|
||||
# return
|
||||
# sys.stdout.write('\033[9999999C\n')
|
||||
if not ON_POSIX:
|
||||
return
|
||||
stty, _ = builtins.__xonsh__.commands_cache.lazyget("stty", (None, None))
|
||||
if stty is None:
|
||||
return
|
||||
os.system(stty + " sane")
|
|
@ -33,7 +33,6 @@ from xonsh.lazyasd import LazyObject, LazyDict, lazyobject
|
|||
from xonsh.tools import (
|
||||
ON_WINDOWS,
|
||||
intensify_colors_for_cmd_exe,
|
||||
ansicolors_to_ptk1_names,
|
||||
ANSICOLOR_NAMES_MAP,
|
||||
PTK_NEW_OLD_COLOR_MAP,
|
||||
hardcode_colors_for_win10,
|
||||
|
@ -385,16 +384,6 @@ class XonshStyle(Style):
|
|||
color_token = color_token_by_name(xonsh_color, self.styles)
|
||||
file_color_tokens[file_type] = color_token
|
||||
|
||||
# Convert new ansicolor names to old PTK1 names
|
||||
# Can be remvoed when PTK1 support is dropped.
|
||||
if (
|
||||
builtins.__xonsh__.shell.shell_type != "prompt_toolkit2"
|
||||
and pygments_version_info()
|
||||
and pygments_version_info() < (2, 4, 0)
|
||||
):
|
||||
for smap in [self.trap, cmap, PTK_STYLE, self._smap]:
|
||||
smap.update(ansicolors_to_ptk1_names(smap))
|
||||
|
||||
if ON_WINDOWS and "prompt_toolkit" in builtins.__xonsh__.shell.shell_type:
|
||||
self.enhance_colors_for_cmd_exe()
|
||||
|
||||
|
@ -1369,20 +1358,24 @@ events.on_lscolors_change(on_lscolors_change)
|
|||
|
||||
def color_file(file_path: str, mode: int) -> (Color, str):
|
||||
"""Determine color to use for file as ls -c would, given stat() results and its name.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file_path : string
|
||||
relative path of file (as user typed it).
|
||||
mode : int
|
||||
stat() results for file_path.
|
||||
|
||||
Returns
|
||||
-------
|
||||
color token, color_key
|
||||
|
||||
Bugs
|
||||
----
|
||||
Notes
|
||||
-----
|
||||
|
||||
* doesn't handle CA (capability)
|
||||
* doesn't handle LS TARGET mapping.
|
||||
* doesn't handle LS TARGET mapping
|
||||
|
||||
"""
|
||||
|
||||
lsc = builtins.__xonsh__.env["LS_COLORS"]
|
||||
|
|
|
@ -11,7 +11,7 @@ from xonsh.platform import (
|
|||
best_shell_type,
|
||||
has_prompt_toolkit,
|
||||
ptk_above_min_supported,
|
||||
ptk_shell_type,
|
||||
minimum_required_ptk_version,
|
||||
)
|
||||
from xonsh.tools import XonshError, print_exception
|
||||
from xonsh.events import events
|
||||
|
@ -121,19 +121,66 @@ class Shell(object):
|
|||
"best": "best",
|
||||
"d": "dumb",
|
||||
"dumb": "dumb",
|
||||
"ptk": "prompt_toolkit",
|
||||
"ptk1": "prompt_toolkit1",
|
||||
"ptk2": "prompt_toolkit2",
|
||||
"ptk": "prompt_toolkit", # there's only 1 prompt_toolkit shell (now)
|
||||
"ptk1": "prompt_toolkit", # allow any old config reference to use it
|
||||
"ptk2": "prompt_toolkit", # so long as user actually has ptk2+ installed.
|
||||
"prompt-toolkit": "prompt_toolkit",
|
||||
"prompt_toolkit": "prompt_toolkit",
|
||||
"prompt-toolkit1": "prompt_toolkit1",
|
||||
"prompt-toolkit2": "prompt_toolkit2",
|
||||
"prompt-toolkit1": "prompt_toolkit",
|
||||
"prompt-toolkit2": "prompt_toolkit",
|
||||
"prompt-toolkit3": "prompt_toolkit",
|
||||
"prompt_toolkit3": "prompt_toolkit",
|
||||
"ptk3": "prompt_toolkit",
|
||||
"rand": "random",
|
||||
"random": "random",
|
||||
"rl": "readline",
|
||||
"readline": "readline",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def choose_shell_type(init_shell_type=None, env=None):
|
||||
# pick a valid shell -- if no shell is specified by the user,
|
||||
# shell type is pulled from env
|
||||
# extracted for testability
|
||||
shell_type = init_shell_type
|
||||
if shell_type is None and env:
|
||||
shell_type = env.get("SHELL_TYPE")
|
||||
if shell_type == "none":
|
||||
# This bricks interactive xonsh
|
||||
# Can happen from the use of .xinitrc, .xsession, etc
|
||||
# odd logic. We don't override if shell.__init__( shell_type="none"),
|
||||
# only if it come from environment?
|
||||
shell_type = "best"
|
||||
shell_type = Shell.shell_type_aliases.get(shell_type, shell_type)
|
||||
if shell_type == "best" or shell_type is None:
|
||||
shell_type = best_shell_type()
|
||||
elif env and env.get("TERM", "") == "dumb":
|
||||
shell_type = "dumb"
|
||||
elif shell_type == "random":
|
||||
shell_type = random.choice(("readline", "prompt_toolkit"))
|
||||
if shell_type == "prompt_toolkit":
|
||||
if not has_prompt_toolkit():
|
||||
warnings.warn(
|
||||
"prompt_toolkit is not available, using " "readline instead."
|
||||
)
|
||||
shell_type = "readline"
|
||||
elif not ptk_above_min_supported():
|
||||
warnings.warn(
|
||||
"prompt-toolkit version < v{}.{}.0 is not ".format(
|
||||
*minimum_required_ptk_version
|
||||
)
|
||||
+ "supported. Please update prompt-toolkit. Using "
|
||||
+ "readline instead."
|
||||
)
|
||||
shell_type = "readline"
|
||||
if init_shell_type in ("ptk1", "prompt_toolkit1"):
|
||||
warnings.warn(
|
||||
"$SHELL_TYPE='{}' now deprecated, please update your run control file'".format(
|
||||
init_shell_type
|
||||
)
|
||||
)
|
||||
return shell_type
|
||||
|
||||
def __init__(self, execer, ctx=None, shell_type=None, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
|
@ -157,44 +204,15 @@ class Shell(object):
|
|||
env=env.detype(), ts=[time.time(), None], locked=True
|
||||
)
|
||||
|
||||
# pick a valid shell -- if no shell is specified by the user,
|
||||
# shell type is pulled from env
|
||||
if shell_type is None:
|
||||
shell_type = env.get("SHELL_TYPE")
|
||||
if shell_type == "none":
|
||||
# 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 env.get("TERM", "") == "dumb":
|
||||
shell_type = "dumb"
|
||||
elif shell_type == "random":
|
||||
shell_type = random.choice(("readline", "prompt_toolkit"))
|
||||
if shell_type == "prompt_toolkit":
|
||||
if not has_prompt_toolkit():
|
||||
warnings.warn(
|
||||
"prompt_toolkit is not available, using " "readline instead."
|
||||
)
|
||||
shell_type = "readline"
|
||||
elif not ptk_above_min_supported():
|
||||
warnings.warn(
|
||||
"prompt-toolkit version < v1.0.0 is not "
|
||||
"supported. Please update prompt-toolkit. Using "
|
||||
"readline instead."
|
||||
)
|
||||
shell_type = "readline"
|
||||
else:
|
||||
shell_type = ptk_shell_type()
|
||||
shell_type = self.choose_shell_type(shell_type, env)
|
||||
|
||||
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_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 == "prompt_toolkit":
|
||||
from xonsh.ptk_shell.shell import PromptToolkitShell as shell_class
|
||||
elif shell_type == "readline":
|
||||
from xonsh.readline_shell import ReadlineShell as shell_class
|
||||
elif shell_type == "jupyter":
|
||||
|
|
|
@ -8,7 +8,6 @@ import builtins
|
|||
from prompt_toolkit.filters import completion_is_selected, IsMultiline
|
||||
from prompt_toolkit.keys import Keys
|
||||
from xonsh.built_ins import DynamicAccessProxy
|
||||
from xonsh.platform import ptk_shell_type
|
||||
from xonsh.tools import check_for_partial_string
|
||||
|
||||
__all__ = ()
|
||||
|
@ -36,18 +35,11 @@ def expand_abbrev(buffer):
|
|||
@events.on_ptk_create
|
||||
def custom_keybindings(bindings, **kw):
|
||||
|
||||
if ptk_shell_type() == "prompt_toolkit2":
|
||||
from xonsh.ptk2.key_bindings import carriage_return
|
||||
from prompt_toolkit.filters import EmacsInsertMode, ViInsertMode
|
||||
from xonsh.ptk_shell.key_bindings import carriage_return
|
||||
from prompt_toolkit.filters import EmacsInsertMode, ViInsertMode
|
||||
|
||||
handler = bindings.add
|
||||
insert_mode = ViInsertMode() | EmacsInsertMode()
|
||||
else:
|
||||
from xonsh.ptk.key_bindings import carriage_return
|
||||
from prompt_toolkit.filters import to_filter
|
||||
|
||||
handler = bindings.registry.add_binding
|
||||
insert_mode = to_filter(True)
|
||||
handler = bindings.add
|
||||
insert_mode = ViInsertMode() | EmacsInsertMode()
|
||||
|
||||
@handler(" ", filter=IsMultiline() & insert_mode)
|
||||
def handle_space(event):
|
||||
|
|
|
@ -4,8 +4,6 @@ Alt+Left/Right remains unmodified to jump over smaller word segments.
|
|||
"""
|
||||
from prompt_toolkit.keys import Keys
|
||||
|
||||
from xonsh.platform import ptk_shell_type
|
||||
|
||||
__all__ = ()
|
||||
|
||||
|
||||
|
@ -17,19 +15,14 @@ def custom_keybindings(bindings, **kw):
|
|||
# Alt+Left and Alt+Right still jump over smaller word segments.
|
||||
# See https://github.com/xonsh/xonsh/issues/2403
|
||||
|
||||
if ptk_shell_type() == "prompt_toolkit2":
|
||||
handler = bindings.add
|
||||
else:
|
||||
handler = bindings.registry.add_binding
|
||||
|
||||
@handler(Keys.ControlLeft)
|
||||
@bindings.add(Keys.ControlLeft)
|
||||
def ctrl_left(event):
|
||||
buff = event.current_buffer
|
||||
pos = buff.document.find_previous_word_beginning(count=event.arg, WORD=True)
|
||||
if pos:
|
||||
buff.cursor_position += pos
|
||||
|
||||
@handler(Keys.ControlRight)
|
||||
@bindings.add(Keys.ControlRight)
|
||||
def ctrl_right(event):
|
||||
buff = event.current_buffer
|
||||
pos = buff.document.find_next_word_ending(count=event.arg, WORD=True)
|
||||
|
|
Loading…
Add table
Reference in a new issue