mirror of
https://github.com/nvbn/thefuck.git
synced 2025-01-31 10:11:14 +00:00
#N/A: Support editing command
This commit is contained in:
parent
6975d30818
commit
15372fcb90
@ -43,6 +43,18 @@ class TestCorrectedCommand(object):
|
||||
out, _ = capsys.readouterr()
|
||||
assert out[:-1] == printed
|
||||
|
||||
def test_run_with_edit(self, capsys, monkeypatch, mocker):
|
||||
script = "git branch"
|
||||
edit_tpl = 'editor "{}"'
|
||||
monkeypatch.setattr(
|
||||
'thefuck.types.shell.edit_command',
|
||||
lambda script: edit_tpl.format(script),
|
||||
)
|
||||
command = CorrectedCommand(script, None, 1000).edit()
|
||||
command.run(Command(script, ''))
|
||||
out, _ = capsys.readouterr()
|
||||
assert out[:-1] == edit_tpl.format(script)
|
||||
|
||||
|
||||
class TestRule(object):
|
||||
def test_from_path(self, mocker):
|
||||
|
@ -16,12 +16,15 @@ def patch_get_key(monkeypatch):
|
||||
return patch
|
||||
|
||||
|
||||
def test_read_actions(patch_get_key):
|
||||
@pytest.mark.parametrize("shell_can_edit", [True, False])
|
||||
def test_read_actions(shell_can_edit, patch_get_key):
|
||||
patch_get_key([
|
||||
# Enter:
|
||||
'\n',
|
||||
# Enter:
|
||||
'\r',
|
||||
# Edit:
|
||||
const.KEY_BACKSPACE, 'd',
|
||||
# Ignored:
|
||||
'x', 'y',
|
||||
# Up:
|
||||
@ -30,11 +33,17 @@ def test_read_actions(patch_get_key):
|
||||
const.KEY_DOWN, 'j',
|
||||
# Ctrl+C:
|
||||
const.KEY_CTRL_C, 'q'])
|
||||
assert (list(islice(ui.read_actions(), 8))
|
||||
== [const.ACTION_SELECT, const.ACTION_SELECT,
|
||||
const.ACTION_PREVIOUS, const.ACTION_PREVIOUS,
|
||||
const.ACTION_NEXT, const.ACTION_NEXT,
|
||||
const.ACTION_ABORT, const.ACTION_ABORT])
|
||||
expected_actions = [const.ACTION_SELECT, const.ACTION_SELECT,
|
||||
const.ACTION_PREVIOUS, const.ACTION_PREVIOUS,
|
||||
const.ACTION_NEXT, const.ACTION_NEXT,
|
||||
const.ACTION_ABORT, const.ACTION_ABORT]
|
||||
number_of_items = 8
|
||||
if shell_can_edit:
|
||||
expected_actions.insert(2, const.ACTION_EDIT)
|
||||
expected_actions.insert(2, const.ACTION_EDIT)
|
||||
number_of_items = 10
|
||||
assert (list(islice(ui.read_actions(shell_can_edit), number_of_items))
|
||||
== expected_actions)
|
||||
|
||||
|
||||
def test_command_selector():
|
||||
@ -106,3 +115,14 @@ class TestSelectCommand(object):
|
||||
u'{mark}\x1b[1K\rcd [enter/↑/↓/ctrl+c]\n'
|
||||
).format(mark=const.USER_COMMAND_MARK)
|
||||
assert capsys.readouterr() == ('', stderr)
|
||||
|
||||
def test_with_edit(self, capsys, patch_get_key, commands, monkeypatch):
|
||||
monkeypatch.setattr('thefuck.ui.shell.can_edit', lambda: True)
|
||||
patch_get_key([const.KEY_BACKSPACE, '\n'])
|
||||
command = ui.select_command(iter(commands))
|
||||
assert command == commands[0]
|
||||
assert command.should_edit is True
|
||||
stderr = (
|
||||
u'{mark}\x1b[1K\rls [enter/edit/↑/↓/ctrl+c]\n'
|
||||
).format(mark=const.USER_COMMAND_MARK)
|
||||
assert capsys.readouterr() == ('', stderr)
|
||||
|
@ -14,15 +14,18 @@ KEY_DOWN = _GenConst('↓')
|
||||
KEY_CTRL_C = _GenConst('Ctrl+C')
|
||||
KEY_CTRL_N = _GenConst('Ctrl+N')
|
||||
KEY_CTRL_P = _GenConst('Ctrl+P')
|
||||
KEY_BACKSPACE = _GenConst('Backspace')
|
||||
|
||||
KEY_MAPPING = {'\x0e': KEY_CTRL_N,
|
||||
'\x03': KEY_CTRL_C,
|
||||
'\x10': KEY_CTRL_P}
|
||||
'\x10': KEY_CTRL_P,
|
||||
'\x7f': KEY_BACKSPACE}
|
||||
|
||||
ACTION_SELECT = _GenConst('select')
|
||||
ACTION_ABORT = _GenConst('abort')
|
||||
ACTION_PREVIOUS = _GenConst('previous')
|
||||
ACTION_NEXT = _GenConst('next')
|
||||
ACTION_EDIT = _GenConst('edit')
|
||||
|
||||
ALL_ENABLED = _GenConst('All rules enabled')
|
||||
DEFAULT_RULES = [ALL_ENABLED]
|
||||
|
@ -56,10 +56,19 @@ def show_corrected_command(corrected_command):
|
||||
reset=color(colorama.Style.RESET_ALL)))
|
||||
|
||||
|
||||
def confirm_text(corrected_command):
|
||||
def edit_part(show_edit):
|
||||
if show_edit:
|
||||
return '/{yellow}e{bold}d{normal}it'.format(
|
||||
yellow=color(colorama.Fore.YELLOW),
|
||||
bold=color(colorama.Style.BRIGHT),
|
||||
normal=color(colorama.Style.NORMAL))
|
||||
return ''
|
||||
|
||||
|
||||
def confirm_text(corrected_command, show_edit):
|
||||
sys.stderr.write(
|
||||
(u'{prefix}{clear}{bold}{script}{reset}{side_effect} '
|
||||
u'[{green}enter{reset}/{blue}↑{reset}/{blue}↓{reset}'
|
||||
u'[{green}enter{reset}{edit}{reset}/{blue}↑{reset}/{blue}↓{reset}'
|
||||
u'/{red}ctrl+c{reset}]').format(
|
||||
prefix=const.USER_COMMAND_MARK,
|
||||
script=corrected_command.script,
|
||||
@ -69,7 +78,8 @@ def confirm_text(corrected_command):
|
||||
green=color(colorama.Fore.GREEN),
|
||||
red=color(colorama.Fore.RED),
|
||||
reset=color(colorama.Style.RESET_ALL),
|
||||
blue=color(colorama.Fore.BLUE)))
|
||||
blue=color(colorama.Fore.BLUE),
|
||||
edit=edit_part(show_edit)))
|
||||
|
||||
|
||||
def debug(msg):
|
||||
|
@ -127,3 +127,10 @@ class Fish(Generic):
|
||||
history.write(entry.encode('utf-8'))
|
||||
else:
|
||||
history.write(entry)
|
||||
|
||||
def can_edit(self):
|
||||
return True
|
||||
|
||||
def edit_command(self, command):
|
||||
"""Return the shell editable command"""
|
||||
return u'commandline -r "{}"'.format(command)
|
||||
|
@ -121,6 +121,13 @@ class Generic(object):
|
||||
|
||||
"""
|
||||
|
||||
def can_edit(self):
|
||||
return False
|
||||
|
||||
def edit_command(self, command):
|
||||
"""Return the shell editable command"""
|
||||
return command
|
||||
|
||||
def get_builtin_commands(self):
|
||||
"""Returns shells builtin commands."""
|
||||
return ['alias', 'bg', 'bind', 'break', 'builtin', 'case', 'cd',
|
||||
|
@ -209,6 +209,7 @@ class CorrectedCommand(object):
|
||||
self.script = script
|
||||
self.side_effect = side_effect
|
||||
self.priority = priority
|
||||
self.should_edit = False
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Ignores `priority` field."""
|
||||
@ -232,6 +233,9 @@ class CorrectedCommand(object):
|
||||
of running fuck in case fixed command fails again.
|
||||
|
||||
"""
|
||||
if self.should_edit:
|
||||
self.script = shell.edit_command(self.script)
|
||||
|
||||
if settings.repeat:
|
||||
repeat_fuck = '{} --repeat {}--force-command {}'.format(
|
||||
get_alias(),
|
||||
@ -241,6 +245,10 @@ class CorrectedCommand(object):
|
||||
else:
|
||||
return self.script
|
||||
|
||||
def edit(self):
|
||||
self.should_edit = True
|
||||
return self
|
||||
|
||||
def run(self, old_cmd):
|
||||
"""Runs command from rule for passed command.
|
||||
|
||||
|
@ -3,21 +3,24 @@
|
||||
import sys
|
||||
from .conf import settings
|
||||
from .exceptions import NoRuleMatched
|
||||
from .shells import shell
|
||||
from .system import get_key
|
||||
from .utils import get_alias
|
||||
from . import logs, const
|
||||
|
||||
|
||||
def read_actions():
|
||||
def read_actions(can_edit):
|
||||
"""Yields actions for pressed keys."""
|
||||
while True:
|
||||
key = get_key()
|
||||
|
||||
# Handle arrows, j/k (qwerty), and n/e (colemak)
|
||||
# Handle arrows, edit, j/k (qwerty), and n/e (colemak)
|
||||
if key in (const.KEY_UP, const.KEY_CTRL_N, 'k', 'e'):
|
||||
yield const.ACTION_PREVIOUS
|
||||
elif key in (const.KEY_DOWN, const.KEY_CTRL_P, 'j', 'n'):
|
||||
yield const.ACTION_NEXT
|
||||
elif can_edit and key in (const.KEY_BACKSPACE, 'd'):
|
||||
yield const.ACTION_EDIT
|
||||
elif key in (const.KEY_CTRL_C, 'q'):
|
||||
yield const.ACTION_ABORT
|
||||
elif key in ('\n', '\r'):
|
||||
@ -78,18 +81,21 @@ def select_command(corrected_commands):
|
||||
logs.show_corrected_command(selector.value)
|
||||
return selector.value
|
||||
|
||||
logs.confirm_text(selector.value)
|
||||
logs.confirm_text(selector.value, shell.can_edit())
|
||||
|
||||
for action in read_actions():
|
||||
for action in read_actions(shell.can_edit()):
|
||||
if action == const.ACTION_SELECT:
|
||||
sys.stderr.write('\n')
|
||||
return selector.value
|
||||
elif action == const.ACTION_EDIT:
|
||||
sys.stderr.write('\n')
|
||||
return selector.value.edit()
|
||||
elif action == const.ACTION_ABORT:
|
||||
logs.failed('\nAborted')
|
||||
return
|
||||
elif action == const.ACTION_PREVIOUS:
|
||||
selector.previous()
|
||||
logs.confirm_text(selector.value)
|
||||
logs.confirm_text(selector.value, shell.can_edit())
|
||||
elif action == const.ACTION_NEXT:
|
||||
selector.next()
|
||||
logs.confirm_text(selector.value)
|
||||
logs.confirm_text(selector.value, shell.can_edit())
|
||||
|
Loading…
x
Reference in New Issue
Block a user