1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-02-21 12:28:41 +00:00

Merge pull request #337 from mcarton/cleanup

Some cleanup and fixes
This commit is contained in:
Vladimir Iakovlev 2015-08-19 17:19:08 +03:00
commit 700d9ac7e9
31 changed files with 203 additions and 114 deletions

View File

@ -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:

View File

@ -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):

View File

@ -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):

View File

@ -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) ==
'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 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)

View File

@ -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
''' '''

View File

@ -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
''' '''

View File

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

View File

@ -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

View File

@ -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
View 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()

View File

@ -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'

View File

@ -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

View File

@ -1,14 +1,14 @@
# Appends --all to the brew upgrade command # Appends --all to the brew upgrade command
# #
# Example: # Example:
# > 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'

View File

@ -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
@ -28,8 +27,8 @@ def match(command, settings):
def get_new_command(command, settings): def get_new_command(command, settings):
""" """
Attempt to rebuild the path string by spellchecking the directories. Attempt to rebuild the path string by spellchecking the directories.
If it fails (i.e. no directories are a close enough match), then it If it fails (i.e. no directories are a close enough match), then it
defaults to the rules of cd_mkdir. defaults to the rules of cd_mkdir.
Change sensitivity by changing MAX_ALLOWED_DIFF. Default value is 0.6 Change sensitivity by changing MAX_ALLOWED_DIFF. Default value is 0.6
""" """
dest = command.script.split()[1].split(os.sep) dest = command.script.split()[1].split(os.sep)

View File

@ -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 ..'

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -1,14 +1,14 @@
# Appends .go when compiling go files # Appends .go when compiling go files
# #
# 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'

View File

@ -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)

View File

@ -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)))

View File

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

View File

@ -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):

View File

@ -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://')

View File

@ -1,14 +1,12 @@
# Fixes careless " and ' usage # Fixes careless " and ' usage
# #
# 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('\'', '\"')

View File

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

View File

@ -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))

View File

@ -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(),

View File

@ -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)