1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-01 15:42:06 +00:00

Compare commits

...

108 Commits
1.43 ... 1.47

Author SHA1 Message Date
nvbn
fbf7b91005 Bump to 1.47 2015-07-07 16:39:40 +03:00
nvbn
3d842fe6eb #N/A Fix setup.py 2015-07-07 16:39:21 +03:00
Vladimir Iakovlev
17d359b43f Merge pull request #281 from scorphus/mercurial
improve(rules): add mercurial (hg) support
2015-07-07 16:36:06 +03:00
nvbn
22503cdb94 #279 Fix merge 2015-07-07 16:34:30 +03:00
nvbn
a8de919300 Merge branch 'master' of https://github.com/mkreder/thefuck into mkreder-master 2015-07-07 16:33:58 +03:00
nvbn
fac18de242 #267 Compare version_info with a tuple 2015-07-07 16:33:41 +03:00
Vladimir Iakovlev
26fc18dfe4 Merge pull request #278 from mcarton/sed
Add a sed_unterminated_s rule
2015-07-07 16:30:42 +03:00
Vladimir Iakovlev
0fb5c9a228 Merge pull request #277 from mcarton/fix-sudo
Fix the pacman rule with `sudo`
2015-07-07 16:30:02 +03:00
Vladimir Iakovlev
04a342bbc7 Merge pull request #276 from mcarton/tmux
Add a tmux rule
2015-07-07 16:29:15 +03:00
Vladimir Iakovlev
669fbff6ce Merge pull request #272 from scorphus/issue-271-ls-lah
fix(rules.ls_lah): make sure script starts with ls
2015-07-07 16:28:49 +03:00
Vladimir Iakovlev
e2542915e4 Merge pull request #267 from SanketDG/version_check
add python version testing in setup.py
2015-07-07 16:28:28 +03:00
Vladimir Iakovlev
1b08a7dcb6 Merge pull request #265 from mcarton/open
Open
2015-07-07 16:27:33 +03:00
Pablo Santiago Blum de Aguiar
8d77a2d528 improve(rules): add mercurial (hg) support
Fix #269
2015-07-06 21:37:31 -03:00
Matias Kreder
18ea4272ab change deps to install_requires #2 2015-07-04 12:26:56 -03:00
Matias Kreder
a75c99eb12 change deps to install_requires 2015-07-04 12:24:09 -03:00
mcarton
f3cdfbdbdb Add a sed_unterminated_s rule 2015-07-04 17:10:11 +02:00
Matias Kreder
30082bc9a9 fixed dependency problem, on python 3.4.0 pathlib is included in the python distribution so it must not be included in the requirements for thefuck otherwise it wont run at startup 2015-07-04 11:55:28 -03:00
mcarton
3822f62d90 Add a tmux rule 2015-07-04 14:17:33 +02:00
Pablo Santiago Blum de Aguiar
25cc98a21a fix(rules.ls_lah): make sure script starts with ls
Fix #271
2015-07-03 14:24:45 -03:00
SanketDG
409d839e92 add python version testing in setup.py 2015-07-03 00:29:07 +05:30
mcarton
51b5dd0460 Fix the pacman rule with sudo
Does not use @sudo_support as this does not place 'sudo' at the right
position.
2015-06-28 22:10:34 +02:00
Vladimir Iakovlev
cb33c912e5 Merge pull request #264 from mcarton/cleanup
Cleanup
2015-06-26 17:30:14 +03:00
Vladimir Iakovlev
470d66eeb2 Merge pull request #263 from mcarton/test.py
Add a test.py rule
2015-06-26 17:28:24 +03:00
mcarton
5552fd3dc9 s/compile/execute when talking about Python
The word 'compile' is just misleading here.
2015-06-26 14:54:33 +02:00
mcarton
ab55c1cccb Add other flavors of open command 2015-06-26 14:52:34 +02:00
mcarton
7173e0dbad Use spaces instead of tabs
The is more common in python and follows other rules usage.
2015-06-26 14:50:01 +02:00
mcarton
369ea7ff46 Add a test.py rule 2015-06-26 14:05:18 +02:00
mcarton
40fe604adc Replace use of '&&' by shells.and_ 2015-06-26 13:58:50 +02:00
mcarton
ef504b6436 Add a few other common patterns for the open rule 2015-06-26 13:45:23 +02:00
mcarton
b59e83cca9 Fix punctuation in README.md 2015-06-26 12:02:05 +02:00
mcarton
6d718a38dc :sort rules in README 2015-06-26 12:00:16 +02:00
mcarton
330f91f5dc Cleanup the systemctl rule 2015-06-26 11:55:10 +02:00
mcarton
e3cc9c52e6 Remove redundant patterns in sudo rule 2015-06-26 11:41:55 +02:00
mcarton
01ce65047a Use spaces instead of tabs
This is more common in python and follows other rules usage.
2015-06-26 11:27:04 +02:00
Vladimir Iakovlev
3203d57b36 Merge pull request #261 from maciekmm/rule-systemctl
Added systemctl rule
2015-06-24 13:50:36 +03:00
Maciej Mionskowski
28ab6c62f8 Added systemctl rule to README.md 2015-06-24 11:07:03 +02:00
Maciej Mionskowski
360e4673eb Added systemctl rule 2015-06-24 09:36:09 +02:00
Vladimir Iakovlev
c6aead735b Merge pull request #259 from diezcami/master
Added Python Compile Rule
2015-06-22 19:19:41 +03:00
Cami Diez
af3e1a555f Edited python compile rule 2015-06-21 09:27:03 +08:00
Cami Diez
897b847975 Added rule 2015-06-21 09:25:20 +08:00
Cami Diez
a0949b1102 Added Python Compile Rule 2015-06-21 09:24:27 +08:00
nvbn
2f1460120e Merge branch 'master' of github.com:nvbn/thefuck 2015-06-16 13:53:24 +03:00
nvbn
43ec397190 #252 Fix bash and zsh aliases 2015-06-16 13:52:41 +03:00
nvbn
eb537bef81 Merge branch 'issue-221-tf-alias' of https://github.com/scorphus/thefuck into scorphus-issue-221-tf-alias 2015-06-16 13:49:17 +03:00
Vladimir Iakovlev
b033d3893b Merge pull request #256 from scorphus/psutil-children
fix(main.wait_output): use Process’ children() instead of get_children()
2015-06-16 13:47:30 +03:00
nvbn
633c4f8415 #257 sudo patterns are case insensitive 2015-06-16 13:46:18 +03:00
TJ Horner
5bfe0ac997 One more trigger word 2015-06-15 17:13:21 -07:00
TJ Horner
e8a55220ad Forgot to commit the actual rule 👎 2015-06-15 17:11:54 -07:00
TJ Horner
ea306038f9 Fix sudo rule 2015-06-15 17:08:08 -07:00
Pablo Santiago Blum de Aguiar
6a88cc47b6 fix(main.wait_output): use Process’ children() instead of get_children()
Since psutil 2.0.0 `get_children()` has become deprecated and the use of
`children()` instead of it has been encouraged. In version 3.0.0, just
released, `get_children()` has been dropped. Check:

https://github.com/giampaolo/psutil/blob/master/HISTORY.rst

