1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-01 23:51:59 +00:00

Compare commits

...

12 Commits
2.5.6 ... 2.6

Author SHA1 Message Date
nvbn
36a0a669b0 Bump to 2.6 2015-07-30 20:27:47 +03:00
Vladimir Iakovlev
b16de9c7c2 Merge pull request #323 from mcarton/fix-file
#320 Add the `fix_file` rule
2015-07-30 18:05:50 +03:00
mcarton
43fead02d3 Test if the file exists in the fix_file rule
This avoid false positives in `match`.
2015-07-30 16:42:00 +02:00
mcarton
de513cacb1 Show user's $EDITOR in output
It looks nicer with confirmation and also checks the user actually has an
$EDITOR.
2015-07-29 21:35:06 +02:00
mcarton
e4b97af73e #320 Add the fix_file rule 2015-07-29 21:03:47 +02:00
Vladimir Iakovlev
0a40e7f0a9 Merge pull request #321 from mcarton/patch-1
Force the travis image to track the master branch
2015-07-29 15:06:44 +03:00
Martin Carton
9c649c05a9 Force the travis image to track the master branch 2015-07-28 23:59:17 +02:00
nvbn
4fc18cb4e7 Decrease count of psutils calls 2015-07-28 16:26:26 +03:00
Vladimir Iakovlev
5d1dd70652 Merge pull request #319 from scorphus/tsuru-not-command
Add a new `tsuru_not_command` rule
2015-07-28 16:20:46 +03:00
Pablo Santiago Blum de Aguiar
65a25d5448 Add a new tsuru_not_command rule 2015-07-27 22:34:24 -03:00
Pablo Santiago Blum de Aguiar
4e854a575e Move get_all_matched_commands over to utils 2015-07-27 22:29:02 -03:00
nvbn
742200a500 #311 Fix build in travis-ci 2015-07-27 23:38:04 +03:00
11 changed files with 412 additions and 14 deletions

View File

@@ -12,6 +12,7 @@ addons:
- zsh
- fish
- tcsh
- pandoc
env:
- FUNCTIONAL=true BARE=true
install:

View File

