1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-14 05:46:00 +00:00

Compare commits

...

26 Commits
3.4 ... 3.7

Author SHA1 Message Date
nvbn
fa1edd4bae Bump to 3.7 2016-03-23 05:12:29 +02:00
Vladimir Iakovlev
333c4b2a3f Merge pull request #483 from shakaran/patch-1
Update install procedure for pip
2016-03-23 05:11:07 +02:00
Vladimir Iakovlev
b1f10642fa Merge pull request #487 from scorphus/stdout-encoding-none
#486: Use alternative encoding when sys.stdout.encoding is None
2016-03-23 05:09:36 +02:00
Pablo Santiago Blum de Aguiar
047efd5575 #486: Use alternative encoding when sys.stdout.encoding is None
Fix #486
2016-03-22 16:13:59 -03:00
Vladimir Iakovlev
f604756cb7 Merge pull request #485 from scorphus/484-stdin-pipe
#484: Use PIPE as stdin when Popening the script
2016-03-21 22:35:18 +02:00
Pablo Santiago Blum de Aguiar
a27115bff1 #484: Use PIPE as stdin when Popening the script
Fix #484
2016-03-21 16:59:35 -03:00
Ángel Guzmán Maeso
5d00b3bc25 Update install procedure for pip
Avoid the warning:

The directory '/home/someuser/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
2016-03-21 17:48:41 +01:00
Vladimir Iakovlev
0cf4f5e8b0 Merge pull request #481 from scorphus/fix-git-add
Fix `git_add` rule
2016-03-19 12:59:22 +02:00
Pablo Santiago Blum de Aguiar
41707b80c6 #N/A: Fix git_add rule 2016-03-18 22:46:38 -03:00
Vladimir Iakovlev
3a39deb485 Merge pull request #478 from MattKotsenas/feature/powershell-shell
Add Powershell as shell
2016-03-19 02:45:26 +02:00
Matt Kotsenas
d4bc8cebf1 Replace raise with return for Ctrl+C in Windows
- Replace the raise `const.CtrlC` with `return const.CtrlC` the match the
  unix implementation and prevent a stacktrace when cancelling a command
  on Windows
2016-03-16 16:37:59 -07:00
Matt Kotsenas
6daf687237 Add Powershell as a supported shell
- There may be additional functionality to implement, but I've been
  running this way for a month with no known issues
2016-03-16 16:06:09 -07:00
Matt Kotsenas
2207dd2668 Update _get_shell to work with Windows
- _get_shell assumed the parent process would always be the shell process, in Powershell the
  parent process is Python, with the grandparent being the shell
- Switched to walking the process tree so the same code path can be used in both places
2016-03-15 14:10:04 -07:00
Vladimir Iakovlev
d1ab08a797 Merge pull request #477 from scorphus/git-rm-recursive
#N/A: Add new `git_rm_recursive` rule
2016-03-15 04:30:31 +03:00
Pablo Santiago Blum de Aguiar
51e89a36ef #N/A: Add new git_rm_recursive rule 2016-03-14 18:59:32 -03:00
nvbn
39f7cc37eb Bump to 3.6 2016-03-13 17:46:14 +03:00
nvbn
251b69b5a0 #475: Try to use already used executable in no_command 2016-03-13 15:10:37 +03:00
nvbn
8b1f078e27 Bump to 3.5 2016-03-13 03:14:18 +03:00
Vladimir Iakovlev
a47e84fa6b Merge pull request #476 from scorphus/git-help-aliased
#N/A: Add new `git_help_aliased` rule
2016-03-13 01:24:57 +03:00
Pablo Santiago Blum de Aguiar
bcab700215 #N/A: Add new git_help_aliased rule 2016-03-12 18:51:47 -03:00
Vladimir Iakovlev
4fb99fd7a8 Merge pull request #474 from scorphus/alias-variables
#301: Set variables within the alias
2016-03-10 13:25:50 +03:00
Pablo Santiago Blum de Aguiar
bb5f6bb705 #301: Set variables within the alias
Fix #301
2016-03-09 21:58:18 -03:00
Vladimir Iakovlev
d92765d5df Merge pull request #473 from scorphus/gdbm-unavailable
#439 & #447: Remove cache if created with unavailable db
2016-03-10 02:52:33 +03:00
Pablo Santiago Blum de Aguiar
d8de5cfd20 #439 & #447: Remove cache if created with unavailable db
When switching between Python versions, the database package used to
create the cache might be unavailable and an ImportError is raised,
such as `ImportError: No module named gdbm`.
2016-03-08 14:38:16 -03:00
Vladimir Iakovlev
d73b14ce4b Merge pull request #472 from PLNech/master
Rules: git remote add instead of set-url if remote does not exist
2016-03-06 13:33:14 +03:00
Paul-Louis NECH
04b83cf7e8 Rules: git remote add instead of set-url if remote does not exist
Tests: Added test for git_remote_seturl_add

