mirror of
https://github.com/nvbn/thefuck.git
synced 2025-01-19 04:21:14 +00:00
Merge pull request #441 from nvbn/simplify-shells
Split `shells` module
This commit is contained in:
commit
af0fe66a9f
@ -1,7 +1,10 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
|
from thefuck import shells
|
||||||
from thefuck import conf
|
from thefuck import conf
|
||||||
|
|
||||||
|
shells.shell = shells.Generic()
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
"""Adds `--run-without-docker` argument."""
|
"""Adds `--run-without-docker` argument."""
|
||||||
@ -46,3 +49,14 @@ def functional(request):
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def source_root():
|
def source_root():
|
||||||
return Path(__file__).parent.parent.resolve()
|
return Path(__file__).parent.parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def set_shell(monkeypatch, request):
|
||||||
|
def _set(cls):
|
||||||
|
shell = cls()
|
||||||
|
monkeypatch.setattr('thefuck.shells.shell', shell)
|
||||||
|
request.addfinalizer()
|
||||||
|
return shell
|
||||||
|
|
||||||
|
return _set
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def generic_shell(monkeypatch):
|
|
||||||
monkeypatch.setattr('thefuck.shells.and_', lambda *x: u' && '.join(x))
|
|
@ -1,5 +1,5 @@
|
|||||||
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 thefuck.shells import shell
|
||||||
from tests.utils import Command
|
from tests.utils import Command
|
||||||
|
|
||||||
|
|
||||||
@ -16,4 +16,4 @@ def test_not_match():
|
|||||||
|
|
||||||
def test_get_new_command():
|
def test_get_new_command():
|
||||||
assert (get_new_command(Command('git branch list')) ==
|
assert (get_new_command(Command('git branch list')) ==
|
||||||
shells.and_('git branch --delete list', 'git branch'))
|
shell.and_('git branch --delete list', 'git branch'))
|
||||||
|
@ -5,14 +5,14 @@ from tests.utils import Command
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def history(mocker):
|
def history(mocker):
|
||||||
return mocker.patch('thefuck.rules.history.get_history',
|
return mocker.patch('thefuck.shells.shell.get_history',
|
||||||
return_value=['le cat', 'fuck', 'ls cat',
|
return_value=['le cat', 'fuck', 'ls cat',
|
||||||
'diff x', 'nocommand x'])
|
'diff x', 'nocommand x'])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def alias(mocker):
|
def alias(mocker):
|
||||||
return mocker.patch('thefuck.rules.history.thefuck_alias',
|
return mocker.patch('thefuck.rules.history.get_alias',
|
||||||
return_value='fuck')
|
return_value='fuck')
|
||||||
|
|
||||||
|
|
||||||
|
0
tests/shells/__init__.py
Normal file
0
tests/shells/__init__.py
Normal file
22
tests/shells/conftest.py
Normal file
22
tests/shells/conftest.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def builtins_open(mocker):
|
||||||
|
return mocker.patch('six.moves.builtins.open')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def isfile(mocker):
|
||||||
|
return mocker.patch('os.path.isfile', return_value=True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
@pytest.mark.usefixtures('isfile')
|
||||||
|
def history_lines(mocker):
|
||||||
|
def aux(lines):
|
||||||
|
mock = mocker.patch('io.open')
|
||||||
|
mock.return_value.__enter__ \
|
||||||
|
.return_value.readlines.return_value = lines
|
||||||
|
|
||||||
|
return aux
|
60
tests/shells/test_bash.py
Normal file
60
tests/shells/test_bash.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from thefuck.shells import Bash
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
|
||||||
|
class TestBash(object):
|
||||||
|
@pytest.fixture
|
||||||
|
def shell(self):
|
||||||
|
return Bash()
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def Popen(self, mocker):
|
||||||
|
mock = mocker.patch('thefuck.shells.bash.Popen')
|
||||||
|
mock.return_value.stdout.read.return_value = (
|
||||||
|
b'alias fuck=\'eval $(thefuck $(fc -ln -1))\'\n'
|
||||||
|
b'alias l=\'ls -CF\'\n'
|
||||||
|
b'alias la=\'ls -A\'\n'
|
||||||
|
b'alias ll=\'ls -alF\'')
|
||||||
|
return mock
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('before, after', [
|
||||||
|
('pwd', 'pwd'),
|
||||||
|
('fuck', 'eval $(thefuck $(fc -ln -1))'),
|
||||||
|
('awk', 'awk'),
|
||||||
|
('ll', 'ls -alF')])
|
||||||
|
def test_from_shell(self, before, after, shell):
|
||||||
|
assert shell.from_shell(before) == after
|
||||||
|
|
||||||
|
def test_to_shell(self, shell):
|
||||||
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('entry, entry_utf8', [
|
||||||
|
('ls', 'ls\n'),
|
||||||
|
(u'echo café', 'echo café\n')])
|
||||||
|
def test_put_to_history(self, entry, entry_utf8, builtins_open, shell):
|
||||||
|
shell.put_to_history(entry)
|
||||||
|
builtins_open.return_value.__enter__.return_value. \
|
||||||
|
write.assert_called_once_with(entry_utf8)
|
||||||
|
|
||||||
|
def test_and_(self, shell):
|
||||||
|
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||||
|
|
||||||
|
def test_get_aliases(self, shell):
|
||||||
|
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
|
||||||
|
'l': 'ls -CF',
|
||||||
|
'la': 'ls -A',
|
||||||
|
'll': 'ls -alF'}
|
||||||
|
|
||||||
|
def test_app_alias(self, shell):
|
||||||
|
assert 'alias fuck' in shell.app_alias('fuck')
|
||||||
|
assert 'alias FUCK' in shell.app_alias('FUCK')
|
||||||
|
assert 'thefuck' in shell.app_alias('fuck')
|
||||||
|
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
||||||
|
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
||||||
|
|
||||||
|
def test_get_history(self, history_lines, shell):
|
||||||
|
history_lines(['ls', 'rm'])
|
||||||
|
assert list(shell.get_history()) == ['ls', 'rm']
|
86
tests/shells/test_fish.py
Normal file
86
tests/shells/test_fish.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from thefuck.shells import Fish
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
|
||||||
|
class TestFish(object):
|
||||||
|
@pytest.fixture
|
||||||
|
def shell(self):
|
||||||
|
return Fish()
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def Popen(self, mocker):
|
||||||
|
mock = mocker.patch('thefuck.shells.fish.Popen')
|
||||||
|
mock.return_value.stdout.read.return_value = (
|
||||||
|
b'cd\nfish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nls\n'
|
||||||
|
b'man\nmath\npopd\npushd\nruby')
|
||||||
|
return mock
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def environ(self, monkeypatch):
|
||||||
|
data = {'TF_OVERRIDDEN_ALIASES': 'cd, ls, man, open'}
|
||||||
|
monkeypatch.setattr('thefuck.shells.fish.os.environ', data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
@pytest.mark.usefixture('environ')
|
||||||
|
def test_get_overridden_aliases(self, shell, environ):
|
||||||
|
assert shell._get_overridden_aliases() == ['cd', 'ls', 'man', 'open']
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('before, after', [
|
||||||
|
('cd', 'cd'),
|
||||||
|
('pwd', 'pwd'),
|
||||||
|
('fuck', 'fish -ic "fuck"'),
|
||||||
|
('find', 'find'),
|
||||||
|
('funced', 'fish -ic "funced"'),
|
||||||
|
('grep', 'grep'),
|
||||||
|
('awk', 'awk'),
|
||||||
|
('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
|
||||||
|
('man', 'man'),
|
||||||
|
('open', 'open'),
|
||||||
|
('vim', 'vim'),
|
||||||
|
('ll', 'fish -ic "ll"'),
|
||||||
|
('ls', 'ls')]) # Fish has no aliases but functions
|
||||||
|
def test_from_shell(self, before, after, shell):
|
||||||
|
assert shell.from_shell(before) == after
|
||||||
|
|
||||||
|
def test_to_shell(self, shell):
|
||||||
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('entry, entry_utf8', [
|
||||||
|
('ls', '- cmd: ls\n when: 1430707243\n'),
|
||||||
|
(u'echo café', '- cmd: echo café\n when: 1430707243\n')])
|
||||||
|
def test_put_to_history(self, entry, entry_utf8, builtins_open, mocker, shell):
|
||||||
|
mocker.patch('thefuck.shells.fish.time',
|
||||||
|
return_value=1430707243.3517463)
|
||||||
|
shell.put_to_history(entry)
|
||||||
|
builtins_open.return_value.__enter__.return_value. \
|
||||||
|
write.assert_called_once_with(entry_utf8)
|
||||||
|
|
||||||
|
def test_and_(self, shell):
|
||||||
|
assert shell.and_('foo', 'bar') == 'foo; and bar'
|
||||||
|
|
||||||
|
def test_get_aliases(self, shell):
|
||||||
|
assert shell.get_aliases() == {'fish_config': 'fish_config',
|
||||||
|
'fuck': 'fuck',
|
||||||
|
'funced': 'funced',
|
||||||
|
'funcsave': 'funcsave',
|
||||||
|
'history': 'history',
|
||||||
|
'll': 'll',
|
||||||
|
'math': 'math',
|
||||||
|
'popd': 'popd',
|
||||||
|
'pushd': 'pushd',
|
||||||
|
'ruby': 'ruby'}
|
||||||
|
|
||||||
|
def test_app_alias(self, shell):
|
||||||
|
assert 'function fuck' in shell.app_alias('fuck')
|
||||||
|
assert 'function FUCK' in shell.app_alias('FUCK')
|
||||||
|
assert 'thefuck' in shell.app_alias('fuck')
|
||||||
|
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
||||||
|
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
||||||
|
|
||||||
|
def test_get_history(self, history_lines, shell):
|
||||||
|
history_lines(['- cmd: ls', ' when: 1432613911',
|
||||||
|
'- cmd: rm', ' when: 1432613916'])
|
||||||
|
assert list(shell.get_history()) == ['ls', 'rm']
|
44
tests/shells/test_generic.py
Normal file
44
tests/shells/test_generic.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from thefuck.shells import Generic
|
||||||
|
|
||||||
|
|
||||||
|
class TestGeneric(object):
|
||||||
|
@pytest.fixture
|
||||||
|
def shell(self):
|
||||||
|
return Generic()
|
||||||
|
|
||||||
|
def test_from_shell(self, shell):
|
||||||
|
assert shell.from_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
|
def test_to_shell(self, shell):
|
||||||
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
|
def test_put_to_history(self, builtins_open, shell):
|
||||||
|
assert shell.put_to_history('ls') is None
|
||||||
|
assert shell.put_to_history(u'echo café') is None
|
||||||
|
assert builtins_open.call_count == 0
|
||||||
|
|
||||||
|
def test_and_(self, shell):
|
||||||
|
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||||
|
|
||||||
|
def test_get_aliases(self, shell):
|
||||||
|
assert shell.get_aliases() == {}
|
||||||
|
|
||||||
|
def test_app_alias(self, shell):
|
||||||
|
assert 'alias fuck' in shell.app_alias('fuck')
|
||||||
|
assert 'alias FUCK' in shell.app_alias('FUCK')
|
||||||
|
assert 'thefuck' in shell.app_alias('fuck')
|
||||||
|
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
||||||
|
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
||||||
|
|
||||||
|
def test_get_history(self, history_lines, shell):
|
||||||
|
history_lines(['ls', 'rm'])
|
||||||
|
# We don't know what to do in generic shell with history lines,
|
||||||
|
# so just ignore them:
|
||||||
|
assert list(shell.get_history()) == []
|
||||||
|
|
||||||
|
def test_split_command(self, shell):
|
||||||
|
assert shell.split_command('ls') == ['ls']
|
||||||
|
assert shell.split_command(u'echo café') == [u'echo', u'café']
|
60
tests/shells/test_tcsh.py
Normal file
60
tests/shells/test_tcsh.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from thefuck.shells.tcsh import Tcsh
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
|
||||||
|
class TestTcsh(object):
|
||||||
|
@pytest.fixture
|
||||||
|
def shell(self):
|
||||||
|
return Tcsh()
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def Popen(self, mocker):
|
||||||
|
mock = mocker.patch('thefuck.shells.tcsh.Popen')
|
||||||
|
mock.return_value.stdout.read.return_value = (
|
||||||
|
b'fuck\teval $(thefuck $(fc -ln -1))\n'
|
||||||
|
b'l\tls -CF\n'
|
||||||
|
b'la\tls -A\n'
|
||||||
|
b'll\tls -alF')
|
||||||
|
return mock
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('before, after', [
|
||||||
|
('pwd', 'pwd'),
|
||||||
|
('fuck', 'eval $(thefuck $(fc -ln -1))'),
|
||||||
|
('awk', 'awk'),
|
||||||
|
('ll', 'ls -alF')])
|
||||||
|
def test_from_shell(self, before, after, shell):
|
||||||
|
assert shell.from_shell(before) == after
|
||||||
|
|
||||||
|
def test_to_shell(self, shell):
|
||||||
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('entry, entry_utf8', [
|
||||||
|
('ls', '#+1430707243\nls\n'),
|
||||||
|
(u'echo café', '#+1430707243\necho café\n')])
|
||||||
|
def test_put_to_history(self, entry, entry_utf8, builtins_open, shell, mocker):
|
||||||
|
mocker.patch('thefuck.shells.tcsh.time',
|
||||||
|
return_value=1430707243.3517463)
|
||||||
|
shell.put_to_history(entry)
|
||||||
|
builtins_open.return_value.__enter__.return_value. \
|
||||||
|
write.assert_called_once_with(entry_utf8)
|
||||||
|
|
||||||
|
def test_and_(self, shell):
|
||||||
|
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||||
|
|
||||||
|
def test_get_aliases(self, shell):
|
||||||
|
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
|
||||||
|
'l': 'ls -CF',
|
||||||
|
'la': 'ls -A',
|
||||||
|
'll': 'ls -alF'}
|
||||||
|
|
||||||
|
def test_app_alias(self, shell):
|
||||||
|
assert 'alias fuck' in shell.app_alias('fuck')
|
||||||
|
assert 'alias FUCK' in shell.app_alias('FUCK')
|
||||||
|
assert 'thefuck' in shell.app_alias('fuck')
|
||||||
|
|
||||||
|
def test_get_history(self, history_lines, shell):
|
||||||
|
history_lines(['ls', 'rm'])
|
||||||
|
assert list(shell.get_history()) == ['ls', 'rm']
|
62
tests/shells/test_zsh.py
Normal file
62
tests/shells/test_zsh.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from thefuck.shells.zsh import Zsh
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
|
||||||
|
class TestZsh(object):
|
||||||
|
@pytest.fixture
|
||||||
|
def shell(self):
|
||||||
|
return Zsh()
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def Popen(self, mocker):
|
||||||
|
mock = mocker.patch('thefuck.shells.zsh.Popen')
|
||||||
|
mock.return_value.stdout.read.return_value = (
|
||||||
|
b'fuck=\'eval $(thefuck $(fc -ln -1 | tail -n 1))\'\n'
|
||||||
|
b'l=\'ls -CF\'\n'
|
||||||
|
b'la=\'ls -A\'\n'
|
||||||
|
b'll=\'ls -alF\'')
|
||||||
|
return mock
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('before, after', [
|
||||||
|
('fuck', 'eval $(thefuck $(fc -ln -1 | tail -n 1))'),
|
||||||
|
('pwd', 'pwd'),
|
||||||
|
('ll', 'ls -alF')])
|
||||||
|
def test_from_shell(self, before, after, shell):
|
||||||
|
assert shell.from_shell(before) == after
|
||||||
|
|
||||||
|
def test_to_shell(self, shell):
|
||||||
|
assert shell.to_shell('pwd') == 'pwd'
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('entry, entry_utf8', [
|
||||||
|
('ls', ': 1430707243:0;ls\n'),
|
||||||
|
(u'echo café', ': 1430707243:0;echo café\n')])
|
||||||
|
def test_put_to_history(self, entry, entry_utf8, builtins_open, mocker, shell):
|
||||||
|
mocker.patch('thefuck.shells.zsh.time',
|
||||||
|
return_value=1430707243.3517463)
|
||||||
|
shell.put_to_history(entry)
|
||||||
|
builtins_open.return_value.__enter__.return_value. \
|
||||||
|
write.assert_called_once_with(entry_utf8)
|
||||||
|
|
||||||
|
def test_and_(self, shell):
|
||||||
|
assert shell.and_('ls', 'cd') == 'ls && cd'
|
||||||
|
|
||||||
|
def test_get_aliases(self, shell):
|
||||||
|
assert shell.get_aliases() == {
|
||||||
|
'fuck': 'eval $(thefuck $(fc -ln -1 | tail -n 1))',
|
||||||
|
'l': 'ls -CF',
|
||||||
|
'la': 'ls -A',
|
||||||
|
'll': 'ls -alF'}
|
||||||
|
|
||||||
|
def test_app_alias(self, shell):
|
||||||
|
assert 'alias fuck' in shell.app_alias('fuck')
|
||||||
|
assert 'alias FUCK' in shell.app_alias('FUCK')
|
||||||
|
assert 'thefuck' in shell.app_alias('fuck')
|
||||||
|
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
||||||
|
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
||||||
|
|
||||||
|
def test_get_history(self, history_lines, shell):
|
||||||
|
history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])
|
||||||
|
assert list(shell.get_history()) == ['ls', 'rm']
|
@ -1,260 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from thefuck import shells
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def builtins_open(mocker):
|
|
||||||
return mocker.patch('six.moves.builtins.open')
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def isfile(mocker):
|
|
||||||
return mocker.patch('os.path.isfile', return_value=True)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
@pytest.mark.usefixtures('isfile')
|
|
||||||
def history_lines(mocker):
|
|
||||||
def aux(lines):
|
|
||||||
mock = mocker.patch('io.open')
|
|
||||||
mock.return_value.__enter__\
|
|
||||||
.return_value.readlines.return_value = lines
|
|
||||||
return aux
|
|
||||||
|
|
||||||
|
|
||||||
class TestGeneric(object):
|
|
||||||
@pytest.fixture
|
|
||||||
def shell(self):
|
|
||||||
return shells.Generic()
|
|
||||||
|
|
||||||
def test_from_shell(self, shell):
|
|
||||||
assert shell.from_shell('pwd') == 'pwd'
|
|
||||||
|
|
||||||
def test_to_shell(self, shell):
|
|
||||||
assert shell.to_shell('pwd') == 'pwd'
|
|
||||||
|
|
||||||
def test_put_to_history(self, builtins_open, shell):
|
|
||||||
assert shell.put_to_history('ls') is None
|
|
||||||
assert shell.put_to_history(u'echo café') is None
|
|
||||||
assert builtins_open.call_count == 0
|
|
||||||
|
|
||||||
def test_and_(self, shell):
|
|
||||||
assert shell.and_('ls', 'cd') == 'ls && cd'
|
|
||||||
|
|
||||||
def test_get_aliases(self, shell):
|
|
||||||
assert shell.get_aliases() == {}
|
|
||||||
|
|
||||||
def test_app_alias(self, shell):
|
|
||||||
assert 'alias fuck' in shell.app_alias('fuck')
|
|
||||||
assert 'alias FUCK' in shell.app_alias('FUCK')
|
|
||||||
assert 'thefuck' in shell.app_alias('fuck')
|
|
||||||
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
|
||||||
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
|
||||||
|
|
||||||
def test_get_history(self, history_lines, shell):
|
|
||||||
history_lines(['ls', 'rm'])
|
|
||||||
# We don't know what to do in generic shell with history lines,
|
|
||||||
# so just ignore them:
|
|
||||||
assert list(shell.get_history()) == []
|
|
||||||
|
|
||||||
def test_split_command(self, shell):
|
|
||||||
assert shell.split_command('ls') == ['ls']
|
|
||||||
assert shell.split_command(u'echo café') == [u'echo', u'café']
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('isfile')
|
|
||||||
class TestBash(object):
|
|
||||||
@pytest.fixture
|
|
||||||
def shell(self):
|
|
||||||
return shells.Bash()
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def Popen(self, mocker):
|
|
||||||
mock = mocker.patch('thefuck.shells.Popen')
|
|
||||||
mock.return_value.stdout.read.return_value = (
|
|
||||||
b'alias fuck=\'eval $(thefuck $(fc -ln -1))\'\n'
|
|
||||||
b'alias l=\'ls -CF\'\n'
|
|
||||||
b'alias la=\'ls -A\'\n'
|
|
||||||
b'alias ll=\'ls -alF\'')
|
|
||||||
return mock
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('before, after', [
|
|
||||||
('pwd', 'pwd'),
|
|
||||||
('fuck', 'eval $(thefuck $(fc -ln -1))'),
|
|
||||||
('awk', 'awk'),
|
|
||||||
('ll', 'ls -alF')])
|
|
||||||
def test_from_shell(self, before, after, shell):
|
|
||||||
assert shell.from_shell(before) == after
|
|
||||||
|
|
||||||
def test_to_shell(self, shell):
|
|
||||||
assert shell.to_shell('pwd') == 'pwd'
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('entry, entry_utf8', [
|
|
||||||
('ls', 'ls\n'),
|
|
||||||
(u'echo café', 'echo café\n')])
|
|
||||||
def test_put_to_history(self, entry, entry_utf8, builtins_open, shell):
|
|
||||||
shell.put_to_history(entry)
|
|
||||||
builtins_open.return_value.__enter__.return_value. \
|
|
||||||
write.assert_called_once_with(entry_utf8)
|
|
||||||
|
|
||||||
def test_and_(self, shell):
|
|
||||||
assert shell.and_('ls', 'cd') == 'ls && cd'
|
|
||||||
|
|
||||||
def test_get_aliases(self, shell):
|
|
||||||
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
|
|
||||||
'l': 'ls -CF',
|
|
||||||
'la': 'ls -A',
|
|
||||||
'll': 'ls -alF'}
|
|
||||||
|
|
||||||
def test_app_alias(self, shell):
|
|
||||||
assert 'alias fuck' in shell.app_alias('fuck')
|
|
||||||
assert 'alias FUCK' in shell.app_alias('FUCK')
|
|
||||||
assert 'thefuck' in shell.app_alias('fuck')
|
|
||||||
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
|
||||||
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
|
||||||
|
|
||||||
def test_get_history(self, history_lines, shell):
|
|
||||||
history_lines(['ls', 'rm'])
|
|
||||||
assert list(shell.get_history()) == ['ls', 'rm']
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('isfile')
|
|
||||||
class TestFish(object):
|
|
||||||
@pytest.fixture
|
|
||||||
def shell(self):
|
|
||||||
return shells.Fish()
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def Popen(self, mocker):
|
|
||||||
mock = mocker.patch('thefuck.shells.Popen')
|
|
||||||
mock.return_value.stdout.read.return_value = (
|
|
||||||
b'cd\nfish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nls\n'
|
|
||||||
b'man\nmath\npopd\npushd\nruby')
|
|
||||||
return mock
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def environ(self, monkeypatch):
|
|
||||||
data = {'TF_OVERRIDDEN_ALIASES': 'cd, ls, man, open'}
|
|
||||||
monkeypatch.setattr('thefuck.shells.os.environ', data)
|
|
||||||
return data
|
|
||||||
|
|
||||||
@pytest.mark.usefixture('environ')
|
|
||||||
def test_get_overridden_aliases(self, shell, environ):
|
|
||||||
assert shell._get_overridden_aliases() == ['cd', 'ls', 'man', 'open']
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('before, after', [
|
|
||||||
('cd', 'cd'),
|
|
||||||
('pwd', 'pwd'),
|
|
||||||
('fuck', 'fish -ic "fuck"'),
|
|
||||||
('find', 'find'),
|
|
||||||
('funced', 'fish -ic "funced"'),
|
|
||||||
('grep', 'grep'),
|
|
||||||
('awk', 'awk'),
|
|
||||||
('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
|
|
||||||
('man', 'man'),
|
|
||||||
('open', 'open'),
|
|
||||||
('vim', 'vim'),
|
|
||||||
('ll', 'fish -ic "ll"'),
|
|
||||||
('ls', 'ls')]) # Fish has no aliases but functions
|
|
||||||
def test_from_shell(self, before, after, shell):
|
|
||||||
assert shell.from_shell(before) == after
|
|
||||||
|
|
||||||
def test_to_shell(self, shell):
|
|
||||||
assert shell.to_shell('pwd') == 'pwd'
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('entry, entry_utf8', [
|
|
||||||
('ls', '- cmd: ls\n when: 1430707243\n'),
|
|
||||||
(u'echo café', '- cmd: echo café\n when: 1430707243\n')])
|
|
||||||
def test_put_to_history(self, entry, entry_utf8, builtins_open, mocker, shell):
|
|
||||||
mocker.patch('thefuck.shells.time',
|
|
||||||
return_value=1430707243.3517463)
|
|
||||||
shell.put_to_history(entry)
|
|
||||||
builtins_open.return_value.__enter__.return_value. \
|
|
||||||
write.assert_called_once_with(entry_utf8)
|
|
||||||
|
|
||||||
def test_and_(self, shell):
|
|
||||||
assert shell.and_('foo', 'bar') == 'foo; and bar'
|
|
||||||
|
|
||||||
def test_get_aliases(self, shell):
|
|
||||||
assert shell.get_aliases() == {'fish_config': 'fish_config',
|
|
||||||
'fuck': 'fuck',
|
|
||||||
'funced': 'funced',
|
|
||||||
'funcsave': 'funcsave',
|
|
||||||
'history': 'history',
|
|
||||||
'll': 'll',
|
|
||||||
'math': 'math',
|
|
||||||
'popd': 'popd',
|
|
||||||
'pushd': 'pushd',
|
|
||||||
'ruby': 'ruby'}
|
|
||||||
|
|
||||||
def test_app_alias(self, shell):
|
|
||||||
assert 'function fuck' in shell.app_alias('fuck')
|
|
||||||
assert 'function FUCK' in shell.app_alias('FUCK')
|
|
||||||
assert 'thefuck' in shell.app_alias('fuck')
|
|
||||||
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
|
||||||
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
|
||||||
|
|
||||||
def test_get_history(self, history_lines, shell):
|
|
||||||
history_lines(['- cmd: ls', ' when: 1432613911',
|
|
||||||
'- cmd: rm', ' when: 1432613916'])
|
|
||||||
assert list(shell.get_history()) == ['ls', 'rm']
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('isfile')
|
|
||||||
class TestZsh(object):
|
|
||||||
@pytest.fixture
|
|
||||||
def shell(self):
|
|
||||||
return shells.Zsh()
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def Popen(self, mocker):
|
|
||||||
mock = mocker.patch('thefuck.shells.Popen')
|
|
||||||
mock.return_value.stdout.read.return_value = (
|
|
||||||
b'fuck=\'eval $(thefuck $(fc -ln -1 | tail -n 1))\'\n'
|
|
||||||
b'l=\'ls -CF\'\n'
|
|
||||||
b'la=\'ls -A\'\n'
|
|
||||||
b'll=\'ls -alF\'')
|
|
||||||
return mock
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('before, after', [
|
|
||||||
('fuck', 'eval $(thefuck $(fc -ln -1 | tail -n 1))'),
|
|
||||||
('pwd', 'pwd'),
|
|
||||||
('ll', 'ls -alF')])
|
|
||||||
def test_from_shell(self, before, after, shell):
|
|
||||||
assert shell.from_shell(before) == after
|
|
||||||
|
|
||||||
def test_to_shell(self, shell):
|
|
||||||
assert shell.to_shell('pwd') == 'pwd'
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('entry, entry_utf8', [
|
|
||||||
('ls', ': 1430707243:0;ls\n'),
|
|
||||||
(u'echo café', ': 1430707243:0;echo café\n')])
|
|
||||||
def test_put_to_history(self, entry, entry_utf8, builtins_open, mocker, shell):
|
|
||||||
mocker.patch('thefuck.shells.time',
|
|
||||||
return_value=1430707243.3517463)
|
|
||||||
shell.put_to_history(entry)
|
|
||||||
builtins_open.return_value.__enter__.return_value. \
|
|
||||||
write.assert_called_once_with(entry_utf8)
|
|
||||||
|
|
||||||
def test_and_(self, shell):
|
|
||||||
assert shell.and_('ls', 'cd') == 'ls && cd'
|
|
||||||
|
|
||||||
def test_get_aliases(self, shell):
|
|
||||||
assert shell.get_aliases() == {
|
|
||||||
'fuck': 'eval $(thefuck $(fc -ln -1 | tail -n 1))',
|
|
||||||
'l': 'ls -CF',
|
|
||||||
'la': 'ls -A',
|
|
||||||
'll': 'ls -alF'}
|
|
||||||
|
|
||||||
def test_app_alias(self, shell):
|
|
||||||
assert 'alias fuck' in shell.app_alias('fuck')
|
|
||||||
assert 'alias FUCK' in shell.app_alias('FUCK')
|
|
||||||
assert 'thefuck' in shell.app_alias('fuck')
|
|
||||||
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
|
||||||
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
|
||||||
|
|
||||||
def test_get_history(self, history_lines, shell):
|
|
||||||
history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])
|
|
||||||
assert list(shell.get_history()) == ['ls', 'rm']
|
|
@ -103,11 +103,6 @@ class TestCommand(object):
|
|||||||
monkeypatch.setattr('thefuck.types.Command._wait_output',
|
monkeypatch.setattr('thefuck.types.Command._wait_output',
|
||||||
staticmethod(lambda *_: True))
|
staticmethod(lambda *_: True))
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def generic_shell(self, monkeypatch):
|
|
||||||
monkeypatch.setattr('thefuck.shells.from_shell', lambda x: x)
|
|
||||||
monkeypatch.setattr('thefuck.shells.to_shell', lambda x: x)
|
|
||||||
|
|
||||||
def test_from_script_calls(self, Popen, settings):
|
def test_from_script_calls(self, Popen, settings):
|
||||||
settings.env = {}
|
settings.env = {}
|
||||||
assert Command.from_raw_script(
|
assert Command.from_raw_script(
|
||||||
|
@ -50,7 +50,7 @@ class TestGetClosest(object):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def get_aliases(mocker):
|
def get_aliases(mocker):
|
||||||
mocker.patch('thefuck.shells.get_aliases',
|
mocker.patch('thefuck.shells.shell.get_aliases',
|
||||||
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
|
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,11 +7,12 @@ from argparse import ArgumentParser
|
|||||||
from warnings import warn
|
from warnings import warn
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
import sys
|
import sys
|
||||||
from . import logs, types, shells
|
from . import logs, types
|
||||||
|
from .shells import shell
|
||||||
from .conf import settings
|
from .conf import settings
|
||||||
from .corrector import get_corrected_commands
|
from .corrector import get_corrected_commands
|
||||||
from .exceptions import EmptyCommand
|
from .exceptions import EmptyCommand
|
||||||
from .utils import get_installation_info
|
from .utils import get_installation_info, get_alias
|
||||||
from .ui import select_command
|
from .ui import select_command
|
||||||
|
|
||||||
|
|
||||||
@ -42,10 +43,10 @@ def print_alias(entry_point=True):
|
|||||||
else:
|
else:
|
||||||
position = 2
|
position = 2
|
||||||
|
|
||||||
alias = shells.thefuck_alias()
|
alias = get_alias()
|
||||||
if len(sys.argv) > position:
|
if len(sys.argv) > position:
|
||||||
alias = sys.argv[position]
|
alias = sys.argv[position]
|
||||||
print(shells.app_alias(alias))
|
print(shell.app_alias(alias))
|
||||||
|
|
||||||
|
|
||||||
def how_to_configure_alias():
|
def how_to_configure_alias():
|
||||||
@ -55,17 +56,17 @@ def how_to_configure_alias():
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
settings.init()
|
settings.init()
|
||||||
logs.how_to_configure_alias(shells.how_to_configure())
|
logs.how_to_configure_alias(shell.how_to_configure())
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = ArgumentParser(prog='thefuck')
|
parser = ArgumentParser(prog='thefuck')
|
||||||
version = get_installation_info().version
|
version = get_installation_info().version
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-v', '--version',
|
'-v', '--version',
|
||||||
action='version',
|
action='version',
|
||||||
version='The Fuck {} using Python {}'.format(
|
version='The Fuck {} using Python {}'.format(
|
||||||
version, sys.version.split()[0]))
|
version, sys.version.split()[0]))
|
||||||
parser.add_argument('-a', '--alias',
|
parser.add_argument('-a', '--alias',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='[custom-alias-name] prints alias for current shell')
|
help='[custom-alias-name] prints alias for current shell')
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from thefuck import shells
|
|
||||||
from thefuck.utils import memoize
|
from thefuck.utils import memoize
|
||||||
|
from thefuck.shells import shell
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import CommandNotFound
|
import CommandNotFound
|
||||||
@ -26,5 +26,5 @@ def match(command):
|
|||||||
|
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
name = get_package(command.script)
|
name = get_package(command.script)
|
||||||
formatme = shells.and_('sudo apt-get install {}', '{}')
|
formatme = shell.and_('sudo apt-get install {}', '{}')
|
||||||
return formatme.format(name, command.script)
|
return formatme.format(name, command.script)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
from thefuck import shells
|
|
||||||
from thefuck.utils import for_app
|
from thefuck.utils import for_app
|
||||||
from thefuck.specific.sudo import sudo_support
|
from thefuck.specific.sudo import sudo_support
|
||||||
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
|
||||||
@sudo_support
|
@sudo_support
|
||||||
@ -14,5 +14,5 @@ def match(command):
|
|||||||
|
|
||||||
@sudo_support
|
@sudo_support
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
repl = shells.and_('mkdir -p \\1', 'cd \\1')
|
repl = shell.and_('mkdir -p \\1', 'cd \\1')
|
||||||
return re.sub(r'^cd (.*)', repl, command.script)
|
return re.sub(r'^cd (.*)', repl, command.script)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import tarfile
|
import tarfile
|
||||||
import os
|
import os
|
||||||
from thefuck import shells
|
|
||||||
from thefuck.utils import for_app
|
from thefuck.utils import for_app
|
||||||
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
|
||||||
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
|
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
|
||||||
@ -33,8 +33,8 @@ def match(command):
|
|||||||
|
|
||||||
|
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
dir = shells.quote(_tar_file(command.script_parts)[1])
|
dir = shell.quote(_tar_file(command.script_parts)[1])
|
||||||
return shells.and_('mkdir -p {dir}', '{cmd} -C {dir}') \
|
return shell.and_('mkdir -p {dir}', '{cmd} -C {dir}') \
|
||||||
.format(dir=dir, cmd=command.script)
|
.format(dir=dir, cmd=command.script)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
from thefuck.utils import for_app
|
from thefuck.utils import for_app
|
||||||
from thefuck.shells import quote
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
|
||||||
def _is_bad_zip(file):
|
def _is_bad_zip(file):
|
||||||
@ -38,7 +38,8 @@ def match(command):
|
|||||||
|
|
||||||
|
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
return u'{} -d {}'.format(command.script, quote(_zip_file(command)[:-4]))
|
return u'{} -d {}'.format(
|
||||||
|
command.script, shell.quote(_zip_file(command)[:-4]))
|
||||||
|
|
||||||
|
|
||||||
def side_effect(old_cmd, command):
|
def side_effect(old_cmd, command):
|
||||||
|
@ -2,7 +2,7 @@ import re
|
|||||||
import os
|
import os
|
||||||
from thefuck.utils import memoize, default_settings
|
from thefuck.utils import memoize, default_settings
|
||||||
from thefuck.conf import settings
|
from thefuck.conf import settings
|
||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
|
||||||
# order is important: only the first match is considered
|
# order is important: only the first match is considered
|
||||||
@ -75,4 +75,4 @@ def get_new_command(command):
|
|||||||
file=m.group('file'),
|
file=m.group('file'),
|
||||||
line=m.group('line'))
|
line=m.group('line'))
|
||||||
|
|
||||||
return shells.and_(editor_call, command.script)
|
return shell.and_(editor_call, command.script)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
from thefuck.specific.git import git_support
|
from thefuck.specific.git import git_support
|
||||||
|
|
||||||
|
|
||||||
@ -15,5 +15,5 @@ def get_new_command(command):
|
|||||||
r"error: pathspec '([^']*)' "
|
r"error: pathspec '([^']*)' "
|
||||||
r"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 = shell.and_('git add -- {}', '{}')
|
||||||
return formatme.format(missing_file, command.script)
|
return formatme.format(missing_file, command.script)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
from thefuck.specific.git import git_support
|
from thefuck.specific.git import git_support
|
||||||
|
|
||||||
|
|
||||||
@ -11,4 +11,4 @@ def match(command):
|
|||||||
|
|
||||||
@git_support
|
@git_support
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
return shells.and_('git branch --delete list', 'git branch')
|
return shell.and_('git branch --delete list', 'git branch')
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from thefuck import shells, utils
|
from thefuck import utils
|
||||||
from thefuck.utils import replace_argument
|
from thefuck.utils import replace_argument
|
||||||
from thefuck.specific.git import git_support
|
from thefuck.specific.git import git_support
|
||||||
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
|
||||||
@git_support
|
@git_support
|
||||||
@ -34,5 +35,5 @@ def get_new_command(command):
|
|||||||
if closest_branch:
|
if closest_branch:
|
||||||
return replace_argument(command.script, missing_file, closest_branch)
|
return replace_argument(command.script, missing_file, closest_branch)
|
||||||
else:
|
else:
|
||||||
return shells.and_('git branch {}', '{}').format(
|
return shell.and_('git branch {}', '{}').format(
|
||||||
missing_file, command.script)
|
missing_file, command.script)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
from thefuck.specific.git import git_support
|
from thefuck.specific.git import git_support
|
||||||
|
|
||||||
|
|
||||||
@ -14,4 +14,4 @@ def get_new_command(command):
|
|||||||
branch = line.split(' ')[-1]
|
branch = line.split(' ')[-1]
|
||||||
set_upstream = line.replace('<remote>', 'origin')\
|
set_upstream = line.replace('<remote>', 'origin')\
|
||||||
.replace('<branch>', branch)
|
.replace('<branch>', branch)
|
||||||
return shells.and_(set_upstream, command.script)
|
return shell.and_(set_upstream, command.script)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
from thefuck.utils import replace_argument
|
from thefuck.utils import replace_argument
|
||||||
from thefuck.specific.git import git_support
|
from thefuck.specific.git import git_support
|
||||||
|
|
||||||
@ -13,5 +13,5 @@ def match(command):
|
|||||||
|
|
||||||
@git_support
|
@git_support
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
return shells.and_(replace_argument(command.script, 'push', 'pull'),
|
return shell.and_(replace_argument(command.script, 'push', 'pull'),
|
||||||
command.script)
|
command.script)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
from thefuck.specific.git import git_support
|
from thefuck.specific.git import git_support
|
||||||
|
|
||||||
|
|
||||||
@ -11,5 +11,5 @@ def match(command):
|
|||||||
|
|
||||||
@git_support
|
@git_support
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
formatme = shells.and_('git stash', '{}')
|
formatme = shell.and_('git stash', '{}')
|
||||||
return formatme.format(command.script)
|
return formatme.format(command.script)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from difflib import get_close_matches
|
from difflib import get_close_matches
|
||||||
from thefuck.shells import get_history, thefuck_alias
|
from thefuck.shells import shell
|
||||||
from thefuck.utils import get_closest, memoize, get_all_executables
|
from thefuck.utils import get_closest, memoize, get_all_executables, get_alias
|
||||||
|
|
||||||
|
|
||||||
def _not_corrected(history, tf_alias):
|
def _not_corrected(history, tf_alias):
|
||||||
@ -16,8 +16,8 @@ def _not_corrected(history, tf_alias):
|
|||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
def _history_of_exists_without_current(command):
|
def _history_of_exists_without_current(command):
|
||||||
history = get_history()
|
history = shell.get_history()
|
||||||
tf_alias = thefuck_alias()
|
tf_alias = get_alias()
|
||||||
executables = get_all_executables()
|
executables = get_all_executables()
|
||||||
return [line for line in _not_corrected(history, tf_alias)
|
return [line for line in _not_corrected(history, tf_alias)
|
||||||
if not line.startswith(tf_alias) and not line == command.script
|
if not line.startswith(tf_alias) and not line == command.script
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
|
||||||
patterns = (
|
patterns = (
|
||||||
@ -26,5 +26,5 @@ def get_new_command(command):
|
|||||||
file = file[0]
|
file = file[0]
|
||||||
dir = file[0:file.rfind('/')]
|
dir = file[0:file.rfind('/')]
|
||||||
|
|
||||||
formatme = shells.and_('mkdir -p {}', '{}')
|
formatme = shell.and_('mkdir -p {}', '{}')
|
||||||
return formatme.format(dir, command.script)
|
return formatme.format(dir, command.script)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from thefuck.specific.archlinux import get_pkgfile, archlinux_env
|
from thefuck.specific.archlinux import get_pkgfile, archlinux_env
|
||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
|
||||||
def match(command):
|
def match(command):
|
||||||
@ -9,7 +9,7 @@ def match(command):
|
|||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
packages = get_pkgfile(command.script)
|
packages = get_pkgfile(command.script)
|
||||||
|
|
||||||
formatme = shells.and_('{} -S {}', '{}')
|
formatme = shell.and_('{} -S {}', '{}')
|
||||||
return [formatme.format(pacman, package, command.script)
|
return [formatme.format(pacman, package, command.script)
|
||||||
for package in packages]
|
for package in packages]
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import shlex
|
import shlex
|
||||||
from thefuck.shells import quote
|
from thefuck.shells import shell
|
||||||
from thefuck.utils import for_app
|
from thefuck.utils import for_app
|
||||||
|
|
||||||
|
|
||||||
@ -15,4 +15,4 @@ def get_new_command(command):
|
|||||||
if e.startswith(('s/', '-es/')) and e[-1] != '/':
|
if e.startswith(('s/', '-es/')) and e[-1] != '/':
|
||||||
script[i] += '/'
|
script[i] += '/'
|
||||||
|
|
||||||
return ' '.join(map(quote, script))
|
return ' '.join(map(shell.quote, script))
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
from thefuck.shells import thefuck_alias
|
from thefuck.utils import memoize, get_alias
|
||||||
from thefuck.utils import memoize
|
|
||||||
|
|
||||||
target_layout = '''qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?'''
|
target_layout = '''qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?'''
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ def match(command):
|
|||||||
return False
|
return False
|
||||||
matched_layout = _get_matched_layout(command)
|
matched_layout = _get_matched_layout(command)
|
||||||
return matched_layout and \
|
return matched_layout and \
|
||||||
_switch_command(command, matched_layout) != thefuck_alias()
|
_switch_command(command, matched_layout) != get_alias()
|
||||||
|
|
||||||
|
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
from thefuck.utils import for_app
|
from thefuck.utils import for_app
|
||||||
|
|
||||||
|
|
||||||
@ -10,4 +10,4 @@ def match(command):
|
|||||||
|
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
path = re.findall(r"touch: cannot touch '(.+)/.+':", command.stderr)[0]
|
path = re.findall(r"touch: cannot touch '(.+)/.+':", command.stderr)[0]
|
||||||
return shells.and_(u'mkdir -p {}'.format(path), command.script)
|
return shell.and_(u'mkdir -p {}'.format(path), command.script)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
from thefuck.utils import for_app
|
from thefuck.utils import for_app
|
||||||
|
|
||||||
|
|
||||||
@ -9,4 +9,4 @@ def match(command):
|
|||||||
|
|
||||||
|
|
||||||
def get_new_command(command):
|
def get_new_command(command):
|
||||||
return shells.and_('tsuru login', command.script)
|
return shell.and_('tsuru login', command.script)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from thefuck import shells
|
from thefuck.shells import shell
|
||||||
from thefuck.utils import for_app
|
from thefuck.utils import for_app
|
||||||
|
|
||||||
|
|
||||||
@ -13,8 +13,8 @@ def get_new_command(command):
|
|||||||
if len(cmds) >= 3:
|
if len(cmds) >= 3:
|
||||||
machine = cmds[2]
|
machine = cmds[2]
|
||||||
|
|
||||||
startAllInstances = shells.and_("vagrant up", command.script)
|
startAllInstances = shell.and_("vagrant up", command.script)
|
||||||
if machine is None:
|
if machine is None:
|
||||||
return startAllInstances
|
return startAllInstances
|
||||||
else:
|
else:
|
||||||
return [shells.and_("vagrant up " + machine, command.script), startAllInstances]
|
return [shell.and_("vagrant up " + machine, command.script), startAllInstances]
|
||||||
|
@ -1,335 +0,0 @@
|
|||||||
"""Module with shell specific actions, each shell class should
|
|
||||||
implement `from_shell`, `to_shell`, `app_alias`, `put_to_history` and
|
|
||||||
`get_aliases` methods.
|
|
||||||
|
|
||||||
"""
|
|
||||||
from collections import defaultdict
|
|
||||||
from psutil import Process
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
from time import time
|
|
||||||
import io
|
|
||||||
import os
|
|
||||||
import shlex
|
|
||||||
import sys
|
|
||||||
import six
|
|
||||||
from .utils import DEVNULL, memoize, cache
|
|
||||||
from .conf import settings
|
|
||||||
from . import logs
|
|
||||||
|
|
||||||
|
|
||||||
class Generic(object):
|
|
||||||
def get_aliases(self):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def _expand_aliases(self, command_script):
|
|
||||||
aliases = self.get_aliases()
|
|
||||||
binary = command_script.split(' ')[0]
|
|
||||||
if binary in aliases:
|
|
||||||
return command_script.replace(binary, aliases[binary], 1)
|
|
||||||
else:
|
|
||||||
return command_script
|
|
||||||
|
|
||||||
def from_shell(self, command_script):
|
|
||||||
"""Prepares command before running in app."""
|
|
||||||
return self._expand_aliases(command_script)
|
|
||||||
|
|
||||||
def to_shell(self, command_script):
|
|
||||||
"""Prepares command for running in shell."""
|
|
||||||
return command_script
|
|
||||||
|
|
||||||
def app_alias(self, fuck):
|
|
||||||
return "alias {0}='eval $(TF_ALIAS={0} PYTHONIOENCODING=utf-8 " \
|
|
||||||
"thefuck $(fc -ln -1))'".format(fuck)
|
|
||||||
|
|
||||||
def _get_history_file_name(self):
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def _get_history_line(self, command_script):
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def put_to_history(self, command_script):
|
|
||||||
"""Puts command script to shell history."""
|
|
||||||
history_file_name = self._get_history_file_name()
|
|
||||||
if os.path.isfile(history_file_name):
|
|
||||||
with open(history_file_name, 'a') as history:
|
|
||||||
entry = self._get_history_line(command_script)
|
|
||||||
if six.PY2:
|
|
||||||
history.write(entry.encode('utf-8'))
|
|
||||||
else:
|
|
||||||
history.write(entry)
|
|
||||||
|
|
||||||
def get_history(self):
|
|
||||||
"""Returns list of history entries."""
|
|
||||||
history_file_name = self._get_history_file_name()
|
|
||||||
if os.path.isfile(history_file_name):
|
|
||||||
with io.open(history_file_name, 'r',
|
|
||||||
encoding='utf-8', errors='ignore') as history_file:
|
|
||||||
|
|
||||||
lines = history_file.readlines()
|
|
||||||
if settings.history_limit:
|
|
||||||
lines = lines[-settings.history_limit:]
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
prepared = self._script_from_history(line) \
|
|
||||||
.strip()
|
|
||||||
if prepared:
|
|
||||||
yield prepared
|
|
||||||
|
|
||||||
def and_(self, *commands):
|
|
||||||
return u' && '.join(commands)
|
|
||||||
|
|
||||||
def how_to_configure(self):
|
|
||||||
return
|
|
||||||
|
|
||||||
def split_command(self, command):
|
|
||||||
"""Split the command using shell-like syntax."""
|
|
||||||
if six.PY2:
|
|
||||||
return [s.decode('utf8') for s in shlex.split(command.encode('utf8'))]
|
|
||||||
return shlex.split(command)
|
|
||||||
|
|
||||||
def quote(self, s):
|
|
||||||
"""Return a shell-escaped version of the string s."""
|
|
||||||
|
|
||||||
if six.PY2:
|
|
||||||
from pipes import quote
|
|
||||||
else:
|
|
||||||
from shlex import quote
|
|
||||||
|
|
||||||
return quote(s)
|
|
||||||
|
|
||||||
def _script_from_history(self, line):
|
|
||||||
return line
|
|
||||||
|
|
||||||
|
|
||||||
class Bash(Generic):
|
|
||||||
def app_alias(self, fuck):
|
|
||||||
return "alias {0}='eval " \
|
|
||||||
"$(TF_ALIAS={0} PYTHONIOENCODING=utf-8 thefuck $(fc -ln -1));" \
|
|
||||||
" history -r'".format(fuck)
|
|
||||||
|
|
||||||
def _parse_alias(self, alias):
|
|
||||||
name, value = alias.replace('alias ', '', 1).split('=', 1)
|
|
||||||
if value[0] == value[-1] == '"' or value[0] == value[-1] == "'":
|
|
||||||
value = value[1:-1]
|
|
||||||
return name, value
|
|
||||||
|
|
||||||
@memoize
|
|
||||||
@cache('.bashrc', '.bash_profile')
|
|
||||||
def get_aliases(self):
|
|
||||||
proc = Popen(['bash', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL)
|
|
||||||
return dict(
|
|
||||||
self._parse_alias(alias)
|
|
||||||
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
|
||||||
if alias and '=' in alias)
|
|
||||||
|
|
||||||
def _get_history_file_name(self):
|
|
||||||
return os.environ.get("HISTFILE",
|
|
||||||
os.path.expanduser('~/.bash_history'))
|
|
||||||
|
|
||||||
def _get_history_line(self, command_script):
|
|
||||||
return u'{}\n'.format(command_script)
|
|
||||||
|
|
||||||
def how_to_configure(self):
|
|
||||||
if os.path.join(os.path.expanduser('~'), '.bashrc'):
|
|
||||||
config = '~/.bashrc'
|
|
||||||
elif os.path.join(os.path.expanduser('~'), '.bash_profile'):
|
|
||||||
config = '~/.bashrc'
|
|
||||||
else:
|
|
||||||
config = 'bash config'
|
|
||||||
return 'eval $(thefuck --alias)', config
|
|
||||||
|
|
||||||
|
|
||||||
class Fish(Generic):
|
|
||||||
def _get_overridden_aliases(self):
|
|
||||||
overridden_aliases = os.environ.get('TF_OVERRIDDEN_ALIASES', '').strip()
|
|
||||||
if overridden_aliases:
|
|
||||||
return [alias.strip() for alias in overridden_aliases.split(',')]
|
|
||||||
else:
|
|
||||||
return ['cd', 'grep', 'ls', 'man', 'open']
|
|
||||||
|
|
||||||
def app_alias(self, fuck):
|
|
||||||
return ('function {0} -d "Correct your previous console command"\n'
|
|
||||||
' set -l exit_code $status\n'
|
|
||||||
' set -l fucked_up_command $history[1]\n'
|
|
||||||
' env TF_ALIAS={0} PYTHONIOENCODING=utf-8'
|
|
||||||
' thefuck $fucked_up_command | read -l unfucked_command\n'
|
|
||||||
' if [ "$unfucked_command" != "" ]\n'
|
|
||||||
' eval $unfucked_command\n'
|
|
||||||
' if test $exit_code -ne 0\n'
|
|
||||||
' history --delete $fucked_up_command\n'
|
|
||||||
' history --merge ^ /dev/null\n'
|
|
||||||
' return 0\n'
|
|
||||||
' end\n'
|
|
||||||
' end\n'
|
|
||||||
'end').format(fuck)
|
|
||||||
|
|
||||||
@memoize
|
|
||||||
@cache('.config/fish/config.fish', '.config/fish/functions')
|
|
||||||
def get_aliases(self):
|
|
||||||
overridden = self._get_overridden_aliases()
|
|
||||||
proc = Popen(['fish', '-ic', 'functions'], stdout=PIPE, stderr=DEVNULL)
|
|
||||||
functions = proc.stdout.read().decode('utf-8').strip().split('\n')
|
|
||||||
return {func: func for func in functions if func not in overridden}
|
|
||||||
|
|
||||||
def _expand_aliases(self, command_script):
|
|
||||||
aliases = self.get_aliases()
|
|
||||||
binary = command_script.split(' ')[0]
|
|
||||||
if binary in aliases:
|
|
||||||
return u'fish -ic "{}"'.format(command_script.replace('"', r'\"'))
|
|
||||||
else:
|
|
||||||
return command_script
|
|
||||||
|
|
||||||
def from_shell(self, command_script):
|
|
||||||
"""Prepares command before running in app."""
|
|
||||||
return self._expand_aliases(command_script)
|
|
||||||
|
|
||||||
def _get_history_file_name(self):
|
|
||||||
return os.path.expanduser('~/.config/fish/fish_history')
|
|
||||||
|
|
||||||
def _get_history_line(self, command_script):
|
|
||||||
return u'- cmd: {}\n when: {}\n'.format(command_script, int(time()))
|
|
||||||
|
|
||||||
def _script_from_history(self, line):
|
|
||||||
if '- cmd: ' in line:
|
|
||||||
return line.split('- cmd: ', 1)[1]
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def and_(self, *commands):
|
|
||||||
return u'; and '.join(commands)
|
|
||||||
|
|
||||||
def how_to_configure(self):
|
|
||||||
return (r"eval (thefuck --alias | tr '\n' ';')",
|
|
||||||
'~/.config/fish/config.fish')
|
|
||||||
|
|
||||||
|
|
||||||
class Zsh(Generic):
|
|
||||||
def app_alias(self, fuck):
|
|
||||||
return "alias {0}='eval $(TF_ALIAS={0} PYTHONIOENCODING=utf-8" \
|
|
||||||
" thefuck $(fc -ln -1 | tail -n 1));" \
|
|
||||||
" fc -R'".format(fuck)
|
|
||||||
|
|
||||||
def _parse_alias(self, alias):
|
|
||||||
name, value = alias.split('=', 1)
|
|
||||||
if value[0] == value[-1] == '"' or value[0] == value[-1] == "'":
|
|
||||||
value = value[1:-1]
|
|
||||||
return name, value
|
|
||||||
|
|
||||||
@memoize
|
|
||||||
@cache('.zshrc')
|
|
||||||
def get_aliases(self):
|
|
||||||
proc = Popen(['zsh', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL)
|
|
||||||
return dict(
|
|
||||||
self._parse_alias(alias)
|
|
||||||
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
|
||||||
if alias and '=' in alias)
|
|
||||||
|
|
||||||
def _get_history_file_name(self):
|
|
||||||
return os.environ.get("HISTFILE",
|
|
||||||
os.path.expanduser('~/.zsh_history'))
|
|
||||||
|
|
||||||
def _get_history_line(self, command_script):
|
|
||||||
return u': {}:0;{}\n'.format(int(time()), command_script)
|
|
||||||
|
|
||||||
def _script_from_history(self, line):
|
|
||||||
if ';' in line:
|
|
||||||
return line.split(';', 1)[1]
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def how_to_configure(self):
|
|
||||||
return 'eval $(thefuck --alias)', '~/.zshrc'
|
|
||||||
|
|
||||||
|
|
||||||
class Tcsh(Generic):
|
|
||||||
def app_alias(self, fuck):
|
|
||||||
return ("alias {0} 'setenv TF_ALIAS {0} && "
|
|
||||||
"set fucked_cmd=`history -h 2 | head -n 1` && "
|
|
||||||
"eval `thefuck ${{fucked_cmd}}`'").format(fuck)
|
|
||||||
|
|
||||||
def _parse_alias(self, alias):
|
|
||||||
name, value = alias.split("\t", 1)
|
|
||||||
return name, value
|
|
||||||
|
|
||||||
@memoize
|
|
||||||
def get_aliases(self):
|
|
||||||
proc = Popen(['tcsh', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL)
|
|
||||||
return dict(
|
|
||||||
self._parse_alias(alias)
|
|
||||||
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
|
||||||
if alias and '\t' in alias)
|
|
||||||
|
|
||||||
def _get_history_file_name(self):
|
|
||||||
return os.environ.get("HISTFILE",
|
|
||||||
os.path.expanduser('~/.history'))
|
|
||||||
|
|
||||||
def _get_history_line(self, command_script):
|
|
||||||
return u'#+{}\n{}\n'.format(int(time()), command_script)
|
|
||||||
|
|
||||||
def how_to_configure(self):
|
|
||||||
return 'eval `thefuck --alias`', '~/.tcshrc'
|
|
||||||
|
|
||||||
|
|
||||||
shells = defaultdict(Generic, {
|
|
||||||
'bash': Bash(),
|
|
||||||
'fish': Fish(),
|
|
||||||
'zsh': Zsh(),
|
|
||||||
'csh': Tcsh(),
|
|
||||||
'tcsh': Tcsh()})
|
|
||||||
|
|
||||||
|
|
||||||
@memoize
|
|
||||||
def _get_shell():
|
|
||||||
try:
|
|
||||||
shell = Process(os.getpid()).parent().name()
|
|
||||||
except TypeError:
|
|
||||||
shell = Process(os.getpid()).parent.name
|
|
||||||
return shells[shell]
|
|
||||||
|
|
||||||
|
|
||||||
def from_shell(command):
|
|
||||||
return _get_shell().from_shell(command)
|
|
||||||
|
|
||||||
|
|
||||||
def to_shell(command):
|
|
||||||
return _get_shell().to_shell(command)
|
|
||||||
|
|
||||||
|
|
||||||
def app_alias(alias):
|
|
||||||
return _get_shell().app_alias(alias)
|
|
||||||
|
|
||||||
|
|
||||||
def thefuck_alias():
|
|
||||||
return os.environ.get('TF_ALIAS', 'fuck')
|
|
||||||
|
|
||||||
|
|
||||||
def put_to_history(command):
|
|
||||||
try:
|
|
||||||
return _get_shell().put_to_history(command)
|
|
||||||
except IOError:
|
|
||||||
logs.exception("Can't update history", sys.exc_info())
|
|
||||||
|
|
||||||
|
|
||||||
def and_(*commands):
|
|
||||||
return _get_shell().and_(*commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_aliases():
|
|
||||||
return list(_get_shell().get_aliases().keys())
|
|
||||||
|
|
||||||
|
|
||||||
def split_command(command):
|
|
||||||
return _get_shell().split_command(command)
|
|
||||||
|
|
||||||
|
|
||||||
def quote(s):
|
|
||||||
return _get_shell().quote(s)
|
|
||||||
|
|
||||||
|
|
||||||
@memoize
|
|
||||||
def get_history():
|
|
||||||
return list(_get_shell().get_history())
|
|
||||||
|
|
||||||
|
|
||||||
def how_to_configure():
|
|
||||||
return _get_shell().how_to_configure()
|
|
28
thefuck/shells/__init__.py
Normal file
28
thefuck/shells/__init__.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"""Package with shell specific actions, each shell class should
|
||||||
|
implement `from_shell`, `to_shell`, `app_alias`, `put_to_history` and
|
||||||
|
`get_aliases` methods.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from psutil import Process
|
||||||
|
from .bash import Bash
|
||||||
|
from .fish import Fish
|
||||||
|
from .generic import Generic
|
||||||
|
from .tcsh import Tcsh
|
||||||
|
from .zsh import Zsh
|
||||||
|
|
||||||
|
shells = {'bash': Bash,
|
||||||
|
'fish': Fish,
|
||||||
|
'zsh': Zsh,
|
||||||
|
'csh': Tcsh,
|
||||||
|
'tcsh': Tcsh}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_shell():
|
||||||
|
try:
|
||||||
|
shell_name = Process(os.getpid()).parent().name()
|
||||||
|
except TypeError:
|
||||||
|
shell_name = Process(os.getpid()).parent.name
|
||||||
|
return shells.get(shell_name, Generic)()
|
||||||
|
|
||||||
|
|
||||||
|
shell = _get_shell()
|
42
thefuck/shells/bash.py
Normal file
42
thefuck/shells/bash.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from subprocess import Popen, PIPE
|
||||||
|
import os
|
||||||
|
from ..utils import DEVNULL, memoize, cache
|
||||||
|
from .generic import Generic
|
||||||
|
|
||||||
|
|
||||||
|
class Bash(Generic):
|
||||||
|
def app_alias(self, fuck):
|
||||||
|
return "alias {0}='eval " \
|
||||||
|
"$(TF_ALIAS={0} PYTHONIOENCODING=utf-8 thefuck $(fc -ln -1));" \
|
||||||
|
" history -r'".format(fuck)
|
||||||
|
|
||||||
|
def _parse_alias(self, alias):
|
||||||
|
name, value = alias.replace('alias ', '', 1).split('=', 1)
|
||||||
|
if value[0] == value[-1] == '"' or value[0] == value[-1] == "'":
|
||||||
|
value = value[1:-1]
|
||||||
|
return name, value
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
@cache('.bashrc', '.bash_profile')
|
||||||
|
def get_aliases(self):
|
||||||
|
proc = Popen(['bash', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL)
|
||||||
|
return dict(
|
||||||
|
self._parse_alias(alias)
|
||||||
|
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
||||||
|
if alias and '=' in alias)
|
||||||
|
|
||||||
|
def _get_history_file_name(self):
|
||||||
|
return os.environ.get("HISTFILE",
|
||||||
|
os.path.expanduser('~/.bash_history'))
|
||||||
|
|
||||||
|
def _get_history_line(self, command_script):
|
||||||
|
return u'{}\n'.format(command_script)
|
||||||
|
|
||||||
|
def how_to_configure(self):
|
||||||
|
if os.path.join(os.path.expanduser('~'), '.bashrc'):
|
||||||
|
config = '~/.bashrc'
|
||||||
|
elif os.path.join(os.path.expanduser('~'), '.bash_profile'):
|
||||||
|
config = '~/.bashrc'
|
||||||
|
else:
|
||||||
|
config = 'bash config'
|
||||||
|
return 'eval $(thefuck --alias)', config
|
69
thefuck/shells/fish.py
Normal file
69
thefuck/shells/fish.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from time import time
|
||||||
|
import os
|
||||||
|
from ..utils import DEVNULL, memoize, cache
|
||||||
|
from .generic import Generic
|
||||||
|
|
||||||
|
|
||||||
|
class Fish(Generic):
|
||||||
|
def _get_overridden_aliases(self):
|
||||||
|
overridden_aliases = os.environ.get('TF_OVERRIDDEN_ALIASES', '').strip()
|
||||||
|
if overridden_aliases:
|
||||||
|
return [alias.strip() for alias in overridden_aliases.split(',')]
|
||||||
|
else:
|
||||||
|
return ['cd', 'grep', 'ls', 'man', 'open']
|
||||||
|
|
||||||
|
def app_alias(self, fuck):
|
||||||
|
return ('function {0} -d "Correct your previous console command"\n'
|
||||||
|
' set -l exit_code $status\n'
|
||||||
|
' set -l fucked_up_command $history[1]\n'
|
||||||
|
' env TF_ALIAS={0} PYTHONIOENCODING=utf-8'
|
||||||
|
' thefuck $fucked_up_command | read -l unfucked_command\n'
|
||||||
|
' if [ "$unfucked_command" != "" ]\n'
|
||||||
|
' eval $unfucked_command\n'
|
||||||
|
' if test $exit_code -ne 0\n'
|
||||||
|
' history --delete $fucked_up_command\n'
|
||||||
|
' history --merge ^ /dev/null\n'
|
||||||
|
' return 0\n'
|
||||||
|
' end\n'
|
||||||
|
' end\n'
|
||||||
|
'end').format(fuck)
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
@cache('.config/fish/config.fish', '.config/fish/functions')
|
||||||
|
def get_aliases(self):
|
||||||
|
overridden = self._get_overridden_aliases()
|
||||||
|
proc = Popen(['fish', '-ic', 'functions'], stdout=PIPE, stderr=DEVNULL)
|
||||||
|
functions = proc.stdout.read().decode('utf-8').strip().split('\n')
|
||||||
|
return {func: func for func in functions if func not in overridden}
|
||||||
|
|
||||||
|
def _expand_aliases(self, command_script):
|
||||||
|
aliases = self.get_aliases()
|
||||||
|
binary = command_script.split(' ')[0]
|
||||||
|
if binary in aliases:
|
||||||
|
return u'fish -ic "{}"'.format(command_script.replace('"', r'\"'))
|
||||||
|
else:
|
||||||
|
return command_script
|
||||||
|
|
||||||
|
def from_shell(self, command_script):
|
||||||
|
"""Prepares command before running in app."""
|
||||||
|
return self._expand_aliases(command_script)
|
||||||
|
|
||||||
|
def _get_history_file_name(self):
|
||||||
|
return os.path.expanduser('~/.config/fish/fish_history')
|
||||||
|
|
||||||
|
def _get_history_line(self, command_script):
|
||||||
|
return u'- cmd: {}\n when: {}\n'.format(command_script, int(time()))
|
||||||
|
|
||||||
|
def _script_from_history(self, line):
|
||||||
|
if '- cmd: ' in line:
|
||||||
|
return line.split('- cmd: ', 1)[1]
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def and_(self, *commands):
|
||||||
|
return u'; and '.join(commands)
|
||||||
|
|
||||||
|
def how_to_configure(self):
|
||||||
|
return (r"eval (thefuck --alias | tr '\n' ';')",
|
||||||
|
'~/.config/fish/config.fish')
|
103
thefuck/shells/generic.py
Normal file
103
thefuck/shells/generic.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import io
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import six
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ..utils import memoize
|
||||||
|
from ..conf import settings
|
||||||
|
from .. import logs
|
||||||
|
|
||||||
|
|
||||||
|
class Generic(object):
|
||||||
|
def get_aliases(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def _expand_aliases(self, command_script):
|
||||||
|
aliases = self.get_aliases()
|
||||||
|
binary = command_script.split(' ')[0]
|
||||||
|
if binary in aliases:
|
||||||
|
return command_script.replace(binary, aliases[binary], 1)
|
||||||
|
else:
|
||||||
|
return command_script
|
||||||
|
|
||||||
|
def from_shell(self, command_script):
|
||||||
|
"""Prepares command before running in app."""
|
||||||
|
return self._expand_aliases(command_script)
|
||||||
|
|
||||||
|
def to_shell(self, command_script):
|
||||||
|
"""Prepares command for running in shell."""
|
||||||
|
return command_script
|
||||||
|
|
||||||
|
def app_alias(self, fuck):
|
||||||
|
return "alias {0}='eval $(TF_ALIAS={0} PYTHONIOENCODING=utf-8 " \
|
||||||
|
"thefuck $(fc -ln -1))'".format(fuck)
|
||||||
|
|
||||||
|
def _get_history_file_name(self):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def _get_history_line(self, command_script):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def put_to_history(self, command):
|
||||||
|
try:
|
||||||
|
return self._put_to_history(command)
|
||||||
|
except IOError:
|
||||||
|
logs.exception("Can't update history", sys.exc_info())
|
||||||
|
|
||||||
|
def _put_to_history(self, command_script):
|
||||||
|
"""Puts command script to shell history."""
|
||||||
|
history_file_name = self._get_history_file_name()
|
||||||
|
if os.path.isfile(history_file_name):
|
||||||
|
with open(history_file_name, 'a') as history:
|
||||||
|
entry = self._get_history_line(command_script)
|
||||||
|
if six.PY2:
|
||||||
|
history.write(entry.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
history.write(entry)
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
def get_history(self):
|
||||||
|
return list(self._get_history_lines())
|
||||||
|
|
||||||
|
def _get_history_lines(self):
|
||||||
|
"""Returns list of history entries."""
|
||||||
|
history_file_name = self._get_history_file_name()
|
||||||
|
if os.path.isfile(history_file_name):
|
||||||
|
with io.open(history_file_name, 'r',
|
||||||
|
encoding='utf-8', errors='ignore') as history_file:
|
||||||
|
|
||||||
|
lines = history_file.readlines()
|
||||||
|
if settings.history_limit:
|
||||||
|
lines = lines[-settings.history_limit:]
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
prepared = self._script_from_history(line) \
|
||||||
|
.strip()
|
||||||
|
if prepared:
|
||||||
|
yield prepared
|
||||||
|
|
||||||
|
def and_(self, *commands):
|
||||||
|
return u' && '.join(commands)
|
||||||
|
|
||||||
|
def how_to_configure(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
def split_command(self, command):
|
||||||
|
"""Split the command using shell-like syntax."""
|
||||||
|
if six.PY2:
|
||||||
|
return [s.decode('utf8') for s in shlex.split(command.encode('utf8'))]
|
||||||
|
return shlex.split(command)
|
||||||
|
|
||||||
|
def quote(self, s):
|
||||||
|
"""Return a shell-escaped version of the string s."""
|
||||||
|
|
||||||
|
if six.PY2:
|
||||||
|
from pipes import quote
|
||||||
|
else:
|
||||||
|
from shlex import quote
|
||||||
|
|
||||||
|
return quote(s)
|
||||||
|
|
||||||
|
def _script_from_history(self, line):
|
||||||
|
return line
|
34
thefuck/shells/tcsh.py
Normal file
34
thefuck/shells/tcsh.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from time import time
|
||||||
|
import os
|
||||||
|
from ..utils import DEVNULL, memoize
|
||||||
|
from .generic import Generic
|
||||||
|
|
||||||
|
|
||||||
|
class Tcsh(Generic):
|
||||||
|
def app_alias(self, fuck):
|
||||||
|
return ("alias {0} 'setenv TF_ALIAS {0} && "
|
||||||
|
"set fucked_cmd=`history -h 2 | head -n 1` && "
|
||||||
|
"eval `thefuck ${{fucked_cmd}}`'").format(fuck)
|
||||||
|
|
||||||
|
def _parse_alias(self, alias):
|
||||||
|
name, value = alias.split("\t", 1)
|
||||||
|
return name, value
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
def get_aliases(self):
|
||||||
|
proc = Popen(['tcsh', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL)
|
||||||
|
return dict(
|
||||||
|
self._parse_alias(alias)
|
||||||
|
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
||||||
|
if alias and '\t' in alias)
|
||||||
|
|
||||||
|
def _get_history_file_name(self):
|
||||||
|
return os.environ.get("HISTFILE",
|
||||||
|
os.path.expanduser('~/.history'))
|
||||||
|
|
||||||
|
def _get_history_line(self, command_script):
|
||||||
|
return u'#+{}\n{}\n'.format(int(time()), command_script)
|
||||||
|
|
||||||
|
def how_to_configure(self):
|
||||||
|
return 'eval `thefuck --alias`', '~/.tcshrc'
|
43
thefuck/shells/zsh.py
Normal file
43
thefuck/shells/zsh.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from time import time
|
||||||
|
import os
|
||||||
|
from ..utils import DEVNULL, memoize, cache
|
||||||
|
from .generic import Generic
|
||||||
|
|
||||||
|
|
||||||
|
class Zsh(Generic):
|
||||||
|
def app_alias(self, fuck):
|
||||||
|
return "alias {0}='eval $(TF_ALIAS={0} PYTHONIOENCODING=utf-8" \
|
||||||
|
" thefuck $(fc -ln -1 | tail -n 1));" \
|
||||||
|
" fc -R'".format(fuck)
|
||||||
|
|
||||||
|
def _parse_alias(self, alias):
|
||||||
|
name, value = alias.split('=', 1)
|
||||||
|
if value[0] == value[-1] == '"' or value[0] == value[-1] == "'":
|
||||||
|
value = value[1:-1]
|
||||||
|
return name, value
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
@cache('.zshrc')
|
||||||
|
def get_aliases(self):
|
||||||
|
proc = Popen(['zsh', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL)
|
||||||
|
return dict(
|
||||||
|
self._parse_alias(alias)
|
||||||
|
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
||||||
|
if alias and '=' in alias)
|
||||||
|
|
||||||
|
def _get_history_file_name(self):
|
||||||
|
return os.environ.get("HISTFILE",
|
||||||
|
os.path.expanduser('~/.zsh_history'))
|
||||||
|
|
||||||
|
def _get_history_line(self, command_script):
|
||||||
|
return u': {}:0;{}\n'.format(int(time()), command_script)
|
||||||
|
|
||||||
|
def _script_from_history(self, line):
|
||||||
|
if ';' in line:
|
||||||
|
return line.split(';', 1)[1]
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def how_to_configure(self):
|
||||||
|
return 'eval $(thefuck --alias)', '~/.zshrc'
|
@ -1,7 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
from decorator import decorator
|
from decorator import decorator
|
||||||
from ..utils import is_app
|
from ..utils import is_app
|
||||||
from ..shells import quote, split_command
|
from ..shells import shell
|
||||||
|
|
||||||
|
|
||||||
@decorator
|
@decorator
|
||||||
@ -23,7 +23,8 @@ def git_support(fn, command):
|
|||||||
# 'commit' '--amend'
|
# 'commit' '--amend'
|
||||||
# which is surprising and does not allow to easily test for
|
# which is surprising and does not allow to easily test for
|
||||||
# eg. 'git commit'
|
# eg. 'git commit'
|
||||||
expansion = ' '.join(map(quote, split_command(search.group(2))))
|
expansion = ' '.join(shell.quote(part)
|
||||||
|
for part in shell.split_command(search.group(2)))
|
||||||
new_script = command.script.replace(alias, expansion)
|
new_script = command.script.replace(alias, expansion)
|
||||||
|
|
||||||
command = command.update(script=new_script)
|
command = command.update(script=new_script)
|
||||||
|
@ -4,7 +4,8 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import six
|
import six
|
||||||
from psutil import Process, TimeoutExpired
|
from psutil import Process, TimeoutExpired
|
||||||
from . import logs, shells
|
from . import logs
|
||||||
|
from .shells import shell
|
||||||
from .conf import settings, DEFAULT_PRIORITY, ALL_ENABLED
|
from .conf import settings, DEFAULT_PRIORITY, ALL_ENABLED
|
||||||
from .exceptions import EmptyCommand
|
from .exceptions import EmptyCommand
|
||||||
from .utils import compatibility_call
|
from .utils import compatibility_call
|
||||||
@ -29,7 +30,7 @@ class Command(object):
|
|||||||
def script_parts(self):
|
def script_parts(self):
|
||||||
if not hasattr(self, '_script_parts'):
|
if not hasattr(self, '_script_parts'):
|
||||||
try:
|
try:
|
||||||
self._script_parts = shells.split_command(self.script)
|
self._script_parts = shell.split_command(self.script)
|
||||||
except Exception:
|
except Exception:
|
||||||
logs.debug(u"Can't split command script {} because:\n {}".format(
|
logs.debug(u"Can't split command script {} because:\n {}".format(
|
||||||
self, sys.exc_info()))
|
self, sys.exc_info()))
|
||||||
@ -93,7 +94,7 @@ class Command(object):
|
|||||||
script = ' '.join(raw_script)
|
script = ' '.join(raw_script)
|
||||||
|
|
||||||
script = script.strip()
|
script = script.strip()
|
||||||
return shells.from_shell(script)
|
return shell.from_shell(script)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_raw_script(cls, raw_script):
|
def from_raw_script(cls, raw_script):
|
||||||
@ -279,7 +280,7 @@ class CorrectedCommand(object):
|
|||||||
if self.side_effect:
|
if self.side_effect:
|
||||||
compatibility_call(self.side_effect, old_cmd, self.script)
|
compatibility_call(self.side_effect, old_cmd, self.script)
|
||||||
if settings.alter_history:
|
if settings.alter_history:
|
||||||
shells.put_to_history(self.script)
|
shell.put_to_history(self.script)
|
||||||
# This depends on correct setting of PYTHONIOENCODING by the alias:
|
# This depends on correct setting of PYTHONIOENCODING by the alias:
|
||||||
logs.debug(u'PYTHONIOENCODING: {}'.format(
|
logs.debug(u'PYTHONIOENCODING: {}'.format(
|
||||||
os.environ.get('PYTHONIOENCODING', '>-not-set-<')))
|
os.environ.get('PYTHONIOENCODING', '>-not-set-<')))
|
||||||
|
@ -93,7 +93,7 @@ def get_closest(word, possibilities, n=3, cutoff=0.6, fallback_to_first=True):
|
|||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
def get_all_executables():
|
def get_all_executables():
|
||||||
from thefuck.shells import thefuck_alias, get_aliases
|
from thefuck.shells import shell
|
||||||
|
|
||||||
def _safe(fn, fallback):
|
def _safe(fn, fallback):
|
||||||
try:
|
try:
|
||||||
@ -101,7 +101,7 @@ def get_all_executables():
|
|||||||
except OSError:
|
except OSError:
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
tf_alias = thefuck_alias()
|
tf_alias = get_alias()
|
||||||
tf_entry_points = get_installation_info().get_entry_map()\
|
tf_entry_points = get_installation_info().get_entry_map()\
|
||||||
.get('console_scripts', {})\
|
.get('console_scripts', {})\
|
||||||
.keys()
|
.keys()
|
||||||
@ -110,7 +110,7 @@ def get_all_executables():
|
|||||||
for exe in _safe(lambda: list(Path(path).iterdir()), [])
|
for exe in _safe(lambda: list(Path(path).iterdir()), [])
|
||||||
if not _safe(exe.is_dir, True)
|
if not _safe(exe.is_dir, True)
|
||||||
and exe.name not in tf_entry_points]
|
and exe.name not in tf_entry_points]
|
||||||
aliases = [alias for alias in get_aliases() if alias != tf_alias]
|
aliases = [alias for alias in shell.get_aliases() if alias != tf_alias]
|
||||||
return bins + aliases
|
return bins + aliases
|
||||||
|
|
||||||
|
|
||||||
@ -260,3 +260,7 @@ def compatibility_call(fn, *args):
|
|||||||
|
|
||||||
def get_installation_info():
|
def get_installation_info():
|
||||||
return pkg_resources.require('thefuck')[0]
|
return pkg_resources.require('thefuck')[0]
|
||||||
|
|
||||||
|
|
||||||
|
def get_alias():
|
||||||
|
return os.environ.get('TF_ALIAS', 'fuck')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user