mirror of
https://github.com/nvbn/thefuck.git
synced 2025-11-01 07:32:09 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d267488520 | ||
|
|
e31124335f | ||
|
|
71a5182b9a | ||
|
|
6a096155dc | ||
|
|
5742d2d910 | ||
|
|
754bb3e21f | ||
|
|
2bbba9a0c8 | ||
|
|
b978c3793e | ||
|
|
8a83b30e73 | ||
|
|
fd20a3f832 | ||
|
|
b6ed499103 | ||
|
|
76600cf40a | ||
|
|
e62666181a | ||
|
|
c88b0792b8 | ||
|
|
06a89427e2 | ||
|
|
3a134f250d | ||
|
|
b54cdf7c49 | ||
|
|
1b05a497e8 | ||
|
|
79602383ec | ||
|
|
84c42168df | ||
|
|
f53d772ac3 | ||
|
|
93d4a4fc3a | ||
|
|
2cb23b1805 | ||
|
|
33f28cf76d | ||
|
|
6322dbd9ed |
10
README.md
10
README.md
@@ -106,13 +106,13 @@ On Ubuntu you can install `The Fuck` with:
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install python3-dev python3-pip
|
||||
pip3 install --user thefuck
|
||||
sudo pip3 install thefuck
|
||||
```
|
||||
|
||||
On other systems you can install `The Fuck` with `pip`:
|
||||
|
||||
```bash
|
||||
pip install --user thefuck
|
||||
pip install thefuck
|
||||
```
|
||||
|
||||
[Or using an OS package manager (OS X, Ubuntu, Arch).](https://github.com/nvbn/thefuck/wiki/Installation)
|
||||
@@ -139,7 +139,7 @@ alias fuck-it='export THEFUCK_REQUIRE_CONFIRMATION=False; fuck; export THEFUCK_R
|
||||
## Update
|
||||
|
||||
```bash
|
||||
pip install --user thefuck --upgrade
|
||||
pip install thefuck --upgrade
|
||||
```
|
||||
|
||||
**Aliases changed in 1.34.**
|
||||
@@ -429,9 +429,9 @@ Project License can be found [here](LICENSE.md).
|
||||
|
||||
[version-badge]: https://img.shields.io/pypi/v/thefuck.svg?label=version
|
||||
[version-link]: https://pypi.python.org/pypi/thefuck/
|
||||
[travis-badge]: https://img.shields.io/travis/nvbn/thefuck.svg
|
||||
[travis-badge]: https://travis-ci.org/nvbn/thefuck.svg?branch=master
|
||||
[travis-link]: https://travis-ci.org/nvbn/thefuck
|
||||
[appveyor-badge]: https://img.shields.io/appveyor/ci/nvbn/thefuck.svg?label=windows%20build
|
||||
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/1sskj4imj02um0gu/branch/master?svg=true
|
||||
[appveyor-link]: https://ci.appveyor.com/project/nvbn/thefuck
|
||||
[coverage-badge]: https://img.shields.io/coveralls/nvbn/thefuck.svg
|
||||
[coverage-link]: https://coveralls.io/github/nvbn/thefuck
|
||||
|
||||
2
setup.py
2
setup.py
@@ -29,7 +29,7 @@ elif (3, 0) < version < (3, 3):
|
||||
' ({}.{} detected).'.format(*version))
|
||||
sys.exit(-1)
|
||||
|
||||
VERSION = '3.16'
|
||||
VERSION = '3.20'
|
||||
|
||||
install_requires = ['psutil', 'colorama', 'six', 'decorator']
|
||||
extras_require = {':python_version<"3.4"': ['pathlib2'],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
import pytest
|
||||
from thefuck import shells
|
||||
from thefuck import conf, const
|
||||
@@ -7,7 +8,7 @@ shells.shell = shells.Generic()
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
"""Adds `--run-without-docker` argument."""
|
||||
"""Adds `--enable-functional` argument."""
|
||||
group = parser.getgroup("thefuck")
|
||||
group.addoption('--enable-functional', action="store_true", default=False,
|
||||
help="Enable functional tests")
|
||||
@@ -56,7 +57,13 @@ def set_shell(monkeypatch, request):
|
||||
def _set(cls):
|
||||
shell = cls()
|
||||
monkeypatch.setattr('thefuck.shells.shell', shell)
|
||||
request.addfinalizer()
|
||||
return shell
|
||||
|
||||
return _set
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def os_environ(monkeypatch):
|
||||
env = {'PATH': os.environ['PATH']}
|
||||
monkeypatch.setattr('os.environ', env)
|
||||
return env
|
||||
|
||||
@@ -39,7 +39,6 @@ parametrize_extensions = pytest.mark.parametrize('ext', tar_extensions)
|
||||
# (filename as typed by the user, unquoted filename, quoted filename as per shells.quote)
|
||||
parametrize_filename = pytest.mark.parametrize('filename, unquoted, quoted', [
|
||||
('foo{}', 'foo{}', 'foo{}'),
|
||||
('foo\ bar{}', 'foo bar{}', "'foo bar{}'"),
|
||||
('"foo bar{}"', 'foo bar{}', "'foo bar{}'")])
|
||||
|
||||
parametrize_script = pytest.mark.parametrize('script, fixed', [
|
||||
|
||||
@@ -64,7 +64,6 @@ def test_side_effect(zip_error, script, filename):
|
||||
@pytest.mark.parametrize('script,fixed,filename', [
|
||||
(u'unzip café', u"unzip café -d 'café'", u'café.zip'),
|
||||
(u'unzip foo', u'unzip foo -d foo', u'foo.zip'),
|
||||
(u"unzip foo\\ bar.zip", u"unzip foo\\ bar.zip -d 'foo bar'", u'foo.zip'),
|
||||
(u"unzip 'foo bar.zip'", u"unzip 'foo bar.zip' -d 'foo bar'", u'foo.zip'),
|
||||
(u'unzip foo.zip', u'unzip foo.zip -d foo', u'foo.zip')])
|
||||
def test_get_new_command(zip_error, script, fixed, filename):
|
||||
|
||||
@@ -7,7 +7,7 @@ from tests.utils import Command
|
||||
def git_not_command():
|
||||
return """git: 'brnch' is not a git command. See 'git --help'.
|
||||
|
||||
Did you mean this?
|
||||
The most similar command is
|
||||
branch
|
||||
"""
|
||||
|
||||
@@ -16,7 +16,7 @@ branch
|
||||
def git_not_command_one_of_this():
|
||||
return """git: 'st' is not a git command. See 'git --help'.
|
||||
|
||||
Did you mean one of these?
|
||||
The most similar commands are
|
||||
status
|
||||
reset
|
||||
stage
|
||||
@@ -29,7 +29,7 @@ stats
|
||||
def git_not_command_closest():
|
||||
return '''git: 'tags' is not a git command. See 'git --help'.
|
||||
|
||||
Did you mean one of these?
|
||||
The most similar commands are
|
||||
\tstage
|
||||
\ttag
|
||||
'''
|
||||
|
||||
@@ -17,7 +17,7 @@ def test_match(script):
|
||||
assert match(Command(script))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script', ['git branch' 'vimfile'])
|
||||
@pytest.mark.parametrize('script', ['git branch', 'vimfile'])
|
||||
def test_not_match(script):
|
||||
assert not match(Command(script))
|
||||
|
||||
|
||||
@@ -106,6 +106,13 @@ def test_not_match(command):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, result', [
|
||||
(Command('yarn whyy webpack', stderr=stderr('whyy')), 'yarn why webpack')])
|
||||
(Command('yarn whyy webpack', stderr=stderr('whyy')),
|
||||
'yarn why webpack'),
|
||||
(Command('yarn require lodash', stderr=stderr('require')),
|
||||
'yarn add lodash')])
|
||||
def test_get_new_command(command, result):
|
||||
assert get_new_command(command)[0] == result
|
||||
fixed_command = get_new_command(command)
|
||||
if isinstance(fixed_command, list):
|
||||
fixed_command = fixed_command[0]
|
||||
|
||||
assert fixed_command == result
|
||||
|
||||
@@ -50,8 +50,8 @@ def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, new_command', [
|
||||
@pytest.mark.parametrize('command, url', [
|
||||
(Command('yarn help clean', stdout=stdout_clean),
|
||||
open_command('https://yarnpkg.com/en/docs/cli/clean'))])
|
||||
def test_get_new_command(command, new_command):
|
||||
assert get_new_command(command) == new_command
|
||||
'https://yarnpkg.com/en/docs/cli/clean')])
|
||||
def test_get_new_command(command, url):
|
||||
assert get_new_command(command) == open_command(url)
|
||||
|
||||
@@ -18,17 +18,14 @@ class TestFish(object):
|
||||
b'man\nmath\npopd\npushd\nruby')
|
||||
return mock
|
||||
|
||||
@pytest.fixture
|
||||
def os_environ(self, monkeypatch, key, value):
|
||||
monkeypatch.setattr('os.environ', {key: value})
|
||||
|
||||
@pytest.mark.parametrize('key, value', [
|
||||
('TF_OVERRIDDEN_ALIASES', 'cut,git,sed'), # legacy
|
||||
('THEFUCK_OVERRIDDEN_ALIASES', 'cut,git,sed'),
|
||||
('THEFUCK_OVERRIDDEN_ALIASES', 'cut, git, sed'),
|
||||
('THEFUCK_OVERRIDDEN_ALIASES', ' cut,\tgit,sed\n'),
|
||||
('THEFUCK_OVERRIDDEN_ALIASES', '\ncut,\n\ngit,\tsed\r')])
|
||||
def test_get_overridden_aliases(self, shell, os_environ):
|
||||
def test_get_overridden_aliases(self, shell, os_environ, key, value):
|
||||
os_environ[key] = value
|
||||
assert shell._get_overridden_aliases() == {'cd', 'cut', 'git', 'grep',
|
||||
'ls', 'man', 'open', 'sed'}
|
||||
|
||||
|
||||
@@ -10,14 +10,6 @@ def load_source(mocker):
|
||||
return mocker.patch('thefuck.conf.load_source')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def environ(monkeypatch):
|
||||
data = {}
|
||||
monkeypatch.setattr('thefuck.conf.os.environ', data)
|
||||
return data
|
||||
|
||||
|
||||
@pytest.mark.usefixture('environ')
|
||||
def test_settings_defaults(load_source, settings):
|
||||
load_source.return_value = object()
|
||||
settings.init()
|
||||
@@ -25,7 +17,6 @@ def test_settings_defaults(load_source, settings):
|
||||
assert getattr(settings, key) == val
|
||||
|
||||
|
||||
@pytest.mark.usefixture('environ')
|
||||
class TestSettingsFromFile(object):
|
||||
def test_from_file(self, load_source, settings):
|
||||
load_source.return_value = Mock(rules=['test'],
|
||||
@@ -54,15 +45,15 @@ class TestSettingsFromFile(object):
|
||||
|
||||
@pytest.mark.usefixture('load_source')
|
||||
class TestSettingsFromEnv(object):
|
||||
def test_from_env(self, environ, settings):
|
||||
environ.update({'THEFUCK_RULES': 'bash:lisp',
|
||||
'THEFUCK_EXCLUDE_RULES': 'git:vim',
|
||||
'THEFUCK_WAIT_COMMAND': '55',
|
||||
'THEFUCK_REQUIRE_CONFIRMATION': 'true',
|
||||
'THEFUCK_NO_COLORS': 'false',
|
||||
'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15',
|
||||
'THEFUCK_WAIT_SLOW_COMMAND': '999',
|
||||
'THEFUCK_SLOW_COMMANDS': 'lein:react-native:./gradlew'})
|
||||
def test_from_env(self, os_environ, settings):
|
||||
os_environ.update({'THEFUCK_RULES': 'bash:lisp',
|
||||
'THEFUCK_EXCLUDE_RULES': 'git:vim',
|
||||
'THEFUCK_WAIT_COMMAND': '55',
|
||||
'THEFUCK_REQUIRE_CONFIRMATION': 'true',
|
||||
'THEFUCK_NO_COLORS': 'false',
|
||||
'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15',
|
||||
'THEFUCK_WAIT_SLOW_COMMAND': '999',
|
||||
'THEFUCK_SLOW_COMMANDS': 'lein:react-native:./gradlew'})
|
||||
settings.init()
|
||||
assert settings.rules == ['bash', 'lisp']
|
||||
assert settings.exclude_rules == ['git', 'vim']
|
||||
@@ -73,8 +64,8 @@ class TestSettingsFromEnv(object):
|
||||
assert settings.wait_slow_command == 999
|
||||
assert settings.slow_commands == ['lein', 'react-native', './gradlew']
|
||||
|
||||
def test_from_env_with_DEFAULT(self, environ, settings):
|
||||
environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})
|
||||
def test_from_env_with_DEFAULT(self, os_environ, settings):
|
||||
os_environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})
|
||||
settings.init()
|
||||
assert settings.rules == const.DEFAULT_RULES + ['bash', 'lisp']
|
||||
|
||||
@@ -116,15 +107,15 @@ class TestInitializeSettingsFile(object):
|
||||
(False, '/user/test/config/', '/user/test/config/thefuck'),
|
||||
(True, '~/.config', '~/.thefuck'),
|
||||
(True, '/user/test/config/', '~/.thefuck')])
|
||||
def test_get_user_dir_path(mocker, environ, settings, legacy_dir_exists,
|
||||
def test_get_user_dir_path(mocker, os_environ, settings, legacy_dir_exists,
|
||||
xdg_config_home, result):
|
||||
mocker.patch('thefuck.conf.Path.is_dir',
|
||||
return_value=legacy_dir_exists)
|
||||
|
||||
if xdg_config_home is not None:
|
||||
environ['XDG_CONFIG_HOME'] = xdg_config_home
|
||||
os_environ['XDG_CONFIG_HOME'] = xdg_config_home
|
||||
else:
|
||||
environ.pop('XDG_CONFIG_HOME', None)
|
||||
os_environ.pop('XDG_CONFIG_HOME', None)
|
||||
|
||||
path = settings._get_user_dir_path().as_posix()
|
||||
assert path == os.path.expanduser(result)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import pytest
|
||||
import json
|
||||
from six import StringIO
|
||||
from mock import MagicMock
|
||||
from thefuck.shells.generic import ShellConfiguration
|
||||
from thefuck.not_configured import main
|
||||
@@ -11,19 +13,33 @@ def usage_tracker(mocker):
|
||||
new_callable=MagicMock)
|
||||
|
||||
|
||||
def _assert_tracker_updated(usage_tracker, pid):
|
||||
@pytest.fixture(autouse=True)
|
||||
def usage_tracker_io(usage_tracker):
|
||||
io = StringIO()
|
||||
usage_tracker.return_value \
|
||||
.open.return_value \
|
||||
.__enter__.return_value \
|
||||
.write.assert_called_once_with(str(pid))
|
||||
.open.return_value \
|
||||
.__enter__.return_value = io
|
||||
return io
|
||||
|
||||
|
||||
def _change_tracker(usage_tracker, pid):
|
||||
usage_tracker.return_value.exists.return_value = True
|
||||
@pytest.fixture(autouse=True)
|
||||
def usage_tracker_exists(usage_tracker):
|
||||
usage_tracker.return_value \
|
||||
.open.return_value \
|
||||
.__enter__.return_value \
|
||||
.read.return_value = str(pid)
|
||||
.exists.return_value = True
|
||||
return usage_tracker.return_value.exists
|
||||
|
||||
|
||||
def _assert_tracker_updated(usage_tracker_io, pid):
|
||||
usage_tracker_io.seek(0)
|
||||
info = json.load(usage_tracker_io)
|
||||
assert info['pid'] == pid
|
||||
|
||||
|
||||
def _change_tracker(usage_tracker_io, pid):
|
||||
usage_tracker_io.truncate(0)
|
||||
info = {'pid': pid, 'time': 0}
|
||||
json.dump(info, usage_tracker_io)
|
||||
usage_tracker_io.seek(0)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -67,29 +83,28 @@ def test_for_generic_shell(shell, logs):
|
||||
logs.how_to_configure_alias.assert_called_once()
|
||||
|
||||
|
||||
def test_on_first_run(usage_tracker, shell_pid, logs):
|
||||
def test_on_first_run(usage_tracker_io, usage_tracker_exists, shell_pid, logs):
|
||||
shell_pid.return_value = 12
|
||||
usage_tracker.return_value.exists.return_value = False
|
||||
main()
|
||||
_assert_tracker_updated(usage_tracker, 12)
|
||||
usage_tracker_exists.return_value = False
|
||||
_assert_tracker_updated(usage_tracker_io, 12)
|
||||
logs.how_to_configure_alias.assert_called_once()
|
||||
|
||||
|
||||
def test_on_run_after_other_commands(usage_tracker, shell_pid, shell, logs):
|
||||
def test_on_run_after_other_commands(usage_tracker_io, shell_pid, shell, logs):
|
||||
shell_pid.return_value = 12
|
||||
shell.get_history.return_value = ['fuck', 'ls']
|
||||
_change_tracker(usage_tracker, 12)
|
||||
_change_tracker(usage_tracker_io, 12)
|
||||
main()
|
||||
logs.how_to_configure_alias.assert_called_once()
|
||||
|
||||
|
||||
def test_on_first_run_from_current_shell(usage_tracker, shell_pid,
|
||||
def test_on_first_run_from_current_shell(usage_tracker_io, shell_pid,
|
||||
shell, logs):
|
||||
shell.get_history.return_value = ['fuck']
|
||||
shell_pid.return_value = 12
|
||||
_change_tracker(usage_tracker, 55)
|
||||
main()
|
||||
_assert_tracker_updated(usage_tracker, 12)
|
||||
_assert_tracker_updated(usage_tracker_io, 12)
|
||||
logs.how_to_configure_alias.assert_called_once()
|
||||
|
||||
|
||||
@@ -104,21 +119,21 @@ def test_when_cant_configure_automatically(shell_pid, shell, logs):
|
||||
logs.how_to_configure_alias.assert_called_once()
|
||||
|
||||
|
||||
def test_when_already_configured(usage_tracker, shell_pid,
|
||||
def test_when_already_configured(usage_tracker_io, shell_pid,
|
||||
shell, shell_config, logs):
|
||||
shell.get_history.return_value = ['fuck']
|
||||
shell_pid.return_value = 12
|
||||
_change_tracker(usage_tracker, 12)
|
||||
_change_tracker(usage_tracker_io, 12)
|
||||
shell_config.read.return_value = 'eval $(thefuck --alias)'
|
||||
main()
|
||||
logs.already_configured.assert_called_once()
|
||||
|
||||
|
||||
def test_when_successfuly_configured(usage_tracker, shell_pid,
|
||||
shell, shell_config, logs):
|
||||
def test_when_successfully_configured(usage_tracker_io, shell_pid,
|
||||
shell, shell_config, logs):
|
||||
shell.get_history.return_value = ['fuck']
|
||||
shell_pid.return_value = 12
|
||||
_change_tracker(usage_tracker, 12)
|
||||
_change_tracker(usage_tracker_io, 12)
|
||||
shell_config.read.return_value = ''
|
||||
main()
|
||||
shell_config.write.assert_any_call('eval $(thefuck --alias)')
|
||||
|
||||
@@ -115,11 +115,10 @@ class TestCommand(object):
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def prepare(self, monkeypatch):
|
||||
monkeypatch.setattr('thefuck.types.os.environ', {})
|
||||
monkeypatch.setattr('thefuck.types.Command._wait_output',
|
||||
staticmethod(lambda *_: True))
|
||||
|
||||
def test_from_script_calls(self, Popen, settings):
|
||||
def test_from_script_calls(self, Popen, settings, os_environ):
|
||||
settings.env = {}
|
||||
assert Command.from_raw_script(
|
||||
['apt-get', 'search', 'vim']) == Command(
|
||||
@@ -129,7 +128,7 @@ class TestCommand(object):
|
||||
stdin=PIPE,
|
||||
stdout=PIPE,
|
||||
stderr=PIPE,
|
||||
env={})
|
||||
env=os_environ)
|
||||
|
||||
@pytest.mark.parametrize('script, result', [
|
||||
([''], None),
|
||||
|
||||
@@ -206,8 +206,7 @@ class TestGetValidHistoryWithoutCurrent(object):
|
||||
return_value='fuck')
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def bins(self, mocker, monkeypatch):
|
||||
monkeypatch.setattr('thefuck.conf.os.environ', {'PATH': 'path'})
|
||||
def bins(self, mocker):
|
||||
callables = list()
|
||||
for name in ['diff', 'ls', 'café']:
|
||||
bin_mock = mocker.Mock(name=name)
|
||||
|
||||
@@ -63,3 +63,5 @@ SETTINGS_HEADER = u"""# The Fuck settings file
|
||||
"""
|
||||
|
||||
ARGUMENT_PLACEHOLDER = 'THEFUCK_ARGUMENT_PLACEHOLDER'
|
||||
|
||||
CONFIGURATION_TIMEOUT = 60
|
||||
|
||||
@@ -125,5 +125,5 @@ def configured_successfully(configuration_details):
|
||||
|
||||
def version(thefuck_version, python_version):
|
||||
sys.stderr.write(
|
||||
u'The Fuck {} using Python {}'.format(thefuck_version,
|
||||
python_version))
|
||||
u'The Fuck {} using Python {}\n'.format(thefuck_version,
|
||||
python_version))
|
||||
|
||||
@@ -4,9 +4,11 @@ from .system import init_output
|
||||
init_output()
|
||||
|
||||
import os # noqa: E402
|
||||
from psutil import Process # noqa: E402
|
||||
import json # noqa: E402
|
||||
import time # noqa: E402
|
||||
import six # noqa: E402
|
||||
from . import logs # noqa: E402
|
||||
from psutil import Process # noqa: E402
|
||||
from . import logs, const # noqa: E402
|
||||
from .shells import shell # noqa: E402
|
||||
from .conf import settings # noqa: E402
|
||||
from .system import Path # noqa: E402
|
||||
@@ -30,19 +32,41 @@ def _get_not_configured_usage_tracker_path():
|
||||
|
||||
def _record_first_run():
|
||||
"""Records shell pid to tracker file."""
|
||||
with _get_not_configured_usage_tracker_path().open('w') as tracker:
|
||||
tracker.write(six.text_type(_get_shell_pid()))
|
||||
info = {'pid': _get_shell_pid(),
|
||||
'time': time.time()}
|
||||
|
||||
mode = 'wb' if six.PY2 else 'w'
|
||||
with _get_not_configured_usage_tracker_path().open(mode) as tracker:
|
||||
json.dump(info, tracker)
|
||||
|
||||
|
||||
def _get_previous_command():
|
||||
history = shell.get_history()
|
||||
|
||||
if history:
|
||||
return history[-1]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _is_second_run():
|
||||
"""Returns `True` when we know that `fuck` called second time."""
|
||||
tracker_path = _get_not_configured_usage_tracker_path()
|
||||
if not tracker_path.exists() or not shell.get_history()[-1] == 'fuck':
|
||||
if not tracker_path.exists():
|
||||
return False
|
||||
|
||||
current_pid = _get_shell_pid()
|
||||
with tracker_path.open('r') as tracker:
|
||||
return tracker.read() == six.text_type(current_pid)
|
||||
try:
|
||||
info = json.load(tracker)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
if not (isinstance(info, dict) and info.get('pid') == current_pid):
|
||||
return False
|
||||
|
||||
return (_get_previous_command() == 'fuck' or
|
||||
time.time() - info.get('time', 0) < const.CONFIGURATION_TIMEOUT)
|
||||
|
||||
|
||||
def _is_already_configured(configuration_details):
|
||||
|
||||
@@ -6,12 +6,13 @@ from thefuck.specific.git import git_support
|
||||
@git_support
|
||||
def match(command):
|
||||
return (" is not a git command. See 'git --help'." in command.stderr
|
||||
and 'Did you mean' in command.stderr)
|
||||
and ('The most similar command' in command.stderr
|
||||
or 'Did you mean' in command.stderr))
|
||||
|
||||
|
||||
@git_support
|
||||
def get_new_command(command):
|
||||
broken_cmd = re.findall(r"git: '([^']*)' is not a git command",
|
||||
command.stderr)[0]
|
||||
matched = get_all_matched_commands(command.stderr)
|
||||
matched = get_all_matched_commands(command.stderr, ['The most similar command', 'Did you mean'])
|
||||
return replace_command(command, broken_cmd, matched)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from thefuck.utils import get_all_executables, memoize, which
|
||||
from thefuck.utils import get_all_executables, memoize
|
||||
|
||||
|
||||
@memoize
|
||||
|
||||
@@ -21,7 +21,8 @@ patterns = ['permission denied',
|
||||
'edspermissionerror',
|
||||
'you don\'t have write permissions',
|
||||
'use `sudo`',
|
||||
'SudoRequiredError']
|
||||
'SudoRequiredError',
|
||||
'error: insufficient privileges']
|
||||
|
||||
|
||||
def match(command):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from thefuck.utils import for_app, eager, replace_command
|
||||
from thefuck.utils import for_app, eager, replace_command, replace_argument
|
||||
|
||||
regex = re.compile(r'error Command "(.*)" not found.')
|
||||
|
||||
@@ -10,6 +10,9 @@ def match(command):
|
||||
return regex.findall(command.stderr)
|
||||
|
||||
|
||||
npm_commands = {'require': 'add'}
|
||||
|
||||
|
||||
@eager
|
||||
def _get_all_tasks():
|
||||
proc = Popen(['yarn', '--help'], stdout=PIPE)
|
||||
@@ -27,5 +30,9 @@ def _get_all_tasks():
|
||||
|
||||
def get_new_command(command):
|
||||
misspelled_task = regex.findall(command.stderr)[0]
|
||||
tasks = _get_all_tasks()
|
||||
return replace_command(command, misspelled_task, tasks)
|
||||
if misspelled_task in npm_commands:
|
||||
yarn_command = npm_commands[misspelled_task]
|
||||
return replace_argument(command.script, misspelled_task, yarn_command)
|
||||
else:
|
||||
tasks = _get_all_tasks()
|
||||
return replace_command(command, misspelled_task, tasks)
|
||||
|
||||
@@ -11,12 +11,14 @@ class Bash(Generic):
|
||||
return '''
|
||||
function {name} () {{
|
||||
TF_PREVIOUS=$(fc -ln -1);
|
||||
TF_PYTHONIOENCODING=$PYTHONIOENCODING;
|
||||
export TF_ALIAS={name};
|
||||
export TF_SHELL_ALIASES=$(alias);
|
||||
export PYTHONIOENCODING=utf-8;
|
||||
TF_CMD=$(
|
||||
TF_ALIAS={name}
|
||||
TF_SHELL_ALIASES=$(alias)
|
||||
PYTHONIOENCODING=utf-8
|
||||
thefuck $TF_PREVIOUS {argument_placeholder} $@
|
||||
) && eval $TF_CMD;
|
||||
export PYTHONIOENCODING=$TF_PYTHONIOENCODING;
|
||||
{alter_history}
|
||||
}}
|
||||
'''.format(
|
||||
|
||||
@@ -77,7 +77,7 @@ class Generic(object):
|
||||
encoded = self.encode_utf8(command)
|
||||
|
||||
try:
|
||||
splitted = shlex.split(encoded)
|
||||
splitted = [s.replace("??", "\ ") for s in shlex.split(encoded.replace('\ ', '??'))]
|
||||
except ValueError:
|
||||
splitted = encoded.split(' ')
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import sys
|
||||
from .conf import settings
|
||||
from .exceptions import NoRuleMatched
|
||||
from .system import get_key
|
||||
from .utils import get_alias
|
||||
from . import logs, const
|
||||
|
||||
|
||||
@@ -69,7 +70,8 @@ def select_command(corrected_commands):
|
||||
try:
|
||||
selector = CommandSelector(corrected_commands)
|
||||
except NoRuleMatched:
|
||||
logs.failed('No fucks given')
|
||||
logs.failed('No fucks given' if get_alias() == 'fuck'
|
||||
else 'Nothing found')
|
||||
return
|
||||
|
||||
if not settings.require_confirmation:
|
||||
|
||||
@@ -141,12 +141,17 @@ def eager(fn, *args, **kwargs):
|
||||
|
||||
@eager
|
||||
def get_all_matched_commands(stderr, separator='Did you mean'):
|
||||
if not isinstance(separator, list):
|
||||
separator = [separator]
|
||||
should_yield = False
|
||||
for line in stderr.split('\n'):
|
||||
if separator in line:
|
||||
should_yield = True
|
||||
elif should_yield and line:
|
||||
yield line.strip()
|
||||
for sep in separator:
|
||||
if sep in line:
|
||||
should_yield = True
|
||||
break
|
||||
else:
|
||||
if should_yield and line:
|
||||
yield line.strip()
|
||||
|
||||
|
||||
def replace_command(command, broken, matched):
|
||||
|
||||
Reference in New Issue
Block a user