mirror of
https://github.com/nvbn/thefuck.git
synced 2025-03-20 01:28:56 +00:00
Merge 52e4c418a401fa9d1408198742fb638bc447324a into 201c01fc74d479d9e418d32c92a88b50b27728a7
This commit is contained in:
commit
c04678b433
@ -14,9 +14,9 @@ from thefuck.types import Command
|
|||||||
class TestCorrectedCommand(object):
|
class TestCorrectedCommand(object):
|
||||||
|
|
||||||
def test_equality(self):
|
def test_equality(self):
|
||||||
assert (CorrectedCommand('ls', None, 100) ==
|
assert (CorrectedCommand('ls', None, 100,) ==
|
||||||
CorrectedCommand('ls', None, 200))
|
CorrectedCommand('ls', None, 200,))
|
||||||
assert (CorrectedCommand('ls', None, 100) !=
|
assert (CorrectedCommand('ls', None, 100,) !=
|
||||||
CorrectedCommand('ls', lambda *_: _, 100))
|
CorrectedCommand('ls', lambda *_: _, 100))
|
||||||
|
|
||||||
def test_hashable(self):
|
def test_hashable(self):
|
||||||
@ -48,16 +48,19 @@ class TestRule(object):
|
|||||||
def test_from_path(self, mocker):
|
def test_from_path(self, mocker):
|
||||||
match = object()
|
match = object()
|
||||||
get_new_command = object()
|
get_new_command = object()
|
||||||
|
post_match = object()
|
||||||
load_source = mocker.patch(
|
load_source = mocker.patch(
|
||||||
'thefuck.types.load_source',
|
'thefuck.types.load_source',
|
||||||
return_value=Mock(match=match,
|
return_value=Mock(match=match,
|
||||||
get_new_command=get_new_command,
|
get_new_command=get_new_command,
|
||||||
|
post_match=post_match,
|
||||||
enabled_by_default=True,
|
enabled_by_default=True,
|
||||||
priority=900,
|
priority=900,
|
||||||
requires_output=True))
|
requires_output=True,
|
||||||
|
is_post_match=False))
|
||||||
rule_path = os.path.join(os.sep, 'rules', 'bash.py')
|
rule_path = os.path.join(os.sep, 'rules', 'bash.py')
|
||||||
assert (Rule.from_path(Path(rule_path))
|
assert (Rule.from_path(Path(rule_path)) == Rule(
|
||||||
== Rule('bash', match, get_new_command, priority=900))
|
'bash', rule_path, match, get_new_command, post_match, priority=900))
|
||||||
load_source.assert_called_once_with('bash', rule_path)
|
load_source.assert_called_once_with('bash', rule_path)
|
||||||
|
|
||||||
@pytest.mark.parametrize('rules, exclude_rules, rule, is_enabled', [
|
@pytest.mark.parametrize('rules, exclude_rules, rule, is_enabled', [
|
||||||
@ -77,16 +80,16 @@ class TestRule(object):
|
|||||||
assert rule.is_enabled == is_enabled
|
assert rule.is_enabled == is_enabled
|
||||||
|
|
||||||
def test_isnt_match(self):
|
def test_isnt_match(self):
|
||||||
assert not Rule('', lambda _: False).is_match(
|
assert not Rule('', match=lambda _: False).is_match(
|
||||||
Command('ls', ''))
|
Command('ls', ''))
|
||||||
|
|
||||||
def test_is_match(self):
|
def test_is_match(self):
|
||||||
rule = Rule('', lambda x: x.script == 'cd ..')
|
rule = Rule('', match=lambda x: x.script == 'cd ..')
|
||||||
assert rule.is_match(Command('cd ..', ''))
|
assert rule.is_match(Command('cd ..', ''))
|
||||||
|
|
||||||
@pytest.mark.usefixtures('no_colors')
|
@pytest.mark.usefixtures('no_colors')
|
||||||
def test_isnt_match_when_rule_failed(self, capsys):
|
def test_isnt_match_when_rule_failed(self, capsys):
|
||||||
rule = Rule('test', Mock(side_effect=OSError('Denied')),
|
rule = Rule('test', match=Mock(side_effect=OSError('Denied')),
|
||||||
requires_output=False)
|
requires_output=False)
|
||||||
assert not rule.is_match(Command('ls', ''))
|
assert not rule.is_match(Command('ls', ''))
|
||||||
assert capsys.readouterr()[1].split('\n')[0] == '[WARN] Rule test:'
|
assert capsys.readouterr()[1].split('\n')[0] == '[WARN] Rule test:'
|
||||||
|
@ -54,13 +54,13 @@ def test_command_selector():
|
|||||||
class TestSelectCommand(object):
|
class TestSelectCommand(object):
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def commands_with_side_effect(self):
|
def commands_with_side_effect(self):
|
||||||
return [CorrectedCommand('ls', lambda *_: None, 100),
|
return [CorrectedCommand('ls', lambda *_: None, 100, None),
|
||||||
CorrectedCommand('cd', lambda *_: None, 100)]
|
CorrectedCommand('cd', lambda *_: None, 100, None)]
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def commands(self):
|
def commands(self):
|
||||||
return [CorrectedCommand('ls', None, 100),
|
return [CorrectedCommand('ls', None, 100, None),
|
||||||
CorrectedCommand('cd', None, 100)]
|
CorrectedCommand('cd', None, 100, None)]
|
||||||
|
|
||||||
def test_without_commands(self, capsys):
|
def test_without_commands(self, capsys):
|
||||||
assert ui.select_command(iter([])) is None
|
assert ui.select_command(iter([])) is None
|
||||||
|
@ -3,18 +3,21 @@ from thefuck.const import DEFAULT_PRIORITY
|
|||||||
|
|
||||||
|
|
||||||
class Rule(types.Rule):
|
class Rule(types.Rule):
|
||||||
def __init__(self, name='', match=lambda *_: True,
|
def __init__(self, name='', path='', match=lambda *_: True,
|
||||||
get_new_command=lambda *_: '',
|
get_new_command=lambda *_: '',
|
||||||
|
post_match=lambda *_: '',
|
||||||
enabled_by_default=True,
|
enabled_by_default=True,
|
||||||
side_effect=None,
|
side_effect=None,
|
||||||
priority=DEFAULT_PRIORITY,
|
priority=DEFAULT_PRIORITY,
|
||||||
requires_output=True):
|
requires_output=True,
|
||||||
super(Rule, self).__init__(name, match, get_new_command,
|
is_post_match=False):
|
||||||
enabled_by_default, side_effect,
|
super(Rule, self).__init__(name, path, match, get_new_command,
|
||||||
priority, requires_output)
|
post_match, enabled_by_default, side_effect,
|
||||||
|
priority, requires_output, is_post_match)
|
||||||
|
|
||||||
|
|
||||||
class CorrectedCommand(types.CorrectedCommand):
|
class CorrectedCommand(types.CorrectedCommand):
|
||||||
def __init__(self, script='', side_effect=None, priority=DEFAULT_PRIORITY):
|
def __init__(self, script='', side_effect=None,
|
||||||
|
priority=DEFAULT_PRIORITY, rule=None):
|
||||||
super(CorrectedCommand, self).__init__(
|
super(CorrectedCommand, self).__init__(
|
||||||
script, side_effect, priority)
|
script, side_effect, priority, rule)
|
||||||
|
@ -50,6 +50,10 @@ class Parser(object):
|
|||||||
'command',
|
'command',
|
||||||
nargs='*',
|
nargs='*',
|
||||||
help='command that should be fixed')
|
help='command that should be fixed')
|
||||||
|
self._parser.add_argument(
|
||||||
|
'--post-match',
|
||||||
|
action='store_true', #TODO: Check if store_true is necessary
|
||||||
|
help=SUPPRESS)
|
||||||
|
|
||||||
def _add_conflicting_arguments(self):
|
def _add_conflicting_arguments(self):
|
||||||
"""It's too dangerous to use `-y` and `-r` together."""
|
"""It's too dangerous to use `-y` and `-r` together."""
|
||||||
|
@ -11,6 +11,7 @@ from ..utils import get_installation_info # noqa: E402
|
|||||||
from ..shells import shell # noqa: E402
|
from ..shells import shell # noqa: E402
|
||||||
from .alias import print_alias # noqa: E402
|
from .alias import print_alias # noqa: E402
|
||||||
from .fix_command import fix_command # noqa: E402
|
from .fix_command import fix_command # noqa: E402
|
||||||
|
from .post_match_execute import post_match_execute # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@ -22,6 +23,8 @@ def main():
|
|||||||
elif known_args.version:
|
elif known_args.version:
|
||||||
logs.version(get_installation_info().version,
|
logs.version(get_installation_info().version,
|
||||||
sys.version.split()[0], shell.info())
|
sys.version.split()[0], shell.info())
|
||||||
|
elif known_args.post_match:
|
||||||
|
post_match_execute(known_args)
|
||||||
elif known_args.command or 'TF_HISTORY' in os.environ:
|
elif known_args.command or 'TF_HISTORY' in os.environ:
|
||||||
fix_command(known_args)
|
fix_command(known_args)
|
||||||
elif known_args.alias:
|
elif known_args.alias:
|
||||||
|
9
thefuck/entrypoints/post_match_execute.py
Normal file
9
thefuck/entrypoints/post_match_execute.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from thefuck.types import Rule
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def post_match_execute(known_args):
|
||||||
|
"""Executes post match funtion. Used when `thefuck` called with `--post-match` argument."""
|
||||||
|
# check the first flag to see what rule
|
||||||
|
rule = Rule.from_path(Path(known_args.command[0]))
|
||||||
|
rule.post_match(known_args.command[1], known_args.command[2])
|
64
thefuck/rules/stackoverflow.py
Normal file
64
thefuck/rules/stackoverflow.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
from urllib import parse
|
||||||
|
import requests
|
||||||
|
import sys
|
||||||
|
import colorama
|
||||||
|
from thefuck.conf import settings
|
||||||
|
|
||||||
|
is_post_match = True
|
||||||
|
priority = 9500
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def match(command):
|
||||||
|
return bool(command.output)
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command):
|
||||||
|
return 'Try Stack Overflow'
|
||||||
|
|
||||||
|
|
||||||
|
def post_match(fc,output):
|
||||||
|
num_of_result = 3;
|
||||||
|
# make sure the output is not too long
|
||||||
|
output = output[0:100]
|
||||||
|
search_query = "{} {}".format(fc, output)
|
||||||
|
sq_encoded = parse.quote_plus(search_query)
|
||||||
|
url = "https://api.stackexchange.com/2.2/search/advanced?pagesize={}&order=desc&sort=votes&q={}&accepted=True&site=stackoverflow".format(num_of_result,sq_encoded)
|
||||||
|
response = requests.get(url)
|
||||||
|
display_list = []
|
||||||
|
if response:
|
||||||
|
response_json = response.json()
|
||||||
|
for i in response_json["items"]:
|
||||||
|
display_list.append({"title":i["title"], "link":i["link"], "tags":i["tags"]})
|
||||||
|
stackoverflow(display_list)
|
||||||
|
|
||||||
|
# import pdb; pdb.set_trace(); # BREAKPOINT
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def stackoverflow(display_list):
|
||||||
|
if not display_list:
|
||||||
|
sys.stdout.write(u"{col}No fuck is found.{reset} \n".format(col=color(colorama.Fore.RED), reset=color(colorama.Style.RESET_ALL)))
|
||||||
|
for i, msg in enumerate(display_list):
|
||||||
|
sys.stdout.write(
|
||||||
|
u'{title_col}{i}. Post Title:{title} \n'
|
||||||
|
u'Post Tags: {tags} \n'
|
||||||
|
u'{url_col}URL: {url}\n\n'
|
||||||
|
.format(
|
||||||
|
title_col=color(colorama.Fore.GREEN),
|
||||||
|
url_col=color(colorama.Fore.RED),
|
||||||
|
title=msg["title"],
|
||||||
|
tags=msg["tags"],
|
||||||
|
url=msg["link"],
|
||||||
|
i=i+1,
|
||||||
|
reset=color(colorama.Style.RESET_ALL)
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def color(color_):
|
||||||
|
"""Utility for ability to disabling colored output."""
|
||||||
|
if settings.no_colors:
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
return color_
|
@ -21,7 +21,7 @@ class Bash(Generic):
|
|||||||
export PYTHONIOENCODING=utf-8;
|
export PYTHONIOENCODING=utf-8;
|
||||||
TF_CMD=$(
|
TF_CMD=$(
|
||||||
thefuck {argument_placeholder} $@
|
thefuck {argument_placeholder} $@
|
||||||
) && eval $TF_CMD;
|
) && eval "$TF_CMD";
|
||||||
unset TF_HISTORY;
|
unset TF_HISTORY;
|
||||||
export PYTHONIOENCODING=$TF_PYTHONIOENCODING;
|
export PYTHONIOENCODING=$TF_PYTHONIOENCODING;
|
||||||
{alter_history}
|
{alter_history}
|
||||||
|
@ -86,9 +86,9 @@ class Command(object):
|
|||||||
class Rule(object):
|
class Rule(object):
|
||||||
"""Rule for fixing commands."""
|
"""Rule for fixing commands."""
|
||||||
|
|
||||||
def __init__(self, name, match, get_new_command,
|
def __init__(self, name, path, match, get_new_command, post_match,
|
||||||
enabled_by_default, side_effect,
|
enabled_by_default, side_effect,
|
||||||
priority, requires_output):
|
priority, requires_output, is_post_match):
|
||||||
"""Initializes rule with given fields.
|
"""Initializes rule with given fields.
|
||||||
|
|
||||||
:type name: basestring
|
:type name: basestring
|
||||||
@ -101,31 +101,36 @@ class Rule(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.path = path
|
||||||
self.match = match
|
self.match = match
|
||||||
self.get_new_command = get_new_command
|
self.get_new_command = get_new_command
|
||||||
|
self.post_match = post_match
|
||||||
self.enabled_by_default = enabled_by_default
|
self.enabled_by_default = enabled_by_default
|
||||||
self.side_effect = side_effect
|
self.side_effect = side_effect
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.requires_output = requires_output
|
self.requires_output = requires_output
|
||||||
|
self.is_post_match = is_post_match
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, Rule):
|
if isinstance(other, Rule):
|
||||||
return ((self.name, self.match, self.get_new_command,
|
return ((self.name, self.path, self.match, self.get_new_command,
|
||||||
self.enabled_by_default, self.side_effect,
|
self.post_match, self.enabled_by_default,
|
||||||
self.priority, self.requires_output)
|
self.side_effect, self.priority,
|
||||||
== (other.name, other.match, other.get_new_command,
|
self.requires_output, self.is_post_match)
|
||||||
other.enabled_by_default, other.side_effect,
|
== (other.name, other.path, other.match, other.get_new_command,
|
||||||
other.priority, other.requires_output))
|
other.post_match, other.enabled_by_default,
|
||||||
|
other.side_effect, other.priority,
|
||||||
|
other.requires_output, other.is_post_match))
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Rule(name={}, match={}, get_new_command={}, ' \
|
return 'Rule(name={}, path={}, match={}, get_new_command={}, ' \
|
||||||
'enabled_by_default={}, side_effect={}, ' \
|
'post_match={}, enabled_by_default={}, side_effect={}, ' \
|
||||||
'priority={}, requires_output)'.format(
|
'priority={}, requires_output={}, is_post_match={})'.format(
|
||||||
self.name, self.match, self.get_new_command,
|
self.name, self.path, self.match, self.get_new_command,
|
||||||
self.enabled_by_default, self.side_effect,
|
self.post_match, self.enabled_by_default, self.side_effect,
|
||||||
self.priority, self.requires_output)
|
self.priority, self.requires_output, self.is_post_match)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_path(cls, path):
|
def from_path(cls, path):
|
||||||
@ -139,12 +144,14 @@ class Rule(object):
|
|||||||
with logs.debug_time(u'Importing rule: {};'.format(name)):
|
with logs.debug_time(u'Importing rule: {};'.format(name)):
|
||||||
rule_module = load_source(name, str(path))
|
rule_module = load_source(name, str(path))
|
||||||
priority = getattr(rule_module, 'priority', DEFAULT_PRIORITY)
|
priority = getattr(rule_module, 'priority', DEFAULT_PRIORITY)
|
||||||
return cls(name, rule_module.match,
|
return cls(name, path.as_posix(), rule_module.match,
|
||||||
rule_module.get_new_command,
|
rule_module.get_new_command,
|
||||||
|
getattr(rule_module, 'post_match', None),
|
||||||
getattr(rule_module, 'enabled_by_default', True),
|
getattr(rule_module, 'enabled_by_default', True),
|
||||||
getattr(rule_module, 'side_effect', None),
|
getattr(rule_module, 'side_effect', None),
|
||||||
settings.priority.get(name, priority),
|
settings.priority.get(name, priority),
|
||||||
getattr(rule_module, 'requires_output', True))
|
getattr(rule_module, 'requires_output', True),
|
||||||
|
getattr(rule_module, 'is_post_match', False))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
@ -192,13 +199,14 @@ class Rule(object):
|
|||||||
for n, new_command in enumerate(new_commands):
|
for n, new_command in enumerate(new_commands):
|
||||||
yield CorrectedCommand(script=new_command,
|
yield CorrectedCommand(script=new_command,
|
||||||
side_effect=self.side_effect,
|
side_effect=self.side_effect,
|
||||||
priority=(n + 1) * self.priority)
|
priority=(n + 1) * self.priority,
|
||||||
|
rule=self)
|
||||||
|
|
||||||
|
|
||||||
class CorrectedCommand(object):
|
class CorrectedCommand(object):
|
||||||
"""Corrected by rule command."""
|
"""Corrected by rule command."""
|
||||||
|
|
||||||
def __init__(self, script, side_effect, priority):
|
def __init__(self, script, side_effect, priority, rule):
|
||||||
"""Initializes instance with given fields.
|
"""Initializes instance with given fields.
|
||||||
|
|
||||||
:type script: basestring
|
:type script: basestring
|
||||||
@ -209,6 +217,7 @@ class CorrectedCommand(object):
|
|||||||
self.script = script
|
self.script = script
|
||||||
self.side_effect = side_effect
|
self.side_effect = side_effect
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
|
self.rule = rule
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Ignores `priority` field."""
|
"""Ignores `priority` field."""
|
||||||
@ -225,14 +234,19 @@ class CorrectedCommand(object):
|
|||||||
return u'CorrectedCommand(script={}, side_effect={}, priority={})'.format(
|
return u'CorrectedCommand(script={}, side_effect={}, priority={})'.format(
|
||||||
self.script, self.side_effect, self.priority)
|
self.script, self.side_effect, self.priority)
|
||||||
|
|
||||||
def _get_script(self):
|
def _get_script(self, old_cmd):
|
||||||
"""Returns fixed commands script.
|
"""Returns fixed commands script.
|
||||||
|
|
||||||
If `settings.repeat` is `True`, appends command with second attempt
|
If `settings.repeat` is `True`, appends command with second attempt
|
||||||
of running fuck in case fixed command fails again.
|
of running fuck in case fixed command fails again.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if settings.repeat:
|
if self.rule and self.rule.is_post_match:
|
||||||
|
return u'thefuck --post-match {} {} {}'.format(
|
||||||
|
self.rule.path,
|
||||||
|
shell.quote(old_cmd.script),
|
||||||
|
shell.quote(old_cmd.output))
|
||||||
|
elif settings.repeat:
|
||||||
repeat_fuck = '{} --repeat {}--force-command {}'.format(
|
repeat_fuck = '{} --repeat {}--force-command {}'.format(
|
||||||
get_alias(),
|
get_alias(),
|
||||||
'--debug ' if settings.debug else '',
|
'--debug ' if settings.debug else '',
|
||||||
@ -255,4 +269,4 @@ class CorrectedCommand(object):
|
|||||||
logs.debug(u'PYTHONIOENCODING: {}'.format(
|
logs.debug(u'PYTHONIOENCODING: {}'.format(
|
||||||
os.environ.get('PYTHONIOENCODING', '!!not-set!!')))
|
os.environ.get('PYTHONIOENCODING', '!!not-set!!')))
|
||||||
|
|
||||||
print(self._get_script())
|
print(self._get_script(old_cmd))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user