Rules: renamed new rule with a more appropriate name

README: added new rule

Style: Formatting

New rule: corrected test name

Developed tests
2016-03-04 22:35:55 +01:00
27 changed files with 344 additions and 111 deletions

View File

@@ -107,7 +107,7 @@ wget -O - https://raw.githubusercontent.com/nvbn/thefuck/master/install.sh | sh
Install `The Fuck` with `pip`:
```bash
sudo pip install thefuck
sudo -H pip install thefuck
```
[Or using an OS package manager (OS X, Ubuntu, Arch).](https://github.com/nvbn/thefuck/wiki/Installation)
@@ -155,17 +155,20 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `dry` – fixes repetitions like `git git push`;
* `fix_alt_space` – replaces Alt+Space with Space character;
* `fix_file` – opens a file with an error in your `$EDITOR`;
* `git_add` – fixes *"Did you forget to 'git add'?"*;
* `git_add` – fixes *"pathspec 'foo' did not match any file(s) known to git."*;
* `git_branch_delete` – changes `git branch -d` to `git branch -D`;
* `git_branch_list` – catches `git branch list` in place of `git branch` and removes created branch;
* `git_checkout` – fixes branch name or creates new branch;
* `git_diff_staged` – adds `--staged` to previous `git diff` with unexpected output;
* `git_fix_stash` – fixes `git stash` commands (misspelled subcommand and missing `save`);
* `git_help_aliased` &ndash; fixes `git help <alias>` commands replacing <alias> with the aliased command;
* `git_not_command` &ndash; fixes wrong git commands like `git brnch`;
* `git_pull` &ndash; sets upstream before executing previous `git pull`;
* `git_pull_clone` &ndash; clones instead of pulling when the repo does not exist;
* `git_push` &ndash; adds `--set-upstream origin $branch` to previous failed `git push`;
* `git_push_pull` &ndash; runs `git pull` when `push` was rejected;
* `git_rm_recursive` &ndash; adds `-r` when you try to `rm` a directory;
* `git_remote_seturl_add` &ndash; runs `git remote add` when `git remote set_url` on nonexistant remote;
* `git_stash` &ndash; stashes you local modifications before rebasing or switching branch;
* `git_two_dashes` &ndash; adds a missing dash to commands like `git commit -amend` or `git rebase -continue`;
* `go_run` &ndash; appends `.go` extension when compiling/running Go programs;

View File

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

View File

@@ -4,36 +4,28 @@ from tests.utils import Command
@pytest.fixture
def did_not_match(target, did_you_forget=True):
error = ("error: pathspec '{}' did not match any "
"file(s) known to git.".format(target))
if did_you_forget:
error = ("{}\nDid you forget to 'git add'?'".format(error))
return error
def stderr(target):
return ("error: pathspec '{}' did not match any "
'file(s) known to git.'.format(target))
@pytest.mark.parametrize('command', [
Command(script='git submodule update unknown',
stderr=did_not_match('unknown')),
Command(script='git commit unknown',
stderr=did_not_match('unknown'))]) # Older versions of Git
def test_match(command):
assert match(command)
@pytest.mark.parametrize('script, target', [
('git submodule update unknown', 'unknown'),
('git commit unknown', 'unknown')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('command', [
Command(script='git submodule update known', stderr=('')),
Command(script='git commit known', stderr=('')),
Command(script='git commit unknown', # Newer versions of Git
stderr=did_not_match('unknown', False))])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('script', [
'git submodule update known', 'git commit known'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('command, new_command', [
(Command('git submodule update unknown', stderr=did_not_match('unknown')),
@pytest.mark.parametrize('script, target, new_command', [
('git submodule update unknown', 'unknown',
'git add -- unknown && git submodule update unknown'),
(Command('git commit unknown', stderr=did_not_match('unknown')), # Old Git
('git commit unknown', 'unknown',
'git add -- unknown && git commit unknown')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -0,0 +1,24 @@
import pytest
from thefuck.rules.git_help_aliased import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('script, stdout', [
('git help st', "`git st' is aliased to `status'"),
('git help ds', "`git ds' is aliased to `diff --staged'")])
def test_match(script, stdout):
assert match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, stdout', [
('git help status', "GIT-STATUS(1)...Git Manual...GIT-STATUS(1)"),
('git help diff', "GIT-DIFF(1)...Git Manual...GIT-DIFF(1)")])
def test_not_match(script, stdout):
assert not match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, stdout, new_command', [
('git help st', "`git st' is aliased to `status'", 'git help status'),
('git help ds', "`git ds' is aliased to `diff --staged'", 'git help diff')])
def test_get_new_command(script, stdout, new_command):
assert get_new_command(Command(script=script, stdout=stdout)) == new_command

View File

@@ -0,0 +1,26 @@
import pytest
from thefuck.rules.git_remote_seturl_add import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='git remote set-url origin url', stderr="fatal: No such remote")])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command('git remote set-url origin url', stderr=""),
Command('git remote add origin url'),
Command('git remote remove origin'),
Command('git remote prune origin'),
Command('git remote set-branches origin branch')
])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command('git remote set-url origin git@github.com:nvbn/thefuck.git'),
'git remote add origin git@github.com:nvbn/thefuck.git')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -0,0 +1,27 @@
import pytest
from thefuck.rules.git_rm_recursive import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(target):
return "fatal: not removing '{}' recursively without -r".format(target)
@pytest.mark.parametrize('script, target', [
('git rm foo', 'foo'),
('git rm foo bar', 'foo bar')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git rm foo', 'git rm foo bar'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, target, new_command', [
('git rm foo', 'foo', 'git rm -r foo'),
('git rm foo bar', 'foo bar', 'git rm -r foo bar')])
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -3,38 +3,23 @@ from thefuck.rules.history import match, get_new_command
from tests.utils import Command
@pytest.fixture
def history(mocker):
return mocker.patch('thefuck.shells.shell.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.history.get_valid_history_without_current',
return_value=['ls cat', 'diff x'])
@pytest.fixture
def alias(mocker):
return mocker.patch('thefuck.rules.history.get_alias',
return_value='fuck')
@pytest.fixture
def callables(mocker):
return mocker.patch('thefuck.rules.history.get_all_executables',
return_value=['diff', 'ls'])
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script', ['ls cet', 'daff x'])
def test_match(script):
assert match(Command(script=script))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script', ['apt-get', 'nocommand y'])
def test_not_match(script):
assert not match(Command(script=script))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script, result', [
('ls cet', 'ls cat'),
('daff x', 'diff x')])