Fix #255
2015-06-15 09:59:58 -03:00
Pablo Santiago Blum de Aguiar
96fe1e77b3 refact(rules.no_command): do not add TF_ALIAS to the “callables” list
Fix #234, #245 and #251

Ref #221
2015-06-12 00:49:55 -03:00
Pablo Santiago Blum de Aguiar
c08d9125e4 refact(shells): use an env var TF_ALIAS to keep the name of the alias
This environment variable may be used by any rule to decide whether it
matches or not.
2015-06-10 20:50:49 -03:00
Pablo Santiago Blum de Aguiar
be682170e5 test(shells): add fuck alias to collection of aliases 2015-06-10 20:49:28 -03:00
nvbn
5e981d9b01 Bump to 1.46 2015-06-07 02:24:08 +03:00
nvbn
add499af7f #250 #247 Fix UnicodeDecodeError with fish 2015-06-07 02:23:48 +03:00
nvbn
9c711734aa Merge branch 'encode_fix' of https://github.com/SanketDG/thefuck into SanketDG-encode_fix 2015-06-07 02:13:48 +03:00
Vladimir Iakovlev
ff7a433f39 Merge pull request #249 from mcarton/cargo
Add two cargo related rules
2015-06-07 02:09:11 +03:00
SanketDG
6bb7d79ddc change encoding of return statement to utf8 2015-06-07 00:03:00 +05:30
mcarton
f6c013d033 Add a cargo_no_command rule 2015-06-06 17:22:14 +02:00
mcarton
01cf199866 Add a cargo rule 2015-06-06 17:05:51 +02:00
Vladimir Iakovlev
3d41a3fb7c Merge pull request #246 from cubuspl42/patch-1
Added mount rule
2015-06-04 20:58:24 +03:00
cubuspl42
f55fa35ebf Added mount rule
$ mount /dev/sda2 /mnt/var
mount: only root can do that
2015-06-04 18:38:09 +02:00
nvbn
ce922758a4 Bump to 1.45 2015-06-02 08:47:53 +03:00
Vladimir Iakovlev
c47968a180 Merge pull request #240 from diezcami/brew-upgrade
Added brew_upgrade rule
2015-06-02 08:46:57 +03:00
Vladimir Iakovlev
581c97ec4b Merge pull request #239 from diezcami/quotation-marks
Added quotation_marks rule
2015-06-02 08:46:06 +03:00
Vladimir Iakovlev
0a53966f9b Merge pull request #238 from diezcami/go-run
Added go_run rule
2015-06-02 08:44:56 +03:00
Camille Diez
ed4e7946d7 Updated brew_upgrade description 2015-06-02 13:27:03 +08:00
Cami Diez
2ed96b1d51 Added brew_upgrade rule 2015-06-02 13:23:34 +08:00
Cami Diez
79d94e2651 Added quotation marks rule 2015-06-02 13:18:13 +08:00
Camille Diez
c08182509d Update README.md 2015-06-02 12:08:28 +08:00
Cami Diez
1d2d907c60 Added go_run rule 2015-06-02 12:05:47 +08:00
Vladimir Iakovlev
13996261be Update README.md 2015-06-02 06:11:29 +03:00
Vladimir Iakovlev
afcd7fc67e Merge pull request #237 from waldyrious/patch-1
+how to make the command available right away
2015-06-02 06:10:13 +03:00
Vladimir Iakovlev
c0c7397057 Update README.md 2015-06-02 06:09:45 +03:00
Vladimir Iakovlev
707743e7a7 Merge pull request #236 from bugaevc/git-branch-list
Git branch list
2015-06-02 06:08:09 +03:00
Waldir Pimenta
d8779dc4a6 +how to make the command available right away 2015-06-02 00:57:00 +01:00
Sergey Bugaev
ba9214f7fc Add a test for git_branch_list rule 2015-06-02 00:17:57 +03:00
Sergey Bugaev
660422806c Add git_branch_list rule 2015-06-01 23:52:41 +03:00
Vladimir Iakovlev
3c8978784b Merge pull request #230 from scorphus/git-diff-staged-rule
add(rule): add the new git_diff_staged rule
2015-06-01 08:10:06 +03:00
Vladimir Iakovlev
995b373347 Merge pull request #232 from bugaevc/fix-sudo
Wrap apt-get rule in sudo_support
2015-05-31 01:20:15 +03:00
Sergey Bugaev
dbe1a94c7d Wrap apt-get rule in sudo_support
Fixes sudo_support not working for no_command rule.
2015-05-30 19:40:01 +03:00
Pablo Santiago Blum de Aguiar
15e13d7c1a add(rule): add the new git_diff_staged rule 2015-05-29 18:41:53 -03:00
Vladimir Iakovlev
3194913965 Merge pull request #228 from mcarton/fix-cd-space
Fix cd command
2015-05-29 04:04:02 +03:00
mcarton
237f43ebdb Fix cd command
Fix #226
2015-05-28 23:29:38 +02:00
nvbn
a5aadc6e90 Bump to 1.44 2015-05-28 21:31:10 +03:00
nvbn
18ce062300 Merge branch 'diezcami-java' 2015-05-28 18:03:37 +03:00
nvbn
73bc6c0184 Merge branch 'java' of https://github.com/diezcami/thefuck into diezcami-java
Conflicts:
	README.md
2015-05-28 18:03:24 +03:00
Vladimir Iakovlev
0296a4a46d Merge pull request #227 from Dugucloud/master
Added a sudo string of Fedora's fedup
2015-05-28 18:02:00 +03:00
Vladimir Iakovlev
54a9769c10 Merge pull request #224 from diezcami/javac
Added javac rule
2015-05-28 18:01:14 +03:00
Vladimir Iakovlev
abc7238d14 Merge pull request #219 from scorphus/fix-shell-fish
fix(shell::Fish): avoid looping when calling `fuck` twice
2015-05-28 18:00:42 +03:00
Dugucloud
710a72ee8c Added sudo string for Fedora's fedup 2015-05-28 09:46:41 +08:00
秋纫
e09c6530e5 Merge pull request #3 from nvbn/master
Sync with master
2015-05-28 09:41:18 +08:00
Cami Diez
b1da6a883a Added java rule 2015-05-27 15:50:41 +08:00
Cami Diez
a9e3b22fa4 Added javac rule 2015-05-27 15:47:34 +08:00
Pablo Santiago Blum de Aguiar
9debcdf676 fix(shells::Fish): avoid looping when calling fuck twice
Or whatever the `thefuck` function name is.

Signed-off-by: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2015-05-27 00:39:47 -03:00
Vladimir Iakovlev
718cadb85a #216 add open rule to readme 2015-05-23 18:49:20 +03:00
Vladimir Iakovlev
910e6f4759 Merge pull request #216 from diezcami/master
Addressed Issue #210
2015-05-23 18:45:52 +03:00
Cami Diez
d3146aa0ac Addressed Issue #210 2015-05-23 23:18:15 +08:00
nvbn
190e47ecdb #215 Use memoize decorator for caching 2015-05-22 17:07:01 +03:00
Vladimir Iakovlev
84a28d8c73 Merge pull request #215 from scorphus/fish-functions
Cache aliases to speed up subsequent calls and add support to Fish functions
2015-05-22 16:55:00 +03:00
Pablo Santiago Blum de Aguiar
551e35e3b6 refact(shells): add support to Fish functions
Signed-off-by: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2015-05-21 23:56:37 -03:00
Pablo Santiago Blum de Aguiar
2bebfabf8d refact(shells): cache aliases to speed up subsequent calls
Signed-off-by: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2015-05-21 23:56:28 -03:00
Vladimir Iakovlev
675317b247 Merge pull request #214 from scorphus/improve-man
refact(man): do not match if there's no argument to man
2015-05-21 15:42:26 +03:00
Pablo Santiago Blum de Aguiar
6cf430cc23 refact(man): do not match if there's no argument to man
If there's no argument to man, a call to thefuck should just give no
fuck.

