mirror of
https://github.com/nvbn/thefuck.git
synced 2025-11-01 23:51:59 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5e333b727 | ||
|
|
1755bcd1b5 | ||
|
|
f773b57bea | ||
|
|
5866ea8433 | ||
|
|
729508e581 | ||
|
|
1b7c8b5498 | ||
|
|
9b6cd0cd7b | ||
|
|
77ea630d84 | ||
|
|
917c6ac887 | ||
|
|
8bea63eb23 | ||
|
|
2bf21d9f0e | ||
|
|
e2f66cb26b | ||
|
|
ff1ee979f0 | ||
|
|
25343dbfb4 | ||
|
|
ea3671f98c | ||
|
|
4584903beb | ||
|
|
990ba57159 | ||
|
|
16c110823d | ||
|
|
01418526b4 | ||
|
|
d2845a0d2e | ||
|
|
42853f41bb | ||
|
|
5f11ecc4f8 | ||
|
|
4bd4c0f731 | ||
|
|
b8c5433dc4 | ||
|
|
e2883430bc | ||
|
|
a4b690369c | ||
|
|
6bbd680e56 | ||
|
|
e5f8e9c0de | ||
|
|
ebf1ea88f5 | ||
|
|
d2b0b6e8ec | ||
|
|
561eb12c08 | ||
|
|
ed38fedf26 | ||
|
|
15bcd7f03f | ||
|
|
9e39bcd55c | ||
|
|
cfa73f10d6 | ||
|
|
c3709682d5 | ||
|
|
96f7e53aa2 | ||
|
|
d1f55603fe |
19
.travis.yml
19
.travis.yml
@@ -5,17 +5,13 @@ python:
|
||||
- "3.4"
|
||||
- "3.3"
|
||||
- "2.7"
|
||||
services:
|
||||
- docker
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- fish-shell/release-2
|
||||
packages:
|
||||
- bash
|
||||
- zsh
|
||||
- fish
|
||||
- tcsh
|
||||
- pandoc
|
||||
- git
|
||||
- python-commandnotfound
|
||||
- python3-commandnotfound
|
||||
install:
|
||||
- pip install -U pip
|
||||
- pip install -U coveralls
|
||||
@@ -24,5 +20,8 @@ install:
|
||||
- rm -rf build
|
||||
script:
|
||||
- export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1}
|
||||
- coverage run --source=thefuck,tests -m py.test -v --capture=sys --run-without-docker --enable-functional
|
||||
after_success: coveralls
|
||||
- export RUN_TESTS="coverage run --source=thefuck,tests -m py.test -v --capture=sys"
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then $RUN_TESTS --enable-functional; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 3.5 ]]; then $RUN_TESTS; fi
|
||||
after_success:
|
||||
- coveralls
|
||||
|
||||
19
README.md
19
README.md
@@ -94,17 +94,23 @@ Reading package lists... Done
|
||||
- pip
|
||||
- python-dev
|
||||
|
||||
## Installation [*experimental*]
|
||||
## Installation
|
||||
|
||||
On Ubuntu and OS X you can install `The Fuck` with installation script:
|
||||
On OS X you can install `The Fuck` with [Homebrew][homebrew]:
|
||||
|
||||
```bash
|
||||
wget -O - https://raw.githubusercontent.com/nvbn/thefuck/master/install.sh | sh - && $0
|
||||
brew install thefuck
|
||||
```
|
||||
|
||||
## Manual installation
|
||||
On Ubuntu you can install `The Fuck` with:
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install python3-dev python3-pip
|
||||
sudo -H pip3 install thefuck
|
||||
```
|
||||
|
||||
Install `The Fuck` with `pip`:
|
||||
On other systems you can install `The Fuck` with `pip`:
|
||||
|
||||
```bash
|
||||
sudo -H pip install thefuck
|
||||
@@ -169,6 +175,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
|
||||
* `git_pull_clone` – clones instead of pulling when the repo does not exist;
|
||||
* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
|
||||
* `git_push_pull` – runs `git pull` when `push` was rejected;
|
||||
* `git_rebase_no_changes` – runs `git rebase --skip` instead of `git rebase --continue` when there are no changes;
|
||||
* `git_rm_recursive` – adds `-r` when you try to `rm` a directory;
|
||||
* `git_remote_seturl_add` – runs `git remote add` when `git remote set_url` on nonexistant remote;
|
||||
* `git_stash` – stashes you local modifications before rebasing or switching branch;
|
||||
@@ -184,6 +191,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
|
||||
* `javac` – appends missing `.java` when compiling Java files;
|
||||
* `lein_not_task` – fixes wrong `lein` tasks like `lein rpl`;
|
||||
* `ln_no_hard_link` – catches hard link creation on directories, suggest symbolic link;
|
||||
* `ln_s_order` – fixes `ln -s` arguments order;
|
||||
* `ls_lah` – adds `-lah` to `ls`;
|
||||
* `man` – changes manual section;
|
||||
* `man_no_space` – fixes man commands without spaces, for example `mandiff`;
|
||||
@@ -373,3 +381,4 @@ Project License can be found [here](LICENSE.md).
|
||||
[coverage-link]: https://coveralls.io/github/nvbn/thefuck
|
||||
[license-badge]: https://img.shields.io/badge/license-MIT-007EC7.svg
|
||||
[examples-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif
|
||||
[homebrew]: http://brew.sh/
|
||||
|
||||
61
install.sh
61
install.sh
@@ -1,61 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
should_add_alias () {
|
||||
[ -f $1 ] && ! grep -q thefuck $1
|
||||
}
|
||||
|
||||
installed () {
|
||||
hash $1 2>/dev/null
|
||||
}
|
||||
|
||||
install_thefuck () {
|
||||
# Install OS dependencies:
|
||||
if installed apt-get; then
|
||||
# Debian/Ubuntu:
|
||||
sudo apt-get update -yy
|
||||
sudo apt-get install -yy python-pip python-dev command-not-found python-gdbm
|
||||
|
||||
if [ -n "$(apt-cache search python-commandnotfound)" ]; then
|
||||
# In case of different python versions:
|
||||
sudo apt-get install -yy python-commandnotfound
|
||||
fi
|
||||
else
|
||||
if installed brew; then
|
||||
# OS X:
|
||||
brew update
|
||||
brew install python
|
||||
else
|
||||
# Generic way:
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
sudo python get-pip.py
|
||||
rm get-pip.py
|
||||
fi
|
||||
fi
|
||||
|
||||
# thefuck requires fresh versions of setuptools and pip:
|
||||
sudo pip install -U pip setuptools
|
||||
sudo pip install -U thefuck
|
||||
|
||||
# Setup aliases:
|
||||
if should_add_alias ~/.bashrc; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.bashrc
|
||||
fi
|
||||
|
||||
if should_add_alias ~/.bash_profile; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.bash_profile
|
||||
fi
|
||||
|
||||
if should_add_alias ~/.zshrc; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.zshrc
|
||||
fi
|
||||
|
||||
if should_add_alias ~/.config/fish/config.fish; then
|
||||
thefuck --alias >> ~/.config/fish/config.fish
|
||||
fi
|
||||
|
||||
if should_add_alias ~/.tcshrc; then
|
||||
echo 'eval `thefuck --alias`' >> ~/.tcshrc
|
||||
fi
|
||||
}
|
||||
|
||||
install_thefuck
|
||||
echo "Installation script is deprecated!"
|
||||
echo "For installation instruction please visit https://github.com/nvbn/thefuck"
|
||||
|
||||
4
setup.py
4
setup.py
@@ -26,10 +26,10 @@ elif (3, 0) < version < (3, 3):
|
||||
' ({}.{} detected).'.format(*version))
|
||||
sys.exit(-1)
|
||||
|
||||
VERSION = '3.9'
|
||||
VERSION = '3.10'
|
||||
|
||||
install_requires = ['psutil', 'colorama', 'six', 'decorator']
|
||||
extras_require = {':python_version<"3.4"': ['pathlib'],
|
||||
extras_require = {':python_version<"3.4"': ['pathlib2'],
|
||||
":sys_platform=='win32'": ['win_unicode_console']}
|
||||
|
||||
setup(name='thefuck',
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
from pathlib import Path
|
||||
try:
|
||||
from pathlib import Path
|
||||
except ImportError:
|
||||
from pathlib2 import Path
|
||||
import pytest
|
||||
from thefuck import shells
|
||||
from thefuck import conf, const
|
||||
|
||||
@@ -29,25 +29,25 @@ def proc(request, spawnu):
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.skip_without_docker
|
||||
@pytest.mark.once_without_docker
|
||||
def test_with_confirmation(proc, TIMEOUT):
|
||||
with_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.skip_without_docker
|
||||
@pytest.mark.once_without_docker
|
||||
def test_select_command_with_arrows(proc, TIMEOUT):
|
||||
select_command_with_arrows(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.skip_without_docker
|
||||
@pytest.mark.once_without_docker
|
||||
def test_refuse_with_confirmation(proc, TIMEOUT):
|
||||
refuse_with_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.skip_without_docker
|
||||
@pytest.mark.once_without_docker
|
||||
def test_without_confirmation(proc, TIMEOUT):
|
||||
without_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import pytest
|
||||
from thefuck.utils import get_installation_info
|
||||
|
||||
envs = ((u'bash', 'thefuck/ubuntu-bash', u'''
|
||||
FROM ubuntu:latest
|
||||
RUN apt-get update
|
||||
RUN apt-get install -yy bash
|
||||
'''), (u'bash', 'thefuck/generic-bash', u'''
|
||||
FROM fedora:latest
|
||||
RUN dnf install -yy python-devel sudo wget gcc
|
||||
'''))
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.skip_without_docker
|
||||
@pytest.mark.parametrize('shell, tag, dockerfile', envs)
|
||||
def test_installation(spawnu, shell, TIMEOUT, tag, dockerfile):
|
||||
proc = spawnu(tag, dockerfile, shell)
|
||||
proc.sendline(u'cat /src/install.sh | sh - && $0')
|
||||
proc.sendline(u'thefuck --version')
|
||||
version = get_installation_info().version
|
||||
assert proc.expect([TIMEOUT, u'thefuck {}'.format(version)],
|
||||
timeout=600)
|
||||
proc.sendline(u'fuck')
|
||||
assert proc.expect([TIMEOUT, u'No fucks given'])
|
||||
@@ -40,7 +40,7 @@ def plot(proc, TIMEOUT):
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.skip_without_docker
|
||||
@pytest.mark.once_without_docker
|
||||
@pytest.mark.benchmark(min_rounds=10)
|
||||
def test_performance(spawnu, TIMEOUT, benchmark):
|
||||
proc = spawnu(u'thefuck/ubuntu-python3-bash-performance',
|
||||
|
||||
@@ -25,6 +25,7 @@ def proc(request, spawnu, run_without_docker):
|
||||
if not run_without_docker:
|
||||
proc.sendline(u'pip install /src')
|
||||
proc.sendline(u'tcsh')
|
||||
proc.sendline(u'setenv PYTHONIOENCODING utf8')
|
||||
proc.sendline(u'eval `thefuck --alias`')
|
||||
return proc
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ def test_select_command_with_arrows(proc, TIMEOUT):
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.skip_without_docker
|
||||
@pytest.mark.once_without_docker
|
||||
def test_refuse_with_confirmation(proc, TIMEOUT):
|
||||
refuse_with_confirmation(proc, TIMEOUT)
|
||||
history_not_changed(proc, TIMEOUT)
|
||||
|
||||
28
tests/rules/test_git_rebase_no_changes.py
Normal file
28
tests/rules/test_git_rebase_no_changes.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import pytest
|
||||
from thefuck.rules.git_rebase_no_changes import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stdout():
|
||||
return '''Applying: Test commit
|
||||
No changes - did you forget to use 'git add'?
|
||||
If there is nothing left to stage, chances are that something else
|
||||
already introduced the same changes; you might want to skip this patch.
|
||||
|
||||
When you have resolved this problem, run "git rebase --continue".
|
||||
If you prefer to skip this patch, run "git rebase --skip" instead.
|
||||
To check out the original branch and stop rebasing, run "git rebase --abort".
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def test_match(stdout):
|
||||
assert match(Command('git rebase --continue', stdout=stdout))
|
||||
assert not match(Command('git rebase --continue'))
|
||||
assert not match(Command('git rebase --skip'))
|
||||
|
||||
|
||||
def test_get_new_command(stdout):
|
||||
assert (get_new_command(Command('git rebase --continue', stdout=stdout)) ==
|
||||
'git rebase --skip')
|
||||
@@ -20,7 +20,7 @@ def test_match(script, stderr):
|
||||
("ln a b", "... hard link"),
|
||||
("sudo ln a b", "... hard link"),
|
||||
("a b", error)])
|
||||
def test_assert_not_match(script, stderr):
|
||||
def test_not_match(script, stderr):
|
||||
command = Command(script, stderr=stderr)
|
||||
assert not match(command)
|
||||
|
||||
|
||||
41
tests/rules/test_ln_s_order.py
Normal file
41
tests/rules/test_ln_s_order.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import pytest
|
||||
from thefuck.rules.ln_s_order import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def file_exists(mocker):
|
||||
return mocker.patch('os.path.exists', return_value=True)
|
||||
|
||||
|
||||
get_stderr = "ln: failed to create symbolic link '{}': File exists".format
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('file_exists')
|
||||
@pytest.mark.parametrize('script', [
|
||||
'ln -s dest source',
|
||||
'ln dest -s source',
|
||||
'ln dest source -s'])
|
||||
def test_match(script):
|
||||
stderr = get_stderr('source')
|
||||
assert match(Command(script, stderr=stderr))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, stderr, exists', [
|
||||
('ln dest source', get_stderr('source'), True),
|
||||
('ls -s dest source', get_stderr('source'), True),
|
||||
('ln -s dest source', '', True),
|
||||
('ln -s dest source', get_stderr('source'), False)])
|
||||
def test_not_match(file_exists, script, stderr, exists):
|
||||
file_exists.return_value = exists
|
||||
assert not match(Command(script, stderr=stderr))
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('file_exists')
|
||||
@pytest.mark.parametrize('script, result', [
|
||||
('ln -s dest source', 'ln -s source dest'),
|
||||
('ln dest -s source', 'ln -s source dest'),
|
||||
('ln dest source -s', 'ln source -s dest')])
|
||||
def test_match(script, result):
|
||||
stderr = get_stderr('source')
|
||||
assert get_new_command(Command(script, stderr=stderr)) == result
|
||||
@@ -74,6 +74,14 @@ class TestFish(object):
|
||||
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
||||
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
||||
|
||||
def test_app_alias_alter_history(self, settings, shell):
|
||||
settings.alter_history = True
|
||||
assert 'history --delete' in shell.app_alias('FUCK')
|
||||
assert 'history --merge' in shell.app_alias('FUCK')
|
||||
settings.alter_history = False
|
||||
assert 'history --delete' not in shell.app_alias('FUCK')
|
||||
assert 'history --merge' not in shell.app_alias('FUCK')
|
||||
|
||||
def test_get_history(self, history_lines, shell):
|
||||
history_lines(['- cmd: ls', ' when: 1432613911',
|
||||
'- cmd: rm', ' when: 1432613916'])
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
try:
|
||||
from pathlib import Path
|
||||
pathlib_name = 'pathlib'
|
||||
except ImportError:
|
||||
from pathlib2 import Path
|
||||
pathlib_name = 'pathlib2'
|
||||
from thefuck import corrector, const
|
||||
from tests.utils import Rule, Command, CorrectedCommand
|
||||
from thefuck.corrector import get_corrected_commands, organize_commands
|
||||
@@ -11,7 +16,7 @@ class TestGetRules(object):
|
||||
@pytest.fixture
|
||||
def glob(self, mocker):
|
||||
results = {}
|
||||
mocker.patch('pathlib.Path.glob',
|
||||
mocker.patch(pathlib_name + '.Path.glob',
|
||||
new_callable=lambda: lambda *_: results.pop('value', []))
|
||||
return lambda value: results.update({'value': value})
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
import os
|
||||
from subprocess import PIPE
|
||||
from mock import Mock
|
||||
from pathlib import Path
|
||||
try:
|
||||
from pathlib import Path
|
||||
except ImportError:
|
||||
from pathlib2 import Path
|
||||
import pytest
|
||||
from tests.utils import CorrectedCommand, Rule, Command
|
||||
from thefuck import const
|
||||
|
||||
@@ -25,15 +25,16 @@ def test_read_actions(patch_get_key):
|
||||
# Ignored:
|
||||
'x', 'y',
|
||||
# Up:
|
||||
const.KEY_UP,
|
||||
const.KEY_UP, 'k',
|
||||
# Down:
|
||||
const.KEY_DOWN,
|
||||
const.KEY_DOWN, 'j',
|
||||
# Ctrl+C:
|
||||
const.KEY_CTRL_C])
|
||||
assert list(islice(ui.read_actions(), 5)) \
|
||||
const.KEY_CTRL_C, 'q'])
|
||||
assert list(islice(ui.read_actions(), 8)) \
|
||||
== [const.ACTION_SELECT, const.ACTION_SELECT,
|
||||
const.ACTION_PREVIOUS, const.ACTION_NEXT,
|
||||
const.ACTION_ABORT]
|
||||
const.ACTION_PREVIOUS, const.ACTION_PREVIOUS,
|
||||
const.ACTION_NEXT, const.ACTION_NEXT,
|
||||
const.ACTION_ABORT, const.ACTION_ABORT]
|
||||
|
||||
|
||||
def test_command_selector():
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
import warnings
|
||||
from mock import Mock
|
||||
import six
|
||||
from thefuck.utils import default_settings, \
|
||||
@@ -199,7 +202,8 @@ class TestCompatibilityCall(object):
|
||||
assert settings == _settings
|
||||
return True
|
||||
|
||||
assert compatibility_call(match, Command())
|
||||
with pytest.warns(UserWarning):
|
||||
assert compatibility_call(match, Command())
|
||||
|
||||
def test_get_new_command(self):
|
||||
def get_new_command(command):
|
||||
@@ -214,7 +218,8 @@ class TestCompatibilityCall(object):
|
||||
assert settings == _settings
|
||||
return True
|
||||
|
||||
assert compatibility_call(get_new_command, Command())
|
||||
with pytest.warns(UserWarning):
|
||||
assert compatibility_call(get_new_command, Command())
|
||||
|
||||
def test_side_effect(self):
|
||||
def side_effect(command, new_command):
|
||||
@@ -229,15 +234,22 @@ class TestCompatibilityCall(object):
|
||||
assert settings == _settings
|
||||
return True
|
||||
|
||||
assert compatibility_call(side_effect, Command(), Command())
|
||||
with pytest.warns(UserWarning):
|
||||
assert compatibility_call(side_effect, Command(), Command())
|
||||
|
||||
|
||||
class TestGetValidHistoryWithoutCurrent(object):
|
||||
@pytest.yield_fixture(autouse=True)
|
||||
def fail_on_warning(self):
|
||||
warnings.simplefilter('error')
|
||||
yield
|
||||
warnings.resetwarnings()
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def history(self, mocker):
|
||||
return mocker.patch('thefuck.shells.shell.get_history',
|
||||
return_value=['le cat', 'fuck', 'ls cat',
|
||||
'diff x', 'nocommand x'])
|
||||
'diff x', 'nocommand x', u'café ô'])
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def alias(self, mocker):
|
||||
@@ -245,14 +257,22 @@ class TestGetValidHistoryWithoutCurrent(object):
|
||||
return_value='fuck')
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def callables(self, mocker):
|
||||
return mocker.patch('thefuck.utils.get_all_executables',
|
||||
return_value=['diff', 'ls'])
|
||||
def bins(self, mocker, monkeypatch):
|
||||
monkeypatch.setattr('thefuck.conf.os.environ', {'PATH': 'path'})
|
||||
callables = list()
|
||||
for name in ['diff', 'ls', 'café']:
|
||||
bin_mock = mocker.Mock(name=name)
|
||||
bin_mock.configure_mock(name=name, is_dir=lambda: False)
|
||||
callables.append(bin_mock)
|
||||
path_mock = mocker.Mock(iterdir=mocker.Mock(return_value=callables))
|
||||
return mocker.patch('thefuck.utils.Path', return_value=path_mock)
|
||||
|
||||
@pytest.mark.parametrize('script, result', [
|
||||
('le cat', ['ls cat', 'diff x']),
|
||||
('diff x', ['ls cat']),
|
||||
('fuck', ['ls cat', 'diff x'])])
|
||||
('le cat', ['ls cat', 'diff x', u'café ô']),
|
||||
('diff x', ['ls cat', u'café ô']),
|
||||
('fuck', ['ls cat', 'diff x', u'café ô']),
|
||||
(u'cafe ô', ['ls cat', 'diff x', u'café ô']),
|
||||
])
|
||||
def test_get_valid_history_without_current(self, script, result):
|
||||
command = Command(script=script)
|
||||
assert get_valid_history_without_current(command) == result
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from imp import load_source
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
try:
|
||||
from pathlib import Path
|
||||
except ImportError:
|
||||
from pathlib2 import Path
|
||||
from six import text_type
|
||||
from . import const
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
from pathlib import Path
|
||||
try:
|
||||
from pathlib import Path
|
||||
except ImportError:
|
||||
from pathlib2 import Path
|
||||
from .conf import settings
|
||||
from .types import Rule
|
||||
from . import logs
|
||||
|
||||
@@ -33,6 +33,8 @@ def fix_command():
|
||||
|
||||
if selected_command:
|
||||
selected_command.run(command)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_alias(entry_point=True):
|
||||
|
||||
15
thefuck/rules/git_rebase_no_changes.py
Normal file
15
thefuck/rules/git_rebase_no_changes.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from thefuck.specific.git import git_support
|
||||
|
||||
|
||||
@git_support
|
||||
def match(command):
|
||||
return ({'rebase', '--continue'}.issubset(command.script_parts) and
|
||||
'No changes - did you forget to use \'git add\'?' in command.stdout)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
return 'git rebase --skip'
|
||||
|
||||
|
||||
enabled_by_default = True
|
||||
requires_output = True
|
||||
@@ -15,7 +15,7 @@ from thefuck.specific.sudo import sudo_support
|
||||
@sudo_support
|
||||
def match(command):
|
||||
return (command.stderr.endswith("hard link not allowed for directory") and
|
||||
command.script.startswith("ln "))
|
||||
command.script_parts[0] == 'ln')
|
||||
|
||||
|
||||
@sudo_support
|
||||
|
||||
26
thefuck/rules/ln_s_order.py
Normal file
26
thefuck/rules/ln_s_order.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
from thefuck.specific.sudo import sudo_support
|
||||
|
||||
|
||||
def _get_destination(script_parts):
|
||||
"""When arguments order is wrong first argument will be destination."""
|
||||
for part in script_parts:
|
||||
if part not in {'ln', '-s', '--symbolic'} and os.path.exists(part):
|
||||
return part
|
||||
|
||||
|
||||
@sudo_support
|
||||
def match(command):
|
||||
return (command.script_parts[0] == 'ln'
|
||||
and {'-s', '--symbolic'}.intersection(command.script_parts)
|
||||
and 'File exists' in command.stderr
|
||||
and _get_destination(command.script_parts))
|
||||
|
||||
|
||||
@sudo_support
|
||||
def get_new_command(command):
|
||||
destination = _get_destination(command.script_parts)
|
||||
parts = command.script_parts[:]
|
||||
parts.remove(destination)
|
||||
parts.append(destination)
|
||||
return ' '.join(parts)
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
import sys
|
||||
import six
|
||||
from .. import logs
|
||||
from ..conf import settings
|
||||
from ..utils import DEVNULL, memoize, cache
|
||||
from .generic import Generic
|
||||
|
||||
@@ -18,17 +19,20 @@ class Fish(Generic):
|
||||
return default
|
||||
|
||||
def app_alias(self, fuck):
|
||||
if settings.alter_history:
|
||||
alter_history = (' history --delete $fucked_up_command\n'
|
||||
' history --merge ^ /dev/null\n')
|
||||
else:
|
||||
alter_history = ''
|
||||
# It is VERY important to have the variables declared WITHIN the alias
|
||||
return ('function {0} -d "Correct your previous console command"\n'
|
||||
' set -l fucked_up_command $history[1]\n'
|
||||
' env TF_ALIAS={0} PYTHONIOENCODING=utf-8'
|
||||
' thefuck $fucked_up_command | read -l unfucked_command\n'
|
||||
' if [ "$unfucked_command" != "" ]\n'
|
||||
' eval $unfucked_command\n'
|
||||
' history --delete $fucked_up_command\n'
|
||||
' history --merge ^ /dev/null\n'
|
||||
' eval $unfucked_command\n{1}'
|
||||
' end\n'
|
||||
'end').format(fuck)
|
||||
'end').format(fuck, alter_history)
|
||||
|
||||
@memoize
|
||||
@cache('.config/fish/config.fish', '.config/fish/functions')
|
||||
@@ -46,10 +50,6 @@ class Fish(Generic):
|
||||
else:
|
||||
return command_script
|
||||
|
||||
def from_shell(self, command_script):
|
||||
"""Prepares command before running in app."""
|
||||
return self._expand_aliases(command_script)
|
||||
|
||||
def _get_history_file_name(self):
|
||||
return os.path.expanduser('~/.config/fish/fish_history')
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ from .generic import Generic
|
||||
class Powershell(Generic):
|
||||
def app_alias(self, fuck):
|
||||
return 'function ' + fuck + ' { \n' \
|
||||
' $fuck = $(thefuck (Get-History -Count 1).CommandLine)\n' \
|
||||
' $fuck = $(thefuck (Get-History -Count 1).CommandLine);\n' \
|
||||
' if (-not [string]::IsNullOrWhiteSpace($fuck)) {\n' \
|
||||
' if ($fuck.StartsWith("echo")) { $fuck = $fuck.Substring(5) }\n' \
|
||||
' else { iex "$fuck" }\n' \
|
||||
' if ($fuck.StartsWith("echo")) { $fuck = $fuck.Substring(5); }\n' \
|
||||
' else { iex "$fuck"; }\n' \
|
||||
' }\n' \
|
||||
'}\n'
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ def read_actions():
|
||||
yield const.ACTION_PREVIOUS
|
||||
elif key in (const.KEY_DOWN, 'j'):
|
||||
yield const.ACTION_NEXT
|
||||
elif key == const.KEY_CTRL_C:
|
||||
elif key in (const.KEY_CTRL_C, 'q'):
|
||||
yield const.ACTION_ABORT
|
||||
elif key in ('\n', '\r'):
|
||||
yield const.ACTION_SELECT
|
||||
|
||||
@@ -10,7 +10,10 @@ from decorator import decorator
|
||||
from difflib import get_close_matches
|
||||
from functools import wraps
|
||||
from inspect import getargspec
|
||||
from pathlib import Path
|
||||
try:
|
||||
from pathlib import Path
|
||||
except ImportError:
|
||||
from pathlib2 import Path
|
||||
from warnings import warn
|
||||
|
||||
DEVNULL = open(os.devnull, 'w')
|
||||
@@ -110,7 +113,7 @@ def get_all_executables():
|
||||
tf_entry_points = get_installation_info().get_entry_map()\
|
||||
.get('console_scripts', {})\
|
||||
.keys()
|
||||
bins = [exe.name
|
||||
bins = [exe.name.decode('utf8') if six.PY2 else exe.name
|
||||
for path in os.environ.get('PATH', '').split(':')
|
||||
for exe in _safe(lambda: list(Path(path).iterdir()), [])
|
||||
if not _safe(exe.is_dir, True)
|
||||
|
||||
Reference in New Issue
Block a user