mirror of
https://github.com/nvbn/thefuck.git
synced 2025-11-07 02:22:05 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce922758a4 | ||
|
|
c47968a180 | ||
|
|
581c97ec4b | ||
|
|
0a53966f9b | ||
|
|
ed4e7946d7 | ||
|
|
2ed96b1d51 | ||
|
|
79d94e2651 | ||
|
|
c08182509d | ||
|
|
1d2d907c60 | ||
|
|
13996261be | ||
|
|
afcd7fc67e | ||
|
|
c0c7397057 | ||
|
|
707743e7a7 | ||
|
|
d8779dc4a6 | ||
|
|
ba9214f7fc | ||
|
|
660422806c | ||
|
|
3c8978784b | ||
|
|
995b373347 | ||
|
|
dbe1a94c7d | ||
|
|
15e13d7c1a | ||
|
|
3194913965 | ||
|
|
237f43ebdb | ||
|
|
a5aadc6e90 | ||
|
|
18ce062300 | ||
|
|
73bc6c0184 | ||
|
|
0296a4a46d | ||
|
|
54a9769c10 | ||
|
|
abc7238d14 | ||
|
|
710a72ee8c | ||
|
|
e09c6530e5 | ||
|
|
b1da6a883a | ||
|
|
a9e3b22fa4 | ||
|
|
9debcdf676 | ||
|
|
718cadb85a | ||
|
|
910e6f4759 | ||
|
|
d3146aa0ac | ||
|
|
190e47ecdb | ||
|
|
84a28d8c73 | ||
|
|
551e35e3b6 | ||
|
|
2bebfabf8d | ||
|
|
675317b247 | ||
|
|
6cf430cc23 | ||
|
|
d088dac0f4 | ||
|
|
c65fdd0f81 | ||
|
|
e7d7b80c09 | ||
|
|
f986df23d5 | ||
|
|
cf5f18bf23 | ||
|
|
44c06c483e | ||
|
|
1f48d5e12a | ||
|
|
2c3df1ad47 | ||
|
|
5319871326 | ||
|
|
d6ce5e1e62 | ||
|
|
c42898dde3 | ||
|
|
17b9104939 | ||
|
|
02d9613618 | ||
|
|
b63ce26853 | ||
|
|
ce6855fd97 | ||
|
|
7e55041963 | ||
|
|
fc364b99b9 | ||
|
|
742f6f9c94 | ||
|
|
cd1bee9cb0 |
36
README.md
36
README.md
@@ -132,6 +132,7 @@ thefuck-alias >> ~/.bashrc
|
|||||||
[Or in your shell config (Bash, Zsh, Fish, Powershell).](https://github.com/nvbn/thefuck/wiki/Shell-aliases)
|
[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.
|
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
|
## Update
|
||||||
@@ -145,40 +146,57 @@ sudo pip install thefuck --upgrade
|
|||||||
The Fuck tries to match a rule for the previous command, creates a new command
|
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:
|
using the matched rule and runs it. Rules enabled by default are as follows:
|
||||||
|
|
||||||
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
|
* `cd_correction` – spellchecks and correct failed cd commands;
|
||||||
* `cpp11` – add missing `-std=c++11` to `g++` or `clang++`;
|
|
||||||
* `cd_parent` – changes `cd..` to `cd ..`;
|
|
||||||
* `cd_mkdir` – creates directories before cd'ing into them;
|
* `cd_mkdir` – creates directories before cd'ing into them;
|
||||||
|
* `cd_parent` – changes `cd..` to `cd ..`;
|
||||||
|
* `composer_not_command` – fixes composer command name;
|
||||||
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
|
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
|
||||||
|
* `cpp11` – add missing `-std=c++11` to `g++` or `clang++`;
|
||||||
* `dry` – fix repetitions like "git git push";
|
* `dry` – fix repetitions like "git git push";
|
||||||
|
* `django_south_ghost` – adds `--delete-ghost-migrations` to failed because ghosts django south migration;
|
||||||
|
* `django_south_merge` – adds `--merge` to inconsistent django south migration;
|
||||||
* `fix_alt_space` – replaces Alt+Space with Space character;
|
* `fix_alt_space` – replaces Alt+Space with Space character;
|
||||||
|
* `javac` – appends missing `.java` when compiling Java files;
|
||||||
|
* `java` – removes `.java` extension when running Java programs;
|
||||||
* `git_add` – fix *"Did you forget to 'git add'?"*;
|
* `git_add` – fix *"Did you forget to 'git add'?"*;
|
||||||
|
* `git_branch_list` – catches `git branch list` in place of `git branch` and removes created branch;
|
||||||
* `git_checkout` – creates the branch before checking-out;
|
* `git_checkout` – creates the branch before checking-out;
|
||||||
|
* `git_diff_staged` – adds `--staged` to previous `git diff` with unexpected output;
|
||||||
* `git_no_command` – fixes wrong git commands like `git brnch`;
|
* `git_no_command` – fixes wrong git commands like `git brnch`;
|
||||||
|
* `git_pull` – sets upstream before executing previous `git pull`;
|
||||||
* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
|
* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
|
||||||
* `git_stash` – stashes you local modifications before rebasing or switching branch;
|
* `git_stash` – stashes you local modifications before rebasing or switching branch;
|
||||||
|
* `go_run` – appends `.go` extension when compiling/running Go programs
|
||||||
|
* `grep_recursive` – adds `-r` when you trying to grep directory;
|
||||||
* `has_exists_script` – prepends `./` when script/binary exists;
|
* `has_exists_script` – prepends `./` when script/binary exists;
|
||||||
* `lein_not_task` – fixes wrong `lein` tasks like `lein rpl`;
|
* `lein_not_task` – fixes wrong `lein` tasks like `lein rpl`;
|
||||||
|
* `ls_lah` – adds -lah to ls;
|
||||||
|
* `man` – change manual section;
|
||||||
|
* `man_no_space` – fixes man commands without spaces, for example `mandiff`;
|
||||||
* `mkdir_p` – adds `-p` when you trying to create directory without parent;
|
* `mkdir_p` – adds `-p` when you trying to create directory without parent;
|
||||||
* `no_command` – fixes wrong console commands, for example `vom/vim`;
|
* `no_command` – fixes wrong console commands, for example `vom/vim`;
|
||||||
* `no_such_file` – creates missing directories with `mv` and `cp` commands;
|
* `no_such_file` – creates missing directories with `mv` and `cp` commands;
|
||||||
* `man_no_space` – fixes man commands without spaces, for example `mandiff`;
|
* `open` – prepends `http` to address passed to `open`;
|
||||||
* `pacman` – installs app with `pacman` or `yaourt` if it is not installed;
|
|
||||||
* `pip_unknown_command` – fixes wrong pip commands, for example `pip instatl/pip install`;
|
* `pip_unknown_command` – fixes wrong pip commands, for example `pip instatl/pip install`;
|
||||||
* `python_command` – prepends `python` when you trying to run not executable/without `./` python script;
|
* `python_command` – prepends `python` when you trying to run not executable/without `./` python script;
|
||||||
* `sl_ls` – changes `sl` to `ls`;
|
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args'
|
||||||
* `rm_dir` – adds `-rf` when you trying to remove directory;
|
* `rm_dir` – adds `-rf` when you trying to remove directory;
|
||||||
|
* `sl_ls` – changes `sl` to `ls`;
|
||||||
* `ssh_known_hosts` – removes host from `known_hosts` on warning;
|
* `ssh_known_hosts` – removes host from `known_hosts` on warning;
|
||||||
* `sudo` – prepends `sudo` to previous command if it failed because of permissions;
|
* `sudo` – prepends `sudo` to previous command if it failed because of permissions;
|
||||||
* `switch_layout` – switches command from your local layout to en;
|
* `switch_layout` – switches command from your local layout to en;
|
||||||
* `whois` – fixes `whois` command;
|
* `whois` – fixes `whois` command.
|
||||||
|
|
||||||
|
Enabled by default only on specific platforms:
|
||||||
|
|
||||||
* `apt_get` – installs app from apt if it not installed;
|
* `apt_get` – installs app from apt if it not installed;
|
||||||
* `brew_install` – fixes formula name for `brew install`;
|
* `brew_install` – fixes formula name for `brew install`;
|
||||||
* `composer_not_command` – fixes composer command name.
|
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
|
||||||
|
* `brew_upgrade` – appends `--all` to `brew upgrade` as per Homebrew's new behaviour
|
||||||
|
* `pacman` – installs app with `pacman` or `yaourt` if it is not installed.
|
||||||
|
|
||||||
Bundled, but not enabled by default:
|
Bundled, but not enabled by default:
|
||||||
|
|
||||||
* `ls_lah` – adds -lah to ls;
|
|
||||||
* `rm_root` – adds `--no-preserve-root` to `rm -rf /` command.
|
* `rm_root` – adds `--no-preserve-root` to `rm -rf /` command.
|
||||||
|
|
||||||
## Creating your own rules
|
## Creating your own rules
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -1,7 +1,7 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
VERSION = '1.41'
|
VERSION = '1.45'
|
||||||
|
|
||||||
|
|
||||||
setup(name='thefuck',
|
setup(name='thefuck',
|
||||||
|
|||||||
15
tests/rules/test_brew_upgrade.py
Normal file
15
tests/rules/test_brew_upgrade.py
Normal 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
|
||||||
53
tests/rules/test_django_south_ghost.py
Normal file
53
tests/rules/test_django_south_ghost.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import pytest
|
||||||
|
from thefuck.rules.django_south_ghost import match, get_new_command
|
||||||
|
from tests.utils import Command
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def stderr():
|
||||||
|
return '''Traceback (most recent call last):
|
||||||
|
File "/home/nvbn/work/.../bin/python", line 42, in <module>
|
||||||
|
exec(compile(__file__f.read(), __file__, "exec"))
|
||||||
|
File "/home/nvbn/work/.../app/manage.py", line 34, in <module>
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
File "/home/nvbn/work/.../lib/django/core/management/__init__.py", line 443, in execute_from_command_line
|
||||||
|
utility.execute()
|
||||||
|
File "/home/nvbn/work/.../lib/django/core/management/__init__.py", line 382, in execute
|
||||||
|
self.fetch_command(subcommand).run_from_argv(self.argv)
|
||||||
|
File "/home/nvbn/work/.../lib/django/core/management/base.py", line 196, in run_from_argv
|
||||||
|
self.execute(*args, **options.__dict__)
|
||||||
|
File "/home/nvbn/work/.../lib/django/core/management/base.py", line 232, in execute
|
||||||
|
output = self.handle(*args, **options)
|
||||||
|
File "/home/nvbn/work/.../app/lib/south/management/commands/migrate.py", line 108, in handle
|
||||||
|
ignore_ghosts = ignore_ghosts,
|
||||||
|
File "/home/nvbn/work/.../app/lib/south/migration/__init__.py", line 193, in migrate_app
|
||||||
|
applied_all = check_migration_histories(applied_all, delete_ghosts, ignore_ghosts)
|
||||||
|
File "/home/nvbn/work/.../app/lib/south/migration/__init__.py", line 88, in check_migration_histories
|
||||||
|
raise exceptions.GhostMigrations(ghosts)
|
||||||
|
south.exceptions.GhostMigrations:
|
||||||
|
|
||||||
|
! These migrations are in the database but not on disk:
|
||||||
|
<app1: 0033_auto__...>
|
||||||
|
<app1: 0034_fill_...>
|
||||||
|
<app1: 0035_rename_...>
|
||||||
|
<app2: 0003_add_...>
|
||||||
|
<app2: 0004_denormalize_...>
|
||||||
|
<app1: 0033_auto....>
|
||||||
|
<app1: 0034_fill...>
|
||||||
|
! I'm not trusting myself; either fix this yourself by fiddling
|
||||||
|
! with the south_migrationhistory table, or pass --delete-ghost-migrations
|
||||||
|
! to South to have it delete ALL of these records (this may not be good).
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def test_match(stderr):
|
||||||
|
assert match(Command('./manage.py migrate', stderr=stderr), None)
|
||||||
|
assert match(Command('python manage.py migrate', stderr=stderr), None)
|
||||||
|
assert not match(Command('./manage.py migrate'), None)
|
||||||
|
assert not match(Command('app migrate', stderr=stderr), None)
|
||||||
|
assert not match(Command('./manage.py test', stderr=stderr), None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_new_command():
|
||||||
|
assert get_new_command(Command('./manage.py migrate auth'), None)\
|
||||||
|
== './manage.py migrate auth --delete-ghost-migrations'
|
||||||
43
tests/rules/test_django_south_merge.py
Normal file
43
tests/rules/test_django_south_merge.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import pytest
|
||||||
|
from thefuck.rules.django_south_merge import match, get_new_command
|
||||||
|
from tests.utils import Command
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def stderr():
|
||||||
|
return '''Running migrations for app:
|
||||||
|
! Migration app:0003_auto... should not have been applied before app:0002_auto__add_field_query_due_date_ but was.
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/nvbn/work/.../bin/python", line 42, in <module>
|
||||||
|
exec(compile(__file__f.read(), __file__, "exec"))
|
||||||
|
File "/home/nvbn/work/.../app/manage.py", line 34, in <module>
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
File "/home/nvbn/work/.../lib/django/core/management/__init__.py", line 443, in execute_from_command_line
|
||||||
|
utility.execute()
|
||||||
|
File "/home/nvbn/work/.../lib/django/core/management/__init__.py", line 382, in execute
|
||||||
|
self.fetch_command(subcommand).run_from_argv(self.argv)
|
||||||
|
File "/home/nvbn/work/.../lib/django/core/management/base.py", line 196, in run_from_argv
|
||||||
|
self.execute(*args, **options.__dict__)
|
||||||
|
File "/home/nvbn/work/.../lib/django/core/management/base.py", line 232, in execute
|
||||||
|
output = self.handle(*args, **options)
|
||||||
|
File "/home/nvbn/work/.../app/lib/south/management/commands/migrate.py", line 108, in handle
|
||||||
|
ignore_ghosts = ignore_ghosts,
|
||||||
|
File "/home/nvbn/work/.../app/lib/south/migration/__init__.py", line 207, in migrate_app
|
||||||
|
raise exceptions.InconsistentMigrationHistory(problems)
|
||||||
|
south.exceptions.InconsistentMigrationHistory: Inconsistent migration history
|
||||||
|
The following options are available:
|
||||||
|
--merge: will just attempt the migration ignoring any potential dependency conflicts.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def test_match(stderr):
|
||||||
|
assert match(Command('./manage.py migrate', stderr=stderr), None)
|
||||||
|
assert match(Command('python manage.py migrate', stderr=stderr), None)
|
||||||
|
assert not match(Command('./manage.py migrate'), None)
|
||||||
|
assert not match(Command('app migrate', stderr=stderr), None)
|
||||||
|
assert not match(Command('./manage.py test', stderr=stderr), None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_new_command():
|
||||||
|
assert get_new_command(Command('./manage.py migrate auth'), None) \
|
||||||
|
== './manage.py migrate auth --merge'
|
||||||
18
tests/rules/test_git_branch_list.py
Normal file
18
tests/rules/test_git_branch_list.py
Normal 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'))
|
||||||
27
tests/rules/test_git_diff_staged.py
Normal file
27
tests/rules/test_git_diff_staged.py
Normal 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
|
||||||
29
tests/rules/test_git_pull.py
Normal file
29
tests/rules/test_git_pull.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import pytest
|
||||||
|
from thefuck.rules.git_pull import match, get_new_command
|
||||||
|
from tests.utils import Command
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def stderr():
|
||||||
|
return '''There is no tracking information for the current branch.
|
||||||
|
Please specify which branch you want to merge with.
|
||||||
|
See git-pull(1) for details
|
||||||
|
|
||||||
|
git pull <remote> <branch>
|
||||||
|
|
||||||
|
If you wish to set tracking information for this branch you can do so with:
|
||||||
|
|
||||||
|
git branch --set-upstream-to=<remote>/<branch> master
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def test_match(stderr):
|
||||||
|
assert match(Command('git pull', stderr=stderr), None)
|
||||||
|
assert not match(Command('git pull'), None)
|
||||||
|
assert not match(Command('ls', stderr=stderr), None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_new_command(stderr):
|
||||||
|
assert get_new_command(Command('git pull', stderr=stderr), None) \
|
||||||
|
== "git branch --set-upstream-to=origin/master master && git pull"
|
||||||
17
tests/rules/test_go_run.py
Normal file
17
tests/rules/test_go_run.py
Normal 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
17
tests/rules/test_java.py
Normal 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
17
tests/rules/test_javac.py
Normal 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
|
||||||
34
tests/rules/test_man.py
Normal file
34
tests/rules/test_man.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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'),
|
||||||
|
Command('man 3 read'),
|
||||||
|
Command('man -s2 read'),
|
||||||
|
Command('man -s3 read'),
|
||||||
|
Command('man -s 2 read'),
|
||||||
|
Command('man -s 3 read')])
|
||||||
|
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'),
|
||||||
|
(Command('man 3 read'), 'man 2 read'),
|
||||||
|
(Command('man -s2 read'), 'man -s3 read'),
|
||||||
|
(Command('man -s3 read'), 'man -s2 read'),
|
||||||
|
(Command('man -s 2 read'), 'man -s 3 read'),
|
||||||
|
(Command('man -s 3 read'), 'man -s 2 read')])
|
||||||
|
def test_get_new_command(command, new_command):
|
||||||
|
assert get_new_command(command, None) == new_command
|
||||||
@@ -3,7 +3,7 @@ from thefuck.rules.no_command import match, get_new_command
|
|||||||
|
|
||||||
|
|
||||||
def test_match():
|
def test_match():
|
||||||
with patch('thefuck.rules.no_command._get_all_bins',
|
with patch('thefuck.rules.no_command._get_all_callables',
|
||||||
return_value=['vim', 'apt-get']):
|
return_value=['vim', 'apt-get']):
|
||||||
assert match(Mock(stderr='vom: not found', script='vom file.py'), None)
|
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='qweqwe: not found', script='qweqwe'), None)
|
||||||
@@ -11,7 +11,7 @@ def test_match():
|
|||||||
|
|
||||||
|
|
||||||
def test_get_new_command():
|
def test_get_new_command():
|
||||||
with patch('thefuck.rules.no_command._get_all_bins',
|
with patch('thefuck.rules.no_command._get_all_callables',
|
||||||
return_value=['vim', 'apt-get']):
|
return_value=['vim', 'apt-get']):
|
||||||
assert get_new_command(
|
assert get_new_command(
|
||||||
Mock(stderr='vom: not found',
|
Mock(stderr='vom: not found',
|
||||||
|
|||||||
25
tests/rules/test_open.py
Normal file
25
tests/rules/test_open.py
Normal 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
|
||||||
19
tests/rules/test_quotation_marks.py
Normal file
19
tests/rules/test_quotation_marks.py
Normal 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
|
||||||
@@ -11,6 +11,10 @@ def test_match(command):
|
|||||||
assert match(command, None)
|
assert match(command, None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_match():
|
||||||
|
assert not match(Command(script='whois'), None)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('command, new_command', [
|
@pytest.mark.parametrize('command, new_command', [
|
||||||
(Command('whois https://en.wikipedia.org/wiki/Main_Page'), 'whois en.wikipedia.org'),
|
(Command('whois https://en.wikipedia.org/wiki/Main_Page'), 'whois en.wikipedia.org'),
|
||||||
(Command('whois https://en.wikipedia.org/'), 'whois en.wikipedia.org'),
|
(Command('whois https://en.wikipedia.org/'), 'whois en.wikipedia.org'),
|
||||||
|
|||||||
@@ -13,19 +13,33 @@ def isfile(mocker):
|
|||||||
|
|
||||||
|
|
||||||
class TestGeneric(object):
|
class TestGeneric(object):
|
||||||
def test_from_shell(self):
|
@pytest.fixture
|
||||||
assert shells.Generic().from_shell('pwd') == 'pwd'
|
def shell(self):
|
||||||
|
return shells.Generic()
|
||||||
|
|
||||||
def test_to_shell(self):
|
def test_from_shell(self, shell):
|
||||||
assert shells.Generic().to_shell('pwd') == 'pwd'
|
assert shell.from_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
def test_put_to_history(self, builtins_open):
|
def test_to_shell(self, shell):
|
||||||
assert shells.Generic().put_to_history('ls') is None
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
|
def test_put_to_history(self, builtins_open, shell):
|
||||||
|
assert shell.put_to_history('ls') is None
|
||||||
assert builtins_open.call_count == 0
|
assert builtins_open.call_count == 0
|
||||||
|
|
||||||
|
def test_and_(self, shell):
|
||||||
|
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||||
|
|
||||||
|
def test_get_aliases(self, shell):
|
||||||
|
assert shell.get_aliases() == {}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('isfile')
|
@pytest.mark.usefixtures('isfile')
|
||||||
class TestBash(object):
|
class TestBash(object):
|
||||||
|
@pytest.fixture
|
||||||
|
def shell(self):
|
||||||
|
return shells.Bash()
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def Popen(self, mocker):
|
def Popen(self, mocker):
|
||||||
mock = mocker.patch('thefuck.shells.Popen')
|
mock = mocker.patch('thefuck.shells.Popen')
|
||||||
@@ -38,42 +52,81 @@ class TestBash(object):
|
|||||||
@pytest.mark.parametrize('before, after', [
|
@pytest.mark.parametrize('before, after', [
|
||||||
('pwd', 'pwd'),
|
('pwd', 'pwd'),
|
||||||
('ll', 'ls -alF')])
|
('ll', 'ls -alF')])
|
||||||
def test_from_shell(self, before, after):
|
def test_from_shell(self, before, after, shell):
|
||||||
assert shells.Bash().from_shell(before) == after
|
assert shell.from_shell(before) == after
|
||||||
|
|
||||||
def test_to_shell(self):
|
def test_to_shell(self, shell):
|
||||||
assert shells.Bash().to_shell('pwd') == 'pwd'
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
def test_put_to_history(self, builtins_open):
|
def test_put_to_history(self, builtins_open, shell):
|
||||||
shells.Bash().put_to_history('ls')
|
shell.put_to_history('ls')
|
||||||
builtins_open.return_value.__enter__.return_value. \
|
builtins_open.return_value.__enter__.return_value. \
|
||||||
write.assert_called_once_with('ls\n')
|
write.assert_called_once_with('ls\n')
|
||||||
|
|
||||||
|
def test_and_(self, shell):
|
||||||
|
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'}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('isfile')
|
@pytest.mark.usefixtures('isfile')
|
||||||
class TestFish(object):
|
class TestFish(object):
|
||||||
|
@pytest.fixture
|
||||||
|
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', [
|
@pytest.mark.parametrize('before, after', [
|
||||||
('pwd', 'pwd'),
|
('pwd', 'pwd'),
|
||||||
('ll', 'll')]) # Fish has no aliases but functions
|
('fuck', 'fish -ic "fuck"'),
|
||||||
def test_from_shell(self, before, after):
|
('find', 'find'),
|
||||||
assert shells.Fish().from_shell(before) == after
|
('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
|
||||||
|
|
||||||
def test_to_shell(self):
|
def test_to_shell(self, shell):
|
||||||
assert shells.Fish().to_shell('pwd') == 'pwd'
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
def test_put_to_history(self, builtins_open, mocker):
|
def test_put_to_history(self, builtins_open, mocker, shell):
|
||||||
mocker.patch('thefuck.shells.time',
|
mocker.patch('thefuck.shells.time',
|
||||||
return_value=1430707243.3517463)
|
return_value=1430707243.3517463)
|
||||||
shells.Fish().put_to_history('ls')
|
shell.put_to_history('ls')
|
||||||
builtins_open.return_value.__enter__.return_value. \
|
builtins_open.return_value.__enter__.return_value. \
|
||||||
write.assert_called_once_with('- cmd: ls\n when: 1430707243\n')
|
write.assert_called_once_with('- cmd: ls\n when: 1430707243\n')
|
||||||
|
|
||||||
def test_and_(self):
|
def test_and_(self, shell):
|
||||||
assert shells.Fish().and_('foo', 'bar') == 'foo; and bar'
|
assert shell.and_('foo', 'bar') == 'foo; and bar'
|
||||||
|
|
||||||
|
def test_get_aliases(self, shell):
|
||||||
|
assert shell.get_aliases() == {'fish_config': 'fish_config',
|
||||||
|
'fuck': 'fuck',
|
||||||
|
'funced': 'funced',
|
||||||
|
'funcsave': 'funcsave',
|
||||||
|
'grep': 'grep',
|
||||||
|
'history': 'history',
|
||||||
|
'll': 'll',
|
||||||
|
'math': 'math'}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('isfile')
|
@pytest.mark.usefixtures('isfile')
|
||||||
class TestZsh(object):
|
class TestZsh(object):
|
||||||
|
@pytest.fixture
|
||||||
|
def shell(self):
|
||||||
|
return shells.Zsh()
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def Popen(self, mocker):
|
def Popen(self, mocker):
|
||||||
mock = mocker.patch('thefuck.shells.Popen')
|
mock = mocker.patch('thefuck.shells.Popen')
|
||||||
@@ -86,15 +139,23 @@ class TestZsh(object):
|
|||||||
@pytest.mark.parametrize('before, after', [
|
@pytest.mark.parametrize('before, after', [
|
||||||
('pwd', 'pwd'),
|
('pwd', 'pwd'),
|
||||||
('ll', 'ls -alF')])
|
('ll', 'ls -alF')])
|
||||||
def test_from_shell(self, before, after):
|
def test_from_shell(self, before, after, shell):
|
||||||
assert shells.Zsh().from_shell(before) == after
|
assert shell.from_shell(before) == after
|
||||||
|
|
||||||
def test_to_shell(self):
|
def test_to_shell(self, shell):
|
||||||
assert shells.Zsh().to_shell('pwd') == 'pwd'
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
def test_put_to_history(self, builtins_open, mocker):
|
def test_put_to_history(self, builtins_open, mocker, shell):
|
||||||
mocker.patch('thefuck.shells.time',
|
mocker.patch('thefuck.shells.time',
|
||||||
return_value=1430707243.3517463)
|
return_value=1430707243.3517463)
|
||||||
shells.Zsh().put_to_history('ls')
|
shell.put_to_history('ls')
|
||||||
builtins_open.return_value.__enter__.return_value. \
|
builtins_open.return_value.__enter__.return_value. \
|
||||||
write.assert_called_once_with(': 1430707243:0;ls\n')
|
write.assert_called_once_with(': 1430707243:0;ls\n')
|
||||||
|
|
||||||
|
def test_and_(self, shell):
|
||||||
|
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'}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from mock import Mock
|
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 thefuck.types import Settings
|
||||||
from tests.utils import Command
|
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__='')
|
fn = Mock(return_value=return_value, __name__='')
|
||||||
assert sudo_support(fn)(Command(command), None) == result
|
assert sudo_support(fn)(Command(command), None) == result
|
||||||
fn.assert_called_once_with(Command(called), None)
|
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()
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
from thefuck import shells
|
from thefuck import shells
|
||||||
|
from thefuck.utils import sudo_support
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import CommandNotFound
|
import CommandNotFound
|
||||||
except ImportError:
|
except ImportError:
|
||||||
enabled_by_default = False
|
enabled_by_default = False
|
||||||
|
|
||||||
|
@sudo_support
|
||||||
def match(command, settings):
|
def match(command, settings):
|
||||||
if 'not found' in command.stderr:
|
if 'not found' in command.stderr:
|
||||||
try:
|
try:
|
||||||
@@ -17,7 +18,7 @@ def match(command, settings):
|
|||||||
# IndexError is thrown when no matching package is found
|
# IndexError is thrown when no matching package is found
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@sudo_support
|
||||||
def get_new_command(command, settings):
|
def get_new_command(command, settings):
|
||||||
c = CommandNotFound.CommandNotFound()
|
c = CommandNotFound.CommandNotFound()
|
||||||
pkgs = c.getPackages(command.script.split(" ")[0])
|
pkgs = c.getPackages(command.script.split(" ")[0])
|
||||||
|
|||||||
14
thefuck/rules/brew_upgrade.py
Normal file
14
thefuck/rules/brew_upgrade.py
Normal 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'
|
||||||
@@ -47,7 +47,7 @@ def get_new_command(command, settings):
|
|||||||
cwd = os.path.join(cwd, best_matches[0])
|
cwd = os.path.join(cwd, best_matches[0])
|
||||||
else:
|
else:
|
||||||
return cd_mkdir.get_new_command(command, settings)
|
return cd_mkdir.get_new_command(command, settings)
|
||||||
return "cd {0}".format(cwd)
|
return 'cd "{0}"'.format(cwd)
|
||||||
|
|
||||||
|
|
||||||
enabled_by_default = True
|
enabled_by_default = True
|
||||||
|
|||||||
8
thefuck/rules/django_south_ghost.py
Normal file
8
thefuck/rules/django_south_ghost.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
def match(command, settings):
|
||||||
|
return 'manage.py' in command.script and \
|
||||||
|
'migrate' in command.script \
|
||||||
|
and 'or pass --delete-ghost-migrations' in command.stderr
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command, settings):
|
||||||
|
return u'{} --delete-ghost-migrations'.format(command.script)
|
||||||
8
thefuck/rules/django_south_merge.py
Normal file
8
thefuck/rules/django_south_merge.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
def match(command, settings):
|
||||||
|
return 'manage.py' in command.script and \
|
||||||
|
'migrate' in command.script \
|
||||||
|
and '--merge: will just attempt the migration' in command.stderr
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command, settings):
|
||||||
|
return u'{} --merge'.format(command.script)
|
||||||
10
thefuck/rules/git_branch_list.py
Normal file
10
thefuck/rules/git_branch_list.py
Normal 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')
|
||||||
6
thefuck/rules/git_diff_staged.py
Normal file
6
thefuck/rules/git_diff_staged.py
Normal 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)
|
||||||
12
thefuck/rules/git_pull.py
Normal file
12
thefuck/rules/git_pull.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
def match(command, settings):
|
||||||
|
return ('git' in command.script
|
||||||
|
and 'pull' in command.script
|
||||||
|
and 'set-upstream' in command.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command, settings):
|
||||||
|
line = command.stderr.split('\n')[-3].strip()
|
||||||
|
branch = line.split(' ')[-1]
|
||||||
|
set_upstream = line.replace('<remote>', 'origin')\
|
||||||
|
.replace('<branch>', branch)
|
||||||
|
return u'{} && {}'.format(set_upstream, command.script)
|
||||||
14
thefuck/rules/go_run.py
Normal file
14
thefuck/rules/go_run.py
Normal 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'
|
||||||
13
thefuck/rules/java.py
Normal file
13
thefuck/rules/java.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 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
15
thefuck/rules/javac.py
Normal 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'
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
enabled_by_default = False
|
|
||||||
|
|
||||||
|
|
||||||
def match(command, settings):
|
def match(command, settings):
|
||||||
return 'ls' in command.script and not ('ls -' in command.script)
|
return 'ls' in command.script and not ('ls -' in command.script)
|
||||||
|
|
||||||
|
|||||||
13
thefuck/rules/man.py
Normal file
13
thefuck/rules/man.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
def match(command, settings):
|
||||||
|
return command.script.strip().startswith('man ')
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command, settings):
|
||||||
|
if '3' in command.script:
|
||||||
|
return command.script.replace("3", "2")
|
||||||
|
if '2' in command.script:
|
||||||
|
return command.script.replace("2", "3")
|
||||||
|
|
||||||
|
split_cmd = command.script.split()
|
||||||
|
split_cmd.insert(1, ' 3 ')
|
||||||
|
return "".join(split_cmd)
|
||||||
@@ -2,6 +2,7 @@ from difflib import get_close_matches
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from thefuck.utils import sudo_support
|
from thefuck.utils import sudo_support
|
||||||
|
from thefuck.shells import get_aliases
|
||||||
|
|
||||||
|
|
||||||
def _safe(fn, fallback):
|
def _safe(fn, fallback):
|
||||||
@@ -11,25 +12,25 @@ def _safe(fn, fallback):
|
|||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
|
|
||||||
def _get_all_bins():
|
def _get_all_callables():
|
||||||
return [exe.name
|
return [exe.name
|
||||||
for path in os.environ.get('PATH', '').split(':')
|
for path in os.environ.get('PATH', '').split(':')
|
||||||
for exe in _safe(lambda: list(Path(path).iterdir()), [])
|
for exe in _safe(lambda: list(Path(path).iterdir()), [])
|
||||||
if not _safe(exe.is_dir, True)]
|
if not _safe(exe.is_dir, True)] + get_aliases()
|
||||||
|
|
||||||
|
|
||||||
@sudo_support
|
@sudo_support
|
||||||
def match(command, settings):
|
def match(command, settings):
|
||||||
return 'not found' in command.stderr and \
|
return 'not found' in command.stderr and \
|
||||||
bool(get_close_matches(command.script.split(' ')[0],
|
bool(get_close_matches(command.script.split(' ')[0],
|
||||||
_get_all_bins()))
|
_get_all_callables()))
|
||||||
|
|
||||||
|
|
||||||
@sudo_support
|
@sudo_support
|
||||||
def get_new_command(command, settings):
|
def get_new_command(command, settings):
|
||||||
old_command = command.script.split(' ')[0]
|
old_command = command.script.split(' ')[0]
|
||||||
new_command = get_close_matches(old_command,
|
new_command = get_close_matches(old_command,
|
||||||
_get_all_bins())[0]
|
_get_all_callables())[0]
|
||||||
return ' '.join([new_command] + command.script.split(' ')[1:])
|
return ' '.join([new_command] + command.script.split(' ')[1:])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
24
thefuck/rules/open.py
Normal file
24
thefuck/rules/open.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# 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')
|
||||||
|
and (
|
||||||
|
# Wanted to use this:
|
||||||
|
# 'http' in command.stderr
|
||||||
|
'.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))
|
||||||
|
|
||||||
|
def get_new_command(command, settings):
|
||||||
|
return 'open http://' + command.script[5:]
|
||||||
14
thefuck/rules/quotation_marks.py
Normal file
14
thefuck/rules/quotation_marks.py
Normal 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 ('\'', '\"')
|
||||||
@@ -11,7 +11,8 @@ patterns = ['permission denied',
|
|||||||
'requested operation requires superuser privilege',
|
'requested operation requires superuser privilege',
|
||||||
'must be run as root',
|
'must be run as root',
|
||||||
'must be superuser',
|
'must be superuser',
|
||||||
'Need to be root']
|
'Need to be root',
|
||||||
|
'you must be root to run this program.']
|
||||||
|
|
||||||
|
|
||||||
def match(command, settings):
|
def match(command, settings):
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ def match(command, settings):
|
|||||||
- www.google.fr → subdomain: www, domain: 'google.fr';
|
- www.google.fr → subdomain: www, domain: 'google.fr';
|
||||||
- google.co.uk → subdomain: None, domain; 'google.co.uk'.
|
- google.co.uk → subdomain: None, domain; 'google.co.uk'.
|
||||||
"""
|
"""
|
||||||
return 'whois' in command.script
|
return 'whois' in command.script and len(command.script.split()) > 1
|
||||||
|
|
||||||
|
|
||||||
def get_new_command(command, settings):
|
def get_new_command(command, settings):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Module with shell specific actions, each shell class should
|
"""Module with shell specific actions, each shell class should
|
||||||
implement `from_shell`, `to_shell`, `app_alias` and `put_to_history`
|
implement `from_shell`, `to_shell`, `app_alias`, `put_to_history` and `get_aliases`
|
||||||
methods.
|
methods.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -8,15 +8,16 @@ from subprocess import Popen, PIPE
|
|||||||
from time import time
|
from time import time
|
||||||
import os
|
import os
|
||||||
from psutil import Process
|
from psutil import Process
|
||||||
from .utils import DEVNULL
|
from .utils import DEVNULL, memoize
|
||||||
|
|
||||||
|
|
||||||
class Generic(object):
|
class Generic(object):
|
||||||
def _get_aliases(self):
|
|
||||||
|
def get_aliases(self):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _expand_aliases(self, command_script):
|
def _expand_aliases(self, command_script):
|
||||||
aliases = self._get_aliases()
|
aliases = self.get_aliases()
|
||||||
binary = command_script.split(' ')[0]
|
binary = command_script.split(' ')[0]
|
||||||
if binary in aliases:
|
if binary in aliases:
|
||||||
return command_script.replace(binary, aliases[binary], 1)
|
return command_script.replace(binary, aliases[binary], 1)
|
||||||
@@ -61,8 +62,10 @@ class Bash(Generic):
|
|||||||
value = value[1:-1]
|
value = value[1:-1]
|
||||||
return name, value
|
return name, value
|
||||||
|
|
||||||
def _get_aliases(self):
|
@memoize
|
||||||
proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
|
def get_aliases(self):
|
||||||
|
proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL,
|
||||||
|
shell=True)
|
||||||
return dict(
|
return dict(
|
||||||
self._parse_alias(alias)
|
self._parse_alias(alias)
|
||||||
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
||||||
@@ -91,6 +94,25 @@ class Fish(Generic):
|
|||||||
" end\n"
|
" end\n"
|
||||||
"end")
|
"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 '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):
|
def _get_history_file_name(self):
|
||||||
return os.path.expanduser('~/.config/fish/fish_history')
|
return os.path.expanduser('~/.config/fish/fish_history')
|
||||||
|
|
||||||
@@ -111,8 +133,10 @@ class Zsh(Generic):
|
|||||||
value = value[1:-1]
|
value = value[1:-1]
|
||||||
return name, value
|
return name, value
|
||||||
|
|
||||||
def _get_aliases(self):
|
@memoize
|
||||||
proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
|
def get_aliases(self):
|
||||||
|
proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL,
|
||||||
|
shell=True)
|
||||||
return dict(
|
return dict(
|
||||||
self._parse_alias(alias)
|
self._parse_alias(alias)
|
||||||
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
||||||
@@ -134,8 +158,10 @@ class Tcsh(Generic):
|
|||||||
name, value = alias.split("\t", 1)
|
name, value = alias.split("\t", 1)
|
||||||
return name, value
|
return name, value
|
||||||
|
|
||||||
def _get_aliases(self):
|
@memoize
|
||||||
proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
|
def get_aliases(self):
|
||||||
|
proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL,
|
||||||
|
shell=True)
|
||||||
return dict(
|
return dict(
|
||||||
self._parse_alias(alias)
|
self._parse_alias(alias)
|
||||||
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
||||||
@@ -153,16 +179,16 @@ shells = defaultdict(lambda: Generic(), {
|
|||||||
'bash': Bash(),
|
'bash': Bash(),
|
||||||
'fish': Fish(),
|
'fish': Fish(),
|
||||||
'zsh': Zsh(),
|
'zsh': Zsh(),
|
||||||
'-csh': Tcsh(),
|
'csh': Tcsh(),
|
||||||
'tcsh': Tcsh()})
|
'tcsh': Tcsh()})
|
||||||
|
|
||||||
|
|
||||||
def _get_shell():
|
def _get_shell():
|
||||||
try:
|
try:
|
||||||
shell = Process(os.getpid()).parent().cmdline()[0]
|
shell = Process(os.getpid()).parent().name()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
shell = Process(os.getpid()).parent.cmdline[0]
|
shell = Process(os.getpid()).parent.name
|
||||||
return shells[os.path.basename(shell)]
|
return shells[shell]
|
||||||
|
|
||||||
|
|
||||||
def from_shell(command):
|
def from_shell(command):
|
||||||
@@ -183,3 +209,7 @@ def put_to_history(command):
|
|||||||
|
|
||||||
def and_(*commands):
|
def and_(*commands):
|
||||||
return _get_shell().and_(*commands)
|
return _get_shell().and_(*commands)
|
||||||
|
|
||||||
|
|
||||||
|
def get_aliases():
|
||||||
|
return list(_get_shell().get_aliases().keys())
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
import os
|
import os
|
||||||
|
import pickle
|
||||||
import six
|
import six
|
||||||
from .types import Command
|
from .types import Command
|
||||||
|
|
||||||
@@ -62,3 +63,18 @@ def sudo_support(fn):
|
|||||||
else:
|
else:
|
||||||
return result
|
return result
|
||||||
return wrapper
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user