@@ -1,4 +1,4 @@
# The Fuck [![Build Status](https://travis-ci.org/nvbn/thefuck.svg)](https://travis-ci.org/nvbn/thefuck)
# The Fuck [![Build Status](https://travis-ci.org/nvbn/thefuck.svg?branch=master)](https://travis-ci.org/nvbn/thefuck)
Magnificent app which corrects your previous console command,
inspired by a [@liamosaur](https://twitter.com/liamosaur/)
@@ -146,6 +146,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `docker_not_command` – fixes wrong docker commands like `docker tags`;
* `dry` – fixes repetitions like `git git push`;
* `fix_alt_space` – replaces Alt+Space with Space character;
* `fix_file` – opens a file with an error in your `$EDITOR`;
* `git_add` – fixes *"Did you forget to 'git add'?"*;
* `git_branch_delete` – changes `git branch -d` to `git branch -D`;
* `git_branch_list` – catches `git branch list` in place of `git branch` and removes created branch;
@@ -188,6 +189,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `systemctl` – correctly orders parameters of confusing `systemctl`;
* `test.py` – runs `py.test` instead of `test.py`;
* `tsuru_login` – runs `tsuru login` if not authenticated or session expired;
* `tsuru_not_command` – fixes wrong tsuru commands like `tsuru shell`;
* `tmux` – fixes `tmux` commands;
* `whois` – fixes `whois` command.

View File

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

View File

@@ -0,0 +1,188 @@
import pytest
import os
from thefuck.rules.fix_file import match, get_new_command
from tests.utils import Command
# (script, file, line, col (or None), stderr)
tests = (
('gcc a.c', 'a.c', 3, 1,
"""
a.c: In function 'main':
a.c:3:1: error: expected expression before '}' token
}
^
"""),
('clang a.c', 'a.c', 3, 1,
"""
a.c:3:1: error: expected expression
}
^
"""),
('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,
"""
Search pattern not terminated at a.pl line 2.
"""),
('sh a.sh', 'a.sh', 2, None,
"""
a.sh: line 2: foo: command not found
"""),
('zsh a.sh', 'a.sh', 2, None,
"""
a.sh:2: command not found: foo
"""),
('bash a.sh', 'a.sh', 2, None,
"""
a.sh: line 2: foo: command not found
"""),
('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,
"""
Compiling test v0.1.0 (file:///tmp/fix-error/test)
src/lib.rs:3:5: 3:6 error: unexpected token: `+`
src/lib.rs:3 +
^
Could not compile `test`.
To learn more, run the command again with --verbose.
"""),
('python a.py', 'a.py', 2, None,
"""
File "a.py", line 2
+
^
SyntaxError: invalid syntax
"""),
('python a.py', 'a.py', 8, None,
"""
Traceback (most recent call last):
File "a.py", line 8, in <module>
match("foo")
File "a.py", line 5, in match
m = re.search(None, command)
File "/usr/lib/python3.4/re.py", line 170, in search
return _compile(pattern, flags).search(string)
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,
"""
a.rb:3: syntax error, unexpected keyword_end
"""),
('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: Unknown command 'foo'
/tmp/fix-error/a.sh (line 2): foo
^
"""),
('./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:1:1: error: expected top-level entity
+
^
"""),
('go build a.go', 'a.go', 1, None,
"""
can't load package:
a.go:1:1: expected 'package', found '+'
"""),
('make', 'Makefile', 2, None,
"""
bidule
make: bidule: Command not found
Makefile:2: recipe for target 'target' failed
make: *** [target] Error 127
"""),
('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,
"""
/Users/pablo/Workspace/barebones/fuck.js:2
conole.log(arg); // this should read console.log(arg);
^
ReferenceError: conole is not defined
at /Users/pablo/Workspace/barebones/fuck.js:2:5
at Array.forEach (native)
at Object.<anonymous> (/Users/pablo/Workspace/barebones/fuck.js:1:85)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
"""),
)
@pytest.mark.parametrize('test', tests)
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)
@pytest.mark.parametrize('test', tests)
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)
@pytest.mark.parametrize('test', tests)
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)
@pytest.mark.parametrize('test', tests)
def test_get_new_command(monkeypatch, test):
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]))

View File

@@ -0,0 +1,90 @@
import pytest
from tests.utils import Command
from thefuck.rules.tsuru_not_command import match, get_new_command
@pytest.mark.parametrize('command', [
Command('tsuru log', stderr=(
'tsuru: "tchururu" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
'\tapp-log\n'
'\tlogin\n'
'\tlogout\n'
)),
Command('tsuru app-l', stderr=(
'tsuru: "tchururu" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
'\tapp-list\n'
'\tapp-log\n'
)),
Command('tsuru user-list', stderr=(
'tsuru: "tchururu" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
'\tteam-user-list\n'
)),
Command('tsuru targetlist', stderr=(
'tsuru: "tchururu" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
'\ttarget-list\n'
)),
])
def test_match(command):
assert match(command, None)
@pytest.mark.parametrize('command', [
Command('tsuru tchururu', stderr=(
'tsuru: "tchururu" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
)),
Command('tsuru version', stderr='tsuru version 0.16.0.'),
Command('tsuru help', stderr=(
'tsuru version 0.16.0.\n'
'\nUsage: tsuru command [args]\n'
)),
Command('tsuru platform-list', stderr=(
'- java\n'
'- logstashgiro\n'
'- newnode\n'
'- nodejs\n'
'- php\n'
'- python\n'
'- python3\n'
'- ruby\n'
'- ruby20\n'
'- static\n'
)),
Command('tsuru env-get', stderr='Error: App thefuck not found.'),
])
def test_not_match(command):
assert not match(command, None)
@pytest.mark.parametrize('command, new_command', [
(Command('tsuru log', stderr=(
'tsuru: "log" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
'\tapp-log\n'
'\tlogin\n'
'\tlogout\n'
)), 'tsuru login'),
(Command('tsuru app-l', stderr=(
'tsuru: "app-l" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
'\tapp-list\n'
'\tapp-log\n'
)), 'tsuru app-log'),
(Command('tsuru user-list', stderr=(
'tsuru: "user-list" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
'\tteam-user-list\n'
)), 'tsuru team-user-list'),
(Command('tsuru targetlist', stderr=(
'tsuru: "targetlist" is not a tsuru command. See "tsuru help".\n'
'\nDid you mean?\n'
'\ttarget-list\n'
)), 'tsuru target-list'),
])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -1,7 +1,8 @@
import pytest
from mock import Mock
from thefuck.utils import git_support, sudo_support, wrap_settings,\
memoize, get_closest, get_all_executables, replace_argument
memoize, get_closest, get_all_executables, replace_argument, \
get_all_matched_commands
from thefuck.types import Settings
from tests.utils import Command
@@ -99,3 +100,32 @@ def test_get_all_callables():
(('git brnch', 'brnch', 'branch'), 'git branch')])
def test_replace_argument(args, result):
assert replace_argument(*args) == result
@pytest.mark.parametrize('stderr, result', [
(("git: 'cone' is not a git command. See 'git --help'.\n"
'\n'
'Did you mean one of these?\n'
'\tclone'), ['clone']),
(("git: 're' is not a git command. See 'git --help'.\n"
'\n'
'Did you mean one of these?\n'
'\trebase\n'
'\treset\n'
'\tgrep\n'
'\trm'), ['rebase', 'reset', 'grep', 'rm']),
(('tsuru: "target" is not a tsuru command. See "tsuru help".\n'
'\n'
'Did you mean one of these?\n'
'\tservice-add\n'
'\tservice-bind\n'
'\tservice-doc\n'
'\tservice-info\n'
'\tservice-list\n'
'\tservice-remove\n'
'\tservice-status\n'
'\tservice-unbind'), ['service-add', 'service-bind', 'service-doc',
'service-info', 'service-list', 'service-remove',
'service-status', 'service-unbind'])])
def test_get_all_matched_commands(stderr, result):
assert list(get_all_matched_commands(stderr)) == result

67
thefuck/rules/fix_file.py Normal file
View File

@@ -0,0 +1,67 @@
import re
import os
from thefuck.utils import memoize
from thefuck import shells
patterns = (
# js, node:
'^ at {file}:{line}:{col}',
# cargo:
'^ {file}:{line}:{col}',
# python, thefuck:
'^ File "{file}", line {line}',
# awk:
'^awk: {file}:{line}:',
# git
'^fatal: bad config file line {line} in {file}',
# llc:
'^llc: {file}:{line}:{col}:',
# lua:
'^lua: {file}:{line}:',
# fish:
'^{file} \(line {line}\):',
# bash, sh, ssh:
'^{file}: line {line}: ',
# ghc, make, ruby, zsh:
'^{file}:{line}:',
# cargo, clang, gcc, go, rustc:
'^{file}:{line}:{col}',
# perl:
'at {file} line {line}',
)
# for the sake of readability do not use named groups above
def _make_pattern(pattern):
pattern = pattern.replace('{file}', '(?P<file>[^:\n]+)')
pattern = pattern.replace('{line}', '(?P<line>[0-9]+)')
pattern = pattern.replace('{col}', '(?P<col>[0-9]+)')
return re.compile(pattern, re.MULTILINE)
patterns = [_make_pattern(p) for p in patterns]
@memoize
def _search(stderr):
for pattern in patterns:
m = re.search(pattern, stderr)
if m:
return m
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'))
def get_new_command(command, settings):
m = _search(command.stderr)
# 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)

View File

@@ -1,5 +1,6 @@
import re
from thefuck.utils import get_closest, git_support, replace_argument
from thefuck.utils import (get_closest, git_support, replace_argument,
get_all_matched_commands)
@git_support
@@ -8,20 +9,11 @@ def match(command, settings):
and 'Did you mean' in command.stderr)
def _get_all_git_matched_commands(stderr):
should_yield = False
for line in stderr.split('\n'):
if 'Did you mean' in line:
should_yield = True
elif should_yield and line:
yield line.strip()
@git_support
def get_new_command(command, settings):
broken_cmd = re.findall(r"git: '([^']*)' is not a git command",
command.stderr)[0]
new_cmd = get_closest(broken_cmd,
_get_all_git_matched_commands(command.stderr))
get_all_matched_commands(command.stderr))
return replace_argument(command.script, broken_cmd, new_cmd)

View File

@@ -0,0 +1,18 @@
import re
from thefuck.utils import (get_closest, replace_argument,
get_all_matched_commands)
def match(command, settings):
return (command.script.startswith('tsuru ')
and ' is not a tsuru command. See "tsuru help".' in command.stderr
and '\nDid you mean?\n\t' in command.stderr)
def get_new_command(command, settings):
broken_cmd = re.findall(r'tsuru: "([^"]*)" is not a tsuru command',
command.stderr)[0]
new_cmd = get_closest(broken_cmd,
get_all_matched_commands(command.stderr))
return replace_argument(command.script, broken_cmd, new_cmd)

View File

@@ -224,6 +224,7 @@ shells = defaultdict(lambda: Generic(), {
'tcsh': Tcsh()})
@memoize
def _get_shell():
try:
shell = Process(os.getpid()).parent().name()

View File

@@ -159,3 +159,12 @@ def replace_argument(script, from_, to):
else:
return script.replace(
u' {} '.format(from_), u' {} '.format(to), 1)
def get_all_matched_commands(stderr, separator='Did you mean'):
should_yield = False
for line in stderr.split('\n'):
if separator in line:
should_yield = True
elif should_yield and line:
yield line.strip()