mirror of
				https://github.com/nvbn/thefuck.git
				synced 2025-10-31 07:04:12 +00:00 
			
		
		
		
	| @@ -163,7 +163,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: | |||||||
| * `grep_recursive` – adds `-r` when you trying to `grep` directory; | * `grep_recursive` – adds `-r` when you trying to `grep` directory; | ||||||
| * `gulp_not_task` – fixes misspelled gulp tasks; | * `gulp_not_task` – fixes misspelled gulp tasks; | ||||||
| * `has_exists_script` – prepends `./` when script/binary exists; | * `has_exists_script` – prepends `./` when script/binary exists; | ||||||
| * `heroku_no_command` – fixes wrong `heroku` commands like `heroku log`; | * `heroku_not_command` – fixes wrong `heroku` commands like `heroku log`; | ||||||
| * `history` – tries to replace command with most similar command from history; | * `history` – tries to replace command with most similar command from history; | ||||||
| * `java` – removes `.java` extension when running Java programs; | * `java` – removes `.java` extension when running Java programs; | ||||||
| * `javac` – appends missing `.java` when compiling Java files; | * `javac` – appends missing `.java` when compiling Java files; | ||||||
| @@ -185,7 +185,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: | |||||||
| * `sl_ls` – changes `sl` to `ls`; | * `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_lang` – switches command from your local layout to en; | ||||||
| * `systemctl` – correctly orders parameters of confusing `systemctl`; | * `systemctl` – correctly orders parameters of confusing `systemctl`; | ||||||
| * `test.py` – runs `py.test` instead of `test.py`; | * `test.py` – runs `py.test` instead of `test.py`; | ||||||
| * `tsuru_login` – runs `tsuru login` if not authenticated or session expired; | * `tsuru_login` – runs `tsuru login` if not authenticated or session expired; | ||||||
| @@ -201,6 +201,7 @@ Enabled by default only on specific platforms: | |||||||
| * `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`; | * `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; | * `brew_upgrade` – appends `--all` to `brew upgrade` as per Homebrew's new behaviour; | ||||||
| * `pacman` – installs app with `pacman` if it is not installed (uses `yaourt` if available). | * `pacman` – installs app with `pacman` if it is not installed (uses `yaourt` if available). | ||||||
|  | * `pacman_not_found` – fix package name with `pacman` or `yaourt`; | ||||||
|  |  | ||||||
| Bundled, but not enabled by default: | Bundled, but not enabled by default: | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,34 +5,38 @@ from tests.utils import Command | |||||||
|  |  | ||||||
| @pytest.fixture | @pytest.fixture | ||||||
| def composer_not_command(): | def composer_not_command(): | ||||||
|     return """ |     # that weird spacing is part of the actual command output | ||||||
|  |     return ( | ||||||
|                                      |         '\n' | ||||||
|   [InvalidArgumentException]         |         '\n' | ||||||
|   Command "udpate" is not defined.   |         '                                    \n' | ||||||
|   Did you mean this?                 |         '  [InvalidArgumentException]        \n' | ||||||
|       update |         '  Command "udpate" is not defined.  \n' | ||||||
|  |         '  Did you mean this?                \n' | ||||||
|  |         '      update                        \n' | ||||||
| """ |         '                                    \n' | ||||||
|  |         '\n' | ||||||
|  |         '\n' | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @pytest.fixture | @pytest.fixture | ||||||
| def composer_not_command_one_of_this(): | def composer_not_command_one_of_this(): | ||||||
|     return """ |     # that weird spacing is part of the actual command output | ||||||
|                              |     return ( | ||||||
|  |         '\n' | ||||||
|  |         '\n' | ||||||
|   [InvalidArgumentException]        |         '                                   \n' | ||||||
|   Command "pdate" is not defined.   |         '  [InvalidArgumentException]       \n' | ||||||
|   Did you mean one of these?        |         '  Command "pdate" is not defined.  \n' | ||||||
|       selfupdate                    |         '  Did you mean one of these?       \n' | ||||||
|       self-update                   |         '      selfupdate                   \n' | ||||||
|       update                        |         '      self-update                  \n' | ||||||
|  |         '      update                       \n' | ||||||
|  |         '                                   \n' | ||||||
|  |         '\n' | ||||||
| """ |         '\n' | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_match(composer_not_command, composer_not_command_one_of_this): | def test_match(composer_not_command, composer_not_command_one_of_this): | ||||||
|   | |||||||
| @@ -40,6 +40,7 @@ parametrize_script = pytest.mark.parametrize('script, fixed', [ | |||||||
|     ('tar -xvf {}', 'mkdir -p foo && tar -xvf {} -C foo'), |     ('tar -xvf {}', 'mkdir -p foo && tar -xvf {} -C foo'), | ||||||
|     ('tar --extract -f {}', 'mkdir -p foo && tar --extract -f {} -C foo')]) |     ('tar --extract -f {}', 'mkdir -p foo && tar --extract -f {} -C foo')]) | ||||||
|  |  | ||||||
|  |  | ||||||
| @parametrize_filename | @parametrize_filename | ||||||
| @parametrize_script | @parametrize_script | ||||||
| def test_match(tar_error, filename, script, fixed): | def test_match(tar_error, filename, script, fixed): | ||||||
|   | |||||||
| @@ -2,11 +2,12 @@ import pytest | |||||||
| import os | import os | ||||||
| from thefuck.rules.fix_file import match, get_new_command | from thefuck.rules.fix_file import match, get_new_command | ||||||
| from tests.utils import 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 = ( | tests = ( | ||||||
| ('gcc a.c', 'a.c', 3, 1, | ('gcc a.c', 'a.c', 3, 1, '', | ||||||
| """ | """ | ||||||
| a.c: In function 'main': | a.c: In function 'main': | ||||||
| a.c:3:1: error: expected expression before '}' token | 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 | 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 | syntax error at a.pl line 3, at EOF | ||||||
| Execution of a.pl aborted due to compilation errors. | 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. | 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 | 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 | 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 | 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:5: 2:6 error: unexpected token: `+` | ||||||
| a.rs:2     + | 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) |    Compiling test v0.1.0 (file:///tmp/fix-error/test) | ||||||
|    src/lib.rs:3:5: 3:6 error: unexpected token: `+` |    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. | 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 |   File "a.py", line 2 | ||||||
|       + |       + | ||||||
| @@ -73,7 +74,7 @@ To learn more, run the command again with --verbose. | |||||||
| SyntaxError: invalid syntax | SyntaxError: invalid syntax | ||||||
| """), | """), | ||||||
|  |  | ||||||
| ('python a.py', 'a.py', 8, None, | ('python a.py', 'a.py', 8, None, '', | ||||||
| """ | """ | ||||||
| Traceback (most recent call last): | Traceback (most recent call last): | ||||||
|   File "a.py", line 8, in <module> |   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 |   File "/usr/lib/python3.4/re.py", line 293, in _compile | ||||||
|     raise TypeError("first argument must be string or compiled pattern") |     raise TypeError("first argument must be string or compiled pattern") | ||||||
| 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 | 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 '+' | 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' | fish: Unknown command 'foo' | ||||||
| /tmp/fix-error/a.sh (line 2): 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: BEGIN { print "Hello, world!" + } | ||||||
| awk: ./a:2:                                 ^ syntax error | 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: | 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 | bidule | ||||||
| make: bidule: Command not found | make: bidule: Command not found | ||||||
| @@ -132,12 +132,12 @@ Makefile:2: recipe for target 'target' failed | |||||||
| make: *** [target] Error 127 | 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 | 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 | /Users/pablo/Workspace/barebones/fuck.js:2 | ||||||
| conole.log(arg);  // this should read console.log(arg); | 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 startup (node.js:129:16) | ||||||
|     at node.js:814:3 |     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.parametrize('test', tests) | ||||||
|  | @pytest.mark.usefixtures('no_memoize') | ||||||
| def test_match(mocker, monkeypatch, test): | def test_match(mocker, monkeypatch, test): | ||||||
|     mocker.patch('os.path.isfile', return_value=True) |     mocker.patch('os.path.isfile', return_value=True) | ||||||
|     monkeypatch.setenv('EDITOR', 'dummy_editor') |     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.parametrize('test', tests) | ||||||
|  | @pytest.mark.usefixtures('no_memoize') | ||||||
| def test_no_editor(mocker, monkeypatch, test): | def test_no_editor(mocker, monkeypatch, test): | ||||||
|     mocker.patch('os.path.isfile', return_value=True) |     mocker.patch('os.path.isfile', return_value=True) | ||||||
|     if 'EDITOR' in os.environ: |     if 'EDITOR' in os.environ: | ||||||
|         monkeypatch.delenv('EDITOR') |         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.parametrize('test', tests) | ||||||
|  | @pytest.mark.usefixtures('no_memoize') | ||||||
| def test_not_file(mocker, monkeypatch, test): | def test_not_file(mocker, monkeypatch, test): | ||||||
|     mocker.patch('os.path.isfile', return_value=False) |     mocker.patch('os.path.isfile', return_value=False) | ||||||
|     monkeypatch.setenv('EDITOR', 'dummy_editor') |     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) | @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') |     monkeypatch.setenv('EDITOR', 'dummy_editor') | ||||||
|     assert (get_new_command(Command(script=test[0], stderr=test[4]), None) == |  | ||||||
|  |     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])) |             'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0])) | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ from thefuck import shells | |||||||
| from thefuck.rules.git_branch_list import match, get_new_command | from thefuck.rules.git_branch_list import match, get_new_command | ||||||
| from tests.utils import Command | from tests.utils import Command | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_match(): | def test_match(): | ||||||
|     assert match(Command('git branch list'), None) |     assert match(Command('git branch list'), None) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ usage: git stash list [<options>] | |||||||
|    or: git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>] |    or: git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>] | ||||||
|    or: git stash branch <branchname> [<stash>] |    or: git stash branch <branchname> [<stash>] | ||||||
|    or: git stash [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] |    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 |    or: git stash clear | ||||||
| ''' | ''' | ||||||
|  |  | ||||||
|   | |||||||
| @@ -30,8 +30,8 @@ def git_not_command_closest(): | |||||||
|     return '''git: 'tags' is not a git command. See 'git --help'. |     return '''git: 'tags' is not a git command. See 'git --help'. | ||||||
|  |  | ||||||
| Did you mean one of these? | Did you mean one of these? | ||||||
| 	stage | \tstage | ||||||
| 	tag | \ttag | ||||||
| ''' | ''' | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,10 @@ from tests.utils import Command | |||||||
|     Command(script='open foo.org'), |     Command(script='open foo.org'), | ||||||
|     Command(script='open foo.net'), |     Command(script='open foo.net'), | ||||||
|     Command(script='open foo.se'), |     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): | def test_match(command): | ||||||
|     assert match(command, None) |     assert match(command, None) | ||||||
|  |  | ||||||
| @@ -20,6 +23,9 @@ def test_match(command): | |||||||
|     (Command('open foo.org'), 'open http://foo.org'), |     (Command('open foo.org'), 'open http://foo.org'), | ||||||
|     (Command('open foo.net'), 'open http://foo.net'), |     (Command('open foo.net'), 'open http://foo.net'), | ||||||
|     (Command('open foo.se'), 'open http://foo.se'), |     (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): | def test_get_new_command(command, new_command): | ||||||
|     assert get_new_command(command, None) == new_command |     assert get_new_command(command, None) == new_command | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ from thefuck.rules.systemctl import match, get_new_command | |||||||
| from tests.utils import Command | from tests.utils import Command | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_match(): | def test_match(): | ||||||
|     assert match(Command('systemctl nginx start', stderr='Unknown operation \'nginx\'.'), None) |     assert match(Command('systemctl nginx start', stderr='Unknown operation \'nginx\'.'), None) | ||||||
|     assert match(Command('sudo 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 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) |     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(): | def test_get_new_command(): | ||||||
|     assert get_new_command(Command('systemctl nginx start'), None) == "systemctl start nginx" |     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" |     assert get_new_command(Command('sudo systemctl nginx start'), None) == "sudo systemctl start nginx" | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								tests/test_readme.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tests/test_readme.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | from pathlib import Path | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_readme(): | ||||||
|  |     with open('README.md') as f: | ||||||
|  |         readme = f.read() | ||||||
|  |  | ||||||
|  |         bundled = Path(__file__).parent.parent \ | ||||||
|  |             .joinpath('thefuck') \ | ||||||
|  |             .joinpath('rules') \ | ||||||
|  |             .glob('*.py') | ||||||
|  |  | ||||||
|  |         for rule in bundled: | ||||||
|  |             if rule.stem != '__init__' and rule.stem not in readme: | ||||||
|  |                 raise Exception('Missing rule "{}" in README.md'.format(rule.stem)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     test_readme() | ||||||
| @@ -11,6 +11,7 @@ def test_rules_names_list(): | |||||||
|  |  | ||||||
| def test_update_settings(): | def test_update_settings(): | ||||||
|     settings = Settings({'key': 'val'}) |     settings = Settings({'key': 'val'}) | ||||||
|     new_settings = settings.update(key='new-val') |     new_settings = settings.update(key='new-val', unset='unset-value') | ||||||
|     assert new_settings.key == 'new-val' |     assert new_settings.key == 'val' | ||||||
|  |     assert new_settings.unset == 'unset-value' | ||||||
|     assert settings.key == 'val' |     assert settings.key == 'val' | ||||||
|   | |||||||
| @@ -9,7 +9,8 @@ from tests.utils import Command | |||||||
|  |  | ||||||
| @pytest.mark.parametrize('override, old, new', [ | @pytest.mark.parametrize('override, old, new', [ | ||||||
|     ({'key': 'val'}, {}, {'key': 'val'}), |     ({'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): | def test_wrap_settings(override, old, new): | ||||||
|     fn = lambda _, settings: settings |     fn = lambda _, settings: settings | ||||||
|     assert wrap_settings(override)(fn)(None, Settings(old)) == new |     assert wrap_settings(override)(fn)(None, Settings(old)) == new | ||||||
|   | |||||||
| @@ -4,11 +4,11 @@ | |||||||
| # > brew upgrade | # > brew upgrade | ||||||
| # Warning: brew upgrade with no arguments will change behaviour soon! | # Warning: brew upgrade with no arguments will change behaviour soon! | ||||||
| # It currently upgrades all formula but this will soon change to require '--all'. | # It currently upgrades all formula but this will soon change to require '--all'. | ||||||
| # |  | ||||||
| #  |  | ||||||
|  |  | ||||||
| def match(command, settings): | def match(command, settings): | ||||||
|     return (command.script == 'brew upgrade') |     return command.script == 'brew upgrade' | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|     return command.script + ' --all' |     return command.script + ' --all' | ||||||
|   | |||||||
| @@ -1,6 +1,3 @@ | |||||||
| #!/usr/bin/env python |  | ||||||
| __author__ = "mmussomele" |  | ||||||
|  |  | ||||||
| """Attempts to spellcheck and correct failed cd commands""" | """Attempts to spellcheck and correct failed cd commands""" | ||||||
|  |  | ||||||
| import os | import os | ||||||
| @@ -8,6 +5,8 @@ from difflib import get_close_matches | |||||||
| from thefuck.utils import sudo_support | from thefuck.utils import sudo_support | ||||||
| from thefuck.rules import cd_mkdir | from thefuck.rules import cd_mkdir | ||||||
|  |  | ||||||
|  | __author__ = "mmussomele" | ||||||
|  |  | ||||||
| MAX_ALLOWED_DIFF = 0.6 | MAX_ALLOWED_DIFF = 0.6 | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,8 +7,10 @@ | |||||||
| # > cd.. | # > cd.. | ||||||
| # cd..: command not found | # cd..: command not found | ||||||
|  |  | ||||||
|  |  | ||||||
| def match(command, settings): | def match(command, settings): | ||||||
|     return command.script == 'cd..' |     return command.script == 'cd..' | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|     return 'cd ..' |     return 'cd ..' | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| from itertools import dropwhile, takewhile, islice | from itertools import dropwhile, takewhile, islice | ||||||
| import re | import re | ||||||
| import subprocess | import subprocess | ||||||
| from thefuck.utils import get_closest, sudo_support, replace_argument, replace_command | from thefuck.utils import sudo_support, replace_command | ||||||
|  |  | ||||||
|  |  | ||||||
| @sudo_support | @sudo_support | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| import re | import re | ||||||
| import os | import os | ||||||
| from thefuck.utils import memoize | from thefuck.utils import memoize, wrap_settings | ||||||
| from thefuck import shells | from thefuck import shells | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # order is important: only the first match is considered | ||||||
| patterns = ( | patterns = ( | ||||||
|         # js, node: |         # js, node: | ||||||
|         '^    at {file}:{line}:{col}', |         '^    at {file}:{line}:{col}', | ||||||
| @@ -20,13 +21,13 @@ patterns = ( | |||||||
|         # lua: |         # lua: | ||||||
|         '^lua: {file}:{line}:', |         '^lua: {file}:{line}:', | ||||||
|         # fish: |         # fish: | ||||||
|         '^{file} \(line {line}\):', |         '^{file} \\(line {line}\\):', | ||||||
|         # bash, sh, ssh: |         # bash, sh, ssh: | ||||||
|         '^{file}: line {line}: ', |         '^{file}: line {line}: ', | ||||||
|  |         # cargo, clang, gcc, go, pep8, rustc: | ||||||
|  |         '^{file}:{line}:{col}', | ||||||
|         # ghc, make, ruby, zsh: |         # ghc, make, ruby, zsh: | ||||||
|         '^{file}:{line}:', |         '^{file}:{line}:', | ||||||
|         # cargo, clang, gcc, go, rustc: |  | ||||||
|         '^{file}:{line}:{col}', |  | ||||||
|         # perl: |         # perl: | ||||||
|         'at {file} line {line}', |         'at {file} line {line}', | ||||||
|     ) |     ) | ||||||
| @@ -45,7 +46,7 @@ patterns = [_make_pattern(p) for p in patterns] | |||||||
| def _search(stderr): | def _search(stderr): | ||||||
|     for pattern in patterns: |     for pattern in patterns: | ||||||
|         m = re.search(pattern, stderr) |         m = re.search(pattern, stderr) | ||||||
|         if m: |         if m and os.path.isfile(m.group('file')): | ||||||
|             return m |             return m | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -53,15 +54,24 @@ def match(command, settings): | |||||||
|     if 'EDITOR' not in os.environ: |     if 'EDITOR' not in os.environ: | ||||||
|         return False |         return False | ||||||
|  |  | ||||||
|     m = _search(command.stderr) |     return _search(command.stderr) or _search(command.stdout) | ||||||
|  |  | ||||||
|     return m and os.path.isfile(m.group('file')) |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @wrap_settings({'fixlinecmd': '{editor} {file} +{line}', | ||||||
|  |                 'fixcolcmd': None}) | ||||||
| def get_new_command(command, settings): | 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 |     # Note: there does not seem to be a standard for columns, so they are just | ||||||
|     # ignored for now |     # ignored by default | ||||||
|     return shells.and_('{} {} +{}'.format(os.environ['EDITOR'], m.group('file'), m.group('line')), |     if settings.fixcolcmd and 'col' in m.groupdict(): | ||||||
|                        command.script) |         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) | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ def match(command, settings): | |||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|     missing_file = re.findall( |     missing_file = re.findall( | ||||||
|             r"error: pathspec '([^']*)' " |             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 -- {}', '{}') |     formatme = shells.and_('git add -- {}', '{}') | ||||||
|     return formatme.format(missing_file, command.script) |     return formatme.format(missing_file, command.script) | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ def get_branches(): | |||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|     missing_file = re.findall( |     missing_file = re.findall( | ||||||
|         r"error: pathspec '([^']*)' " |         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(), |     closest_branch = utils.get_closest(missing_file, get_branches(), | ||||||
|                                        fallback_to_first=False) |                                        fallback_to_first=False) | ||||||
|     if closest_branch: |     if closest_branch: | ||||||
|   | |||||||
| @@ -3,12 +3,12 @@ | |||||||
| # Example: | # Example: | ||||||
| # > go run foo | # > go run foo | ||||||
| # error: go run: no go files listed | # error: go run: no go files listed | ||||||
| # |  | ||||||
| #  |  | ||||||
|  |  | ||||||
| def match(command, settings): | def match(command, settings): | ||||||
|     return (command.script.startswith ('go run ') |     return (command.script.startswith('go run ') | ||||||
|             and not command.script.endswith('.go')) |             and not command.script.endswith('.go')) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|     return command.script + '.go' |     return command.script + '.go' | ||||||
|   | |||||||
| @@ -11,4 +11,3 @@ def match(command, settings): | |||||||
| @sudo_support | @sudo_support | ||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|     return u'./{}'.format(command.script) |     return u'./{}'.format(command.script) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ def _history_of_exists_without_current(command): | |||||||
|             if not line.startswith(tf_alias) and not line == command.script |             if not line.startswith(tf_alias) and not line == command.script | ||||||
|             and line.split(' ')[0] in executables] |             and line.split(' ')[0] in executables] | ||||||
|  |  | ||||||
|  |  | ||||||
| def match(command, settings): | def match(command, settings): | ||||||
|     return len(get_close_matches(command.script, |     return len(get_close_matches(command.script, | ||||||
|                                  _history_of_exists_without_current(command))) |                                  _history_of_exists_without_current(command))) | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| def match(command, settings): | def match(command, settings): | ||||||
|     return (command.script == 'ls' |     return (command.script == 'ls' | ||||||
|             or command.script.startswith('ls ') |             or command.script.startswith('ls ') | ||||||
|             and not ('ls -' in command.script)) |             and 'ls -' not in command.script) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|   | |||||||
| @@ -23,4 +23,4 @@ def match(command, settings): | |||||||
|  |  | ||||||
|  |  | ||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|     return 'open http://' + command.script[5:] |     return command.script.replace('open ', 'open http://') | ||||||
|   | |||||||
| @@ -2,13 +2,11 @@ | |||||||
| # | # | ||||||
| # Example: | # Example: | ||||||
| # > git commit -m 'My Message" | # > git commit -m 'My Message" | ||||||
| #  |  | ||||||
| # |  | ||||||
| #  |  | ||||||
|  |  | ||||||
| def match(command, settings): | def match(command, settings): | ||||||
|     return ('\'' in command.script |     return '\'' in command.script and '\"' in command.script | ||||||
|             and '\"' in command.script) |  | ||||||
|  |  | ||||||
| def get_new_command(command, settings): | def get_new_command(command, settings): | ||||||
|     return command.script.replace ('\'', '\"') |     return command.script.replace('\'', '\"') | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| from thefuck.utils import get_closest, replace_command | from thefuck.utils import replace_command | ||||||
| import re | import re | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| import re | import re | ||||||
| from thefuck.utils import (get_closest, replace_argument, | from thefuck.utils import get_all_matched_commands, replace_command | ||||||
|                            get_all_matched_commands, replace_command) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def match(command, settings): | def match(command, settings): | ||||||
| @@ -14,4 +13,3 @@ def get_new_command(command, settings): | |||||||
|                             command.stderr)[0] |                             command.stderr)[0] | ||||||
|     return replace_command(command, broken_cmd, |     return replace_command(command, broken_cmd, | ||||||
|                            get_all_matched_commands(command.stderr)) |                            get_all_matched_commands(command.stderr)) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -216,7 +216,7 @@ class Tcsh(Generic): | |||||||
|         return u'#+{}\n{}\n'.format(int(time()), command_script) |         return u'#+{}\n{}\n'.format(int(time()), command_script) | ||||||
|  |  | ||||||
|  |  | ||||||
| shells = defaultdict(lambda: Generic(), { | shells = defaultdict(Generic, { | ||||||
|     'bash': Bash(), |     'bash': Bash(), | ||||||
|     'fish': Fish(), |     'fish': Fish(), | ||||||
|     'zsh': Zsh(), |     'zsh': Zsh(), | ||||||
|   | |||||||
| @@ -23,7 +23,9 @@ class Settings(dict): | |||||||
|         return self.get(item) |         return self.get(item) | ||||||
|  |  | ||||||
|     def update(self, **kwargs): |     def update(self, **kwargs): | ||||||
|         """Returns new settings with new values from `kwargs`.""" |         """ | ||||||
|         conf = dict(self) |         Returns new settings with values from `kwargs` for unset settings. | ||||||
|         conf.update(kwargs) |         """ | ||||||
|  |         conf = dict(kwargs) | ||||||
|  |         conf.update(self) | ||||||
|         return Settings(conf) |         return Settings(conf) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user