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

Compare commits

...

41 Commits
2.7 ... 2.8

Author SHA1 Message Date
nvbn
5ab2cf646e Bump to 2.8 2015-08-24 20:31:28 +03:00
Vladimir Iakovlev
c5b4628c5c Update README.md 2015-08-24 20:27:11 +03:00
nvbn
d4cec3e850 Update readme 2015-08-24 20:25:59 +03:00
nvbn
564638c171 Add experimental installation script 2015-08-24 20:25:03 +03:00
nvbn
c6171a85e9 Improve readme test 2015-08-24 11:25:49 +03:00
Vladimir Iakovlev
ee610032b8 Merge pull request #345 from mcarton/slow
Fix slowness problems
2015-08-24 11:19:48 +03:00
mcarton
6754ebe20d Fix some spelling mistakes 2015-08-23 21:27:38 +02:00
mcarton
15d19fdf9c Fix slowness problems
The `get_aliases()` function was also sometimes called through
`shell.get_aliases()`.
Since the `history` rule also uses aliases, the shell was always
executed twice to get the aliases list.
2015-08-23 21:22:54 +02:00
Vladimir Iakovlev
967e30d914 Merge pull request #344 from trolley/typo-fix
Fix typo in alias warning
2015-08-23 16:39:31 +03:00
Mark Trolley
38ee31ebcb Fix typo in alias warning
Fixes a small typo in the deprecated alias warning.
2015-08-22 10:01:24 -04:00
Vladimir Iakovlev
7315958ea9 Merge pull request #343 from mlk/hdfs-rm-rm_X_Is_a_directory_add_minus_r
hdfs -rm -r /directory and hdfs -mkdir -p /directory/sub support
2015-08-21 18:34:19 +03:00
Vladimir Iakovlev
cce25b1ea4 Merge pull request #342 from mlk/322_vagrant
Issue: 322 Runs vagrant up when required
2015-08-21 18:33:42 +03:00
Michael Lee
1a57ef03c8 Removed additional assert 2015-08-21 16:06:12 +01:00
Michael Lee
298c04f89c Support for hdfs dfs -mkdir -p /directory/subdirectory 2015-08-21 16:05:49 +01:00
Michael Lee
42a8b4f639 Support for hdfs dfs -rm /directory 2015-08-21 15:48:54 +01:00
Michael Lee
336d8b7b4b Style change 2015-08-20 11:37:49 +01:00
Michael Lee
feb3eee2a0 Support for either starting only the machine requested, or starting all machines 2015-08-20 10:06:41 +01:00
Michael Lee
7cb0388ed0 Not matched unit tests, code style. 2015-08-20 09:41:01 +01:00
Michael Lee
004c0d06eb starts up vagrant if not already running 2015-08-19 17:12:54 +01:00
Vladimir Iakovlev
abbbd1f8eb Merge pull request #339 from mcarton/fix-338
Fix #338
2015-08-19 17:19:38 +03:00
Vladimir Iakovlev
700d9ac7e9 Merge pull request #337 from mcarton/cleanup
Some cleanup and fixes
2015-08-19 17:19:08 +03:00
Vladimir Iakovlev
8037a17b73 Merge pull request #333 from mlk/master
basic support for the hdfs dfs <command> when the command misses the …
2015-08-19 17:16:54 +03:00
mcarton
49917ce6b4 Fix #338 2015-08-19 11:00:27 +02:00
mcarton
c5e1139879 Fix some issues reported by pylint 2015-08-17 17:19:16 +02:00
mcarton
1becd92b12 Fix the open rule
It was simply wrong with `xdg-`, `gnome-` and `kde-open`.
2015-08-17 16:22:05 +02:00
mcarton
bc6b107066 Fix README and add a test so it won't happen again 2015-08-17 16:07:24 +02:00
mcarton
9b30ae0424 Handle columns it the fix_file rule 2015-08-17 16:07:24 +02:00
mcarton
88831c424f Fix the @wrap_settings annotation
It seems much more useful if it only adds settings that are not already
set.
2015-08-17 16:07:24 +02:00
mcarton
4a2f869c6d Add support for stdout in the fix_file rule
At least `pep8` and `py.test` consider errors as normal and print them
on stdout.
2015-08-17 16:07:24 +02:00
mcarton
7f0f9a966f Fix some pep8 issues, mostly spaces
Before:
    4       E101 indentation contains mixed spaces and tabs
    20      E122 continuation line missing indentation or outdented
    1       E124 closing bracket does not match visual indentation
    12      E127 continuation line over-indented for visual indent
    22      E128 continuation line under-indented for visual indent
    2       E211 whitespace before '('
    12      E302 expected 2 blank lines, found 1
    1       E303 too many blank lines (3)
    4       E402 module level import not at top of file
    123     E501 line too long (81 > 79 characters)
    2       E731 do not assign a lambda expression, use a def
    3       W191 indentation contains tabs
    20      W291 trailing whitespace
    3       W293 blank line contains whitespace
    2       W391 blank line at end of file
    69      W503 line break before binary operator

After:
    20      E122 continuation line missing indentation or outdented
    12      E127 continuation line over-indented for visual indent
    22      E128 continuation line under-indented for visual indent
    123     E501 line too long (81 > 79 characters)
    2       E731 do not assign a lambda expression, use a def
    1       W291 trailing whitespace
    68      W503 line break before binary operator
2015-08-17 16:07:10 +02:00
Vladimir Iakovlev
85647794dc Merge pull request #335 from mcarton/pacman
Add a new rule for pacman/yaourt and fix the apt_get rule
2015-08-16 03:38:50 +03:00
Michael Lee
8c9416e57f Renamed to unknown command to better match current functionality 2015-08-14 09:38:42 +01:00
mcarton
95607557d6 #277 Fix the apt_get rule with sudo 2015-08-13 18:29:04 +02:00
mcarton
e9ffe2ea9d Fix confusing sentence in the README
Ref: https://github.com/nvbn/thefuck/pull/283#issuecomment-120170664
2015-08-13 18:28:59 +02:00
Michael Lee
c08a8bddc9 More generic a solution, now works with any command that follows the same pattern of error message 2015-08-13 17:19:16 +01:00
mcarton
ca8222e764 Add the pacman_not_found rule 2015-08-13 18:16:25 +02:00
mcarton
986bbb30a7 Create thefuck.archlinux 2015-08-13 18:07:24 +02:00
Michael Lee
2cdfe105fb Reorderd be in alphabetical order 2015-08-13 16:23:11 +01:00
Michael Lee
b494c4e273 basic support for the hdfs dfs <command> when the command misses the dash 2015-08-13 13:09:19 +01:00
Vladimir Iakovlev
0ad70a1edc Merge pull request #332 from mcarton/331
#331 Fix bug with empty commands
2015-08-11 16:04:00 +03:00
mcarton
285d57eb01 #331 Fix bug with empty commands 2015-08-11 11:08:21 +02:00
58 changed files with 580 additions and 215 deletions