View File

@@ -6,22 +6,37 @@ from tests.utils import Command
@pytest.fixture(autouse=True)
def get_all_executables(mocker):
mocker.patch('thefuck.rules.no_command.get_all_executables',
return_value=['vim', 'apt-get', 'fsck'])
return_value=['vim', 'fsck', 'git', 'go'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.no_command.get_valid_history_without_current',
return_value=['git commit'])
@pytest.mark.usefixtures('no_memoize')
def test_match():
assert match(Command(stderr='vom: not found', script='vom file.py'))
assert match(Command(stderr='fucck: not found', script='fucck'))
assert not match(Command(stderr='qweqwe: not found', script='qweqwe'))
assert not match(Command(stderr='some text', script='vom file.py'))
@pytest.mark.parametrize('script, stderr', [
('vom file.py', 'vom: not found'),
('fucck', 'fucck: not found'),
('got commit', 'got: command not found')])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
def test_get_new_command():
assert get_new_command(
Command(stderr='vom: not found',
script='vom file.py')) == ['vim file.py']
assert get_new_command(
Command(stderr='fucck: not found',
script='fucck')) == ['fsck']
@pytest.mark.parametrize('script, stderr', [
('qweqwe', 'qweqwe: not found'),
('vom file.py', 'some text')])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, result', [
('vom file.py', ['vim file.py']),
('fucck', ['fsck']),
('got commit', ['git commit', 'go commit'])])
def test_get_new_command(script, result):
assert get_new_command(Command(script)) == result