Signed-off-by: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2015-05-21 00:00:22 -03:00
Dugucloud
7e55041963 Merge branch 'master' of https://github.com/nvbn/thefuck 2015-05-10 15:46:06 +08:00
Dugucloud
fc364b99b9 Revert "Added colorama in requirements.txt"
This reverts commit 742f6f9c94.
2015-04-22 23:18:11 +08:00
Dugucloud
742f6f9c94 Added colorama in requirements.txt 2015-04-22 21:48:17 +08:00
秋纫
cd1bee9cb0 Merge pull request #2 from nvbn/master
Sync with master
2015-04-22 21:36:07 +08:00
50 changed files with 889 additions and 58 deletions

View File

@@ -132,6 +132,7 @@ thefuck-alias >> ~/.bashrc
[Or in your shell config (Bash, Zsh, Fish, Powershell).](https://github.com/nvbn/thefuck/wiki/Shell-aliases)
Changes will be available only in a new shell session.
To make them available immediately, run `source ~/.bashrc` (or your shell config file like `.zshrc`).
## Update
@@ -145,24 +146,31 @@ sudo pip install thefuck --upgrade
The Fuck tries to match a rule for the previous command, creates a new command
using the matched rule and runs it. Rules enabled by default are as follows:
* `cargo` &ndash; runs `cargo build` instead of `cargo`;
* `cargo_no_command` &ndash; fixes wrongs commands like `cargo buid`;
* `cd_correction` &ndash; spellchecks and correct failed cd commands;
* `cd_mkdir` &ndash; creates directories before cd'ing into them;
* `cd_parent` &ndash; changes `cd..` to `cd ..`;
* `composer_not_command` &ndash; fixes composer command name;
* `cp_omitting_directory` &ndash; adds `-a` when you `cp` directory;
* `cpp11` &ndash; add missing `-std=c++11` to `g++` or `clang++`;
* `dry` &ndash; fix repetitions like "git git push";
* `django_south_ghost` &ndash; adds `--delete-ghost-migrations` to failed because ghosts django south migration;
* `django_south_merge` &ndash; adds `--merge` to inconsistent django south migration;
* `dry` &ndash; fix repetitions like "git git push";
* `fix_alt_space` &ndash; replaces Alt+Space with Space character;
* `git_add` &ndash; fix *"Did you forget to 'git add'?"*;
* `git_branch_list` &ndash; catches `git branch list` in place of `git branch` and removes created branch;
* `git_checkout` &ndash; creates the branch before checking-out;
* `git_diff_staged` &ndash; adds `--staged` to previous `git diff` with unexpected output;
* `git_no_command` &ndash; fixes wrong git commands like `git brnch`;
* `git_pull` &ndash; sets upstream before executing previous `git pull`;
* `git_push` &ndash; adds `--set-upstream origin $branch` to previous failed `git push`;
* `git_stash` &ndash; stashes you local modifications before rebasing or switching branch;
* `grep_recursive` &ndash; adds `-r` when you trying to grep directory;
* `go_run` &ndash; appends `.go` extension when compiling/running Go programs
* `grep_recursive` &ndash; adds `-r` when you trying to grep directory;
* `has_exists_script` &ndash; prepends `./` when script/binary exists;
* `java` &ndash; removes `.java` extension when running Java programs;
* `javac` &ndash; appends missing `.java` when compiling Java files;
* `lein_not_task` &ndash; fixes wrong `lein` tasks like `lein rpl`;
* `ls_lah` &ndash; adds -lah to ls;
* `man` &ndash; change manual section;
@@ -170,13 +178,20 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `mkdir_p` &ndash; adds `-p` when you trying to create directory without parent;
* `no_command` &ndash; fixes wrong console commands, for example `vom/vim`;
* `no_such_file` &ndash; creates missing directories with `mv` and `cp` commands;
* `open` &ndash; prepends `http` to address passed to `open`;
* `pip_unknown_command` &ndash; fixes wrong pip commands, for example `pip instatl/pip install`;
* `python_command` &ndash; prepends `python` when you trying to run not executable/without `./` python script;
* `python_execute` &ndash; appends missing `.py` when executing Python files;
* `quotation_marks` &ndash; fixes uneven usage of `'` and `"` when containing args'
* `rm_dir` &ndash; adds `-rf` when you trying to remove directory;
* `sed` &ndash; adds missing '/' to `sed`'s `s` commands;
* `sl_ls` &ndash; changes `sl` to `ls`;
* `ssh_known_hosts` &ndash; removes host from `known_hosts` on warning;
* `sudo` &ndash; prepends `sudo` to previous command if it failed because of permissions;
* `switch_layout` &ndash; switches command from your local layout to en;
* `systemctl` &ndash; correctly orders parameters of confusing systemctl;
* `test.py` &ndash; runs `py.test` instead of `test.py`;
* `tmux` &ndash; fixes tmux commands;
* `whois` &ndash; fixes `whois` command.
Enabled by default only on specific platforms:
@@ -184,6 +199,7 @@ Enabled by default only on specific platforms:
* `apt_get` &ndash; installs app from apt if it not installed;
* `brew_install` &ndash; fixes formula name for `brew install`;
* `brew_unknown_command` &ndash; fixes wrong brew commands, for example `brew docto/brew doctor`;
* `brew_upgrade` &ndash; appends `--all` to `brew upgrade` as per Homebrew's new behaviour
* `pacman` &ndash; installs app with `pacman` or `yaourt` if it is not installed.
Bundled, but not enabled by default:
@@ -198,7 +214,7 @@ in `~/.thefuck/rules`. Rule should contain two functions:
and `get_new_command(command: Command, settings: Settings) -> str`.
Also the rule can contain optional function
`side_effect(command: Command, settings: Settings) -> None` and
optional boolean `enabled_by_default`
optional boolean `enabled_by_default`.
`Command` has three attributes: `script`, `stdout` and `stderr`.

18
setup.py Normal file → Executable file
View File

@@ -1,8 +1,22 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
import sys
if sys.version_info < (2, 7):
print('thefuck requires Python version 2.7 or later' +
' ({}.{} detected).'.format(*sys.version_info[:2]))
sys.exit(-1)
elif (3, 0) < sys.version_info < (3, 3):
print('thefuck requires Python version 3.3 or later' +
' ({}.{} detected).'.format(*sys.version_info[:2]))
sys.exit(-1)
VERSION = '1.43'
VERSION = '1.47'
install_requires = ['psutil', 'colorama', 'six']
if sys.version_info < (3, 4):
install_requires.append('pathlib')
setup(name='thefuck',
version=VERSION,
@@ -15,7 +29,7 @@ setup(name='thefuck',
'tests', 'release']),
include_package_data=True,
zip_safe=False,
install_requires=['pathlib', 'psutil', 'colorama', 'six'],
install_requires=install_requires,
entry_points={'console_scripts': [
'thefuck = thefuck.main:main',
'thefuck-alias = thefuck.shells:app_alias']})