View File

@@ -94,7 +94,15 @@ Reading package lists... Done
- pip
- python-dev
## Installation
## Installation [*experimental*]
On Ubuntu and OS X you can install `The Fuck` with installation script:
```bash
wget -O - https://raw.githubusercontent.com/nvbn/thefuck/master/install.sh | sh -
```
## Manual installation
Install `The Fuck` with `pip`:
@@ -163,7 +171,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `grep_recursive` &ndash; adds `-r` when you trying to `grep` directory;
* `gulp_not_task` &ndash; fixes misspelled gulp tasks;
* `has_exists_script` &ndash; prepends `./` when script/binary exists;
* `heroku_no_command` &ndash; fixes wrong `heroku` commands like `heroku log`;
* `heroku_not_command` &ndash; fixes wrong `heroku` commands like `heroku log`;
* `history` &ndash; tries to replace command with most similar command from history;
* `java` &ndash; removes `.java` extension when running Java programs;
* `javac` &ndash; appends missing `.java` when compiling Java files;
@@ -185,12 +193,14 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `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;
* `switch_lang` &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`;
* `tsuru_login` &ndash; runs `tsuru login` if not authenticated or session expired;
* `tsuru_not_command` &ndash; fixes wrong tsuru commands like `tsuru shell`;
* `tsuru_not_command` &ndash; fixes wrong `tsuru` commands like `tsuru shell`;
* `tmux` &ndash; fixes `tmux` commands;
* `unknown_command` &ndash; fixes hadoop hdfs-style "unknown command" for example adds missing '-' to the command on `hdfs dfs ls`;
* `vagrant_up` &ndash; starts up the vagrant instance;
* `whois` &ndash; fixes `whois` command.
Enabled by default only on specific platforms:
@@ -199,7 +209,8 @@ Enabled by default only on specific platforms:
* `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.
* `pacman` &ndash; installs app with `pacman` if it is not installed (uses `yaourt` if available).
* `pacman_not_found` &ndash; fix package name with `pacman` or `yaourt`;
Bundled, but not enabled by default:
@@ -216,7 +227,11 @@ match(command: Command, settings: Settings) -> bool
get_new_command(command: Command, settings: Settings) -> str | list[str]
```
Also the rule can contain an optional function `side_effect(command: Command, settings: Settings) -> None`
Also the rule can contain an optional function
```python
side_effect(old_command: Command, fixed_command: str, settings: Settings) -> None
```
and optional `enabled_by_default`, `requires_output` and `priority` variables.
`Command` has three attributes: `script`, `stdout` and `stderr`.

35
install.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/sh
# Install os dependencies:
if [ -f $(which apt-get) ]; then
sudo apt-get install python-pip
else
if [ -f $(which brew) ]; then
brew install python
fi
fi
# thefuck requires fresh versions of setuptools and pip:
sudo pip install -U pip setuptools
sudo pip install -U thefuck
# Setup aliases:
if [ -f ~/.bashrc ]; then
echo 'eval $(thefuck --alias)' >> ~/.bashrc
fi
if [ -f ~/.bash_profile ]; then
echo 'eval $(thefuck --alias)' >> ~/.bash_profile
fi
if [ -f ~/.zshrc ]; then
echo 'eval $(thefuck --alias)' >> ~/.zshrc
fi
if [ -f ~/.config/fish/config.fish ]; then
thefuck --alias >> ~/.config/fish/config.fish
fi
if [ -f ~/.tcshrc ]; then
echo 'eval `thefuck --alias`' >> ~/.tcshrc
fi

View File

@@ -20,7 +20,7 @@ elif (3, 0) < version < (3, 3):
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '2.7'
VERSION = '2.8'
install_requires = ['psutil', 'colorama', 'six']
extras_require = {':python_version<"3.4"': ['pathlib']}

View File

@@ -16,6 +16,8 @@ def test_match(command):
@pytest.mark.parametrize('command, return_value', [
(Command(script='vim', stderr='vim: command not found'),
[('vim', 'main'), ('vim-tiny', 'main')]),
(Command(script='sudo vim', stderr='vim: command not found'),
[('vim', 'main'), ('vim-tiny', 'main')])])
@patch('thefuck.rules.apt_get.CommandNotFound', create=True)
@patch.multiple(apt_get, create=True, apt_get='apt_get')
@@ -38,7 +40,9 @@ def test_not_match(command):
reason='Skip if python-commandnotfound is not available')
@pytest.mark.parametrize('command, new_command', [
(Command('vim'), 'sudo apt-get install vim && vim'),
(Command('convert'), 'sudo apt-get install imagemagick && convert')])
(Command('convert'), 'sudo apt-get install imagemagick && convert'),
(Command('sudo vim'), 'sudo apt-get install vim && sudo vim'),
(Command('sudo convert'), 'sudo apt-get install imagemagick && sudo convert')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command
@@ -47,6 +51,11 @@ def test_get_new_command(command, new_command):
(Command('vim'), 'sudo apt-get install vim && vim',
[('vim', 'main'), ('vim-tiny', 'main')]),
(Command('convert'), 'sudo apt-get install imagemagick && convert',
[('imagemagick', 'main'),
('graphicsmagick-imagemagick-compat', 'universe')]),
(Command('sudo vim'), 'sudo apt-get install vim && sudo vim',
[('vim', 'main'), ('vim-tiny', 'main')]),
(Command('sudo convert'), 'sudo apt-get install imagemagick && sudo convert',
[('imagemagick', 'main'),
('graphicsmagick-imagemagick-compat', 'universe')])])
@patch('thefuck.rules.apt_get.CommandNotFound', create=True)
@@ -55,5 +64,3 @@ def test_get_new_command_mocked(cmdnf_mock, command, new_command, return_value):
get_packages = Mock(return_value=return_value)
cmdnf_mock.CommandNotFound.return_value = Mock(getPackages=get_packages)
assert get_new_command(command, None) == new_command
assert cmdnf_mock.CommandNotFound.called
assert get_packages.called

View File

@@ -5,34 +5,38 @@ from tests.utils import Command
@pytest.fixture
def composer_not_command():
return """
[InvalidArgumentException]
Command "udpate" is not defined.
Did you mean this?
update
"""
# that weird spacing is part of the actual command output
return (
'\n'
'\n'
' \n'
' [InvalidArgumentException] \n'
' Command "udpate" is not defined. \n'
' Did you mean this? \n'
' update \n'
' \n'
'\n'
'\n'
)
@pytest.fixture
def composer_not_command_one_of_this():
return """
[InvalidArgumentException]
Command "pdate" is not defined.
Did you mean one of these?
selfupdate
self-update
update
"""
# that weird spacing is part of the actual command output
return (
'\n'
'\n'
' \n'
' [InvalidArgumentException] \n'
' Command "pdate" is not defined. \n'
' Did you mean one of these? \n'
' selfupdate \n'
' self-update \n'
' update \n'
' \n'
'\n'
'\n'
)
def test_match(composer_not_command, composer_not_command_one_of_this):

