mirror of
				https://github.com/nvbn/thefuck.git
				synced 2025-10-30 22:54:14 +00:00 
			
		
		
		
	
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -102,14 +102,20 @@ sudo pip install thefuck | ||||
|  | ||||
| [Or using an OS package manager (OS X, Ubuntu, Arch).](https://github.com/nvbn/thefuck/wiki/Installation) | ||||
|  | ||||
| And add to `.bashrc` or `.zshrc` or `.bash_profile`(for OSX): | ||||
| And add to `.bashrc` or `.bash_profile`(for OSX): | ||||
|  | ||||
| ```bash | ||||
| alias fuck='eval $(thefuck $(fc -ln -1))' | ||||
| alias fuck='eval $(thefuck $(fc -ln -1)); history -r' | ||||
| # You can use whatever you want as an alias, like for Mondays: | ||||
| alias FUCK='fuck' | ||||
| ``` | ||||
|  | ||||
| Or in your `.zshrc`: | ||||
|  | ||||
| ```bash | ||||
| alias fuck='eval $(thefuck $(fc -ln -1 | tail -n 1)); fc -R' | ||||
| ``` | ||||
|  | ||||
| Alternatively, you can redirect the output of `thefuck-alias`: | ||||
|  | ||||
| ```bash | ||||
|   | ||||
| @@ -1,42 +0,0 @@ | ||||
| import pytest | ||||
| from mock import Mock | ||||
| from thefuck.history import History | ||||
|  | ||||
|  | ||||
| class TestHistory(object): | ||||
|     @pytest.fixture(autouse=True) | ||||
|     def process(self, monkeypatch): | ||||
|         Process = Mock() | ||||
|         Process.return_value.parent.return_value.pid = 1 | ||||
|         monkeypatch.setattr('thefuck.history.Process', Process) | ||||
|         return Process | ||||
|  | ||||
|     @pytest.fixture(autouse=True) | ||||
|     def db(self, monkeypatch): | ||||
|         class DBMock(dict): | ||||
|             def __init__(self): | ||||
|                 super(DBMock, self).__init__() | ||||
|                 self.sync = Mock() | ||||
|  | ||||
|             def __call__(self, *args, **kwargs): | ||||
|                 return self | ||||
|  | ||||
|         db = DBMock() | ||||
|         monkeypatch.setattr('thefuck.history.shelve.open', db) | ||||
|         return db | ||||
|  | ||||
|     def test_set(self, db): | ||||
|         history = History() | ||||
|         history.update(last_command='ls', | ||||
|                        last_fixed_command=None) | ||||
|         assert db == {'1-last_command': 'ls', | ||||
|                       '1-last_fixed_command': None} | ||||
|  | ||||
|     def test_get(self, db): | ||||
|         history = History() | ||||
|         db['1-last_command'] = 'cd ..' | ||||
|         assert history.last_command == 'cd ..' | ||||
|  | ||||
|     def test_get_without_value(self): | ||||
|         history = History() | ||||
|         assert history.last_command is None | ||||
| @@ -56,7 +56,7 @@ class TestGetCommand(object): | ||||
|         monkeypatch.setattr('thefuck.shells.to_shell', lambda x: x) | ||||
|  | ||||
|     def test_get_command_calls(self, Popen): | ||||
|         assert main.get_command(Mock(), Mock(), | ||||
|         assert main.get_command(Mock(), | ||||
|             ['thefuck', 'apt-get', 'search', 'vim']) \ | ||||
|                == Command('apt-get search vim', 'stdout', 'stderr') | ||||
|         Popen.assert_called_once_with('apt-get search vim', | ||||
| @@ -64,22 +64,14 @@ class TestGetCommand(object): | ||||
|                                       stdout=PIPE, | ||||
|                                       stderr=PIPE, | ||||
|                                       env={'LANG': 'C'}) | ||||
|  | ||||
|     @pytest.mark.parametrize('history, args, result', [ | ||||
|         (Mock(), [''], None), | ||||
|         (Mock(last_command='ls', last_fixed_command='ls -la'), | ||||
|          ['thefuck', 'fuck'], 'ls -la'), | ||||
|         (Mock(last_command='ls', last_fixed_command='ls -la'), | ||||
|          ['thefuck', 'ls'], 'ls -la'), | ||||
|         (Mock(last_command='ls', last_fixed_command=''), | ||||
|          ['thefuck', 'ls'], 'ls'), | ||||
|         (Mock(last_command='ls', last_fixed_command=''), | ||||
|          ['thefuck', 'fuck'], 'ls')]) | ||||
|     def test_get_command_script(self, history, args, result): | ||||
|     @pytest.mark.parametrize('args, result', [ | ||||
|         (['thefuck', 'ls', '-la'], 'ls -la'), | ||||
|         (['thefuck', 'ls'], 'ls')]) | ||||
|     def test_get_command_script(self, args, result): | ||||
|         if result: | ||||
|             assert main.get_command(Mock(), history, args).script == result | ||||
|             assert main.get_command(Mock(), args).script == result | ||||
|         else: | ||||
|             assert main.get_command(Mock(), history, args) is None | ||||
|             assert main.get_command(Mock(), args) is None | ||||
|  | ||||
|  | ||||
| class TestGetMatchedRule(object): | ||||
| @@ -109,7 +101,7 @@ class TestRunRule(object): | ||||
|  | ||||
|     def test_run_rule(self, capsys): | ||||
|         main.run_rule(Rule(get_new_command=lambda *_: 'new-command'), | ||||
|                       Command(), Mock(), None) | ||||
|                       Command(), None) | ||||
|         assert capsys.readouterr() == ('new-command\n', '') | ||||
|  | ||||
|     def test_run_rule_with_side_effect(self, capsys): | ||||
| @@ -118,21 +110,14 @@ class TestRunRule(object): | ||||
|         command = Command() | ||||
|         main.run_rule(Rule(get_new_command=lambda *_: 'new-command', | ||||
|                            side_effect=side_effect), | ||||
|                       command, Mock(), settings) | ||||
|                       command, settings) | ||||
|         assert capsys.readouterr() == ('new-command\n', '') | ||||
|         side_effect.assert_called_once_with(command, settings) | ||||
|  | ||||
|     def test_hisotry_updated(self): | ||||
|         history = Mock() | ||||
|         main.run_rule(Rule(get_new_command=lambda *_: 'ls -lah'), | ||||
|                       Command('ls'), history, None) | ||||
|         history.update.assert_called_once_with(last_command='ls', | ||||
|                                                last_fixed_command='ls -lah') | ||||
|  | ||||
|     def test_when_not_comfirmed(self, capsys, confirm): | ||||
|         confirm.return_value = False | ||||
|         main.run_rule(Rule(get_new_command=lambda *_: 'new-command'), | ||||
|                       Command(), Mock(), None) | ||||
|                       Command(), None) | ||||
|         assert capsys.readouterr() == ('', '') | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,16 +1,35 @@ | ||||
| import pytest | ||||
| from mock import Mock | ||||
| from mock import Mock, MagicMock | ||||
| from thefuck import shells | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def builtins_open(monkeypatch): | ||||
|     mock = MagicMock() | ||||
|     monkeypatch.setattr('six.moves.builtins.open', mock) | ||||
|     return mock | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def isfile(monkeypatch): | ||||
|     mock = Mock(return_value=True) | ||||
|     monkeypatch.setattr('os.path.isfile', mock) | ||||
|     return mock | ||||
|  | ||||
|  | ||||
| class TestGeneric(object): | ||||
|     def test_from_shell(self): | ||||
|         assert shells.Generic().from_shell('pwd') == 'pwd' | ||||
|  | ||||
|     def test_to_shell(self): | ||||
|         assert shells.Bash().to_shell('pwd') == 'pwd' | ||||
|         assert shells.Generic().to_shell('pwd') == 'pwd' | ||||
|  | ||||
|     def test_put_to_history(self, builtins_open): | ||||
|         assert shells.Generic().put_to_history('ls') is None | ||||
|         assert builtins_open.call_count == 0 | ||||
|  | ||||
|  | ||||
| @pytest.mark.usefixtures('isfile') | ||||
| class TestBash(object): | ||||
|     @pytest.fixture(autouse=True) | ||||
|     def Popen(self, monkeypatch): | ||||
| @@ -31,7 +50,13 @@ class TestBash(object): | ||||
|     def test_to_shell(self): | ||||
|         assert shells.Bash().to_shell('pwd') == 'pwd' | ||||
|  | ||||
|     def test_put_to_history(self, builtins_open): | ||||
|         shells.Bash().put_to_history('ls') | ||||
|         builtins_open.return_value.__enter__.return_value.\ | ||||
|             write.assert_called_once_with('ls\n') | ||||
|  | ||||
|  | ||||
| @pytest.mark.usefixtures('isfile') | ||||
| class TestZsh(object): | ||||
|     @pytest.fixture(autouse=True) | ||||
|     def Popen(self, monkeypatch): | ||||
| @@ -51,3 +76,10 @@ class TestZsh(object): | ||||
|  | ||||
|     def test_to_shell(self): | ||||
|         assert shells.Zsh().to_shell('pwd') == 'pwd' | ||||
|  | ||||
|     def test_put_to_history(self, builtins_open, monkeypatch): | ||||
|         monkeypatch.setattr('thefuck.shells.time', | ||||
|                             lambda: 1430707243.3517463) | ||||
|         shells.Zsh().put_to_history('ls') | ||||
|         builtins_open.return_value.__enter__.return_value. \ | ||||
|             write.assert_called_once_with(': 1430707243:0;ls\n') | ||||
| @@ -1,27 +0,0 @@ | ||||
| import os | ||||
| import shelve | ||||
| from tempfile import gettempdir | ||||
| from psutil import Process | ||||
|  | ||||
|  | ||||
| class History(object): | ||||
|     """Temporary history of commands/fixed-commands dependent on | ||||
|     current shell instance. | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     def __init__(self): | ||||
|         self._path = os.path.join(gettempdir(), '.thefuck_history') | ||||
|         self._pid = Process(os.getpid()).parent().pid | ||||
|         self._db = shelve.open(self._path) | ||||
|  | ||||
|     def _prepare_key(self, key): | ||||
|         return '{}-{}'.format(self._pid, key) | ||||
|  | ||||
|     def update(self, **kwargs): | ||||
|         self._db.update({self._prepare_key(k): v for k,v in kwargs.items()}) | ||||
|         self._db.sync() | ||||
|         return self | ||||
|  | ||||
|     def __getattr__(self, item): | ||||
|         return self._db.get(self._prepare_key(item)) | ||||
| @@ -6,7 +6,7 @@ import os | ||||
| import sys | ||||
| from psutil import Process, TimeoutExpired | ||||
| import colorama | ||||
| from .history import History | ||||
| import six | ||||
| from . import logs, conf, types, shells | ||||
|  | ||||
|  | ||||
| @@ -60,22 +60,17 @@ def wait_output(settings, popen): | ||||
|         return False | ||||
|  | ||||
|  | ||||
| def get_command(settings, history, args): | ||||
| def get_command(settings, args): | ||||
|     """Creates command from `args` and executes it.""" | ||||
|     if sys.version_info[0] < 3: | ||||
|     if six.PY2: | ||||
|         script = ' '.join(arg.decode('utf-8') for arg in args[1:]) | ||||
|     else: | ||||
|         script = ' '.join(args[1:]) | ||||
|  | ||||
|     if script == 'fuck' or script == history.last_command: | ||||
|         script = history.last_fixed_command or history.last_command | ||||
|  | ||||
|     if not script: | ||||
|         return | ||||
|  | ||||
|     script = shells.from_shell(script) | ||||
|     history.update(last_command=script, | ||||
|                    last_fixed_command=None) | ||||
|     result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE, | ||||
|                    env=dict(os.environ, LANG='C')) | ||||
|     if wait_output(settings, result): | ||||
| @@ -108,14 +103,13 @@ def confirm(new_command, side_effect, settings): | ||||
|         return False | ||||
|  | ||||
|  | ||||
| def run_rule(rule, command, history, settings): | ||||
| def run_rule(rule, command, settings): | ||||
|     """Runs command from rule for passed command.""" | ||||
|     new_command = shells.to_shell(rule.get_new_command(command, settings)) | ||||
|     if confirm(new_command, rule.side_effect, settings): | ||||
|         if rule.side_effect: | ||||
|             rule.side_effect(command, settings) | ||||
|         history.update(last_command=command.script, | ||||
|                        last_fixed_command=new_command) | ||||
|         shells.put_to_history(new_command) | ||||
|         print(new_command) | ||||
|  | ||||
|  | ||||
| @@ -127,14 +121,13 @@ def main(): | ||||
|     colorama.init() | ||||
|     user_dir = setup_user_dir() | ||||
|     settings = conf.get_settings(user_dir) | ||||
|     history = History() | ||||
|  | ||||
|     command = get_command(settings, history, sys.argv) | ||||
|     command = get_command(settings, sys.argv) | ||||
|     if command: | ||||
|         rules = get_rules(user_dir, settings) | ||||
|         matched_rule = get_matched_rule(command, rules, settings) | ||||
|         if matched_rule: | ||||
|             run_rule(matched_rule, command, history, settings) | ||||
|             run_rule(matched_rule, command, settings) | ||||
|             return | ||||
|  | ||||
|     logs.failed('No fuck given', settings) | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| """Module with shell specific actions, each shell class should | ||||
| implement `from_shell` and `to_shell` methods. | ||||
| implement `from_shell`, `to_shell`, `app_alias` and `put_to_history` | ||||
| methods. | ||||
|  | ||||
| """ | ||||
| from collections import defaultdict | ||||
| from subprocess import Popen, PIPE | ||||
| from time import time | ||||
| import os | ||||
| from psutil import Process | ||||
|  | ||||
| @@ -34,6 +36,19 @@ class Generic(object): | ||||
|     def app_alias(self): | ||||
|         return "\nalias fuck='eval $(thefuck $(fc -ln -1))'\n" | ||||
|  | ||||
|     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: | ||||
|                 history.write(self._get_history_line(command_script)) | ||||
|  | ||||
|  | ||||
| class Bash(Generic): | ||||
|     def _parse_alias(self, alias): | ||||
| @@ -49,6 +64,13 @@ class Bash(Generic): | ||||
|             for alias in proc.stdout.read().decode('utf-8').split('\n') | ||||
|             if 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) | ||||
|  | ||||
|  | ||||
| class Zsh(Generic): | ||||
|     def _parse_alias(self, alias): | ||||
| @@ -64,6 +86,13 @@ class Zsh(Generic): | ||||
|             for alias in proc.stdout.read().decode('utf-8').split('\n') | ||||
|             if 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) | ||||
|  | ||||
|  | ||||
| shells = defaultdict(lambda: Generic(), { | ||||
|     'bash': Bash(), | ||||
| @@ -85,3 +114,7 @@ def to_shell(command): | ||||
|  | ||||
| def app_alias(): | ||||
|     return _get_shell().app_alias() | ||||
|  | ||||
|  | ||||
| def put_to_history(command): | ||||
|     return _get_shell().put_to_history(command) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user