View File

@@ -0,0 +1,15 @@
import pytest
from thefuck.rules.brew_upgrade import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='brew upgrade')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('brew upgrade'), 'brew upgrade --all')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -0,0 +1,21 @@
import pytest
from thefuck.rules.cargo_no_command import match, get_new_command
from tests.utils import Command
no_such_subcommand = """No such subcommand
Did you mean `build`?
"""
@pytest.mark.parametrize('command', [
Command(script='cargo buid', stderr=no_such_subcommand)])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('cargo buid', stderr=no_such_subcommand), 'cargo build')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -0,0 +1,18 @@
from thefuck import shells
from thefuck.rules.git_branch_list import match, get_new_command
from tests.utils import Command
def test_match():
assert match(Command('git branch list'), None)
def test_not_match():
assert not match(Command(), None)
assert not match(Command('git commit'), None)
assert not match(Command('git branch'), None)
assert not match(Command('git stash list'), None)
def test_get_new_command():
assert (get_new_command(Command('git branch list'), None) ==
shells.and_('git branch --delete list', 'git branch'))

View File

@@ -0,0 +1,27 @@
import pytest
from thefuck.rules.git_diff_staged import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='git diff'),
Command(script='git df'),
Command(script='git ds')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command', [
Command(script='git tag'),
Command(script='git branch'),
Command(script='git log')])
def test_not_match(command):
assert not match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('git diff'), 'git diff --staged'),
(Command('git df'), 'git df --staged'),
(Command('git ds'), 'git ds --staged')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -0,0 +1,17 @@
import pytest
from thefuck.rules.go_run import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='go run foo'),
Command(script='go run bar')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('go run foo'), 'go run foo.go'),
(Command('go run bar'), 'go run bar.go')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

17
tests/rules/test_java.py Normal file
View File

@@ -0,0 +1,17 @@
import pytest
from thefuck.rules.java import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='java foo.java'),
Command(script='java bar.java')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('java foo.java'), 'java foo'),
(Command('java bar.java'), 'java bar')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

17
tests/rules/test_javac.py Normal file
View File

@@ -0,0 +1,17 @@
import pytest
from thefuck.rules.javac import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='javac foo'),
Command(script='javac bar')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('javac foo'), 'javac foo.java'),
(Command('javac bar'), 'javac bar.java')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -3,9 +3,12 @@ from thefuck.rules.ls_lah import match, get_new_command
def test_match():
assert match(Mock(script='ls'), None)
assert match(Mock(script='ls file.py'), None)
assert match(Mock(script='ls /opt'), None)
assert not match(Mock(script='ls -lah /opt'), None)
assert not match(Mock(script='pacman -S binutils'), None)
assert not match(Mock(script='lsof'), None)
def test_get_new_command():

View File

@@ -2,6 +2,7 @@ import pytest
from thefuck.rules.man import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command('man read'),
Command('man 2 read'),
@@ -14,6 +15,13 @@ def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command', [
Command('man'),
Command('man ')])
def test_not_match(command):
assert not match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('man read'), 'man 3 read'),
(Command('man 2 read'), 'man 3 read'),

View File