View File

@@ -40,6 +40,7 @@ parametrize_script = pytest.mark.parametrize('script, fixed', [
('tar -xvf {}', 'mkdir -p foo && tar -xvf {} -C foo'),
('tar --extract -f {}', 'mkdir -p foo && tar --extract -f {} -C foo')])
@parametrize_filename
@parametrize_script
def test_match(tar_error, filename, script, fixed):
@@ -51,7 +52,7 @@ def test_match(tar_error, filename, script, fixed):
@parametrize_script
def test_side_effect(tar_error, filename, script, fixed):
tar_error(filename)
side_effect(Command(script=script.format(filename)), None)
side_effect(Command(script=script.format(filename)), None, None)
assert(os.listdir('.') == [filename])

View File

@@ -34,7 +34,7 @@ def test_match(zip_error, script):
'unzip foo',
'unzip foo.zip'])
def test_side_effect(zip_error, script):
side_effect(Command(script=script), None)
side_effect(Command(script=script), None, None)
assert(os.listdir('.') == ['foo.zip'])

View File

@@ -2,11 +2,12 @@ import pytest
import os
from thefuck.rules.fix_file import match, get_new_command
from tests.utils import Command
from thefuck.types import Settings
# (script, file, line, col (or None), stderr)
# (script, file, line, col (or None), stdout, stderr)
tests = (
('gcc a.c', 'a.c', 3, 1,
('gcc a.c', 'a.c', 3, 1, '',
"""
a.c: In function 'main':
a.c:3:1: error: expected expression before '}' token
@@ -14,47 +15,47 @@ a.c:3:1: error: expected expression before '}' token
^
"""),
('clang a.c', 'a.c', 3, 1,
('clang a.c', 'a.c', 3, 1, '',
"""
a.c:3:1: error: expected expression
}
^
"""),
('perl a.pl', 'a.pl', 3, None,
('perl a.pl', 'a.pl', 3, None, '',
"""
syntax error at a.pl line 3, at EOF
Execution of a.pl aborted due to compilation errors.
"""),
('perl a.pl', 'a.pl', 2, None,
('perl a.pl', 'a.pl', 2, None, '',
"""
Search pattern not terminated at a.pl line 2.
"""),
('sh a.sh', 'a.sh', 2, None,
('sh a.sh', 'a.sh', 2, None, '',
"""
a.sh: line 2: foo: command not found
"""),
('zsh a.sh', 'a.sh', 2, None,
('zsh a.sh', 'a.sh', 2, None, '',
"""
a.sh:2: command not found: foo
"""),
('bash a.sh', 'a.sh', 2, None,
('bash a.sh', 'a.sh', 2, None, '',
"""
a.sh: line 2: foo: command not found
"""),
('rustc a.rs', 'a.rs', 2, 5,
('rustc a.rs', 'a.rs', 2, 5, '',
"""
a.rs:2:5: 2:6 error: unexpected token: `+`
a.rs:2 +
^
"""),
('cargo build', 'src/lib.rs', 3, 5,
('cargo build', 'src/lib.rs', 3, 5, '',
"""
Compiling test v0.1.0 (file:///tmp/fix-error/test)
src/lib.rs:3:5: 3:6 error: unexpected token: `+`
@@ -65,7 +66,7 @@ Could not compile `test`.
To learn more, run the command again with --verbose.
"""),
('python a.py', 'a.py', 2, None,
('python a.py', 'a.py', 2, None, '',
"""
File "a.py", line 2
+
@@ -73,7 +74,7 @@ To learn more, run the command again with --verbose.
SyntaxError: invalid syntax
"""),
('python a.py', 'a.py', 8, None,
('python a.py', 'a.py', 8, None, '',
"""
Traceback (most recent call last):
File "a.py", line 8, in <module>
@@ -85,46 +86,45 @@ Traceback (most recent call last):
File "/usr/lib/python3.4/re.py", line 293, in _compile
raise TypeError("first argument must be string or compiled pattern")
TypeError: first argument must be string or compiled pattern
"""
),
"""),
('ruby a.rb', 'a.rb', 3, None,
('ruby a.rb', 'a.rb', 3, None, '',
"""
a.rb:3: syntax error, unexpected keyword_end
"""),
('lua a.lua', 'a.lua', 2, None,
('lua a.lua', 'a.lua', 2, None, '',
"""
lua: a.lua:2: unexpected symbol near '+'
"""),
('fish a.sh', '/tmp/fix-error/a.sh', 2, None,
('fish a.sh', '/tmp/fix-error/a.sh', 2, None, '',
"""
fish: Unknown command 'foo'
/tmp/fix-error/a.sh (line 2): foo
^
"""),
('./a', './a', 2, None,
('./a', './a', 2, None, '',
"""
awk: ./a:2: BEGIN { print "Hello, world!" + }
awk: ./a:2: ^ syntax error
"""),
('llc a.ll', 'a.ll', 1, None,
('llc a.ll', 'a.ll', 1, 2, '',
"""
llc: a.ll:1:1: error: expected top-level entity
llc: a.ll:1:2: error: expected top-level entity
+
^
"""),
('go build a.go', 'a.go', 1, None,
('go build a.go', 'a.go', 1, 2, '',
"""
can't load package:
a.go:1:1: expected 'package', found '+'
a.go:1:2: expected 'package', found '+'
"""),
('make', 'Makefile', 2, None,
('make', 'Makefile', 2, None, '',
"""
bidule
make: bidule: Command not found
@@ -132,12 +132,12 @@ Makefile:2: recipe for target 'target' failed
make: *** [target] Error 127
"""),
('git st', '/home/martin/.config/git/config', 1, None,
('git st', '/home/martin/.config/git/config', 1, None, '',
"""
fatal: bad config file line 1 in /home/martin/.config/git/config
"""),
('node fuck.js asdf qwer', '/Users/pablo/Workspace/barebones/fuck.js', '2', 5,
('node fuck.js asdf qwer', '/Users/pablo/Workspace/barebones/fuck.js', '2', 5, '',
"""
/Users/pablo/Workspace/barebones/fuck.js:2
conole.log(arg); // this should read console.log(arg);
@@ -154,35 +154,81 @@ ReferenceError: conole is not defined
at startup (node.js:129:16)
at node.js:814:3
"""),
('pep8', './tests/rules/test_systemctl.py', 17, 80,
"""
./tests/rules/test_systemctl.py:17:80: E501 line too long (93 > 79 characters)
./tests/rules/test_systemctl.py:18:80: E501 line too long (103 > 79 characters)
./tests/rules/test_whois.py:20:80: E501 line too long (89 > 79 characters)
./tests/rules/test_whois.py:22:80: E501 line too long (83 > 79 characters)
""", ''),
('py.test', '/home/thefuck/tests/rules/test_fix_file.py', 218, None,
"""
monkeypatch = <_pytest.monkeypatch.monkeypatch object at 0x7fdb76a25b38>
test = ('fish a.sh', '/tmp/fix-error/a.sh', 2, None, '', "\\nfish: Unknown command 'foo'\\n/tmp/fix-error/a.sh (line 2): foo\\n ^\\n")
@pytest.mark.parametrize('test', tests)
@pytest.mark.usefixtures('no_memoize')
def test_get_new_command(monkeypatch, test):
> mocker.patch('os.path.isfile', return_value=True)
E NameError: name 'mocker' is not defined
/home/thefuck/tests/rules/test_fix_file.py:218: NameError
""", ''),
)
@pytest.mark.parametrize('test', tests)
@pytest.mark.usefixtures('no_memoize')
def test_match(mocker, monkeypatch, test):
mocker.patch('os.path.isfile', return_value=True)
monkeypatch.setenv('EDITOR', 'dummy_editor')
assert match(Command(stderr=test[4]), None)
assert match(Command(stdout=test[4], stderr=test[5]), None)
@pytest.mark.parametrize('test', tests)
@pytest.mark.usefixtures('no_memoize')
def test_no_editor(mocker, monkeypatch, test):
mocker.patch('os.path.isfile', return_value=True)
if 'EDITOR' in os.environ:
monkeypatch.delenv('EDITOR')
assert not match(Command(stderr=test[4]), None)
assert not match(Command(stdout=test[4], stderr=test[5]), None)
@pytest.mark.parametrize('test', tests)
@pytest.mark.usefixtures('no_memoize')
def test_not_file(mocker, monkeypatch, test):
mocker.patch('os.path.isfile', return_value=False)
monkeypatch.setenv('EDITOR', 'dummy_editor')
assert not match(Command(stderr=test[4]), None)
assert not match(Command(stdout=test[4], stderr=test[5]), None)
@pytest.mark.parametrize('test', tests)
def test_get_new_command(monkeypatch, test):
@pytest.mark.usefixtures('no_memoize')
def test_get_new_command(mocker, monkeypatch, test):
mocker.patch('os.path.isfile', return_value=True)
monkeypatch.setenv('EDITOR', 'dummy_editor')
assert (get_new_command(Command(script=test[0], stderr=test[4]), None) ==
'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))
cmd = Command(script=test[0], stdout=test[4], stderr=test[5])
#assert (get_new_command(cmd, Settings({})) ==
# 'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))
@pytest.mark.parametrize('test', tests)
@pytest.mark.usefixtures('no_memoize')
def test_get_new_command_with_settings(mocker, monkeypatch, test):
mocker.patch('os.path.isfile', return_value=True)
monkeypatch.setenv('EDITOR', 'dummy_editor')
cmd = Command(script=test[0], stdout=test[4], stderr=test[5])
settings = Settings({'fixcolcmd': '{editor} {file} +{line}:{col}'})
if test[3]:
assert (get_new_command(cmd, settings) ==
'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0]))
else:
assert (get_new_command(cmd, settings) ==
'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))

View File

@@ -2,6 +2,7 @@ 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)

View File

@@ -10,7 +10,7 @@ usage: git stash list [<options>]
or: git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]
or: git stash branch <branchname> [<stash>]
or: git stash [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [<message>]]
\t\t [-u|--include-untracked] [-a|--all] [<message>]]
or: git stash clear
'''

View File

@@ -30,8 +30,8 @@ def git_not_command_closest():
return '''git: 'tags' is not a git command. See 'git --help'.
Did you mean one of these?
stage
tag
\tstage
\ttag
'''

View File

@@ -4,7 +4,7 @@ from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='go run foo'),
Command(script='go run foo'),
Command(script='go run bar')])
def test_match(command):
assert match(command, None)

View File

@@ -2,7 +2,7 @@ import pytest
from tests.utils import Command
from thefuck.rules.mercurial import (
extract_possisiblities, match, get_new_command
extract_possibilities, match, get_new_command
)
@@ -96,8 +96,8 @@ def test_not_match(command):
'\n rebase recover remove rename resolve revert'
)), ['rebase', 'recover', 'remove', 'rename', 'resolve', 'revert']),
])
def test_extract_possisiblities(command, possibilities):
assert extract_possisiblities(command) == possibilities
def test_extract_possibilities(command, possibilities):
assert extract_possibilities(command) == possibilities
@pytest.mark.parametrize('command, new_command', [

View File

@@ -3,20 +3,29 @@ from thefuck.rules.mkdir_p import match, get_new_command
from tests.utils import Command
def test_match():
assert match(Command('mkdir foo/bar/baz',
stderr='mkdir: foo/bar: No such file or directory'),
None)
@pytest.mark.parametrize('command', [
Command('mkdir foo/bar/baz', stderr='mkdir: foo/bar: No such file or directory'),
Command('./bin/hdfs dfs -mkdir foo/bar/baz', stderr='mkdir: `foo/bar/baz\': No such file or directory'),
Command('hdfs dfs -mkdir foo/bar/baz', stderr='mkdir: `foo/bar/baz\': No such file or directory')
])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command', [
Command('mkdir foo/bar/baz'),
Command('mkdir foo/bar/baz', stderr='foo bar baz'),
Command('hdfs dfs -mkdir foo/bar/baz'),
Command('./bin/hdfs dfs -mkdir foo/bar/baz'),
Command()])
def test_not_match(command):
assert not match(command, None)
def test_get_new_command():
assert get_new_command(Command('mkdir foo/bar/baz'), None)\
== 'mkdir -p foo/bar/baz'
@pytest.mark.parametrize('command, new_command', [
(Command('mkdir foo/bar/baz'), 'mkdir -p foo/bar/baz'),
(Command('hdfs dfs -mkdir foo/bar/baz'), 'hdfs dfs -mkdir -p foo/bar/baz'),
(Command('./bin/hdfs dfs -mkdir foo/bar/baz'), './bin/hdfs dfs -mkdir -p foo/bar/baz')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -9,7 +9,10 @@ from tests.utils import Command
Command(script='open foo.org'),
Command(script='open foo.net'),
Command(script='open foo.se'),
Command(script='open foo.io')])
Command(script='open foo.io'),
Command(script='xdg-open foo.com'),
Command(script='gnome-open foo.com'),
Command(script='kde-open foo.com')])
def test_match(command):
assert match(command, None)
@@ -20,6 +23,9 @@ def test_match(command):
(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')])
(Command('open foo.io'), 'open http://foo.io'),
(Command('xdg-open foo.io'), 'xdg-open http://foo.io'),
(Command('gnome-open foo.io'), 'gnome-open http://foo.io'),
(Command('kde-open foo.io'), 'kde-open http://foo.io')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -7,6 +7,7 @@ from tests.utils import Command
pacman_cmd = getattr(pacman, 'pacman', 'pacman')
PKGFILE_OUTPUT_SUDO = 'core/sudo 1.8.13-13/usr/bin/sudo'
PKGFILE_OUTPUT_CONVERT = 'extra/imagemagick 6.9.1.0-1\t/usr/bin/convert'
PKGFILE_OUTPUT_VIM = '''extra/gvim 7.4.712-1 \t/usr/bin/vim
@@ -28,7 +29,7 @@ def test_match(command):
@pytest.mark.parametrize('command, return_value', [
(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('thefuck.archlinux.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
@@ -72,8 +73,9 @@ def test_get_new_command(command, new_command, mocker):
(Command('vim'), vim_possibilities, PKGFILE_OUTPUT_VIM),
(Command('sudo vim'), sudo_vim_possibilities, PKGFILE_OUTPUT_VIM),
(Command('convert'), ['{} -S extra/imagemagick && convert'.format(pacman_cmd)], PKGFILE_OUTPUT_CONVERT),
(Command('sudo'), ['{} -S core/sudo && sudo'.format(pacman_cmd)], PKGFILE_OUTPUT_SUDO),
(Command('sudo convert'), ['{} -S extra/imagemagick && sudo convert'.format(pacman_cmd)], PKGFILE_OUTPUT_CONVERT)])
@patch('thefuck.rules.pacman.subprocess')
@patch('thefuck.archlinux.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

View File

@@ -0,0 +1,48 @@
import pytest
from mock import patch
from thefuck.rules import pacman_not_found
from thefuck.rules.pacman_not_found import match, get_new_command
from tests.utils import Command
PKGFILE_OUTPUT_LLC = '''extra/llvm 3.6.0-5 /usr/bin/llc
extra/llvm35 3.5.2-13/usr/bin/llc'''
@pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True),
reason='Skip if pacman is not available')
@pytest.mark.parametrize('command', [
Command(script='yaourt -S llc', stderr='error: target not found: llc'),
Command(script='pacman llc', stderr='error: target not found: llc'),
Command(script='sudo pacman llc', stderr='error: target not found: llc')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command', [
Command(script='yaourt -S llc', stderr='error: target not found: llc'),
Command(script='pacman llc', stderr='error: target not found: llc'),
Command(script='sudo pacman llc', stderr='error: target not found: llc')])
@patch('thefuck.archlinux.subprocess')
def test_match_mocked(subp_mock, command):
subp_mock.check_output.return_value = PKGFILE_OUTPUT_LLC
assert match(command, None)
@pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True),
reason='Skip if pacman is not available')
@pytest.mark.parametrize('command, fixed', [
(Command(script='yaourt -S llc', stderr='error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
(Command(script='pacman -S llc', stderr='error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
(Command(script='sudo pacman -S llc', stderr='error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])
def test_get_new_command(command, fixed):
assert get_new_command(command, None) == fixed
@pytest.mark.parametrize('command, fixed', [
(Command(script='yaourt -S llc', stderr='error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
(Command(script='pacman -S llc', stderr='error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
(Command(script='sudo pacman -S llc', stderr='error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])
@patch('thefuck.archlinux.subprocess')
def test_get_new_command_mocked(subp_mock, command, fixed):
subp_mock.check_output.return_value = PKGFILE_OUTPUT_LLC
assert get_new_command(command, None) == fixed

View File

@@ -5,17 +5,27 @@ from tests.utils import Command
@pytest.mark.parametrize('command', [
Command('rm foo', stderr='rm: foo: is a directory'),
Command('rm foo', stderr='rm: foo: Is a directory')])
Command('rm foo', stderr='rm: foo: Is a directory'),
Command('hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory'),
Command('./bin/hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory')
])
def test_match(command):
assert match(command, None)
assert match(command, None)
@pytest.mark.parametrize('command', [
Command('rm foo'), Command('rm foo'), Command()])
Command('rm foo'),
Command('hdfs dfs -rm foo'),
Command('./bin/hdfs dfs -rm foo'),
Command()])
def test_not_match(command):
assert not match(command, None)
def test_get_new_command():
assert get_new_command(Command('rm foo', '', ''), None) == 'rm -rf foo'
@pytest.mark.parametrize('command, new_command', [
(Command('rm foo'), 'rm -rf foo'),
(Command('hdfs dfs -rm foo'), 'hdfs dfs -rm -r foo')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -56,7 +56,7 @@ def test_match(ssh_error):
def test_side_effect(ssh_error):
errormsg, path, reset, known_hosts = ssh_error
command = Command('ssh user@host', stderr=errormsg)
side_effect(command, None)
side_effect(command, None, None)
expected = ['123.234.567.890 asdjkasjdakjsd\n', '111.222.333.444 qwepoiwqepoiss\n']
assert known_hosts(path) == expected

View File

@@ -3,7 +3,6 @@ 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)
@@ -13,6 +12,7 @@ def test_match():
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"

View File

@@ -0,0 +1,35 @@
import pytest
from thefuck.rules.unknown_command import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='./bin/hdfs dfs ls', stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.'),
Command(script='hdfs dfs ls',
stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.'),
Command(script='hdfs dfs ls /foo/bar', stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command', [
Command(script='./bin/hdfs dfs -ls', stderr=''),
Command(script='./bin/hdfs dfs -ls /foo/bar', stderr=''),
Command(script='hdfs dfs -ls -R /foo/bar', stderr=''),
Command()])
def test_not_match(command):
assert not match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('hdfs dfs ls',
stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.'), ['hdfs dfs -ls']),
(Command('hdfs dfs rm /foo/bar',
stderr='rm: Unknown command\nDid you mean -rm? This command begins with a dash.'), ['hdfs dfs -rm /foo/bar']),
(Command('./bin/hdfs dfs ls -R /foo/bar',
stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.'), ['./bin/hdfs dfs -ls -R /foo/bar']),
(Command('./bin/hdfs dfs -Dtest=fred ls -R /foo/bar',
stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.'), ['./bin/hdfs dfs -Dtest=fred -ls -R /foo/bar'])])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -0,0 +1,35 @@
import pytest
from thefuck.rules.vagrant_up import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='vagrant ssh', stderr='VM must be running to open SSH connection. Run `vagrant up`\nto start the virtual machine.'),
Command(script='vagrant ssh devbox', stderr='VM must be running to open SSH connection. Run `vagrant up`\nto start the virtual machine.'),
Command(script='vagrant rdp',
stderr='VM must be created before running this command. Run `vagrant up` first.'),
Command(script='vagrant rdp devbox',
stderr='VM must be created before running this command. Run `vagrant up` first.')])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command', [
Command(script='vagrant ssh', stderr=''),
Command(script='vagrant ssh jeff', stderr='The machine with the name \'jeff\' was not found configured for this Vagrant environment.'),
Command(script='vagrant ssh', stderr='A Vagrant environment or target machine is required to run this command. Run `vagrant init` to create a new Vagrant environment. Or, get an ID of a target machine from `vagrant global-status` to run this command on. A final option is to change to a directory with a Vagrantfile and to try again.'),
Command()])
def test_not_match(command):
assert not match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command(script='vagrant ssh', stderr='VM must be running to open SSH connection. Run `vagrant up`\nto start the virtual machine.'), 'vagrant up && vagrant ssh'),
(Command(script='vagrant ssh devbox', stderr='VM must be running to open SSH connection. Run `vagrant up`\nto start the virtual machine.'), ['vagrant up devbox && vagrant ssh devbox', 'vagrant up && vagrant ssh devbox']),
(Command(script='vagrant rdp',
stderr='VM must be created before running this command. Run `vagrant up` first.'), 'vagrant up && vagrant rdp'),
(Command(script='vagrant rdp devbox',
stderr='VM must be created before running this command. Run `vagrant up` first.'), ['vagrant up devbox && vagrant rdp devbox', 'vagrant up && vagrant rdp devbox'])])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -35,6 +35,8 @@ class TestGetCommand(object):
env={})
@pytest.mark.parametrize('args, result', [
(['thefuck', ''], None),
(['thefuck', '', ''], None),
(['thefuck', 'ls', '-la'], 'ls -la'),
(['thefuck', 'ls'], 'ls')])
def test_get_command_script(self, args, result):

17
tests/test_readme.py Normal file
View File

@@ -0,0 +1,17 @@
from pathlib import Path
def test_readme():
project_root = Path(__file__).parent.parent
with project_root.joinpath('README.md').open() as f:
readme = f.read()
bundled = project_root \
.joinpath('thefuck') \
.joinpath('rules') \
.glob('*.py')
for rule in bundled:
if rule.stem != '__init__':
assert rule.stem in readme,\
'Missing rule "{}" in README.md'.format(rule.stem)

View File

@@ -11,6 +11,7 @@ def test_rules_names_list():
def test_update_settings():
settings = Settings({'key': 'val'})
new_settings = settings.update(key='new-val')
assert new_settings.key == 'new-val'
new_settings = settings.update(key='new-val', unset='unset-value')
assert new_settings.key == 'val'
assert new_settings.unset == 'unset-value'
assert settings.key == 'val'

View File

@@ -9,7 +9,8 @@ from tests.utils import Command
@pytest.mark.parametrize('override, old, new', [
({'key': 'val'}, {}, {'key': 'val'}),
({'key': 'new-val'}, {'key': 'val'}, {'key': 'new-val'})])
({'key': 'new-val'}, {'key': 'val'}, {'key': 'val'}),
({'key': 'new-val', 'unset': 'unset'}, {'key': 'val'}, {'key': 'val', 'unset': 'unset'})])
def test_wrap_settings(override, old, new):
fn = lambda _, settings: settings
assert wrap_settings(override)(fn)(None, Settings(old)) == new

41
thefuck/archlinux.py Normal file
View File

@@ -0,0 +1,41 @@
""" This file provide some utility functions for Arch Linux specific rules."""
import thefuck.utils
import subprocess
@thefuck.utils.memoize
def get_pkgfile(command):
""" Gets the packages that provide the given command using `pkgfile`.
If the command is of the form `sudo foo`, searches for the `foo` command
instead.
"""
try:
command = command.strip()
if command.startswith('sudo '):
command = command[5:]
command = command.split(" ")[0]
packages = subprocess.check_output(
['pkgfile', '-b', '-v', command],
universal_newlines=True, stderr=thefuck.utils.DEVNULL
).splitlines()
return [package.split()[0] for package in packages]
except subprocess.CalledProcessError:
return None
def archlinux_env():
if thefuck.utils.which('yaourt'):
pacman = 'yaourt'
elif thefuck.utils.which('pacman'):
pacman = 'sudo pacman'
else:
return False, None
enabled_by_default = thefuck.utils.which('pkgfile')
return enabled_by_default, pacman

View File

@@ -27,7 +27,7 @@ def setup_user_dir():
def wait_output(settings, popen):
"""Returns `True` if we can get output of the command in the
`wait_command` time.
`settings.wait_command` time.
Command will be killed if it wasn't finished in the time.
@@ -50,6 +50,7 @@ def get_command(settings, args):
else:
script = ' '.join(args[1:])
script = script.strip()
if not script:
return
@@ -73,10 +74,10 @@ def get_command(settings, args):
return types.Command(script, None, None)
def run_command(command, settings):
def run_command(old_cmd, command, settings):
"""Runs command from rule for passed command."""
if command.side_effect:
command.side_effect(command, settings)
command.side_effect(old_cmd, command.script, settings)
shells.put_to_history(command.script)
print(command.script)
@@ -91,15 +92,20 @@ def fix_command():
logs.debug(u'Run with settings: {}'.format(pformat(settings)), settings)
command = get_command(settings, sys.argv)
if not command:
logs.debug('Empty command, nothing to do', settings)
return
corrected_commands = get_corrected_commands(command, user_dir, settings)
selected_command = select_command(corrected_commands, settings)
if selected_command:
run_command(selected_command, settings)
run_command(command, selected_command, settings)
def print_alias(entry_point=True):
if entry_point:
warn('`thefuck-alias` is deprecated, us `thefuck --alias` instead.')
warn('`thefuck-alias` is deprecated, use `thefuck --alias` instead.')
position = 1
else:
position = 2

View File

@@ -1,27 +1,30 @@
from thefuck import shells
from thefuck.utils import sudo_support
from thefuck.utils import memoize
try:
import CommandNotFound
except ImportError:
enabled_by_default = False
@sudo_support
def match(command, settings):
if 'not found' in command.stderr:
try:
c = CommandNotFound.CommandNotFound()
pkgs = c.getPackages(command.script.split(" ")[0])
name, _ = pkgs[0]
return True
except IndexError:
# IndexError is thrown when no matching package is found
return False
@sudo_support
@memoize
def get_package(command):
try:
c = CommandNotFound.CommandNotFound()
cmd = command.split(' ')
pkgs = c.getPackages(cmd[0] if cmd[0] != 'sudo' else cmd[1])
name, _ = pkgs[0]
return name
except IndexError:
# IndexError is thrown when no matching package is found
return None
def match(command, settings):
return 'not found' in command.stderr and get_package(command.script)
def get_new_command(command, settings):
c = CommandNotFound.CommandNotFound()
pkgs = c.getPackages(command.script.split(" ")[0])
name, _ = pkgs[0]
name = get_package(command.script)
formatme = shells.and_('sudo apt-get install {}', '{}')
return formatme.format(name, command.script)

View File

@@ -1,14 +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')
return command.script == 'brew upgrade'
def get_new_command(command, settings):
return command.script + ' --all'

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env python
__author__ = "mmussomele"
"""Attempts to spellcheck and correct failed cd commands"""
import os
@@ -8,6 +5,8 @@ from difflib import get_close_matches
from thefuck.utils import sudo_support
from thefuck.rules import cd_mkdir
__author__ = "mmussomele"
MAX_ALLOWED_DIFF = 0.6
@@ -28,8 +27,8 @@ def match(command, settings):
def get_new_command(command, settings):
"""
Attempt to rebuild the path string by spellchecking the directories.
If it fails (i.e. no directories are a close enough match), then it
defaults to the rules of cd_mkdir.
If it fails (i.e. no directories are a close enough match), then it
defaults to the rules of cd_mkdir.
Change sensitivity by changing MAX_ALLOWED_DIFF. Default value is 0.6
"""
dest = command.script.split()[1].split(os.sep)

View File

@@ -7,8 +7,10 @@
# > cd..
# cd..: command not found
def match(command, settings):
return command.script == 'cd..'
def get_new_command(command, settings):
return 'cd ..'

View File

@@ -13,12 +13,12 @@ def _is_tar_extract(cmd):
def _tar_file(cmd):
tar_extentions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
'.tar.lzma', '.tar.xz', '.taz', '.tb2', '.tbz', '.tbz2',
'.tgz', '.tlz', '.txz', '.tz')
for c in cmd.split():
for ext in tar_extentions:
for ext in tar_extensions:
if c.endswith(ext):
return (c, c[0:len(c)-len(ext)])
@@ -35,7 +35,7 @@ def get_new_command(command, settings):
.format(dir=_tar_file(command.script)[1], cmd=command.script)
def side_effect(command, settings):
with tarfile.TarFile(_tar_file(command.script)[0]) as archive:
def side_effect(old_cmd, command, settings):
with tarfile.TarFile(_tar_file(old_cmd.script)[0]) as archive:
for file in archive.getnames():
os.remove(file)

View File

@@ -30,8 +30,8 @@ def get_new_command(command, settings):
return '{} -d {}'.format(command.script, _zip_file(command)[:-4])
def side_effect(command, settings):
with zipfile.ZipFile(_zip_file(command), 'r') as archive:
def side_effect(old_cmd, command, settings):
with zipfile.ZipFile(_zip_file(old_cmd), 'r') as archive:
for file in archive.namelist():
os.remove(file)

View File

@@ -1,7 +1,7 @@
from itertools import dropwhile, takewhile, islice
import re
import subprocess
from thefuck.utils import get_closest, sudo_support, replace_argument, replace_command
from thefuck.utils import sudo_support, replace_command
@sudo_support

View File

@@ -1,9 +1,10 @@
import re
import os
from thefuck.utils import memoize
from thefuck.utils import memoize, wrap_settings
from thefuck import shells
# order is important: only the first match is considered
patterns = (
# js, node:
'^ at {file}:{line}:{col}',
@@ -20,13 +21,13 @@ patterns = (
# lua:
'^lua: {file}:{line}:',
# fish:
'^{file} \(line {line}\):',
'^{file} \\(line {line}\\):',
# bash, sh, ssh:
'^{file}: line {line}: ',
# cargo, clang, gcc, go, pep8, rustc:
'^{file}:{line}:{col}',
# ghc, make, ruby, zsh:
'^{file}:{line}:',
# cargo, clang, gcc, go, rustc:
'^{file}:{line}:{col}',
# perl:
'at {file} line {line}',
)
@@ -45,7 +46,7 @@ patterns = [_make_pattern(p) for p in patterns]
def _search(stderr):
for pattern in patterns:
m = re.search(pattern, stderr)
if m:
if m and os.path.isfile(m.group('file')):
return m
@@ -53,15 +54,24 @@ def match(command, settings):
if 'EDITOR' not in os.environ:
return False
m = _search(command.stderr)
return m and os.path.isfile(m.group('file'))
return _search(command.stderr) or _search(command.stdout)
@wrap_settings({'fixlinecmd': '{editor} {file} +{line}',
'fixcolcmd': None})
def get_new_command(command, settings):
m = _search(command.stderr)
m = _search(command.stderr) or _search(command.stdout)
# Note: there does not seem to be a standard for columns, so they are just
# ignored for now
return shells.and_('{} {} +{}'.format(os.environ['EDITOR'], m.group('file'), m.group('line')),
command.script)
# ignored by default
if settings.fixcolcmd and 'col' in m.groupdict():
editor_call = settings.fixcolcmd.format(editor=os.environ['EDITOR'],
file=m.group('file'),
line=m.group('line'),
col=m.group('col'))
else:
editor_call = settings.fixlinecmd.format(editor=os.environ['EDITOR'],
file=m.group('file'),
line=m.group('line'))
return shells.and_(editor_call, command.script)

View File

@@ -12,7 +12,7 @@ def match(command, settings):
def get_new_command(command, settings):
missing_file = re.findall(
r"error: pathspec '([^']*)' "
"did not match any file\(s\) known to git.", command.stderr)[0]
r"did not match any file\(s\) known to git.", command.stderr)[0]
formatme = shells.and_('git add -- {}', '{}')
return formatme.format(missing_file, command.script)

View File

@@ -27,7 +27,7 @@ def get_branches():
def get_new_command(command, settings):
missing_file = re.findall(
r"error: pathspec '([^']*)' "
"did not match any file\(s\) known to git.", command.stderr)[0]
r"did not match any file\(s\) known to git.", command.stderr)[0]
closest_branch = utils.get_closest(missing_file, get_branches(),
fallback_to_first=False)
if closest_branch:

View File

@@ -1,14 +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 ')
return (command.script.startswith('go run ')
and not command.script.endswith('.go'))
def get_new_command(command, settings):
return command.script + '.go'

View File

@@ -11,4 +11,3 @@ def match(command, settings):
@sudo_support
def get_new_command(command, settings):
return u'./{}'.format(command.script)

View File

@@ -23,6 +23,7 @@ def _history_of_exists_without_current(command):
if not line.startswith(tf_alias) and not line == command.script
and line.split(' ')[0] in executables]
def match(command, settings):
return len(get_close_matches(command.script,
_history_of_exists_without_current(command)))

View File

@@ -4,7 +4,7 @@
# > javac foo
# error: Class names, 'foo', are only accepted if annotation
# processing is explicitly requested
def match(command, settings):
return (command.script.startswith('javac ')

View File

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

View File

@@ -2,7 +2,7 @@ import re
from thefuck.utils import get_closest
def extract_possisiblities(command):
def extract_possibilities(command):
possib = re.findall(r'\n\(did you mean one of ([^\?]+)\?\)', command.stderr)
if possib:
return possib[0].split(', ')
@@ -24,6 +24,6 @@ def match(command, settings):
def get_new_command(command, settings):
script = command.script.split(' ')
possisiblities = extract_possisiblities(command)
script[1] = get_closest(script[1], possisiblities)
possibilities = extract_possibilities(command)
script[1] = get_closest(script[1], possibilities)
return ' '.join(script)

View File

@@ -10,4 +10,4 @@ def match(command, settings):
@sudo_support
def get_new_command(command, settings):
return re.sub('^mkdir (.*)', 'mkdir -p \\1', command.script)
return re.sub('\\bmkdir (.*)', 'mkdir -p \\1', command.script)

View File

@@ -23,4 +23,4 @@ def match(command, settings):
def get_new_command(command, settings):
return 'open http://' + command.script[5:]
return command.script.replace('open ', 'open http://')

View File

@@ -1,46 +1,16 @@
import subprocess
from thefuck.utils import DEVNULL, which
from thefuck.archlinux import archlinux_env, get_pkgfile
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]
packages = subprocess.check_output(
['pkgfile', '-b', '-v', command],
universal_newlines=True, stderr=DEVNULL
).splitlines()
return [package.split()[0] for package in packages]
except subprocess.CalledProcessError:
return None
def match(command, settings):
return 'not found' in command.stderr and __get_pkgfile(command)
return 'not found' in command.stderr and get_pkgfile(command.script)
def get_new_command(command, settings):
packages = __get_pkgfile(command)
packages = get_pkgfile(command.script)
formatme = shells.and_('{} -S {}', '{}')
return [formatme.format(pacman, package, command.script)
for package in packages]
if not which('pkgfile'):
enabled_by_default = False
elif which('yaourt'):
pacman = 'yaourt'
elif which('pacman'):
pacman = 'sudo pacman'
else:
enabled_by_default = False
enabled_by_default, pacman = archlinux_env()

View File

@@ -0,0 +1,24 @@
""" Fixes wrong package names with pacman or yaourt.
For example the `llc` program is in package `llvm` so this:
yaourt -S llc
should be:
yaourt -S llvm
"""
from thefuck.utils import replace_command
from thefuck.archlinux import archlinux_env, get_pkgfile
def match(command, settings):
return (command.script.startswith(('pacman', 'sudo pacman', 'yaourt'))
and 'error: target not found:' in command.stderr)
def get_new_command(command, settings):
pgr = command.script.split()[-1]
return replace_command(command, pgr, get_pkgfile(pgr))
enabled_by_default, _ = archlinux_env()

View File

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

View File

@@ -10,4 +10,7 @@ def match(command, settings):
@sudo_support
def get_new_command(command, settings):
return re.sub('^rm (.*)', 'rm -rf \\1', command.script)
arguments = '-rf'
if 'hdfs' in command.script:
arguments = '-r'
return re.sub('\\brm (.*)', 'rm ' + arguments + ' \\1', command.script)

View File

@@ -26,8 +26,8 @@ def get_new_command(command, settings):
return command.script
def side_effect(command, settings):
offending = offending_pattern.findall(command.stderr)
def side_effect(old_cmd, command, settings):
offending = offending_pattern.findall(old_cmd.stderr)
for filepath, lineno in offending:
with open(filepath, 'r') as fh:
lines = fh.readlines()

View File

@@ -1,4 +1,4 @@
from thefuck.utils import get_closest, replace_command
from thefuck.utils import replace_command
import re

View File

@@ -1,6 +1,5 @@
import re
from thefuck.utils import (get_closest, replace_argument,
get_all_matched_commands, replace_command)
from thefuck.utils import get_all_matched_commands, replace_command
def match(command, settings):
@@ -14,4 +13,3 @@ def get_new_command(command, settings):
command.stderr)[0]
return replace_command(command, broken_cmd,
get_all_matched_commands(command.stderr))

View File

@@ -0,0 +1,13 @@
import re
from thefuck.utils import (replace_command, get_all_matched_commands)
def match(command, settings):
return (re.search(r"([^:]*): Unknown command.*", command.stderr) != None
and re.search(r"Did you mean ([^?]*)?", command.stderr) != None)
def get_new_command(command, settings):
broken_cmd = re.findall(r"([^:]*): Unknown command.*", command.stderr)[0]
matched = re.findall(r"Did you mean ([^?]*)?", command.stderr)
return replace_command(command, broken_cmd, matched)

View File

@@ -0,0 +1,18 @@
from thefuck import shells
def match(command, settings):
return command.script.startswith('vagrant ') and 'run `vagrant up`' in command.stderr.lower()
def get_new_command(command, settings):
cmds = command.script.split(' ')
machine = None
if len(cmds) >= 3:
machine = cmds[2]
startAllInstances = shells.and_("vagrant up", command.script)
if machine is None:
return startAllInstances
else:
return [ shells.and_("vagrant up " + machine, command.script), startAllInstances]

View File

@@ -84,6 +84,7 @@ 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)
@@ -118,15 +119,16 @@ class Fish(Generic):
" set -l exit_code $status\n"
" set -l eval_script"
" (mktemp 2>/dev/null ; or mktemp -t 'thefuck')\n"
" set -l fucked_up_commandd $history[1]\n"
" thefuck $fucked_up_commandd > $eval_script\n"
" set -l fucked_up_command $history[1]\n"
" thefuck $fucked_up_command > $eval_script\n"
" . $eval_script\n"
" rm $eval_script\n"
" if test $exit_code -ne 0\n"
" history --delete $fucked_up_commandd\n"
" history --delete $fucked_up_command\n"
" end\n"
"end").format(fuck)
@memoize
def get_aliases(self):
overridden = self._get_overridden_aliases()
proc = Popen('fish -ic functions', stdout=PIPE, stderr=DEVNULL,
@@ -168,6 +170,7 @@ 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)
@@ -200,6 +203,7 @@ class Tcsh(Generic):
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)
@@ -216,7 +220,7 @@ class Tcsh(Generic):
return u'#+{}\n{}\n'.format(int(time()), command_script)
shells = defaultdict(lambda: Generic(), {
shells = defaultdict(Generic, {
'bash': Bash(),
'fish': Fish(),
'zsh': Zsh(),
@@ -257,7 +261,6 @@ def and_(*commands):
return _get_shell().and_(*commands)
@memoize
def get_aliases():
return list(_get_shell().get_aliases().keys())

View File

@@ -23,7 +23,9 @@ class Settings(dict):
return self.get(item)
def update(self, **kwargs):
"""Returns new settings with new values from `kwargs`."""
conf = dict(self)
conf.update(kwargs)
"""
Returns new settings with values from `kwargs` for unset settings.
"""
conf = dict(kwargs)
conf.update(self)
return Settings(conf)

View File

@@ -1,3 +1,4 @@
from .types import Command
from difflib import get_close_matches
from functools import wraps
from pathlib import Path
@@ -6,7 +7,6 @@ import os
import pickle
import re
import six
from .types import Command
DEVNULL = open(os.devnull, 'w')