mirror of
https://github.com/nvbn/thefuck.git
synced 2025-02-21 20:38:54 +00:00
#614: Add --repeat
option
This commit is contained in:
parent
c3eca8234a
commit
cfa831c88d
@ -33,6 +33,9 @@ class TestBash(object):
|
||||
def test_and_(self, shell):
|
||||
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||
|
||||
def test_or_(self, shell):
|
||||
assert shell.or_('ls', 'cd') == 'ls || cd'
|
||||
|
||||
def test_get_aliases(self, shell):
|
||||
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
|
||||
'l': 'ls -CF',
|
||||
|
@ -55,6 +55,9 @@ class TestFish(object):
|
||||
def test_and_(self, shell):
|
||||
assert shell.and_('foo', 'bar') == 'foo; and bar'
|
||||
|
||||
def test_or_(self, shell):
|
||||
assert shell.or_('foo', 'bar') == 'foo; or bar'
|
||||
|
||||
def test_get_aliases(self, shell):
|
||||
assert shell.get_aliases() == {'fish_config': 'fish_config',
|
||||
'fuck': 'fuck',
|
||||
|
@ -18,6 +18,9 @@ class TestGeneric(object):
|
||||
def test_and_(self, shell):
|
||||
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||
|
||||
def test_or_(self, shell):
|
||||
assert shell.or_('ls', 'cd') == 'ls || cd'
|
||||
|
||||
def test_get_aliases(self, shell):
|
||||
assert shell.get_aliases() == {}
|
||||
|
||||
|
@ -34,6 +34,9 @@ class TestTcsh(object):
|
||||
def test_and_(self, shell):
|
||||
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||
|
||||
def test_or_(self, shell):
|
||||
assert shell.or_('ls', 'cd') == 'ls || cd'
|
||||
|
||||
def test_get_aliases(self, shell):
|
||||
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
|
||||
'l': 'ls -CF',
|
||||
|
@ -32,6 +32,9 @@ class TestZsh(object):
|
||||
def test_and_(self, shell):
|
||||
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||
|
||||
def test_or_(self, shell):
|
||||
assert shell.or_('ls', 'cd') == 'ls || cd'
|
||||
|
||||
def test_get_aliases(self, shell):
|
||||
assert shell.get_aliases() == {
|
||||
'fuck': 'eval $(thefuck $(fc -ln -1 | tail -n 1))',
|
||||
|
@ -3,29 +3,27 @@ from thefuck.argument_parser import Parser
|
||||
from thefuck.const import ARGUMENT_PLACEHOLDER
|
||||
|
||||
|
||||
def _args(**override):
|
||||
args = {'alias': None, 'command': [], 'yes': False,
|
||||
'help': False, 'version': False, 'debug': False,
|
||||
'force_command': None, 'repeat': False}
|
||||
args.update(override)
|
||||
return args
|
||||
|
||||
|
||||
@pytest.mark.parametrize('argv, result', [
|
||||
(['thefuck'], {'alias': None, 'command': [], 'yes': False,
|
||||
'help': False, 'version': False, 'debug': False}),
|
||||
(['thefuck', '-a'],
|
||||
{'alias': 'fuck', 'command': [], 'yes': False,
|
||||
'help': False, 'version': False, 'debug': False}),
|
||||
(['thefuck', '-a', 'fix'],
|
||||
{'alias': 'fix', 'command': [], 'yes': False,
|
||||
'help': False, 'version': False, 'debug': False}),
|
||||
(['thefuck'], _args()),
|
||||
(['thefuck', '-a'], _args(alias='fuck')),
|
||||
(['thefuck', '-a', 'fix'], _args(alias='fix')),
|
||||
(['thefuck', 'git', 'branch', ARGUMENT_PLACEHOLDER, '-y'],
|
||||
{'alias': None, 'command': ['git', 'branch'], 'yes': True,
|
||||
'help': False, 'version': False, 'debug': False}),
|
||||
_args(command=['git', 'branch'], yes=True)),
|
||||
(['thefuck', 'git', 'branch', '-a', ARGUMENT_PLACEHOLDER, '-y'],
|
||||
{'alias': None, 'command': ['git', 'branch', '-a'], 'yes': True,
|
||||
'help': False, 'version': False, 'debug': False}),
|
||||
(['thefuck', ARGUMENT_PLACEHOLDER, '-v'],
|
||||
{'alias': None, 'command': [], 'yes': False, 'help': False,
|
||||
'version': True, 'debug': False}),
|
||||
(['thefuck', ARGUMENT_PLACEHOLDER, '--help'],
|
||||
{'alias': None, 'command': [], 'yes': False, 'help': True,
|
||||
'version': False, 'debug': False}),
|
||||
_args(command=['git', 'branch', '-a'], yes=True)),
|
||||
(['thefuck', ARGUMENT_PLACEHOLDER, '-v'], _args(version=True)),
|
||||
(['thefuck', ARGUMENT_PLACEHOLDER, '--help'], _args(help=True)),
|
||||
(['thefuck', 'git', 'branch', '-a', ARGUMENT_PLACEHOLDER, '-y', '-d'],
|
||||
{'alias': None, 'command': ['git', 'branch', '-a'], 'yes': True,
|
||||
'help': False, 'version': False, 'debug': True})])
|
||||
_args(command=['git', 'branch', '-a'], yes=True, debug=True)),
|
||||
(['thefuck', 'git', 'branch', '-a', ARGUMENT_PLACEHOLDER, '-r', '-d'],
|
||||
_args(command=['git', 'branch', '-a'], repeat=True, debug=True))])
|
||||
def test_parse(argv, result):
|
||||
assert vars(Parser().parse(argv)) == result
|
||||
|
@ -80,9 +80,10 @@ class TestSettingsFromEnv(object):
|
||||
|
||||
|
||||
def test_settings_from_args(settings):
|
||||
settings.init(Mock(yes=True, debug=True))
|
||||
settings.init(Mock(yes=True, debug=True, repeat=True))
|
||||
assert not settings.require_confirmation
|
||||
assert settings.debug
|
||||
assert settings.repeat
|
||||
|
||||
|
||||
class TestInitializeSettingsFile(object):
|
||||
|
@ -28,6 +28,20 @@ class TestCorrectedCommand(object):
|
||||
assert u'{}'.format(CorrectedCommand(u'echo café', None, 100)) == \
|
||||
u'CorrectedCommand(script=echo café, side_effect=None, priority=100)'
|
||||
|
||||
@pytest.mark.parametrize('script, printed, override_settings', [
|
||||
('git branch', 'git branch', {'repeat': False, 'debug': False}),
|
||||
('git brunch',
|
||||
"git brunch || fuck --repeat --force-command 'git brunch'",
|
||||
{'repeat': True, 'debug': False}),
|
||||
('git brunch',
|
||||
"git brunch || fuck --repeat --debug --force-command 'git brunch'",
|
||||
{'repeat': True, 'debug': True})])
|
||||
def test_run(self, capsys, settings, script, printed, override_settings):
|
||||
settings.update(override_settings)
|
||||
CorrectedCommand(script, None, 1000).run(Command())
|
||||
out, _ = capsys.readouterr()
|
||||
assert out[:-1] == printed
|
||||
|
||||
|
||||
class TestRule(object):
|
||||
def test_from_path(self, mocker):
|
||||
|
@ -1,5 +1,5 @@
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from argparse import ArgumentParser, SUPPRESS
|
||||
from .const import ARGUMENT_PLACEHOLDER
|
||||
from .utils import get_alias
|
||||
|
||||
@ -20,17 +20,27 @@ class Parser(object):
|
||||
'-h', '--help',
|
||||
action='store_true',
|
||||
help='show this help message and exit')
|
||||
self._parser.add_argument(
|
||||
group = self._parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
'-y', '--yes',
|
||||
action='store_true',
|
||||
help='execute fixed command without confirmation')
|
||||
group.add_argument(
|
||||
'-r', '--repeat',
|
||||
action='store_true',
|
||||
help='repeat on failure')
|
||||
self._parser.add_argument(
|
||||
'-d', '--debug',
|
||||
action='store_true',
|
||||
help='enable debug output')
|
||||
self._parser.add_argument('command',
|
||||
nargs='*',
|
||||
help='command that should be fixed')
|
||||
self._parser.add_argument(
|
||||
'--force-command',
|
||||
action='store',
|
||||
help=SUPPRESS)
|
||||
self._parser.add_argument(
|
||||
'command',
|
||||
nargs='*',
|
||||
help='command that should be fixed')
|
||||
|
||||
def _get_arguments(self, argv):
|
||||
if ARGUMENT_PLACEHOLDER in argv:
|
||||
|
@ -121,6 +121,8 @@ class Settings(dict):
|
||||
from_args['require_confirmation'] = not args.yes
|
||||
if args.debug:
|
||||
from_args['debug'] = args.debug
|
||||
if args.repeat:
|
||||
from_args['repeat'] = args.repeat
|
||||
return from_args
|
||||
|
||||
|
||||
|
@ -34,6 +34,7 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
|
||||
'wait_slow_command': 15,
|
||||
'slow_commands': ['lein', 'react-native', 'gradle',
|
||||
'./gradlew', 'vagrant'],
|
||||
'repeat': False,
|
||||
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}}
|
||||
|
||||
ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
|
||||
@ -46,7 +47,8 @@ ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
|
||||
'THEFUCK_HISTORY_LIMIT': 'history_limit',
|
||||
'THEFUCK_ALTER_HISTORY': 'alter_history',
|
||||
'THEFUCK_WAIT_SLOW_COMMAND': 'wait_slow_command',
|
||||
'THEFUCK_SLOW_COMMANDS': 'slow_commands'}
|
||||
'THEFUCK_SLOW_COMMANDS': 'slow_commands',
|
||||
'THEFUCK_REPEAT': 'repeat'}
|
||||
|
||||
SETTINGS_HEADER = u"""# The Fuck settings file
|
||||
#
|
||||
|
@ -20,9 +20,11 @@ def fix_command(known_args):
|
||||
settings.init(known_args)
|
||||
with logs.debug_time('Total'):
|
||||
logs.debug(u'Run with settings: {}'.format(pformat(settings)))
|
||||
raw_command = ([known_args.force_command] if known_args.force_command
|
||||
else known_args.command)
|
||||
|
||||
try:
|
||||
command = types.Command.from_raw_script(known_args.command)
|
||||
command = types.Command.from_raw_script(raw_command)
|
||||
except EmptyCommand:
|
||||
logs.debug('Empty command, nothing to do')
|
||||
return
|
||||
|
@ -66,6 +66,9 @@ class Fish(Generic):
|
||||
def and_(self, *commands):
|
||||
return u'; and '.join(commands)
|
||||
|
||||
def or_(self, *commands):
|
||||
return u'; or '.join(commands)
|
||||
|
||||
def how_to_configure(self):
|
||||
return self._create_shell_configuration(
|
||||
content=u"eval (thefuck --alias | tr '\n' ';')",
|
||||
|
@ -66,6 +66,9 @@ class Generic(object):
|
||||
def and_(self, *commands):
|
||||
return u' && '.join(commands)
|
||||
|
||||
def or_(self, *commands):
|
||||
return u' || '.join(commands)
|
||||
|
||||
def how_to_configure(self):
|
||||
return
|
||||
|
||||
|
@ -9,6 +9,7 @@ from .shells import shell
|
||||
from .conf import settings
|
||||
from .const import DEFAULT_PRIORITY, ALL_ENABLED
|
||||
from .exceptions import EmptyCommand
|
||||
from .utils import get_alias
|
||||
|
||||
|
||||
class Command(object):
|
||||
@ -276,6 +277,22 @@ class CorrectedCommand(object):
|
||||
return u'CorrectedCommand(script={}, side_effect={}, priority={})'.format(
|
||||
self.script, self.side_effect, self.priority)
|
||||
|
||||
def _get_script(self):
|
||||
"""Returns fixed commands script.
|
||||
|
||||
If `settings.repeat` is `True`, appends command with second attempt
|
||||
of running fuck in case fixed command fails again.
|
||||
|
||||
"""
|
||||
if settings.repeat:
|
||||
repeat_fuck = '{} --repeat {}--force-command {}'.format(
|
||||
get_alias(),
|
||||
'--debug ' if settings.debug else '',
|
||||
shell.quote(self.script))
|
||||
return shell.or_(self.script, repeat_fuck)
|
||||
else:
|
||||
return self.script
|
||||
|
||||
def run(self, old_cmd):
|
||||
"""Runs command from rule for passed command.
|
||||
|
||||
@ -289,4 +306,5 @@ class CorrectedCommand(object):
|
||||
# This depends on correct setting of PYTHONIOENCODING by the alias:
|
||||
logs.debug(u'PYTHONIOENCODING: {}'.format(
|
||||
os.environ.get('PYTHONIOENCODING', '!!not-set!!')))
|
||||
print(self.script)
|
||||
|
||||
print(self._get_script())
|
||||
|
Loading…
x
Reference in New Issue
Block a user