@@ -0,0 +1,134 @@
import pytest
from tests.utils import Command
from thefuck.rules.mercurial import (
extract_possisiblities, match, get_new_command
)
@pytest.mark.parametrize('command', [
Command('hg base', stderr=(
"hg: unknown command 'base'"
'\n(did you mean one of blame, phase, rebase?)'
)),
Command('hg branchch', stderr=(
"hg: unknown command 'branchch'"
'\n(did you mean one of branch, branches?)'
)),
Command('hg vert', stderr=(
"hg: unknown command 'vert'"
'\n(did you mean one of revert?)'
)),
Command('hg lgo -r tip', stderr=(
"hg: command 're' is ambiguous:"
'\n(did you mean one of log?)'
)),
Command('hg rerere', stderr=(
"hg: unknown command 'rerere'"
'\n(did you mean one of revert?)'
)),
Command('hg re', stderr=(
"hg: command 're' is ambiguous:"
'\n rebase recover remove rename resolve revert'
)),
Command('hg re re', stderr=(
"hg: command 're' is ambiguous:"
'\n rebase recover remove rename resolve revert'
)),
])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command', [
Command('hg', stderr=(
'\nMercurial Distributed SCM\n\nbasic commands:'
)),
Command('hg asdf', stderr=(
"hg: unknown command 'asdf'"
'\nMercurial Distributed SCM\n\nbasic commands:'
)),
Command('hg qwer', stderr=(
"hg: unknown command 'qwer'"
'\nMercurial Distributed SCM\n\nbasic commands:'
)),
Command('hg me', stderr=(
"\nabort: no repository found in './thefuck' (.hg not found)!"
)),
Command('hg reb', stderr=(
"\nabort: no repository found in './thefuck' (.hg not found)!"
)),
Command('hg co', stderr=(
"\nabort: no repository found in './thefuck' (.hg not found)!"
)),
])
def test_not_match(command):
assert not match(command, None)
@pytest.mark.parametrize('command, possibilities', [
(Command('hg base', stderr=(
"hg: unknown command 'base'"
'\n(did you mean one of blame, phase, rebase?)'
)), ['blame', 'phase', 'rebase']),
(Command('hg branchch', stderr=(
"hg: unknown command 'branchch'"
'\n(did you mean one of branch, branches?)'
)), ['branch', 'branches']),
(Command('hg vert', stderr=(
"hg: unknown command 'vert'"
'\n(did you mean one of revert?)'
)), ['revert']),
(Command('hg lgo -r tip', stderr=(
"hg: command 're' is ambiguous:"
'\n(did you mean one of log?)'
)), ['log']),
(Command('hg rerere', stderr=(
"hg: unknown command 'rerere'"
'\n(did you mean one of revert?)'
)), ['revert']),
(Command('hg re', stderr=(
"hg: command 're' is ambiguous:"
'\n rebase recover remove rename resolve revert'
)), ['rebase', 'recover', 'remove', 'rename', 'resolve', 'revert']),
(Command('hg re re', stderr=(
"hg: command 're' is ambiguous:"
'\n rebase recover remove rename resolve revert'
)), ['rebase', 'recover', 'remove', 'rename', 'resolve', 'revert']),
])
def test_extract_possisiblities(command, possibilities):
assert extract_possisiblities(command) == possibilities
@pytest.mark.parametrize('command, new_command', [
(Command('hg base', stderr=(
"hg: unknown command 'base'"
'\n(did you mean one of blame, phase, rebase?)'
)), 'hg rebase'),
(Command('hg branchch', stderr=(
"hg: unknown command 'branchch'"
'\n(did you mean one of branch, branches?)'
)), 'hg branch'),
(Command('hg vert', stderr=(
"hg: unknown command 'vert'"
'\n(did you mean one of revert?)'
)), 'hg revert'),
(Command('hg lgo -r tip', stderr=(
"hg: command 're' is ambiguous:"
'\n(did you mean one of log?)'
)), 'hg log -r tip'),
(Command('hg rerere', stderr=(
"hg: unknown command 'rerere'"
'\n(did you mean one of revert?)'
)), 'hg revert'),
(Command('hg re', stderr=(
"hg: command 're' is ambiguous:"
'\n rebase recover remove rename resolve revert'
)), 'hg rebase'),
(Command('hg re re', stderr=(
"hg: command 're' is ambiguous:"
'\n rebase recover remove rename resolve revert'
)), 'hg rebase re'),
])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -1,19 +1,36 @@
from mock import patch, Mock
from thefuck.rules.no_command import match, get_new_command
from thefuck.rules.no_command import match, get_new_command, _get_all_callables
def test_match():
with patch('thefuck.rules.no_command._get_all_callables',
return_value=['vim', 'apt-get']):
assert match(Mock(stderr='vom: not found', script='vom file.py'), None)
assert not match(Mock(stderr='qweqwe: not found', script='qweqwe'), None)
assert not match(Mock(stderr='some text', script='vom file.py'), None)
@patch('thefuck.rules.no_command._safe', return_value=[])
@patch('thefuck.rules.no_command.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
def test_get_all_callables(*args):
all_callables = _get_all_callables()
assert 'vim' in all_callables
assert 'fsck' in all_callables
assert 'fuck' not in all_callables
def test_get_new_command():
with patch('thefuck.rules.no_command._get_all_callables',
return_value=['vim', 'apt-get']):
assert get_new_command(
Mock(stderr='vom: not found',
script='vom file.py'),
None) == 'vim file.py'
@patch('thefuck.rules.no_command._safe', return_value=[])
@patch('thefuck.rules.no_command.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
def test_match(*args):
assert match(Mock(stderr='vom: not found', script='vom file.py'), None)
assert match(Mock(stderr='fucck: not found', script='fucck'), None)
assert not match(Mock(stderr='qweqwe: not found', script='qweqwe'), None)
assert not match(Mock(stderr='some text', script='vom file.py'), None)
@patch('thefuck.rules.no_command._safe', return_value=[])
@patch('thefuck.rules.no_command.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
def test_get_new_command(*args):
assert get_new_command(
Mock(stderr='vom: not found',
script='vom file.py'),
None) == 'vim file.py'
assert get_new_command(
Mock(stderr='fucck: not found',
script='fucck'),
None) == 'fsck'

25
tests/rules/test_open.py Normal file
View File

@@ -0,0 +1,25 @@
import pytest
from thefuck.rules.open import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='open foo.com'),
Command(script='open foo.ly'),
Command(script='open foo.org'),
Command(script='open foo.net'),
Command(script='open foo.se'),
Command(script='open foo.io')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('open foo.com'), 'open http://foo.com'),
(Command('open foo.ly'), 'open http://foo.ly'),
(Command('open foo.org'), 'open http://foo.org'),
(Command('open foo.net'), 'open http://foo.net'),
(Command('open foo.se'), 'open http://foo.se'),
(Command('open foo.io'), 'open http://foo.io')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -23,23 +23,25 @@ extra/vim-python3 7.4.712-1 \t/usr/bin/vim
@pytest.mark.skipif(not getattr(pacman, 'enabled_by_default', True),
reason='Skip if pacman is not available')
@pytest.mark.parametrize('command', [
Command(script='vim', stderr='vim: command not found')])
Command(script='vim', stderr='vim: command not found'),
Command(script='sudo vim', stderr='sudo: vim: command not found')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, return_value', [
(Command(script='vim', stderr='vim: command not found'), PKGFILE_OUTPUT_VIM)])
(Command(script='vim', stderr='vim: command not found'), PKGFILE_OUTPUT_VIM),
(Command(script='sudo vim', stderr='sudo: vim: command not found'), PKGFILE_OUTPUT_VIM)])
@patch('thefuck.rules.pacman.subprocess')
@patch.multiple(pacman, create=True, pacman=pacman_cmd)
def test_match_mocked(subp_mock, command, return_value):
subp_mock.check_output.return_value = return_value
assert match(command, None)
assert subp_mock.check_output.called
@pytest.mark.parametrize('command', [
Command(script='vim', stderr=''), Command()])
Command(script='vim', stderr=''), Command(),
Command(script='sudo vim', stderr=''), Command()])
def test_not_match(command):
assert not match(command, None)
@@ -48,19 +50,20 @@ def test_not_match(command):
reason='Skip if pacman is not available')
@pytest.mark.parametrize('command, new_command', [
(Command('vim'), '{} -S extra/gvim && vim'.format(pacman_cmd)),
(Command('convert'), '{} -S extra/imagemagick && convert'.format(pacman_cmd))])
(Command('sudo vim'), '{} -S extra/gvim && sudo vim'.format(pacman_cmd)),
(Command('convert'), '{} -S extra/imagemagick && convert'.format(pacman_cmd)),
(Command('sudo convert'), '{} -S extra/imagemagick && sudo convert'.format(pacman_cmd))])
def test_get_new_command(command, new_command, mocker):
assert get_new_command(command, None) == new_command
@pytest.mark.parametrize('command, new_command, return_value', [
(Command('vim'), '{} -S extra/gvim && vim'.format(pacman_cmd),
PKGFILE_OUTPUT_VIM),
(Command('convert'), '{} -S extra/imagemagick && convert'.format(pacman_cmd),
PKGFILE_OUTPUT_CONVERT)])
(Command('vim'), '{} -S extra/gvim && vim'.format(pacman_cmd), PKGFILE_OUTPUT_VIM),
(Command('sudo vim'), '{} -S extra/gvim && sudo vim'.format(pacman_cmd), PKGFILE_OUTPUT_VIM),
(Command('convert'), '{} -S extra/imagemagick && convert'.format(pacman_cmd), PKGFILE_OUTPUT_CONVERT),
(Command('sudo convert'), '{} -S extra/imagemagick && sudo convert'.format(pacman_cmd), PKGFILE_OUTPUT_CONVERT)])
@patch('thefuck.rules.pacman.subprocess')
@patch.multiple(pacman, create=True, pacman=pacman_cmd)
def test_get_new_command_mocked(subp_mock, command, new_command, return_value):
subp_mock.check_output.return_value = return_value
assert get_new_command(command, None) == new_command
assert subp_mock.check_output.called

View File

