diff --git a/README.md b/README.md index 8f4be820..0749d7ee 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,27 @@ Did you mean this? repl ➜ fuck +lein repl nREPL server started on port 54848 on host 127.0.0.1 - nrepl://127.0.0.1:54848 REPL-y 0.3.1 ... ``` +If you are scary to blindly run changed command, there's `require_confirmation` +[settings](#Settings) option: + +```bash +➜ apt-get install vim +E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied) +E: Unable to lock the administration directory (/var/lib/dpkg/), are you root? + +➜ fuck +sudo apt-get install vim [Enter/Ctrl+C] +[sudo] password for nvbn: +Reading package lists... Done +... +``` + ## Requirements - pip @@ -167,9 +183,10 @@ def get_new_command(command, settings): ## Settings -The Fuck has a few settings parameters: +The Fuck has a few settings parameters, they can be changed in `~/.thefuck/settings.py`: * `rules` – list of enabled rules, by default all; +* `require_confirmation` – require confirmation before running new command, by default `False`; * `wait_command` – max amount of time in seconds for getting previous command output; * `command_not_found` – path to `command_not_found` binary, by default `/usr/lib/command-not-found`. diff --git a/tests/test_main.py b/tests/test_main.py index 84a9ebeb..f2c2843e 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -70,3 +70,28 @@ def test_get_matched_rule(): rules, None) is None assert main.get_matched_rule(main.Command('cd ..', '', ''), rules, None) == rules[0] + + +def test_run_rule(capsys): + with patch('thefuck.main.confirm', return_value=True): + main.run_rule(main.Rule(None, lambda *_: 'new-command'), + None, None) + assert capsys.readouterr() == ('new-command\n', '') + with patch('thefuck.main.confirm', return_value=False): + main.run_rule(main.Rule(None, lambda *_: 'new-command'), + None, None) + assert capsys.readouterr() == ('', '') + + +def test_confirm(capsys): + # When confirmation not required: + assert main.confirm('command', Mock(require_confirmation=False)) + assert capsys.readouterr() == ('', 'command\n') + # When confirmation required and confirmed: + with patch('thefuck.main.sys.stdin.read', return_value='\n'): + assert main.confirm('command', Mock(require_confirmation=True)) + assert capsys.readouterr() == ('', 'command [Enter/Ctrl+C]') + # When confirmation required and ctrl+c: + with patch('thefuck.main.sys.stdin.read', side_effect=KeyboardInterrupt): + assert not main.confirm('command', Mock(require_confirmation=True)) + assert capsys.readouterr() == ('', 'command [Enter/Ctrl+C]Aborted\n') diff --git a/thefuck/main.py b/thefuck/main.py index 36c0a9a2..e5ca16cd 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -28,6 +28,7 @@ def get_settings(user_dir): str(user_dir.joinpath('settings.py'))) settings.__dict__.setdefault('rules', None) settings.__dict__.setdefault('wait_command', 3) + settings.__dict__.setdefault('require_confirmation', False) return settings @@ -90,11 +91,27 @@ def get_matched_rule(command, rules, settings): return rule +def confirm(new_command, settings): + """Returns `True` when running of new command confirmed.""" + if not settings.require_confirmation: + sys.stderr.write(new_command + '\n') + return True + + sys.stderr.write(new_command + ' [Enter/Ctrl+C]') + sys.stderr.flush() + try: + sys.stdin.read(1) + return True + except KeyboardInterrupt: + sys.stderr.write('Aborted\n') + return False + + def run_rule(rule, command, settings): """Runs command from rule for passed command.""" new_command = rule.get_new_command(command, settings) - sys.stderr.write(new_command + '\n') - print(new_command) + if confirm(new_command, settings): + print(new_command) def is_second_run(command):