View File

@@ -46,6 +46,13 @@ class TestBash(object):
assert 'TF_ALIAS=fuck' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING=utf-8' in shell.app_alias('fuck')
def test_app_alias_variables_correctly_set(self, shell):
alias = shell.app_alias('fuck')
assert "alias fuck='TF_CMD=$(TF_ALIAS" in alias
assert '$(TF_ALIAS=fuck PYTHONIOENCODING' in alias
assert 'PYTHONIOENCODING=utf-8 TF_SHELL_ALIASES' in alias
assert 'ALIASES=$(alias) thefuck' in alias
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells import Powershell
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestPowershell(object):
@pytest.fixture
def shell(self):
return Powershell()
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == '(ls) -and (cd)'
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')

View File

@@ -45,6 +45,13 @@ class TestZsh(object):
assert 'thefuck' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING' in shell.app_alias('fuck')
def test_app_alias_variables_correctly_set(self, shell):
alias = shell.app_alias('fuck')
assert "alias fuck='TF_CMD=$(TF_ALIAS" in alias
assert '$(TF_ALIAS=fuck PYTHONIOENCODING' in alias
assert 'PYTHONIOENCODING=utf-8 TF_SHELL_ALIASES' in alias
assert 'ALIASES=$(alias) thefuck' in alias
def test_get_history(self, history_lines, shell):
history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -110,6 +110,7 @@ class TestCommand(object):
'apt-get search vim', 'stdout', 'stderr')
Popen.assert_called_once_with('apt-get search vim',
shell=True,
stdin=PIPE,
stdout=PIPE,
stderr=PIPE,
env={})

View File

@@ -3,7 +3,8 @@ from mock import Mock
import six
from thefuck.utils import default_settings, \
memoize, get_closest, get_all_executables, replace_argument, \
get_all_matched_commands, is_app, for_app, cache, compatibility_call
get_all_matched_commands, is_app, for_app, cache, compatibility_call, \
get_valid_history_without_current
from tests.utils import Command
@@ -229,3 +230,29 @@ class TestCompatibilityCall(object):
return True
assert compatibility_call(side_effect, Command(), Command())
class TestGetValidHistoryWithoutCurrent(object):
@pytest.fixture(autouse=True)
def history(self, mocker):
return mocker.patch('thefuck.shells.shell.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture(autouse=True)
def alias(self, mocker):
return mocker.patch('thefuck.utils.get_alias',
return_value='fuck')
@pytest.fixture(autouse=True)
def callables(self, mocker):
return mocker.patch('thefuck.utils.get_all_executables',
return_value=['diff', 'ls'])
@pytest.mark.parametrize('script, result', [
('le cat', ['ls cat', 'diff x']),
('diff x', ['ls cat']),
('fuck', ['ls cat', 'diff x'])])
def test_get_valid_history_without_current(self, script, result):
command = Command(script=script)
assert get_valid_history_without_current(command) == result

View File

@@ -5,15 +5,14 @@ from thefuck.specific.git import git_support
@git_support
def match(command):
return ('did not match any file(s) known to git.' in command.stderr
and "Did you forget to 'git add'?" in command.stderr)
return 'did not match any file(s) known to git.' in command.stderr
@git_support
def get_new_command(command):
missing_file = re.findall(
r"error: pathspec '([^']*)' "
r"did not match any file\(s\) known to git.", command.stderr)[0]
r"error: pathspec '([^']*)' "
r'did not match any file\(s\) known to git.', command.stderr)[0]
formatme = shell.and_('git add -- {}', '{}')
return formatme.format(missing_file, command.script)

View File

@@ -0,0 +1,12 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return 'help' in command.script and ' is aliased to ' in command.stdout
@git_support
def get_new_command(command):
aliased = command.stdout.split('`', 2)[2].split("'", 1)[0].split(' ', 1)[0]
return 'git help {}'.format(aliased)