@@ -0,0 +1,17 @@
import pytest
from thefuck.rules.python_execute import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='python foo'),
Command(script='python bar')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('python foo'), 'python foo.py'),
(Command('python bar'), 'python bar.py')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -0,0 +1,19 @@
import pytest
from thefuck.rules.quotation_marks import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script="git commit -m \'My Message\""),
Command(script="git commit -am \"Mismatched Quotation Marks\'"),
Command(script="echo \"hello\'")])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command("git commit -m \'My Message\""), "git commit -m \"My Message\""),
(Command("git commit -am \"Mismatched Quotation Marks\'"), "git commit -am \"Mismatched Quotation Marks\""),
(Command("echo \"hello\'"), "echo \"hello\"")])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -0,0 +1,28 @@
import pytest
from thefuck.rules.sed_unterminated_s import match, get_new_command
from tests.utils import Command
@pytest.fixture
def sed_unterminated_s():
return "sed: -e expression #1, char 9: unterminated `s' command"
def test_match(sed_unterminated_s):
assert match(Command('sed -e s/foo/bar', stderr=sed_unterminated_s), None)
assert match(Command('sed -es/foo/bar', stderr=sed_unterminated_s), None)
assert match(Command('sed -e s/foo/bar -e s/baz/quz', stderr=sed_unterminated_s), None)
assert not match(Command('sed -e s/foo/bar'), None)
assert not match(Command('sed -es/foo/bar'), None)
assert not match(Command('sed -e s/foo/bar -e s/baz/quz'), None)
def test_get_new_command(sed_unterminated_s):
assert get_new_command(Command('sed -e s/foo/bar', stderr=sed_unterminated_s), None) \
== 'sed -e s/foo/bar/'
assert get_new_command(Command('sed -es/foo/bar', stderr=sed_unterminated_s), None) \
== 'sed -es/foo/bar/'
assert get_new_command(Command(r"sed -e 's/\/foo/bar'", stderr=sed_unterminated_s), None) \
== r"sed -e 's/\/foo/bar/'"
assert get_new_command(Command(r"sed -e s/foo/bar -es/baz/quz", stderr=sed_unterminated_s), None) \
== r"sed -e s/foo/bar/ -es/baz/quz/"

View File

@@ -8,6 +8,9 @@ from tests.utils import Command
('permission denied', ''),
("npm ERR! Error: EACCES, unlink", ''),
('requested operation requires superuser privilege', ''),
('need to be root', ''),
('need root', ''),
('must be root', ''),
('', "error: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/ipaddr.py'")])
def test_match(stderr, stdout):
assert match(Command(stderr=stderr, stdout=stdout), None)

View File

@@ -0,0 +1,18 @@
import pytest
from thefuck.rules.systemctl import match, get_new_command
from tests.utils import Command
def test_match():
assert match(Command('systemctl nginx start', stderr='Unknown operation \'nginx\'.'), None)
assert match(Command('sudo systemctl nginx start', stderr='Unknown operation \'nginx\'.'), None)
assert not match(Command('systemctl start nginx'), None)
assert not match(Command('systemctl start nginx'), None)
assert not match(Command('sudo systemctl nginx', stderr='Unknown operation \'nginx\'.'), None)
assert not match(Command('systemctl nginx', stderr='Unknown operation \'nginx\'.'), None)
assert not match(Command('systemctl start wtf', stderr='Failed to start wtf.service: Unit wtf.service failed to load: No such file or directory.'), None)
def test_get_new_command():
assert get_new_command(Command('systemctl nginx start'), None) == "systemctl start nginx"
assert get_new_command(Command('sudo systemctl nginx start'), None) == "sudo systemctl start nginx"

19
tests/rules/test_tmux.py Normal file
View File

@@ -0,0 +1,19 @@
import pytest
from thefuck.rules.tmux import match, get_new_command
from tests.utils import Command
@pytest.fixture
def tmux_ambiguous():
return "ambiguous command: list, could be: " \
"list-buffers, list-clients, list-commands, list-keys, " \
"list-panes, list-sessions, list-windows"
def test_match(tmux_ambiguous):
assert match(Command('tmux list', stderr=tmux_ambiguous), None)
def test_get_new_command(tmux_ambiguous):
assert get_new_command(Command('tmux list', stderr=tmux_ambiguous), None)\
== 'tmux list-buffers'

View File

