mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
Merge branch 'master' into environment_fixes
This commit is contained in:
commit
88bba37c3a
29 changed files with 580 additions and 74 deletions
|
@ -255,6 +255,21 @@ may be useful to share entries between shell sessions. In such a case, one can u
|
||||||
the ``flush`` action to immediately save the session history to disk and make it
|
the ``flush`` action to immediately save the session history to disk and make it
|
||||||
accessible from other shell sessions.
|
accessible from other shell sessions.
|
||||||
|
|
||||||
|
``clear`` action
|
||||||
|
================
|
||||||
|
Deletes the history from the current session up until this point. Later commands
|
||||||
|
will still be saved.
|
||||||
|
|
||||||
|
``off`` action
|
||||||
|
================
|
||||||
|
Deletes the history from the current session and turns off history saving for the
|
||||||
|
rest of the session. Only session metadata will be saved, not commands or output.
|
||||||
|
|
||||||
|
``on`` action
|
||||||
|
================
|
||||||
|
Turns history saving back on. Previous commands won't be saved, but future
|
||||||
|
commands will be.
|
||||||
|
|
||||||
``gc`` action
|
``gc`` action
|
||||||
===============
|
===============
|
||||||
Last, but certainly not least, the ``gc`` action is a manual hook into executing
|
Last, but certainly not least, the ``gc`` action is a manual hook into executing
|
||||||
|
|
23
news/ansi_osc.rst
Normal file
23
news/ansi_osc.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* Support for ANSI OSC escape sequences in ``$PROMPT``, setting ``$TITLE`` for example. (#374, #1403)
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
23
news/cache-executables-in.rst
Normal file
23
news/cache-executables-in.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* Use command_cache when finding available commands, to speedup command-not-found suggestions
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
23
news/history_testing-ead.rst
Normal file
23
news/history_testing-ead.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* history clear, history off and history on actions, for managing whether history in the current session is saved.
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
23
news/name_in_error.rst
Normal file
23
news/name_in_error.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* ValueErrors from environ.register now report the name of the bad env var
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
23
news/prompt-speed.rst
Normal file
23
news/prompt-speed.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* Minor improvements to the get prompt speed. (Mostly in git.)
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
25
news/ptk-complete-select-first.rst
Normal file
25
news/ptk-complete-select-first.rst
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* ptk key binding for TAB -- hitting TAB to start completion now automatically selects the first displayed completion (if any).
|
||||||
|
hitting TAB when in insert mode inserts TAB, as heretofore. This more exactly follows behavior of readline ``menu-complete``.
|
||||||
|
There is no configuration option for tailoring this behavior.
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
23
news/shift_del.rst
Normal file
23
news/shift_del.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* Added to xontrib whole_word_jumping: Shift+Delete hotkey to delete whole word.
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
23
news/windows_app_execution_aliases.rst
Normal file
23
news/windows_app_execution_aliases.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* Fix crash when xonsh tries to run windows app execution aliases.
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
23
news/xontrib_descr.rst
Normal file
23
news/xontrib_descr.rst
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
* xontrib-argcomplete and xontrib-pipeliner description improvement.
|
||||||
|
|
||||||
|
**Deprecated:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
|
||||||
|
* <news item>
|
||||||
|
|
||||||
|
**Security:**
|
||||||
|
|
||||||
|
* <news item>
|
|
@ -4,8 +4,12 @@ from rever.activities.ghrelease import git_archive_asset
|
||||||
$PROJECT = $GITHUB_ORG = $GITHUB_REPO = 'xonsh'
|
$PROJECT = $GITHUB_ORG = $GITHUB_REPO = 'xonsh'
|
||||||
$WEBSITE_URL = 'http://xon.sh'
|
$WEBSITE_URL = 'http://xon.sh'
|
||||||
$ACTIVITIES = ['authors', 'version_bump', 'changelog', 'pytest', 'appimage',
|
$ACTIVITIES = ['authors', 'version_bump', 'changelog', 'pytest', 'appimage',
|
||||||
'tag', 'push_tag', 'ghrelease', 'sphinx',
|
'tag', 'push_tag',
|
||||||
'ghpages', 'pypi', 'conda_forge',
|
'ghrelease',
|
||||||
|
'sphinx',
|
||||||
|
'ghpages',
|
||||||
|
'pypi',
|
||||||
|
'conda_forge',
|
||||||
]
|
]
|
||||||
$PYPI_SIGN = False
|
$PYPI_SIGN = False
|
||||||
|
|
||||||
|
|
|
@ -483,3 +483,65 @@ def test__xhj_gc_xx_to_rmfiles(
|
||||||
assert minute_diff <= 60
|
assert minute_diff <= 60
|
||||||
else:
|
else:
|
||||||
assert act_size == exp_size
|
assert act_size == exp_size
|
||||||
|
|
||||||
|
|
||||||
|
def test_hist_clear_cmd(hist, xonsh_builtins, capsys, tmpdir):
|
||||||
|
"""Verify that the CLI history clear command works."""
|
||||||
|
xonsh_builtins.__xonsh__.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
||||||
|
xonsh_builtins.__xonsh__.history = hist
|
||||||
|
xonsh_builtins.__xonsh__.env["HISTCONTROL"] = set()
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 6
|
||||||
|
|
||||||
|
history_main(["clear"])
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert err.rstrip() == "History cleared"
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_hist_off_cmd(hist, xonsh_builtins, capsys, tmpdir):
|
||||||
|
"""Verify that the CLI history off command works."""
|
||||||
|
xonsh_builtins.__xonsh__.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
||||||
|
xonsh_builtins.__xonsh__.history = hist
|
||||||
|
xonsh_builtins.__xonsh__.env["HISTCONTROL"] = set()
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 6
|
||||||
|
|
||||||
|
history_main(["off"])
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert err.rstrip() == "History off"
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 0
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # attempt to populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_hist_on_cmd(hist, xonsh_builtins, capsys, tmpdir):
|
||||||
|
"""Verify that the CLI history on command works."""
|
||||||
|
xonsh_builtins.__xonsh__.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
||||||
|
xonsh_builtins.__xonsh__.history = hist
|
||||||
|
xonsh_builtins.__xonsh__.env["HISTCONTROL"] = set()
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 6
|
||||||
|
|
||||||
|
history_main(["off"])
|
||||||
|
history_main(["on"])
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert err.rstrip().endswith("History on")
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 0
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 6
|
||||||
|
|
|
@ -215,3 +215,65 @@ def test_history_getitem(index, exp, hist, xonsh_builtins):
|
||||||
assert [(e.cmd, e.out, e.rtn, e.ts) for e in entry] == exp
|
assert [(e.cmd, e.out, e.rtn, e.ts) for e in entry] == exp
|
||||||
else:
|
else:
|
||||||
assert (entry.cmd, entry.out, entry.rtn, entry.ts) == exp
|
assert (entry.cmd, entry.out, entry.rtn, entry.ts) == exp
|
||||||
|
|
||||||
|
|
||||||
|
def test_hist_clear_cmd(hist, xonsh_builtins, capsys, tmpdir):
|
||||||
|
"""Verify that the CLI history clear command works."""
|
||||||
|
xonsh_builtins.__xonsh__.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
||||||
|
xonsh_builtins.__xonsh__.history = hist
|
||||||
|
xonsh_builtins.__xonsh__.env["HISTCONTROL"] = set()
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 6
|
||||||
|
|
||||||
|
history_main(["clear"])
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert err.rstrip() == "History cleared"
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_hist_off_cmd(hist, xonsh_builtins, capsys, tmpdir):
|
||||||
|
"""Verify that the CLI history off command works."""
|
||||||
|
xonsh_builtins.__xonsh__.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
||||||
|
xonsh_builtins.__xonsh__.history = hist
|
||||||
|
xonsh_builtins.__xonsh__.env["HISTCONTROL"] = set()
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 6
|
||||||
|
|
||||||
|
history_main(["off"])
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert err.rstrip() == "History off"
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 0
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # attempt to populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_hist_on_cmd(hist, xonsh_builtins, capsys, tmpdir):
|
||||||
|
"""Verify that the CLI history on command works."""
|
||||||
|
xonsh_builtins.__xonsh__.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
||||||
|
xonsh_builtins.__xonsh__.history = hist
|
||||||
|
xonsh_builtins.__xonsh__.env["HISTCONTROL"] = set()
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 6
|
||||||
|
|
||||||
|
history_main(["off"])
|
||||||
|
history_main(["on"])
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert err.rstrip().endswith("History on")
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 0
|
||||||
|
|
||||||
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
||||||
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
||||||
|
|
||||||
|
assert len(xonsh_builtins.__xonsh__.history) == 6
|
||||||
|
|
|
@ -8,7 +8,7 @@ from xonsh.platform import minimum_required_ptk_version
|
||||||
|
|
||||||
# verify error if ptk not installed or below min
|
# verify error if ptk not installed or below min
|
||||||
|
|
||||||
from xonsh.ptk_shell.shell import tokenize_ansi
|
from xonsh.ptk_shell.shell import tokenize_ansi, remove_ansi_osc
|
||||||
from xonsh.shell import Shell
|
from xonsh.shell import Shell
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,4 +105,31 @@ def test_tokenize_ansi(prompt_tokens, ansi_string_parts):
|
||||||
assert token[1] == text
|
assert token[1] == text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"raw_prompt, prompt, osc_tokens",
|
||||||
|
[
|
||||||
|
# no title
|
||||||
|
("test prompt", "test prompt", []),
|
||||||
|
# starts w/ title
|
||||||
|
("\033]0;TITLE THIS\007test prompt", "test prompt", ["\033]0;TITLE THIS\007"]),
|
||||||
|
# ends w/ title
|
||||||
|
("test prompt\033]0;TITLE THIS\007", "test prompt", ["\033]0;TITLE THIS\007"]),
|
||||||
|
# title in the middle
|
||||||
|
("test \033]0;TITLE THIS\007prompt", "test prompt", ["\033]0;TITLE THIS\007"]),
|
||||||
|
# title + iTerm2 OSC exapmle
|
||||||
|
(
|
||||||
|
"test \033]0;TITLE THIS\007prompt \033]133;A\007here",
|
||||||
|
"test prompt here",
|
||||||
|
["\033]0;TITLE THIS\007", "\033]133;A\007"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_remove_ansi_osc(raw_prompt, prompt, osc_tokens):
|
||||||
|
checked_prompt, removed_osc = remove_ansi_osc(raw_prompt)
|
||||||
|
assert prompt == checked_prompt
|
||||||
|
assert len(removed_osc) == len(osc_tokens)
|
||||||
|
for removed, ref in zip(removed_osc, osc_tokens):
|
||||||
|
assert removed == ref
|
||||||
|
|
||||||
|
|
||||||
# someday: initialize PromptToolkitShell and have it actually do something.
|
# someday: initialize PromptToolkitShell and have it actually do something.
|
||||||
|
|
|
@ -179,16 +179,30 @@ def pathsearch(func, s, pymode=False, pathobj=False):
|
||||||
RE_SHEBANG = LazyObject(lambda: re.compile(r"#![ \t]*(.+?)$"), globals(), "RE_SHEBANG")
|
RE_SHEBANG = LazyObject(lambda: re.compile(r"#![ \t]*(.+?)$"), globals(), "RE_SHEBANG")
|
||||||
|
|
||||||
|
|
||||||
|
def is_app_execution_alias(fname):
|
||||||
|
""" App execution aliases behave strangly on windows and python.
|
||||||
|
Here try to detect if a file is an app execution alias
|
||||||
|
"""
|
||||||
|
fname = pathlib.Path(fname)
|
||||||
|
return not os.path.exists(fname) and fname.name in os.listdir(fname.parent)
|
||||||
|
|
||||||
|
|
||||||
def _is_binary(fname, limit=80):
|
def _is_binary(fname, limit=80):
|
||||||
with open(fname, "rb") as f:
|
try:
|
||||||
for i in range(limit):
|
with open(fname, "rb") as f:
|
||||||
char = f.read(1)
|
for i in range(limit):
|
||||||
if char == b"\0":
|
char = f.read(1)
|
||||||
return True
|
if char == b"\0":
|
||||||
if char == b"\n":
|
return True
|
||||||
return False
|
if char == b"\n":
|
||||||
if char == b"":
|
return False
|
||||||
return False
|
if char == b"":
|
||||||
|
return
|
||||||
|
except OSError as e:
|
||||||
|
if ON_WINDOWS and is_app_execution_alias(fname):
|
||||||
|
return True
|
||||||
|
raise e
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -61,10 +61,7 @@ class RichCompletion(str):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "RichCompletion({}, prefix_len={}, display={}, description={})".format(
|
return "RichCompletion({}, prefix_len={}, display={}, description={})".format(
|
||||||
repr(str(self)),
|
repr(str(self)), self.prefix_len, repr(self.display), repr(self.description)
|
||||||
self.prefix_len,
|
|
||||||
repr(self.display),
|
|
||||||
repr(self.description),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2099,7 +2099,7 @@ class Env(cabc.MutableMapping):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Default value does not match type specified by validate"
|
f"Default value for {name} does not match type specified by validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
self._vars[name] = Var(
|
self._vars[name] = Var(
|
||||||
|
|
|
@ -71,6 +71,7 @@ class History:
|
||||||
self.last_cmd_out = None
|
self.last_cmd_out = None
|
||||||
self.hist_size = None
|
self.hist_size = None
|
||||||
self.hist_units = None
|
self.hist_units = None
|
||||||
|
self.remember_history = True
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
"""Return the number of items in current session."""
|
"""Return the number of items in current session."""
|
||||||
|
@ -153,3 +154,9 @@ class History:
|
||||||
If set blocking, then wait until gc action finished.
|
If set blocking, then wait until gc action finished.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""Clears the history of the current session from both the disk and
|
||||||
|
memory.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
|
@ -202,7 +202,7 @@ class JsonHistoryGC(threading.Thread):
|
||||||
# info: file size, closing timestamp, number of commands, filename
|
# info: file size, closing timestamp, number of commands, filename
|
||||||
ts = lj.get("ts", (0.0, None))
|
ts = lj.get("ts", (0.0, None))
|
||||||
files.append(
|
files.append(
|
||||||
(ts[1] or ts[0], len(lj.sizes["cmds"]) - 1, f, cur_file_size,),
|
(ts[1] or ts[0], len(lj.sizes["cmds"]) - 1, f, cur_file_size)
|
||||||
)
|
)
|
||||||
lj.close()
|
lj.close()
|
||||||
if xonsh_debug:
|
if xonsh_debug:
|
||||||
|
@ -303,6 +303,9 @@ class JsonCommandField(cabc.Sequence):
|
||||||
return len(self.hist)
|
return len(self.hist)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
|
if not self.hist.remember_history:
|
||||||
|
return ""
|
||||||
|
|
||||||
size = len(self)
|
size = len(self)
|
||||||
if isinstance(key, slice):
|
if isinstance(key, slice):
|
||||||
return [self[i] for i in range(*key.indices(size))]
|
return [self[i] for i in range(*key.indices(size))]
|
||||||
|
@ -407,6 +410,8 @@ class JsonHistory(History):
|
||||||
hf : JsonHistoryFlusher or None
|
hf : JsonHistoryFlusher or None
|
||||||
The thread that was spawned to flush history
|
The thread that was spawned to flush history
|
||||||
"""
|
"""
|
||||||
|
if not self.remember_history:
|
||||||
|
return
|
||||||
self.buffer.append(cmd)
|
self.buffer.append(cmd)
|
||||||
self._len += 1 # must come before flushing
|
self._len += 1 # must come before flushing
|
||||||
if len(self.buffer) >= self.buffersize:
|
if len(self.buffer) >= self.buffersize:
|
||||||
|
@ -429,6 +434,7 @@ class JsonHistory(History):
|
||||||
hf : JsonHistoryFlusher or None
|
hf : JsonHistoryFlusher or None
|
||||||
The thread that was spawned to flush history
|
The thread that was spawned to flush history
|
||||||
"""
|
"""
|
||||||
|
# Implicitly covers case of self.remember_history being False.
|
||||||
if len(self.buffer) == 0:
|
if len(self.buffer) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -502,3 +508,18 @@ class JsonHistory(History):
|
||||||
if blocking:
|
if blocking:
|
||||||
while self.gc.is_alive(): # while waiting for gc.
|
while self.gc.is_alive(): # while waiting for gc.
|
||||||
time.sleep(0.1) # don't monopolize the thread (or Python GIL?)
|
time.sleep(0.1) # don't monopolize the thread (or Python GIL?)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""Clears the current session's history from both memory and disk."""
|
||||||
|
|
||||||
|
# Wipe history from memory. Keep sessionid and other metadata.
|
||||||
|
self.buffer = []
|
||||||
|
self.tss = JsonCommandField("ts", self)
|
||||||
|
self.inps = JsonCommandField("inp", self)
|
||||||
|
self.outs = JsonCommandField("out", self)
|
||||||
|
self.rtns = JsonCommandField("rtn", self)
|
||||||
|
self._len = 0
|
||||||
|
self._skipped = 0
|
||||||
|
|
||||||
|
# Flush empty history object to disk, overwriting previous data.
|
||||||
|
self.flush()
|
||||||
|
|
|
@ -224,7 +224,18 @@ def _XH_HISTORY_SESSIONS():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_XH_MAIN_ACTIONS = {"show", "id", "file", "info", "diff", "gc", "flush"}
|
_XH_MAIN_ACTIONS = {
|
||||||
|
"show",
|
||||||
|
"id",
|
||||||
|
"file",
|
||||||
|
"info",
|
||||||
|
"diff",
|
||||||
|
"gc",
|
||||||
|
"flush",
|
||||||
|
"off",
|
||||||
|
"on",
|
||||||
|
"clear",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@functools.lru_cache()
|
@functools.lru_cache()
|
||||||
|
@ -354,6 +365,17 @@ def _xh_create_parser():
|
||||||
# 'flush' subcommand
|
# 'flush' subcommand
|
||||||
subp.add_parser("flush", help="flush the current history to disk")
|
subp.add_parser("flush", help="flush the current history to disk")
|
||||||
|
|
||||||
|
# 'off' subcommand
|
||||||
|
subp.add_parser("off", help="history will not be saved for this session")
|
||||||
|
|
||||||
|
# 'on' subcommand
|
||||||
|
subp.add_parser(
|
||||||
|
"on", help="history will be saved for the rest of the session (default)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 'clear' subcommand
|
||||||
|
subp.add_parser("clear", help="one-time wipe of session history")
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
@ -417,5 +439,17 @@ def history_main(
|
||||||
hf = hist.flush()
|
hf = hist.flush()
|
||||||
if isinstance(hf, threading.Thread):
|
if isinstance(hf, threading.Thread):
|
||||||
hf.join()
|
hf.join()
|
||||||
|
elif ns.action == "off":
|
||||||
|
if hist.remember_history:
|
||||||
|
hist.clear()
|
||||||
|
hist.remember_history = False
|
||||||
|
print("History off", file=sys.stderr)
|
||||||
|
elif ns.action == "on":
|
||||||
|
if not hist.remember_history:
|
||||||
|
hist.remember_history = True
|
||||||
|
print("History on", file=sys.stderr)
|
||||||
|
elif ns.action == "clear":
|
||||||
|
hist.clear()
|
||||||
|
print("History cleared", file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
print("Unknown history action {}".format(ns.action), file=sys.stderr)
|
print("Unknown history action {}".format(ns.action), file=sys.stderr)
|
||||||
|
|
|
@ -240,6 +240,8 @@ class SqliteHistory(History):
|
||||||
setattr(XH_SQLITE_CACHE, XH_SQLITE_CREATED_SQL_TBL, False)
|
setattr(XH_SQLITE_CACHE, XH_SQLITE_CREATED_SQL_TBL, False)
|
||||||
|
|
||||||
def append(self, cmd):
|
def append(self, cmd):
|
||||||
|
if not self.remember_history:
|
||||||
|
return
|
||||||
envs = builtins.__xonsh__.env
|
envs = builtins.__xonsh__.env
|
||||||
inp = cmd["inp"].rstrip()
|
inp = cmd["inp"].rstrip()
|
||||||
self.inps.append(inp)
|
self.inps.append(inp)
|
||||||
|
@ -296,3 +298,18 @@ class SqliteHistory(History):
|
||||||
if blocking:
|
if blocking:
|
||||||
while self.gc.is_alive():
|
while self.gc.is_alive():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""Clears the current session's history from both memory and disk."""
|
||||||
|
# Wipe memory
|
||||||
|
self.inps = []
|
||||||
|
self.rtns = []
|
||||||
|
self.outs = []
|
||||||
|
self.tss = []
|
||||||
|
|
||||||
|
# Wipe the current session's entries from the database.
|
||||||
|
sql = "DELETE FROM xonsh_history WHERE sessionid = ?"
|
||||||
|
with _xh_sqlite_get_conn(filename=self.filename) as conn:
|
||||||
|
c = conn.cursor()
|
||||||
|
_xh_sqlite_create_history_table(c)
|
||||||
|
c.execute(sql, (str(self.sessionid),))
|
||||||
|
|
|
@ -181,7 +181,7 @@ class FStringAdaptor:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
field_node = ast.Tuple(
|
field_node = ast.Tuple(
|
||||||
elts=elts, ctx=ast.Load(), lineno=lineno, col_offset=col_offset,
|
elts=elts, ctx=ast.Load(), lineno=lineno, col_offset=col_offset
|
||||||
)
|
)
|
||||||
node.args[0] = field_node
|
node.args[0] = field_node
|
||||||
|
|
||||||
|
|
|
@ -24,26 +24,15 @@ RE_REMOVE_ANSI = LazyObject(
|
||||||
def _get_git_branch(q):
|
def _get_git_branch(q):
|
||||||
denv = builtins.__xonsh__.env.detype()
|
denv = builtins.__xonsh__.env.detype()
|
||||||
try:
|
try:
|
||||||
branches = xt.decode_bytes(
|
cmd = ["git", "rev-parse", "--abbrev-ref", "HEAD"]
|
||||||
subprocess.check_output(
|
branch = xt.decode_bytes(
|
||||||
["git", "branch"], env=denv, stderr=subprocess.DEVNULL
|
subprocess.check_output(cmd, env=denv, stderr=subprocess.DEVNULL)
|
||||||
)
|
)
|
||||||
).splitlines()
|
branch = branch.splitlines()[0] or None
|
||||||
except (subprocess.CalledProcessError, OSError, FileNotFoundError):
|
except (subprocess.CalledProcessError, OSError, FileNotFoundError):
|
||||||
q.put(None)
|
q.put(None)
|
||||||
else:
|
else:
|
||||||
for branch in branches:
|
q.put(branch)
|
||||||
if not branch.startswith("* "):
|
|
||||||
continue
|
|
||||||
elif branch.endswith(")"):
|
|
||||||
branch = branch.split()[-1][:-1]
|
|
||||||
else:
|
|
||||||
branch = branch.split()[-1]
|
|
||||||
|
|
||||||
q.put(branch)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
q.put(None)
|
|
||||||
|
|
||||||
|
|
||||||
def get_git_branch():
|
def get_git_branch():
|
||||||
|
@ -59,7 +48,6 @@ def get_git_branch():
|
||||||
t.join(timeout=timeout)
|
t.join(timeout=timeout)
|
||||||
try:
|
try:
|
||||||
branch = q.get_nowait()
|
branch = q.get_nowait()
|
||||||
# branch = RE_REMOVE_ANSI.sub("", branch or "")
|
|
||||||
if branch:
|
if branch:
|
||||||
branch = RE_REMOVE_ANSI.sub("", branch)
|
branch = RE_REMOVE_ANSI.sub("", branch)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
|
@ -149,6 +137,15 @@ def _first_branch_timeout_message():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _vc_has(binary):
|
||||||
|
""" This allows us to locate binaries after git only if necessary """
|
||||||
|
cmds = builtins.__xonsh__.commands_cache
|
||||||
|
if cmds.is_empty():
|
||||||
|
return bool(cmds.locate_binary(binary, ignore_alias=True))
|
||||||
|
else:
|
||||||
|
return bool(cmds.lazy_locate_binary(binary, ignore_alias=True))
|
||||||
|
|
||||||
|
|
||||||
def current_branch():
|
def current_branch():
|
||||||
"""Gets the branch for a current working directory. Returns an empty string
|
"""Gets the branch for a current working directory. Returns an empty string
|
||||||
if the cwd is not a repository. This currently only works for git and hg
|
if the cwd is not a repository. This currently only works for git and hg
|
||||||
|
@ -156,17 +153,9 @@ def current_branch():
|
||||||
'<branch-timeout>' is returned.
|
'<branch-timeout>' is returned.
|
||||||
"""
|
"""
|
||||||
branch = None
|
branch = None
|
||||||
cmds = builtins.__xonsh__.commands_cache
|
if _vc_has("git"):
|
||||||
# check for binary only once
|
|
||||||
if cmds.is_empty():
|
|
||||||
has_git = bool(cmds.locate_binary("git", ignore_alias=True))
|
|
||||||
has_hg = bool(cmds.locate_binary("hg", ignore_alias=True))
|
|
||||||
else:
|
|
||||||
has_git = bool(cmds.lazy_locate_binary("git", ignore_alias=True))
|
|
||||||
has_hg = bool(cmds.lazy_locate_binary("hg", ignore_alias=True))
|
|
||||||
if has_git:
|
|
||||||
branch = get_git_branch()
|
branch = get_git_branch()
|
||||||
if not branch and has_hg:
|
if not branch and _vc_has("hg"):
|
||||||
branch = get_hg_branch()
|
branch = get_hg_branch()
|
||||||
if isinstance(branch, subprocess.TimeoutExpired):
|
if isinstance(branch, subprocess.TimeoutExpired):
|
||||||
branch = "<branch-timeout>"
|
branch = "<branch-timeout>"
|
||||||
|
@ -175,19 +164,31 @@ def current_branch():
|
||||||
|
|
||||||
|
|
||||||
def _git_dirty_working_directory(q, include_untracked):
|
def _git_dirty_working_directory(q, include_untracked):
|
||||||
status = None
|
|
||||||
denv = builtins.__xonsh__.env.detype()
|
denv = builtins.__xonsh__.env.detype()
|
||||||
try:
|
try:
|
||||||
cmd = ["git", "status", "--porcelain"]
|
# Borrowed from this conversation
|
||||||
|
# https://github.com/sindresorhus/pure/issues/115
|
||||||
if include_untracked:
|
if include_untracked:
|
||||||
cmd.append("--untracked-files=normal")
|
cmd = [
|
||||||
|
"git",
|
||||||
|
"status",
|
||||||
|
"--porcelain",
|
||||||
|
"--quiet",
|
||||||
|
"--untracked-files=normal",
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
cmd.append("--untracked-files=no")
|
unindexed = ["git", "diff", "--no-ext-diff", "--quiet"]
|
||||||
status = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, env=denv)
|
indexed = unindexed + ["--cached", "HEAD"]
|
||||||
|
cmd = unindexed + ["||"] + indexed
|
||||||
|
child = subprocess.run(cmd, stderr=subprocess.DEVNULL, env=denv)
|
||||||
|
# "--quiet" git commands imply "--exit-code", which returns:
|
||||||
|
# 1 if there are differences
|
||||||
|
# 0 if there are no differences
|
||||||
|
dwd = bool(child.returncode)
|
||||||
except (subprocess.CalledProcessError, OSError, FileNotFoundError):
|
except (subprocess.CalledProcessError, OSError, FileNotFoundError):
|
||||||
q.put(None)
|
q.put(None)
|
||||||
if status is not None:
|
else:
|
||||||
return q.put(bool(status))
|
q.put(dwd)
|
||||||
|
|
||||||
|
|
||||||
def git_dirty_working_directory(include_untracked=False):
|
def git_dirty_working_directory(include_untracked=False):
|
||||||
|
@ -241,10 +242,9 @@ def dirty_working_directory():
|
||||||
None. Currently supports git and hg.
|
None. Currently supports git and hg.
|
||||||
"""
|
"""
|
||||||
dwd = None
|
dwd = None
|
||||||
cmds = builtins.__xonsh__.commands_cache
|
if _vc_has("git"):
|
||||||
if cmds.lazy_locate_binary("git", ignore_alias=True):
|
|
||||||
dwd = git_dirty_working_directory()
|
dwd = git_dirty_working_directory()
|
||||||
if cmds.lazy_locate_binary("hg", ignore_alias=True) and dwd is None:
|
if dwd is None and _vc_has("hg"):
|
||||||
dwd = hg_dirty_working_directory()
|
dwd = hg_dirty_working_directory()
|
||||||
return dwd
|
return dwd
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,15 @@ def load_xonsh_bindings() -> KeyBindingsBase:
|
||||||
env = builtins.__xonsh__.env
|
env = builtins.__xonsh__.env
|
||||||
event.cli.current_buffer.insert_text(env.get("INDENT"))
|
event.cli.current_buffer.insert_text(env.get("INDENT"))
|
||||||
|
|
||||||
|
@handle(Keys.Tab, filter=~tab_insert_indent)
|
||||||
|
def start_complete(event):
|
||||||
|
"""If starting completions, automatically move to first option"""
|
||||||
|
buff = event.app.current_buffer
|
||||||
|
if buff.complete_state:
|
||||||
|
buff.complete_next()
|
||||||
|
else:
|
||||||
|
buff.start_completion(select_first=True)
|
||||||
|
|
||||||
@handle(Keys.ControlX, Keys.ControlE, filter=~has_selection)
|
@handle(Keys.ControlX, Keys.ControlE, filter=~has_selection)
|
||||||
def open_editor(event):
|
def open_editor(event):
|
||||||
""" Open current buffer in editor """
|
""" Open current buffer in editor """
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""The prompt_toolkit based xonsh shell."""
|
"""The prompt_toolkit based xonsh shell."""
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import builtins
|
import builtins
|
||||||
from types import MethodType
|
from types import MethodType
|
||||||
|
@ -37,6 +38,7 @@ from prompt_toolkit.styles.pygments import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
ANSI_OSC_PATTERN = re.compile("\x1b].*?\007")
|
||||||
Token = _TokenType()
|
Token = _TokenType()
|
||||||
|
|
||||||
events.transmogrify("on_ptk_create", "LoadEvent")
|
events.transmogrify("on_ptk_create", "LoadEvent")
|
||||||
|
@ -76,6 +78,19 @@ def tokenize_ansi(tokens):
|
||||||
return ansi_tokens
|
return ansi_tokens
|
||||||
|
|
||||||
|
|
||||||
|
def remove_ansi_osc(prompt):
|
||||||
|
"""Removes the ANSI OSC escape codes - ``prompt_toolkit`` does not support them.
|
||||||
|
Some terminal emulators - like iTerm2 - uses them for various things.
|
||||||
|
|
||||||
|
See: https://www.iterm2.com/documentation-escape-codes.html
|
||||||
|
"""
|
||||||
|
|
||||||
|
osc_tokens = ANSI_OSC_PATTERN.findall(prompt)
|
||||||
|
prompt = ANSI_OSC_PATTERN.sub("", prompt)
|
||||||
|
|
||||||
|
return prompt, osc_tokens
|
||||||
|
|
||||||
|
|
||||||
class PromptToolkitShell(BaseShell):
|
class PromptToolkitShell(BaseShell):
|
||||||
"""The xonsh shell for prompt_toolkit v2 and later."""
|
"""The xonsh shell for prompt_toolkit v2 and later."""
|
||||||
|
|
||||||
|
@ -250,10 +265,21 @@ class PromptToolkitShell(BaseShell):
|
||||||
p = self.prompt_formatter(p)
|
p = self.prompt_formatter(p)
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
print_exception()
|
print_exception()
|
||||||
|
|
||||||
|
p, osc_tokens = remove_ansi_osc(p)
|
||||||
|
|
||||||
toks = partial_color_tokenize(p)
|
toks = partial_color_tokenize(p)
|
||||||
if self._first_prompt:
|
if self._first_prompt:
|
||||||
carriage_return()
|
carriage_return()
|
||||||
self._first_prompt = False
|
self._first_prompt = False
|
||||||
|
|
||||||
|
# handle OSC tokens
|
||||||
|
for osc in osc_tokens:
|
||||||
|
if osc[2:4] == "0;":
|
||||||
|
builtins.__xonsh__.env["TITLE"] = osc[4:-1]
|
||||||
|
else:
|
||||||
|
print(osc, file=sys.__stdout__, flush=True)
|
||||||
|
|
||||||
self.settitle()
|
self.settitle()
|
||||||
return tokenize_ansi(PygmentsTokens(toks))
|
return tokenize_ansi(PygmentsTokens(toks))
|
||||||
|
|
||||||
|
|
|
@ -882,13 +882,10 @@ def suggest_commands(cmd, env, aliases):
|
||||||
if levenshtein(alias.lower(), cmd, thresh) < thresh:
|
if levenshtein(alias.lower(), cmd, thresh) < thresh:
|
||||||
suggested[alias] = "Alias"
|
suggested[alias] = "Alias"
|
||||||
|
|
||||||
for path in filter(os.path.isdir, env.get("PATH")):
|
for _cmd in builtins.__xonsh__.commands_cache.all_commands:
|
||||||
for _file in executables_in(path):
|
if _cmd not in suggested:
|
||||||
if (
|
if levenshtein(_cmd.lower(), cmd, thresh) < thresh:
|
||||||
_file not in suggested
|
suggested[_cmd] = "Command ({0})".format(_cmd)
|
||||||
and levenshtein(_file.lower(), cmd, thresh) < thresh
|
|
||||||
):
|
|
||||||
suggested[_file] = "Command ({0})".format(os.path.join(path, _file))
|
|
||||||
|
|
||||||
suggested = collections.OrderedDict(
|
suggested = collections.OrderedDict(
|
||||||
sorted(
|
sorted(
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
{"name": "argcomplete",
|
{"name": "argcomplete",
|
||||||
"package": "xontrib-argcomplete",
|
"package": "xontrib-argcomplete",
|
||||||
"url": "https://github.com/anki-code/xontrib-argcomplete",
|
"url": "https://github.com/anki-code/xontrib-argcomplete",
|
||||||
"description": ["Adding support of kislyuk/argcomplete to xonsh."]
|
"description": ["Argcomplete support to tab completion of python and xonsh scripts in xonsh."]
|
||||||
},
|
},
|
||||||
{"name": "autojump",
|
{"name": "autojump",
|
||||||
"package": "xontrib-autojump",
|
"package": "xontrib-autojump",
|
||||||
|
@ -222,7 +222,7 @@
|
||||||
{"name": "pipeliner",
|
{"name": "pipeliner",
|
||||||
"package": "xontrib-pipeliner",
|
"package": "xontrib-pipeliner",
|
||||||
"url": "https://github.com/anki-code/xontrib-pipeliner",
|
"url": "https://github.com/anki-code/xontrib-pipeliner",
|
||||||
"description": ["Easily process the lines using pipes."]
|
"description": ["Let your pipe lines flow thru the Python code in xonsh."]
|
||||||
},
|
},
|
||||||
{"name": "vox",
|
{"name": "vox",
|
||||||
"package": "xonsh",
|
"package": "xonsh",
|
||||||
|
|
|
@ -159,13 +159,7 @@ def _ul_add_action(actions, opt, res_type, stderr):
|
||||||
actions.append(
|
actions.append(
|
||||||
[
|
[
|
||||||
_ul_show,
|
_ul_show,
|
||||||
{
|
{"res": r[0], "res_type": res_type, "desc": r[3], "unit": r[4], "opt": opt},
|
||||||
"res": r[0],
|
|
||||||
"res_type": res_type,
|
|
||||||
"desc": r[3],
|
|
||||||
"unit": r[4],
|
|
||||||
"opt": opt,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -28,3 +28,14 @@ def custom_keybindings(bindings, **kw):
|
||||||
pos = buff.document.find_next_word_ending(count=event.arg, WORD=True)
|
pos = buff.document.find_next_word_ending(count=event.arg, WORD=True)
|
||||||
if pos:
|
if pos:
|
||||||
buff.cursor_position += pos
|
buff.cursor_position += pos
|
||||||
|
|
||||||
|
@bindings.add(Keys.ShiftDelete)
|
||||||
|
def shift_delete(event):
|
||||||
|
buff = event.current_buffer
|
||||||
|
startpos, endpos = buff.document.find_boundaries_of_current_word(WORD=True)
|
||||||
|
startpos = buff.cursor_position + startpos - 1
|
||||||
|
startpos = 0 if startpos < 0 else startpos
|
||||||
|
endpos = buff.cursor_position + endpos
|
||||||
|
endpos = endpos + 1 if startpos == 0 else endpos
|
||||||
|
buff.text = buff.text[:startpos] + buff.text[endpos:]
|
||||||
|
buff.cursor_position = startpos
|
||||||
|
|
Loading…
Add table
Reference in a new issue