View File

@@ -0,0 +1,14 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('set-url' in command.script and 'fatal: No such remote' in command.stderr)
def get_new_command(command):
return replace_argument(command.script, 'set-url', 'add')
enabled_by_default = True

View File

@@ -0,0 +1,15 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return (' rm ' in command.script
and "fatal: not removing '" in command.stderr
and "' recursively without -r" in command.stderr)
@git_support
def get_new_command(command):
index = command.script_parts.index('rm') + 1
command.script_parts.insert(index, '-r')
return u' '.join(command.script_parts)

View File

@@ -1,37 +1,15 @@
from difflib import get_close_matches
from thefuck.shells import shell
from thefuck.utils import get_closest, memoize, get_all_executables, get_alias
def _not_corrected(history, tf_alias):
"""Returns all lines from history except that comes before `fuck`."""
previous = None
for line in history:
if previous is not None and line != tf_alias:
yield previous
previous = line
if history:
yield history[-1]
@memoize
def _history_of_exists_without_current(command):
history = shell.get_history()
tf_alias = get_alias()
executables = get_all_executables()
return [line for line in _not_corrected(history, tf_alias)
if not line.startswith(tf_alias) and not line == command.script
and line.split(' ')[0] in executables]
from thefuck.utils import get_closest, get_valid_history_without_current
def match(command):
return len(get_close_matches(command.script,
_history_of_exists_without_current(command)))
get_valid_history_without_current(command)))
def get_new_command(command):
return get_closest(command.script,
_history_of_exists_without_current(command))
get_valid_history_without_current(command))
priority = 9999

View File

@@ -1,5 +1,6 @@
from difflib import get_close_matches
from thefuck.utils import get_all_executables
from thefuck.utils import get_all_executables, \
get_valid_history_without_current, get_closest
from thefuck.specific.sudo import sudo_support
@@ -11,10 +12,29 @@ def match(command):
get_all_executables())))
def _get_used_executables(command):
for script in get_valid_history_without_current(command):
yield script.split(' ')[0]
@sudo_support
def get_new_command(command):
old_command = command.script_parts[0]
new_cmds = get_close_matches(old_command, get_all_executables(), cutoff=0.1)
# One from history:
already_used = get_closest(
old_command, _get_used_executables(command),
fallback_to_first=False)
if already_used:
new_cmds = [already_used]
else:
new_cmds = []
# Other from all executables:
new_cmds += [cmd for cmd in get_close_matches(old_command,
get_all_executables())
if cmd not in new_cmds]
return [' '.join([new_command] + command.script_parts[1:])
for new_command in new_cmds]

View File

@@ -9,20 +9,37 @@ from .fish import Fish
from .generic import Generic
from .tcsh import Tcsh
from .zsh import Zsh
from .powershell import Powershell
shells = {'bash': Bash,
'fish': Fish,
'zsh': Zsh,
'csh': Tcsh,
'tcsh': Tcsh}
'tcsh': Tcsh,
'powershell': Powershell}
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)()
proc = Process(os.getpid())
while (proc is not None):
name = None
try:
name = proc.name()
except TypeError:
name = proc.name
name = os.path.splitext(name)[0]
if name in shells:
return shells[name]()
try:
proc = proc.parent()
except TypeError:
proc = proc.parent
return Generic()
shell = _get_shell()

View File

@@ -6,9 +6,11 @@ from .generic import Generic
class Bash(Generic):
def app_alias(self, fuck):
alias = "TF_ALIAS={0}" \
" alias {0}='PYTHONIOENCODING=utf-8" \
" TF_CMD=$(TF_SHELL_ALIASES=$(alias) thefuck $(fc -ln -1)) && " \
# It is VERY important to have the variables declared WITHIN the alias
alias = "alias {0}='TF_CMD=$(TF_ALIAS={0}" \
" PYTHONIOENCODING=utf-8" \
" TF_SHELL_ALIASES=$(alias)" \
" thefuck $(fc -ln -1)) &&" \
" eval $TF_CMD".format(fuck)
if settings.alter_history:

View File