@@ -33,6 +33,11 @@ class TestGeneric(object):
def test_get_aliases(self, shell):
assert shell.get_aliases() == {}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias()
assert 'thefuck' in shell.app_alias()
assert 'TF_ALIAS' in shell.app_alias()
@pytest.mark.usefixtures('isfile')
class TestBash(object):
@@ -44,6 +49,7 @@ class TestBash(object):
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'alias fuck=\'eval $(thefuck $(fc -ln -1))\'\n'
b'alias l=\'ls -CF\'\n'
b'alias la=\'ls -A\'\n'
b'alias ll=\'ls -alF\'')
@@ -51,6 +57,8 @@ class TestBash(object):
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('fuck', 'eval $(thefuck $(fc -ln -1))'),
('awk', 'awk'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
@@ -67,10 +75,16 @@ class TestBash(object):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'l': 'ls -CF',
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias()
assert 'thefuck' in shell.app_alias()
assert 'TF_ALIAS' in shell.app_alias()
@pytest.mark.usefixtures('isfile')
class TestFish(object):
@@ -78,9 +92,22 @@ class TestFish(object):
def shell(self):
return shells.Fish()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'fish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nmath')
return mock
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('ll', 'll')]) # Fish has no aliases but functions
('fuck', 'fish -ic "fuck"'),
('find', 'find'),
('funced', 'fish -ic "funced"'),
('awk', 'awk'),
('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
('vim', 'vim'),
('ll', 'fish -ic "ll"')]) # Fish has no aliases but functions
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
@@ -98,7 +125,19 @@ class TestFish(object):
assert shell.and_('foo', 'bar') == 'foo; and bar'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {}
assert shell.get_aliases() == {'fish_config': 'fish_config',
'fuck': 'fuck',
'funced': 'funced',
'funcsave': 'funcsave',
'grep': 'grep',
'history': 'history',
'll': 'll',
'math': 'math'}
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias()
assert 'thefuck' in shell.app_alias()
assert 'TF_ALIAS' in shell.app_alias()
@pytest.mark.usefixtures('isfile')
@@ -111,12 +150,14 @@ class TestZsh(object):
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'fuck=\'eval $(thefuck $(fc -ln -1 | tail -n 1))\'\n'
b'l=\'ls -CF\'\n'
b'la=\'ls -A\'\n'
b'll=\'ls -alF\'')
return mock
@pytest.mark.parametrize('before, after', [
('fuck', 'eval $(thefuck $(fc -ln -1 | tail -n 1))'),
('pwd', 'pwd'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
@@ -136,6 +177,13 @@ class TestZsh(object):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
assert shell.get_aliases() == {
'fuck': 'eval $(thefuck $(fc -ln -1 | tail -n 1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias()
assert 'thefuck' in shell.app_alias()
assert 'TF_ALIAS' in shell.app_alias()

View File

@@ -1,6 +1,6 @@
import pytest
from mock import Mock
from thefuck.utils import sudo_support, wrap_settings
from thefuck.utils import sudo_support, wrap_settings, memoize
from thefuck.types import Settings
from tests.utils import Command
@@ -24,3 +24,11 @@ def test_sudo_support(return_value, command, called, result):
fn = Mock(return_value=return_value, __name__='')
assert sudo_support(fn)(Command(command), None) == result
fn.assert_called_once_with(Command(called), None)
def test_memoize():
fn = Mock(__name__='fn')
memoized = memoize(fn)
memoized()
memoized()
fn.assert_called_once_with()

View File

@@ -62,7 +62,7 @@ def wait_output(settings, popen):
proc.wait(settings.wait_command)
return True
except TimeoutExpired:
for child in proc.get_children(recursive=True):
for child in proc.children(recursive=True):
child.kill()
proc.kill()
return False

View File

@@ -1,11 +1,12 @@
from thefuck import shells
from thefuck.utils import sudo_support
try:
import CommandNotFound
except ImportError:
enabled_by_default = False
@sudo_support
def match(command, settings):
if 'not found' in command.stderr:
try:
@@ -17,7 +18,7 @@ def match(command, settings):
# IndexError is thrown when no matching package is found
return False
@sudo_support
def get_new_command(command, settings):
c = CommandNotFound.CommandNotFound()
pkgs = c.getPackages(command.script.split(" ")[0])

View File

@@ -0,0 +1,14 @@
# Appends --all to the brew upgrade command
#
# Example:
# > brew upgrade
# Warning: brew upgrade with no arguments will change behaviour soon!
# It currently upgrades all formula but this will soon change to require '--all'.
#
#
def match(command, settings):
return (command.script == 'brew upgrade')
def get_new_command(command, settings):
return command.script + ' --all'

6
thefuck/rules/cargo.py Normal file
View File

@@ -0,0 +1,6 @@
def match(command, settings):
return command.script == 'cargo'
def get_new_command(command, settings):
return 'cargo build'

View File

@@ -0,0 +1,14 @@
import re
def match(command, settings):
return ('cargo' in command.script
and 'No such subcommand' in command.stderr
and 'Did you mean' in command.stderr)
def get_new_command(command, settings):
broken = command.script.split()[1]
fix = re.findall(r'Did you mean `([^`]*)`', command.stderr)[0]
return command.script.replace(broken, fix, 1)

View File

@@ -47,7 +47,7 @@ def get_new_command(command, settings):
cwd = os.path.join(cwd, best_matches[0])
else:
return cd_mkdir.get_new_command(command, settings)
return "cd {0}".format(cwd)
return 'cd "{0}"'.format(cwd)
enabled_by_default = True

View File

@@ -0,0 +1,10 @@
from thefuck import shells
def match(command, settings):
# catches "git branch list" in place of "git branch"
return command.script.split() == 'git branch list'.split()
def get_new_command(command, settings):
return shells.and_('git branch --delete list', 'git branch')

View File

@@ -0,0 +1,6 @@
def match(command, settings):
return command.script.startswith('git d')
def get_new_command(command, settings):
return '{} --staged'.format(command.script)

View File

@@ -1,3 +1,6 @@
from thefuck import shells
def match(command, settings):
return ('git' in command.script
and 'pull' in command.script
@@ -9,4 +12,4 @@ def get_new_command(command, settings):
branch = line.split(' ')[-1]
set_upstream = line.replace('<remote>', 'origin')\
.replace('<branch>', branch)
return u'{} && {}'.format(set_upstream, command.script)
return shells.and_(set_upstream, command.script)

14
thefuck/rules/go_run.py Normal file
View File

@@ -0,0 +1,14 @@
# Appends .go when compiling go files
#
# Example:
# > go run foo
# error: go run: no go files listed
#
#
def match(command, settings):
return (command.script.startswith ('go run ')
and not command.script.endswith('.go'))
def get_new_command(command, settings):
return command.script + '.go'

14
thefuck/rules/java.py Normal file
View File

@@ -0,0 +1,14 @@
# Fixes common java command mistake
#
# Example:
# > java foo.java
# Error: Could not find or load main class foo.java
def match(command, settings):
return (command.script.startswith('java ')
and command.script.endswith('.java'))
def get_new_command(command, settings):
return command.script[:-5]

15
thefuck/rules/javac.py Normal file
View File

@@ -0,0 +1,15 @@
# Appends .java when compiling java files
#
# Example:
# > javac foo
# error: Class names, 'foo', are only accepted if annotation
# processing is explicitly requested
def match(command, settings):
return (command.script.startswith('javac ')
and not command.script.endswith('.java'))
def get_new_command(command, settings):
return command.script + '.java'

View File

@@ -1,5 +1,7 @@
def match(command, settings):
return 'ls' in command.script and not ('ls -' in command.script)
return (command.script == 'ls'
or command.script.startswith('ls ')
and not ('ls -' in command.script))
def get_new_command(command, settings):

View File

@@ -1,5 +1,5 @@
def match(command, settings):
return command.script.startswith('man')
return command.script.strip().startswith('man ')
def get_new_command(command, settings):

View File

@@ -0,0 +1,34 @@
import re
from difflib import get_close_matches
def extract_possisiblities(command):
possib = re.findall(r'\n\(did you mean one of ([^\?]+)\?\)', command.stderr)
if possib:
return possib[0].split(', ')
possib = re.findall(r'\n ([^$]+)$', command.stderr)
if possib:
return possib[0].split(' ')
return possib
def match(command, settings):
return (command.script.startswith('hg ')
and ('hg: unknown command' in command.stderr
and '(did you mean one of ' in command.stderr
or "hg: command '" in command.stderr
and "' is ambiguous:" in command.stderr
)
)
def get_new_command(command, settings):
script = command.script.split(' ')
possisiblities = extract_possisiblities(command)
matches = get_close_matches(script[1], possisiblities)
if matches:
script[1] = matches[0]
else:
script[1] = possisiblities[0]
return ' '.join(script)

View File

@@ -2,7 +2,7 @@ from difflib import get_close_matches
import os
from pathlib import Path
from thefuck.utils import sudo_support
from thefuck.shells import get_aliases
from thefuck.shells import thefuck_alias, get_aliases
def _safe(fn, fallback):
@@ -13,10 +13,12 @@ def _safe(fn, fallback):
def _get_all_callables():
tf_alias = thefuck_alias()
return [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)] + get_aliases()
if not _safe(exe.is_dir, True)] + [
alias for alias in get_aliases() if alias != tf_alias]
@sudo_support

26
thefuck/rules/open.py Normal file
View File

@@ -0,0 +1,26 @@
# Opens URL's in the default web browser
#
# Example:
# > open github.com
# The file ~/github.com does not exist.
# Perhaps you meant 'http://github.com'?
#
def match(command, settings):
return (command.script.startswith(('open', 'xdg-open', 'gnome-open', 'kde-open'))
and (
'.com' in command.script
or '.net' in command.script
or '.org' in command.script
or '.ly' in command.script
or '.io' in command.script
or '.se' in command.script
or '.edu' in command.script
or '.info' in command.script
or '.me' in command.script
or 'www.' in command.script))
def get_new_command(command, settings):
return 'open http://' + command.script[5:]

View File

@@ -1,12 +1,21 @@
import subprocess
from thefuck.utils import DEVNULL, which
from thefuck import shells
from thefuck.utils import memoize
@memoize
def __get_pkgfile(command):
try:
command = command.script
if command.startswith('sudo'):
command = command[5:]
command = command.split(" ")[0]
return subprocess.check_output(
['pkgfile', '-b', '-v', command.script.split(" ")[0]],
['pkgfile', '-b', '-v', command],
universal_newlines=True, stderr=DEVNULL
).split()
except subprocess.CalledProcessError:

View File

@@ -0,0 +1,14 @@
# Appends .py when executing python files
#
# Example:
# > python foo
# error: python: can't open file 'foo': [Errno 2] No such file or directory
def match(command, settings):
return (command.script.startswith('python ')
and not command.script.endswith('.py'))
def get_new_command(command, settings):
return command.script + '.py'

View File

@@ -0,0 +1,14 @@
# Fixes careless " and ' usage
#
# Example:
# > git commit -m 'My Message"
#
#
#
def match(command, settings):
return ('\'' in command.script
and '\"' in command.script)
def get_new_command(command, settings):
return command.script.replace ('\'', '\"')

View File

@@ -0,0 +1,17 @@
import shlex
from thefuck.utils import quote
def match(command, settings):
return ('sed' in command.script
and "unterminated `s' command" in command.stderr)
def get_new_command(command, settings):
script = shlex.split(command.script)
for (i, e) in enumerate(script):
if e.startswith(('s/', '-es/')) and e[-1] != '/':
script[i] += '/'
return ' '.join(map(quote, script))

View File

@@ -7,11 +7,13 @@ patterns = ['permission denied',
'root privilege',
'This command has to be run under the root user.',
'This operation requires root.',
'You need to be root to perform this command.',
'requested operation requires superuser privilege',
'must be run as root',
'must be superuser',
'Need to be root']
'must be root',
'need to be root',
'need root',
'only root can do that']
def match(command, settings):

View File

@@ -0,0 +1,21 @@
"""
The confusion in systemctl's param order is massive.
"""
from thefuck.utils import sudo_support
@sudo_support
def match(command, settings):
# Catches 'Unknown operation 'service'.' when executing systemctl with
# misordered arguments
cmd = command.script.split()
return ('systemctl' in command.script and
'Unknown operation \'' in command.stderr and
len(cmd) - cmd.index('systemctl') == 3)
@sudo_support
def get_new_command(command, settings):
cmd = command.script.split()
cmd[-1], cmd[-2] = cmd[-2], cmd[-1]
return ' '.join(cmd)

10
thefuck/rules/test.py.py Normal file
View File

@@ -0,0 +1,10 @@
def match(command, settings):
return command.script == 'test.py' and 'not found' in command.stderr
def get_new_command(command, settings):
return 'py.test'
# make it come before the python_command rule
priority = 900

14
thefuck/rules/tmux.py Normal file
View File

@@ -0,0 +1,14 @@
import re
def match(command, settings):
return ('tmux' in command.script
and 'ambiguous command:' in command.stderr
and 'could be:' in command.stderr)
def get_new_command(command, settings):
cmd = re.match(r"ambiguous command: (.*), could be: ([^, \n]*)",
command.stderr)
return command.script.replace(cmd.group(1), cmd.group(2))

View File

@@ -8,10 +8,11 @@ from subprocess import Popen, PIPE
from time import time
import os
from psutil import Process
from .utils import DEVNULL
from .utils import DEVNULL, memoize
class Generic(object):
def get_aliases(self):
return {}
@@ -32,7 +33,7 @@ class Generic(object):
return command_script
def app_alias(self):
return "\nalias fuck='eval $(thefuck $(fc -ln -1))'\n"
return "\nalias fuck='TF_ALIAS=fuck eval $(thefuck $(fc -ln -1))'\n"
def _get_history_file_name(self):
return ''
@@ -48,12 +49,13 @@ class Generic(object):
history.write(self._get_history_line(command_script))
def and_(self, *commands):
return ' && '.join(commands)
return u' && '.join(commands)
class Bash(Generic):
def app_alias(self):
return "\nalias fuck='eval $(thefuck $(fc -ln -1)); history -r'\n"
return "\nTF_ALIAS=fuck alias fuck='eval $(thefuck $(fc -ln -1));" \
" history -r'\n"
def _parse_alias(self, alias):
name, value = alias.replace('alias ', '', 1).split('=', 1)
@@ -61,8 +63,10 @@ class Bash(Generic):
value = value[1:-1]
return name, value
@memoize
def get_aliases(self):
proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL,
shell=True)
return dict(
self._parse_alias(alias)
for alias in proc.stdout.read().decode('utf-8').split('\n')
@@ -80,6 +84,7 @@ class Fish(Generic):
def app_alias(self):
return ("function fuck -d 'Correct your previous console command'\n"
" set -l exit_code $status\n"
" set -l TF_ALIAS fuck\n"
" set -l eval_script"
" (mktemp 2>/dev/null ; or mktemp -t 'thefuck')\n"
" set -l fucked_up_commandd $history[1]\n"
@@ -91,6 +96,25 @@ class Fish(Generic):
" end\n"
"end")
@memoize
def get_aliases(self):
proc = Popen('fish -ic functions', stdout=PIPE, stderr=DEVNULL,
shell=True)
functions = proc.stdout.read().decode('utf-8').strip().split('\n')
return {function: function for function in functions}
def _expand_aliases(self, command_script):
aliases = self.get_aliases()
binary = command_script.split(' ')[0]
if binary in aliases:
return u'fish -ic "{}"'.format(command_script.replace('"', r'\"'))
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')
@@ -98,12 +122,14 @@ class Fish(Generic):
return u'- cmd: {}\n when: {}\n'.format(command_script, int(time()))
def and_(self, *commands):
return '; and '.join(commands)
return u'; and '.join(commands)
class Zsh(Generic):
def app_alias(self):
return "\nalias fuck='eval $(thefuck $(fc -ln -1 | tail -n 1)); fc -R'\n"
return "\nTF_ALIAS=fuck" \
" alias fuck='eval $(thefuck $(fc -ln -1 | tail -n 1));" \
" fc -R'\n"
def _parse_alias(self, alias):
name, value = alias.split('=', 1)
@@ -111,8 +137,10 @@ class Zsh(Generic):
value = value[1:-1]
return name, value
@memoize
def get_aliases(self):
proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL,
shell=True)
return dict(
self._parse_alias(alias)
for alias in proc.stdout.read().decode('utf-8').split('\n')
@@ -128,14 +156,16 @@ class Zsh(Generic):
class Tcsh(Generic):
def app_alias(self):
return "\nalias fuck 'set fucked_cmd=`history -h 2 | head -n 1` && eval `thefuck ${fucked_cmd}`'\n"
return "\nalias fuck 'setenv TF_ALIAS fuck && set fucked_cmd=`history -h 2 | head -n 1` && eval `thefuck ${fucked_cmd}`'\n"
def _parse_alias(self, alias):
name, value = alias.split("\t", 1)
return name, value
@memoize
def get_aliases(self):
proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL,
shell=True)
return dict(
self._parse_alias(alias)
for alias in proc.stdout.read().decode('utf-8').split('\n')
@@ -177,6 +207,10 @@ def app_alias():
print(_get_shell().app_alias())
def thefuck_alias():
return os.environ.get('TF_ALIAS', 'fuck')
def put_to_history(command):
return _get_shell().put_to_history(command)

View File

@@ -1,11 +1,19 @@
from functools import wraps
import os
import pickle
import six
from .types import Command
DEVNULL = open(os.devnull, 'w')
if six.PY2:
import pipes
quote = pipes.quote
else:
import shlex
quote = shlex.quote
def which(program):
"""Returns `program` path or `None`."""
@@ -62,3 +70,18 @@ def sudo_support(fn):
else:
return result
return wrapper
def memoize(fn):
"""Caches previous calls to the function."""
memo = {}
@wraps(fn)
def wrapper(*args, **kwargs):
key = pickle.dumps((args, kwargs))
if key not in memo:
memo[key] = fn(*args, **kwargs)
return memo[key]
return wrapper