@@ -14,6 +14,7 @@ class Fish(Generic):
return ['cd', 'grep', 'ls', 'man', 'open']
def app_alias(self, fuck):
# It is VERY important to have the variables declared WITHIN the alias
return ('function {0} -d "Correct your previous console command"\n'
' set -l fucked_up_command $history[1]\n'
' env TF_ALIAS={0} PYTHONIOENCODING=utf-8'

View File

@@ -0,0 +1,18 @@
from .generic import Generic
class Powershell(Generic):
def app_alias(self, fuck):
return 'function ' + fuck + ' { \n' \
' $fuck = $(thefuck (Get-History -Count 1).CommandLine)\n' \
' if (-not [string]::IsNullOrWhiteSpace($fuck)) {\n' \
' if ($fuck.StartsWith("echo")) { $fuck = $fuck.Substring(5) }\n' \
' else { iex "$fuck" }\n' \
' }\n' \
'}\n'
def and_(self, *commands):
return u' -and '.join('({0})'.format(c) for c in commands)
def how_to_configure(self):
return 'iex "thefuck --alias"', '$profile'

View File

@@ -7,10 +7,11 @@ from .generic import Generic
class Zsh(Generic):
def app_alias(self, alias_name):
alias = "alias {0}='TF_ALIAS={0}" \
# It is VERY important to have the variables declared WITHIN the alias
alias = "alias {0}='TF_CMD=$(TF_ALIAS={0}" \
" PYTHONIOENCODING=utf-8" \
' TF_SHELL_ALIASES=$(alias)' \
" TF_CMD=$(thefuck $(fc -ln -1 | tail -n 1)) &&" \
" TF_SHELL_ALIASES=$(alias)" \
" thefuck $(fc -ln -1 | tail -n 1)) &&" \
" eval $TF_CMD".format(alias_name)
if settings.alter_history:

View File

@@ -1,3 +1,4 @@
import os
import sys
import msvcrt
import win_unicode_console
@@ -16,10 +17,11 @@ def get_key():
ch = msvcrt.getch() # second call returns the actual key code
if ch == b'\x03':
raise const.KEY_CTRL_C
return const.KEY_CTRL_C
if ch == b'H':
return const.KEY_UP
if ch == b'P':
return const.KEY_DOWN
return ch.decode(sys.stdout.encoding)
encoding = sys.stdout.encoding or os.environ.get('PYTHONIOENCODING', 'utf-8')
return ch.decode(encoding)

View File

@@ -114,7 +114,7 @@ class Command(object):
env.update(settings.env)
with logs.debug_time(u'Call: {}; with env: {};'.format(script, env)):
result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE, env=env)
result = Popen(script, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
if cls._wait_output(result):
stdout = result.stdout.read().decode('utf-8')
stderr = result.stderr.read().decode('utf-8')
@@ -282,5 +282,5 @@ class CorrectedCommand(object):
compatibility_call(self.side_effect, old_cmd, self.script)
# This depends on correct setting of PYTHONIOENCODING by the alias:
logs.debug(u'PYTHONIOENCODING: {}'.format(
os.environ.get('PYTHONIOENCODING', '>-not-set-<')))
os.environ.get('PYTHONIOENCODING', '!!not-set!!')))
print(self.script)

View File

@@ -228,8 +228,8 @@ def cache(*depends_on):
value = fn(*args, **kwargs)
db[key] = {'etag': etag, 'value': value}
return value
except shelve_open_error:
# Caused when going from Python 2 to Python 3 and vice-versa
except (shelve_open_error, ImportError):
# Caused when switching between Python versions
warn("Removing possibly out-dated cache")
os.remove(cache_path)
@@ -269,3 +269,24 @@ def get_installation_info():
def get_alias():
return os.environ.get('TF_ALIAS', 'fuck')
@memoize
def get_valid_history_without_current(command):
def _not_corrected(history, tf_alias):
"""Returns all lines from history except that comes before `fuck`."""
previous = None
for line in history:
if previous is not None and line != tf_alias:
yield previous
previous = line
if history:
yield history[-1]
from thefuck.shells import shell
history = shell.get_history()
tf_alias = get_alias()
executables = get_all_executables()
return [line for line in _not_corrected(history, tf_alias)
if not line.startswith(tf_alias) and not line == command.script
and line.split(' ')[0] in executables]