thefuck-leeguoo 3.41__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thefuck/__init__.py +0 -0
- thefuck/ai.py +765 -0
- thefuck/argument_parser.py +96 -0
- thefuck/conf.py +141 -0
- thefuck/const.py +111 -0
- thefuck/corrector.py +92 -0
- thefuck/entrypoints/__init__.py +0 -0
- thefuck/entrypoints/alias.py +28 -0
- thefuck/entrypoints/fix_command.py +105 -0
- thefuck/entrypoints/main.py +50 -0
- thefuck/entrypoints/not_configured.py +201 -0
- thefuck/entrypoints/setup.py +227 -0
- thefuck/entrypoints/shell_logger.py +79 -0
- thefuck/exceptions.py +10 -0
- thefuck/logs.py +255 -0
- thefuck/output_readers/__init__.py +20 -0
- thefuck/output_readers/read_log.py +108 -0
- thefuck/output_readers/rerun.py +72 -0
- thefuck/output_readers/shell_logger.py +60 -0
- thefuck/rules/__init__.py +0 -0
- thefuck/rules/adb_unknown_command.py +54 -0
- thefuck/rules/ag_literal.py +10 -0
- thefuck/rules/apt_get.py +50 -0
- thefuck/rules/apt_get_search.py +14 -0
- thefuck/rules/apt_invalid_operation.py +63 -0
- thefuck/rules/apt_list_upgradable.py +16 -0
- thefuck/rules/apt_upgrade.py +16 -0
- thefuck/rules/aws_cli.py +17 -0
- thefuck/rules/az_cli.py +17 -0
- thefuck/rules/brew_cask_dependency.py +33 -0
- thefuck/rules/brew_install.py +24 -0
- thefuck/rules/brew_link.py +15 -0
- thefuck/rules/brew_reinstall.py +19 -0
- thefuck/rules/brew_uninstall.py +14 -0
- thefuck/rules/brew_unknown_command.py +82 -0
- thefuck/rules/brew_update_formula.py +12 -0
- thefuck/rules/cargo.py +6 -0
- thefuck/rules/cargo_no_command.py +15 -0
- thefuck/rules/cat_dir.py +14 -0
- thefuck/rules/cd_correction.py +61 -0
- thefuck/rules/cd_cs.py +21 -0
- thefuck/rules/cd_mkdir.py +21 -0
- thefuck/rules/cd_parent.py +16 -0
- thefuck/rules/chmod_x.py +15 -0
- thefuck/rules/choco_install.py +25 -0
- thefuck/rules/composer_not_command.py +22 -0
- thefuck/rules/conda_mistype.py +17 -0
- thefuck/rules/cp_create_destination.py +15 -0
- thefuck/rules/cp_omitting_directory.py +15 -0
- thefuck/rules/cpp11.py +12 -0
- thefuck/rules/dirty_untar.py +53 -0
- thefuck/rules/dirty_unzip.py +60 -0
- thefuck/rules/django_south_ghost.py +8 -0
- thefuck/rules/django_south_merge.py +8 -0
- thefuck/rules/dnf_no_such_command.py +37 -0
- thefuck/rules/docker_image_being_used_by_container.py +20 -0
- thefuck/rules/docker_login.py +13 -0
- thefuck/rules/docker_not_command.py +49 -0
- thefuck/rules/dry.py +15 -0
- thefuck/rules/fab_command_not_found.py +38 -0
- thefuck/rules/fix_alt_space.py +15 -0
- thefuck/rules/fix_file.py +80 -0
- thefuck/rules/gem_unknown_command.py +36 -0
- thefuck/rules/git_add.py +27 -0
- thefuck/rules/git_add_force.py +13 -0
- thefuck/rules/git_bisect_usage.py +16 -0
- thefuck/rules/git_branch_0flag.py +24 -0
- thefuck/rules/git_branch_delete.py +13 -0
- thefuck/rules/git_branch_delete_checked_out.py +19 -0
- thefuck/rules/git_branch_exists.py +25 -0
- thefuck/rules/git_branch_list.py +14 -0
- thefuck/rules/git_checkout.py +49 -0
- thefuck/rules/git_clone_git_clone.py +12 -0
- thefuck/rules/git_clone_missing.py +42 -0
- thefuck/rules/git_commit_add.py +17 -0
- thefuck/rules/git_commit_amend.py +11 -0
- thefuck/rules/git_commit_reset.py +11 -0
- thefuck/rules/git_diff_no_index.py +16 -0
- thefuck/rules/git_diff_staged.py +13 -0
- thefuck/rules/git_fix_stash.py +37 -0
- thefuck/rules/git_flag_after_filename.py +31 -0
- thefuck/rules/git_help_aliased.py +12 -0
- thefuck/rules/git_hook_bypass.py +27 -0
- thefuck/rules/git_lfs_mistype.py +18 -0
- thefuck/rules/git_main_master.py +16 -0
- thefuck/rules/git_merge.py +18 -0
- thefuck/rules/git_merge_unrelated.py +12 -0
- thefuck/rules/git_not_command.py +18 -0
- thefuck/rules/git_pull.py +16 -0
- thefuck/rules/git_pull_clone.py +13 -0
- thefuck/rules/git_pull_uncommitted_changes.py +14 -0
- thefuck/rules/git_push.py +44 -0
- thefuck/rules/git_push_different_branch_names.py +12 -0
- thefuck/rules/git_push_force.py +18 -0
- thefuck/rules/git_push_pull.py +20 -0
- thefuck/rules/git_push_without_commits.py +12 -0
- thefuck/rules/git_rebase_merge_dir.py +17 -0
- thefuck/rules/git_rebase_no_changes.py +13 -0
- thefuck/rules/git_remote_delete.py +13 -0
- thefuck/rules/git_remote_seturl_add.py +12 -0
- thefuck/rules/git_rm_local_modifications.py +19 -0
- thefuck/rules/git_rm_recursive.py +16 -0
- thefuck/rules/git_rm_staged.py +19 -0
- thefuck/rules/git_stash.py +15 -0
- thefuck/rules/git_stash_pop.py +18 -0
- thefuck/rules/git_tag_force.py +13 -0
- thefuck/rules/git_two_dashes.py +14 -0
- thefuck/rules/go_run.py +16 -0
- thefuck/rules/go_unknown_command.py +28 -0
- thefuck/rules/gradle_no_task.py +34 -0
- thefuck/rules/gradle_wrapper.py +13 -0
- thefuck/rules/grep_arguments_order.py +23 -0
- thefuck/rules/grep_recursive.py +10 -0
- thefuck/rules/grunt_task_not_found.py +37 -0
- thefuck/rules/gulp_not_task.py +22 -0
- thefuck/rules/has_exists_script.py +13 -0
- thefuck/rules/heroku_multiple_apps.py +12 -0
- thefuck/rules/heroku_not_command.py +11 -0
- thefuck/rules/history.py +15 -0
- thefuck/rules/hostscli.py +27 -0
- thefuck/rules/ifconfig_device_not_found.py +23 -0
- thefuck/rules/java.py +17 -0
- thefuck/rules/javac.py +18 -0
- thefuck/rules/lein_not_task.py +19 -0
- thefuck/rules/ln_no_hard_link.py +23 -0
- thefuck/rules/ln_s_order.py +26 -0
- thefuck/rules/long_form_help.py +27 -0
- thefuck/rules/ls_all.py +10 -0
- thefuck/rules/ls_lah.py +12 -0
- thefuck/rules/man.py +33 -0
- thefuck/rules/man_no_space.py +10 -0
- thefuck/rules/mercurial.py +27 -0
- thefuck/rules/missing_space_before_subcommand.py +21 -0
- thefuck/rules/mkdir_p.py +13 -0
- thefuck/rules/mvn_no_command.py +11 -0
- thefuck/rules/mvn_unknown_lifecycle_phase.py +30 -0
- thefuck/rules/nixos_cmd_not_found.py +15 -0
- thefuck/rules/no_command.py +41 -0
- thefuck/rules/no_such_file.py +30 -0
- thefuck/rules/npm_missing_script.py +17 -0
- thefuck/rules/npm_run_script.py +17 -0
- thefuck/rules/npm_wrong_command.py +42 -0
- thefuck/rules/omnienv_no_such_command.py +35 -0
- thefuck/rules/open.py +40 -0
- thefuck/rules/pacman.py +17 -0
- thefuck/rules/pacman_invalid_option.py +20 -0
- thefuck/rules/pacman_not_found.py +26 -0
- thefuck/rules/path_from_history.py +53 -0
- thefuck/rules/php_s.py +11 -0
- thefuck/rules/pip_install.py +15 -0
- thefuck/rules/pip_unknown_command.py +19 -0
- thefuck/rules/port_already_in_use.py +40 -0
- thefuck/rules/prove_recursively.py +27 -0
- thefuck/rules/python_command.py +17 -0
- thefuck/rules/python_execute.py +15 -0
- thefuck/rules/python_module_error.py +13 -0
- thefuck/rules/quotation_marks.py +12 -0
- thefuck/rules/rails_migrations_pending.py +14 -0
- thefuck/rules/react_native_command_unrecognized.py +34 -0
- thefuck/rules/remove_shell_prompt_literal.py +23 -0
- thefuck/rules/remove_trailing_cedilla.py +11 -0
- thefuck/rules/rm_dir.py +16 -0
- thefuck/rules/rm_root.py +16 -0
- thefuck/rules/scm_correction.py +32 -0
- thefuck/rules/sed_unterminated_s.py +18 -0
- thefuck/rules/sl_ls.py +14 -0
- thefuck/rules/ssh_known_hosts.py +37 -0
- thefuck/rules/sudo.py +47 -0
- thefuck/rules/sudo_command_from_user_path.py +21 -0
- thefuck/rules/switch_lang.py +117 -0
- thefuck/rules/systemctl.py +22 -0
- thefuck/rules/terraform_init.py +13 -0
- thefuck/rules/terraform_no_command.py +16 -0
- thefuck/rules/test.py.py +10 -0
- thefuck/rules/tmux.py +18 -0
- thefuck/rules/touch.py +14 -0
- thefuck/rules/tsuru_login.py +12 -0
- thefuck/rules/tsuru_not_command.py +15 -0
- thefuck/rules/unknown_command.py +13 -0
- thefuck/rules/unsudo.py +15 -0
- thefuck/rules/vagrant_up.py +21 -0
- thefuck/rules/whois.py +34 -0
- thefuck/rules/workon_doesnt_exists.py +32 -0
- thefuck/rules/wrong_hyphen_before_subcommand.py +20 -0
- thefuck/rules/yarn_alias.py +14 -0
- thefuck/rules/yarn_command_not_found.py +43 -0
- thefuck/rules/yarn_command_replaced.py +13 -0
- thefuck/rules/yarn_help.py +17 -0
- thefuck/rules/yum_invalid_operation.py +39 -0
- thefuck/shells/__init__.py +52 -0
- thefuck/shells/bash.py +94 -0
- thefuck/shells/fish.py +131 -0
- thefuck/shells/generic.py +154 -0
- thefuck/shells/powershell.py +43 -0
- thefuck/shells/tcsh.py +44 -0
- thefuck/shells/zsh.py +98 -0
- thefuck/specific/__init__.py +0 -0
- thefuck/specific/apt.py +3 -0
- thefuck/specific/archlinux.py +48 -0
- thefuck/specific/brew.py +15 -0
- thefuck/specific/dnf.py +3 -0
- thefuck/specific/git.py +32 -0
- thefuck/specific/nix.py +3 -0
- thefuck/specific/npm.py +21 -0
- thefuck/specific/sudo.py +18 -0
- thefuck/specific/yum.py +3 -0
- thefuck/system/__init__.py +7 -0
- thefuck/system/unix.py +57 -0
- thefuck/system/win32.py +43 -0
- thefuck/types.py +261 -0
- thefuck/ui.py +116 -0
- thefuck/utils.py +385 -0
- thefuck_leeguoo-3.41.dist-info/METADATA +681 -0
- thefuck_leeguoo-3.41.dist-info/RECORD +218 -0
- thefuck_leeguoo-3.41.dist-info/WHEEL +6 -0
- thefuck_leeguoo-3.41.dist-info/entry_points.txt +3 -0
- thefuck_leeguoo-3.41.dist-info/licenses/LICENSE.md +22 -0
- thefuck_leeguoo-3.41.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from itertools import dropwhile, islice, takewhile
|
|
3
|
+
|
|
4
|
+
from thefuck.specific.sudo import sudo_support
|
|
5
|
+
from thefuck.specific.yum import yum_available
|
|
6
|
+
from thefuck.utils import for_app, replace_command, which, cache
|
|
7
|
+
|
|
8
|
+
enabled_by_default = yum_available
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@sudo_support
|
|
12
|
+
@for_app('yum')
|
|
13
|
+
def match(command):
|
|
14
|
+
return 'No such command: ' in command.output
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_operations():
|
|
18
|
+
proc = subprocess.Popen('yum', stdout=subprocess.PIPE)
|
|
19
|
+
|
|
20
|
+
lines = proc.stdout.readlines()
|
|
21
|
+
lines = [line.decode('utf-8') for line in lines]
|
|
22
|
+
lines = dropwhile(lambda line: not line.startswith("List of Commands:"), lines)
|
|
23
|
+
lines = islice(lines, 2, None)
|
|
24
|
+
lines = list(takewhile(lambda line: line.strip(), lines))
|
|
25
|
+
return [line.strip().split(' ')[0] for line in lines]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if which('yum'):
|
|
29
|
+
_get_operations = cache(which('yum'))(_get_operations)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@sudo_support
|
|
33
|
+
def get_new_command(command):
|
|
34
|
+
invalid_operation = command.script_parts[1]
|
|
35
|
+
|
|
36
|
+
if invalid_operation == 'uninstall':
|
|
37
|
+
return [command.script.replace('uninstall', 'remove')]
|
|
38
|
+
|
|
39
|
+
return replace_command(command, invalid_operation, _get_operations())
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Package with shell specific actions, each shell class should
|
|
2
|
+
implement `from_shell`, `to_shell`, `app_alias`, `put_to_history` and
|
|
3
|
+
`get_aliases` methods.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
from psutil import Process
|
|
7
|
+
from .bash import Bash
|
|
8
|
+
from .fish import Fish
|
|
9
|
+
from .generic import Generic
|
|
10
|
+
from .tcsh import Tcsh
|
|
11
|
+
from .zsh import Zsh
|
|
12
|
+
from .powershell import Powershell
|
|
13
|
+
|
|
14
|
+
shells = {'bash': Bash,
|
|
15
|
+
'fish': Fish,
|
|
16
|
+
'zsh': Zsh,
|
|
17
|
+
'csh': Tcsh,
|
|
18
|
+
'tcsh': Tcsh,
|
|
19
|
+
'powershell': Powershell,
|
|
20
|
+
'pwsh': Powershell}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_shell_from_env():
|
|
24
|
+
name = os.environ.get('TF_SHELL')
|
|
25
|
+
|
|
26
|
+
if name in shells:
|
|
27
|
+
return shells[name]()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _get_shell_from_proc():
|
|
31
|
+
proc = Process(os.getpid())
|
|
32
|
+
|
|
33
|
+
while proc is not None and proc.pid > 0:
|
|
34
|
+
try:
|
|
35
|
+
name = proc.name()
|
|
36
|
+
except TypeError:
|
|
37
|
+
name = proc.name
|
|
38
|
+
|
|
39
|
+
name = os.path.splitext(name)[0]
|
|
40
|
+
|
|
41
|
+
if name in shells:
|
|
42
|
+
return shells[name]()
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
proc = proc.parent()
|
|
46
|
+
except TypeError:
|
|
47
|
+
proc = proc.parent
|
|
48
|
+
|
|
49
|
+
return Generic()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
shell = _get_shell_from_env() or _get_shell_from_proc()
|
thefuck/shells/bash.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from subprocess import Popen, PIPE
|
|
3
|
+
from tempfile import gettempdir
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
from ..conf import settings
|
|
6
|
+
from ..const import ARGUMENT_PLACEHOLDER, USER_COMMAND_MARK
|
|
7
|
+
from ..utils import DEVNULL, memoize
|
|
8
|
+
from .generic import Generic
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Bash(Generic):
|
|
12
|
+
friendly_name = 'Bash'
|
|
13
|
+
|
|
14
|
+
def app_alias(self, alias_name):
|
|
15
|
+
# It is VERY important to have the variables declared WITHIN the function
|
|
16
|
+
return '''
|
|
17
|
+
function {name} () {{
|
|
18
|
+
TF_PYTHONIOENCODING=$PYTHONIOENCODING;
|
|
19
|
+
export TF_SHELL=bash;
|
|
20
|
+
export TF_ALIAS={name};
|
|
21
|
+
export TF_SHELL_ALIASES=$(alias);
|
|
22
|
+
export TF_HISTORY=$(fc -ln -10);
|
|
23
|
+
export TF_PROMPT="$*";
|
|
24
|
+
export PYTHONIOENCODING=utf-8;
|
|
25
|
+
TF_CMD=$(
|
|
26
|
+
thefuck {argument_placeholder} "$@"
|
|
27
|
+
) && eval "$TF_CMD";
|
|
28
|
+
unset TF_HISTORY;
|
|
29
|
+
unset TF_PROMPT;
|
|
30
|
+
export PYTHONIOENCODING=$TF_PYTHONIOENCODING;
|
|
31
|
+
{alter_history}
|
|
32
|
+
}}
|
|
33
|
+
'''.format(
|
|
34
|
+
name=alias_name,
|
|
35
|
+
argument_placeholder=ARGUMENT_PLACEHOLDER,
|
|
36
|
+
alter_history=('history -s $TF_CMD;'
|
|
37
|
+
if settings.alter_history else ''))
|
|
38
|
+
|
|
39
|
+
def instant_mode_alias(self, alias_name):
|
|
40
|
+
if os.environ.get('THEFUCK_INSTANT_MODE', '').lower() == 'true':
|
|
41
|
+
mark = USER_COMMAND_MARK + '\b' * len(USER_COMMAND_MARK)
|
|
42
|
+
return '''
|
|
43
|
+
export PS1="{user_command_mark}$PS1";
|
|
44
|
+
{app_alias}
|
|
45
|
+
'''.format(user_command_mark=mark,
|
|
46
|
+
app_alias=self.app_alias(alias_name))
|
|
47
|
+
else:
|
|
48
|
+
log_path = os.path.join(
|
|
49
|
+
gettempdir(), 'thefuck-script-log-{}'.format(uuid4().hex))
|
|
50
|
+
return '''
|
|
51
|
+
export THEFUCK_INSTANT_MODE=True;
|
|
52
|
+
export THEFUCK_OUTPUT_LOG={log};
|
|
53
|
+
thefuck --shell-logger {log};
|
|
54
|
+
rm {log};
|
|
55
|
+
exit
|
|
56
|
+
'''.format(log=log_path)
|
|
57
|
+
|
|
58
|
+
def _parse_alias(self, alias):
|
|
59
|
+
name, value = alias.replace('alias ', '', 1).split('=', 1)
|
|
60
|
+
if value[0] == value[-1] == '"' or value[0] == value[-1] == "'":
|
|
61
|
+
value = value[1:-1]
|
|
62
|
+
return name, value
|
|
63
|
+
|
|
64
|
+
@memoize
|
|
65
|
+
def get_aliases(self):
|
|
66
|
+
raw_aliases = os.environ.get('TF_SHELL_ALIASES', '').split('\n')
|
|
67
|
+
return dict(self._parse_alias(alias)
|
|
68
|
+
for alias in raw_aliases if alias and '=' in alias)
|
|
69
|
+
|
|
70
|
+
def _get_history_file_name(self):
|
|
71
|
+
return os.environ.get("HISTFILE",
|
|
72
|
+
os.path.expanduser('~/.bash_history'))
|
|
73
|
+
|
|
74
|
+
def _get_history_line(self, command_script):
|
|
75
|
+
return u'{}\n'.format(command_script)
|
|
76
|
+
|
|
77
|
+
def how_to_configure(self):
|
|
78
|
+
if os.path.join(os.path.expanduser('~'), '.bashrc'):
|
|
79
|
+
config = '~/.bashrc'
|
|
80
|
+
elif os.path.join(os.path.expanduser('~'), '.bash_profile'):
|
|
81
|
+
config = '~/.bash_profile'
|
|
82
|
+
else:
|
|
83
|
+
config = 'bash config'
|
|
84
|
+
|
|
85
|
+
return self._create_shell_configuration(
|
|
86
|
+
content=u'eval "$(thefuck --alias)"',
|
|
87
|
+
path=config,
|
|
88
|
+
reload=u'source {}'.format(config))
|
|
89
|
+
|
|
90
|
+
def _get_version(self):
|
|
91
|
+
"""Returns the version of the current shell"""
|
|
92
|
+
proc = Popen(['bash', '-c', 'echo $BASH_VERSION'],
|
|
93
|
+
stdout=PIPE, stderr=DEVNULL)
|
|
94
|
+
return proc.stdout.read().decode('utf-8').strip()
|
thefuck/shells/fish.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from subprocess import Popen, PIPE
|
|
2
|
+
from time import time
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import six
|
|
6
|
+
from .. import logs
|
|
7
|
+
from ..conf import settings
|
|
8
|
+
from ..const import ARGUMENT_PLACEHOLDER
|
|
9
|
+
from ..utils import DEVNULL, cache
|
|
10
|
+
from .generic import Generic
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@cache('~/.config/fish/config.fish', '~/.config/fish/functions')
|
|
14
|
+
def _get_functions(overridden):
|
|
15
|
+
proc = Popen(['fish', '-ic', 'functions'], stdout=PIPE, stderr=DEVNULL)
|
|
16
|
+
functions = proc.stdout.read().decode('utf-8').strip().split('\n')
|
|
17
|
+
return {func: func for func in functions if func not in overridden}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@cache('~/.config/fish/config.fish')
|
|
21
|
+
def _get_aliases(overridden):
|
|
22
|
+
aliases = {}
|
|
23
|
+
proc = Popen(['fish', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL)
|
|
24
|
+
alias_out = proc.stdout.read().decode('utf-8').strip()
|
|
25
|
+
if not alias_out:
|
|
26
|
+
return aliases
|
|
27
|
+
for alias in alias_out.split('\n'):
|
|
28
|
+
for separator in (' ', '='):
|
|
29
|
+
split_alias = alias.replace('alias ', '', 1).split(separator, 1)
|
|
30
|
+
if len(split_alias) == 2:
|
|
31
|
+
name, value = split_alias
|
|
32
|
+
break
|
|
33
|
+
else:
|
|
34
|
+
continue
|
|
35
|
+
if name not in overridden:
|
|
36
|
+
aliases[name] = value
|
|
37
|
+
return aliases
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Fish(Generic):
|
|
41
|
+
friendly_name = 'Fish Shell'
|
|
42
|
+
|
|
43
|
+
def _get_overridden_aliases(self):
|
|
44
|
+
overridden = os.environ.get('THEFUCK_OVERRIDDEN_ALIASES',
|
|
45
|
+
os.environ.get('TF_OVERRIDDEN_ALIASES', ''))
|
|
46
|
+
default = {'cd', 'grep', 'ls', 'man', 'open'}
|
|
47
|
+
for alias in overridden.split(','):
|
|
48
|
+
default.add(alias.strip())
|
|
49
|
+
return sorted(default)
|
|
50
|
+
|
|
51
|
+
def app_alias(self, alias_name):
|
|
52
|
+
if settings.alter_history:
|
|
53
|
+
alter_history = (' builtin history delete --exact'
|
|
54
|
+
' --case-sensitive -- $fucked_up_command\n'
|
|
55
|
+
' builtin history merge\n')
|
|
56
|
+
else:
|
|
57
|
+
alter_history = ''
|
|
58
|
+
# It is VERY important to have the variables declared WITHIN the alias
|
|
59
|
+
return ('function {0} -d "Correct your previous console command"\n'
|
|
60
|
+
' set -l fucked_up_command $history[1]\n'
|
|
61
|
+
' set -l tf_prompt (string join " " -- $argv)\n'
|
|
62
|
+
' env TF_SHELL=fish TF_ALIAS={0} PYTHONIOENCODING=utf-8'
|
|
63
|
+
' TF_COMMAND="$fucked_up_command" TF_PROMPT="$tf_prompt"'
|
|
64
|
+
' thefuck $fucked_up_command {2} $argv | read -l unfucked_command\n'
|
|
65
|
+
' if [ "$unfucked_command" != "" ]\n'
|
|
66
|
+
' eval $unfucked_command\n{1}'
|
|
67
|
+
' end\n'
|
|
68
|
+
'end').format(alias_name, alter_history, ARGUMENT_PLACEHOLDER)
|
|
69
|
+
|
|
70
|
+
def get_aliases(self):
|
|
71
|
+
overridden = self._get_overridden_aliases()
|
|
72
|
+
functions = _get_functions(overridden)
|
|
73
|
+
raw_aliases = _get_aliases(overridden)
|
|
74
|
+
functions.update(raw_aliases)
|
|
75
|
+
return functions
|
|
76
|
+
|
|
77
|
+
def _expand_aliases(self, command_script):
|
|
78
|
+
aliases = self.get_aliases()
|
|
79
|
+
binary = command_script.split(' ')[0]
|
|
80
|
+
if binary in aliases and aliases[binary] != binary:
|
|
81
|
+
return command_script.replace(binary, aliases[binary], 1)
|
|
82
|
+
elif binary in aliases:
|
|
83
|
+
return u'fish -ic "{}"'.format(command_script.replace('"', r'\"'))
|
|
84
|
+
else:
|
|
85
|
+
return command_script
|
|
86
|
+
|
|
87
|
+
def _get_history_file_name(self):
|
|
88
|
+
return os.path.expanduser('~/.config/fish/fish_history')
|
|
89
|
+
|
|
90
|
+
def _get_history_line(self, command_script):
|
|
91
|
+
return u'- cmd: {}\n when: {}\n'.format(command_script, int(time()))
|
|
92
|
+
|
|
93
|
+
def _script_from_history(self, line):
|
|
94
|
+
if '- cmd: ' in line:
|
|
95
|
+
return line.split('- cmd: ', 1)[1]
|
|
96
|
+
else:
|
|
97
|
+
return ''
|
|
98
|
+
|
|
99
|
+
def and_(self, *commands):
|
|
100
|
+
return u'; and '.join(commands)
|
|
101
|
+
|
|
102
|
+
def or_(self, *commands):
|
|
103
|
+
return u'; or '.join(commands)
|
|
104
|
+
|
|
105
|
+
def how_to_configure(self):
|
|
106
|
+
return self._create_shell_configuration(
|
|
107
|
+
content=u"thefuck --alias | source",
|
|
108
|
+
path='~/.config/fish/config.fish',
|
|
109
|
+
reload='fish')
|
|
110
|
+
|
|
111
|
+
def _get_version(self):
|
|
112
|
+
"""Returns the version of the current shell"""
|
|
113
|
+
proc = Popen(['fish', '--version'], stdout=PIPE, stderr=DEVNULL)
|
|
114
|
+
return proc.stdout.read().decode('utf-8').split()[-1]
|
|
115
|
+
|
|
116
|
+
def put_to_history(self, command):
|
|
117
|
+
try:
|
|
118
|
+
return self._put_to_history(command)
|
|
119
|
+
except IOError:
|
|
120
|
+
logs.exception("Can't update history", sys.exc_info())
|
|
121
|
+
|
|
122
|
+
def _put_to_history(self, command_script):
|
|
123
|
+
"""Puts command script to shell history."""
|
|
124
|
+
history_file_name = self._get_history_file_name()
|
|
125
|
+
if os.path.isfile(history_file_name):
|
|
126
|
+
with open(history_file_name, 'a') as history:
|
|
127
|
+
entry = self._get_history_line(command_script)
|
|
128
|
+
if six.PY2:
|
|
129
|
+
history.write(entry.encode('utf-8'))
|
|
130
|
+
else:
|
|
131
|
+
history.write(entry)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import os
|
|
3
|
+
import shlex
|
|
4
|
+
import six
|
|
5
|
+
from collections import namedtuple
|
|
6
|
+
from ..logs import warn
|
|
7
|
+
from ..utils import memoize
|
|
8
|
+
from ..conf import settings
|
|
9
|
+
from ..system import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
ShellConfiguration = namedtuple('ShellConfiguration', (
|
|
13
|
+
'content', 'path', 'reload', 'can_configure_automatically'))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Generic(object):
|
|
17
|
+
friendly_name = 'Generic Shell'
|
|
18
|
+
|
|
19
|
+
def get_aliases(self):
|
|
20
|
+
return {}
|
|
21
|
+
|
|
22
|
+
def _expand_aliases(self, command_script):
|
|
23
|
+
aliases = self.get_aliases()
|
|
24
|
+
binary = command_script.split(' ')[0]
|
|
25
|
+
if binary in aliases:
|
|
26
|
+
return command_script.replace(binary, aliases[binary], 1)
|
|
27
|
+
else:
|
|
28
|
+
return command_script
|
|
29
|
+
|
|
30
|
+
def from_shell(self, command_script):
|
|
31
|
+
"""Prepares command before running in app."""
|
|
32
|
+
return self._expand_aliases(command_script)
|
|
33
|
+
|
|
34
|
+
def to_shell(self, command_script):
|
|
35
|
+
"""Prepares command for running in shell."""
|
|
36
|
+
return command_script
|
|
37
|
+
|
|
38
|
+
def app_alias(self, alias_name):
|
|
39
|
+
return """alias {0}='eval "$(TF_ALIAS={0} PYTHONIOENCODING=utf-8 """ \
|
|
40
|
+
"""thefuck "$(fc -ln -1)")"'""".format(alias_name)
|
|
41
|
+
|
|
42
|
+
def instant_mode_alias(self, alias_name):
|
|
43
|
+
warn("Instant mode not supported by your shell")
|
|
44
|
+
return self.app_alias(alias_name)
|
|
45
|
+
|
|
46
|
+
def _get_history_file_name(self):
|
|
47
|
+
return ''
|
|
48
|
+
|
|
49
|
+
def _get_history_line(self, command_script):
|
|
50
|
+
return ''
|
|
51
|
+
|
|
52
|
+
@memoize
|
|
53
|
+
def get_history(self):
|
|
54
|
+
return list(self._get_history_lines())
|
|
55
|
+
|
|
56
|
+
def _get_history_lines(self):
|
|
57
|
+
"""Returns list of history entries."""
|
|
58
|
+
history_file_name = self._get_history_file_name()
|
|
59
|
+
if os.path.isfile(history_file_name):
|
|
60
|
+
with io.open(history_file_name, 'r',
|
|
61
|
+
encoding='utf-8', errors='ignore') as history_file:
|
|
62
|
+
|
|
63
|
+
lines = history_file.readlines()
|
|
64
|
+
if settings.history_limit:
|
|
65
|
+
lines = lines[-settings.history_limit:]
|
|
66
|
+
|
|
67
|
+
for line in lines:
|
|
68
|
+
prepared = self._script_from_history(line) \
|
|
69
|
+
.strip()
|
|
70
|
+
if prepared:
|
|
71
|
+
yield prepared
|
|
72
|
+
|
|
73
|
+
def and_(self, *commands):
|
|
74
|
+
return u' && '.join(commands)
|
|
75
|
+
|
|
76
|
+
def or_(self, *commands):
|
|
77
|
+
return u' || '.join(commands)
|
|
78
|
+
|
|
79
|
+
def how_to_configure(self):
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
def split_command(self, command):
|
|
83
|
+
"""Split the command using shell-like syntax."""
|
|
84
|
+
encoded = self.encode_utf8(command)
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
splitted = [s.replace("??", "\\ ") for s in shlex.split(encoded.replace('\\ ', '??'))]
|
|
88
|
+
except ValueError:
|
|
89
|
+
splitted = encoded.split(' ')
|
|
90
|
+
|
|
91
|
+
return self.decode_utf8(splitted)
|
|
92
|
+
|
|
93
|
+
def encode_utf8(self, command):
|
|
94
|
+
if six.PY2:
|
|
95
|
+
return command.encode('utf8')
|
|
96
|
+
return command
|
|
97
|
+
|
|
98
|
+
def decode_utf8(self, command_parts):
|
|
99
|
+
if six.PY2:
|
|
100
|
+
return [s.decode('utf8') for s in command_parts]
|
|
101
|
+
return command_parts
|
|
102
|
+
|
|
103
|
+
def quote(self, s):
|
|
104
|
+
"""Return a shell-escaped version of the string s."""
|
|
105
|
+
|
|
106
|
+
if six.PY2:
|
|
107
|
+
from pipes import quote
|
|
108
|
+
else:
|
|
109
|
+
from shlex import quote
|
|
110
|
+
|
|
111
|
+
return quote(s)
|
|
112
|
+
|
|
113
|
+
def _script_from_history(self, line):
|
|
114
|
+
return line
|
|
115
|
+
|
|
116
|
+
def put_to_history(self, command):
|
|
117
|
+
"""Adds fixed command to shell history.
|
|
118
|
+
|
|
119
|
+
In most of shells we change history on shell-level, but not
|
|
120
|
+
all shells support it (Fish).
|
|
121
|
+
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def get_builtin_commands(self):
|
|
125
|
+
"""Returns shells builtin commands."""
|
|
126
|
+
return ['alias', 'bg', 'bind', 'break', 'builtin', 'case', 'cd',
|
|
127
|
+
'command', 'compgen', 'complete', 'continue', 'declare',
|
|
128
|
+
'dirs', 'disown', 'echo', 'enable', 'eval', 'exec', 'exit',
|
|
129
|
+
'export', 'fc', 'fg', 'getopts', 'hash', 'help', 'history',
|
|
130
|
+
'if', 'jobs', 'kill', 'let', 'local', 'logout', 'popd',
|
|
131
|
+
'printf', 'pushd', 'pwd', 'read', 'readonly', 'return', 'set',
|
|
132
|
+
'shift', 'shopt', 'source', 'suspend', 'test', 'times', 'trap',
|
|
133
|
+
'type', 'typeset', 'ulimit', 'umask', 'unalias', 'unset',
|
|
134
|
+
'until', 'wait', 'while']
|
|
135
|
+
|
|
136
|
+
def _get_version(self):
|
|
137
|
+
"""Returns the version of the current shell"""
|
|
138
|
+
return ''
|
|
139
|
+
|
|
140
|
+
def info(self):
|
|
141
|
+
"""Returns the name and version of the current shell"""
|
|
142
|
+
try:
|
|
143
|
+
version = self._get_version()
|
|
144
|
+
except Exception as e:
|
|
145
|
+
warn(u'Could not determine shell version: {}'.format(e))
|
|
146
|
+
version = ''
|
|
147
|
+
return u'{} {}'.format(self.friendly_name, version).rstrip()
|
|
148
|
+
|
|
149
|
+
def _create_shell_configuration(self, content, path, reload):
|
|
150
|
+
return ShellConfiguration(
|
|
151
|
+
content=content,
|
|
152
|
+
path=path,
|
|
153
|
+
reload=reload,
|
|
154
|
+
can_configure_automatically=Path(path).expanduser().exists())
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from subprocess import Popen, PIPE
|
|
2
|
+
from ..utils import DEVNULL
|
|
3
|
+
from .generic import Generic, ShellConfiguration
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Powershell(Generic):
|
|
7
|
+
friendly_name = 'PowerShell'
|
|
8
|
+
|
|
9
|
+
def app_alias(self, alias_name):
|
|
10
|
+
return 'function ' + alias_name + ' {\n' \
|
|
11
|
+
' $history = (Get-History -Count 1).CommandLine;\n' \
|
|
12
|
+
' if (-not [string]::IsNullOrWhiteSpace($history)) {\n' \
|
|
13
|
+
' $fuck = $(thefuck $args $history);\n' \
|
|
14
|
+
' if (-not [string]::IsNullOrWhiteSpace($fuck)) {\n' \
|
|
15
|
+
' if ($fuck.StartsWith("echo")) { $fuck = $fuck.Substring(5); }\n' \
|
|
16
|
+
' else { iex "$fuck"; }\n' \
|
|
17
|
+
' }\n' \
|
|
18
|
+
' }\n' \
|
|
19
|
+
' [Console]::ResetColor() \n' \
|
|
20
|
+
'}\n'
|
|
21
|
+
|
|
22
|
+
def and_(self, *commands):
|
|
23
|
+
return u' -and '.join('({0})'.format(c) for c in commands)
|
|
24
|
+
|
|
25
|
+
def how_to_configure(self):
|
|
26
|
+
return ShellConfiguration(
|
|
27
|
+
content=u'iex "$(thefuck --alias)"',
|
|
28
|
+
path='$profile',
|
|
29
|
+
reload='. $profile',
|
|
30
|
+
can_configure_automatically=False)
|
|
31
|
+
|
|
32
|
+
def _get_version(self):
|
|
33
|
+
"""Returns the version of the current shell"""
|
|
34
|
+
try:
|
|
35
|
+
proc = Popen(
|
|
36
|
+
['powershell.exe', '$PSVersionTable.PSVersion'],
|
|
37
|
+
stdout=PIPE,
|
|
38
|
+
stderr=DEVNULL)
|
|
39
|
+
version = proc.stdout.read().decode('utf-8').rstrip().split('\n')
|
|
40
|
+
return '.'.join(version[-1].split())
|
|
41
|
+
except IOError:
|
|
42
|
+
proc = Popen(['pwsh', '--version'], stdout=PIPE, stderr=DEVNULL)
|
|
43
|
+
return proc.stdout.read().decode('utf-8').split()[-1]
|
thefuck/shells/tcsh.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from subprocess import Popen, PIPE
|
|
2
|
+
from time import time
|
|
3
|
+
import os
|
|
4
|
+
from ..utils import DEVNULL, memoize
|
|
5
|
+
from .generic import Generic
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Tcsh(Generic):
|
|
9
|
+
friendly_name = 'Tcsh'
|
|
10
|
+
|
|
11
|
+
def app_alias(self, alias_name):
|
|
12
|
+
return ("alias {0} 'setenv TF_SHELL tcsh && setenv TF_ALIAS {0} && "
|
|
13
|
+
"set fucked_cmd=`history -h 2 | head -n 1` && "
|
|
14
|
+
"eval `thefuck ${{fucked_cmd}}`'").format(alias_name)
|
|
15
|
+
|
|
16
|
+
def _parse_alias(self, alias):
|
|
17
|
+
name, value = alias.split("\t", 1)
|
|
18
|
+
return name, value
|
|
19
|
+
|
|
20
|
+
@memoize
|
|
21
|
+
def get_aliases(self):
|
|
22
|
+
proc = Popen(['tcsh', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL)
|
|
23
|
+
return dict(
|
|
24
|
+
self._parse_alias(alias)
|
|
25
|
+
for alias in proc.stdout.read().decode('utf-8').split('\n')
|
|
26
|
+
if alias and '\t' in alias)
|
|
27
|
+
|
|
28
|
+
def _get_history_file_name(self):
|
|
29
|
+
return os.environ.get("HISTFILE",
|
|
30
|
+
os.path.expanduser('~/.history'))
|
|
31
|
+
|
|
32
|
+
def _get_history_line(self, command_script):
|
|
33
|
+
return u'#+{}\n{}\n'.format(int(time()), command_script)
|
|
34
|
+
|
|
35
|
+
def how_to_configure(self):
|
|
36
|
+
return self._create_shell_configuration(
|
|
37
|
+
content=u'eval `thefuck --alias`',
|
|
38
|
+
path='~/.tcshrc',
|
|
39
|
+
reload='tcsh')
|
|
40
|
+
|
|
41
|
+
def _get_version(self):
|
|
42
|
+
"""Returns the version of the current shell"""
|
|
43
|
+
proc = Popen(['tcsh', '--version'], stdout=PIPE, stderr=DEVNULL)
|
|
44
|
+
return proc.stdout.read().decode('utf-8').split()[1]
|
thefuck/shells/zsh.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from time import time
|
|
2
|
+
import os
|
|
3
|
+
from subprocess import Popen, PIPE
|
|
4
|
+
from tempfile import gettempdir
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
from ..conf import settings
|
|
7
|
+
from ..const import ARGUMENT_PLACEHOLDER, USER_COMMAND_MARK
|
|
8
|
+
from ..utils import DEVNULL, memoize
|
|
9
|
+
from .generic import Generic
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Zsh(Generic):
|
|
13
|
+
friendly_name = 'ZSH'
|
|
14
|
+
|
|
15
|
+
def app_alias(self, alias_name):
|
|
16
|
+
# It is VERY important to have the variables declared WITHIN the function
|
|
17
|
+
return '''
|
|
18
|
+
{name} () {{
|
|
19
|
+
TF_PYTHONIOENCODING=$PYTHONIOENCODING;
|
|
20
|
+
export TF_SHELL=zsh;
|
|
21
|
+
export TF_ALIAS={name};
|
|
22
|
+
TF_SHELL_ALIASES=$(alias);
|
|
23
|
+
export TF_SHELL_ALIASES;
|
|
24
|
+
TF_HISTORY="$(fc -ln -10)";
|
|
25
|
+
export TF_HISTORY;
|
|
26
|
+
export TF_PROMPT="$*";
|
|
27
|
+
export PYTHONIOENCODING=utf-8;
|
|
28
|
+
TF_CMD=$(
|
|
29
|
+
thefuck {argument_placeholder} $@
|
|
30
|
+
) && eval $TF_CMD;
|
|
31
|
+
unset TF_HISTORY;
|
|
32
|
+
unset TF_PROMPT;
|
|
33
|
+
export PYTHONIOENCODING=$TF_PYTHONIOENCODING;
|
|
34
|
+
{alter_history}
|
|
35
|
+
}}
|
|
36
|
+
'''.format(
|
|
37
|
+
name=alias_name,
|
|
38
|
+
argument_placeholder=ARGUMENT_PLACEHOLDER,
|
|
39
|
+
alter_history=('test -n "$TF_CMD" && print -s $TF_CMD'
|
|
40
|
+
if settings.alter_history else ''))
|
|
41
|
+
|
|
42
|
+
def instant_mode_alias(self, alias_name):
|
|
43
|
+
if os.environ.get('THEFUCK_INSTANT_MODE', '').lower() == 'true':
|
|
44
|
+
mark = ('%{' +
|
|
45
|
+
USER_COMMAND_MARK + '\b' * len(USER_COMMAND_MARK)
|
|
46
|
+
+ '%}')
|
|
47
|
+
return '''
|
|
48
|
+
export PS1="{user_command_mark}$PS1";
|
|
49
|
+
{app_alias}
|
|
50
|
+
'''.format(user_command_mark=mark,
|
|
51
|
+
app_alias=self.app_alias(alias_name))
|
|
52
|
+
else:
|
|
53
|
+
log_path = os.path.join(
|
|
54
|
+
gettempdir(), 'thefuck-script-log-{}'.format(uuid4().hex))
|
|
55
|
+
return '''
|
|
56
|
+
export THEFUCK_INSTANT_MODE=True;
|
|
57
|
+
export THEFUCK_OUTPUT_LOG={log};
|
|
58
|
+
thefuck --shell-logger {log};
|
|
59
|
+
rm -f {log};
|
|
60
|
+
exit
|
|
61
|
+
'''.format(log=log_path)
|
|
62
|
+
|
|
63
|
+
def _parse_alias(self, alias):
|
|
64
|
+
name, value = alias.split('=', 1)
|
|
65
|
+
if value[0] == value[-1] == '"' or value[0] == value[-1] == "'":
|
|
66
|
+
value = value[1:-1]
|
|
67
|
+
return name, value
|
|
68
|
+
|
|
69
|
+
@memoize
|
|
70
|
+
def get_aliases(self):
|
|
71
|
+
raw_aliases = os.environ.get('TF_SHELL_ALIASES', '').split('\n')
|
|
72
|
+
return dict(self._parse_alias(alias)
|
|
73
|
+
for alias in raw_aliases if alias and '=' in alias)
|
|
74
|
+
|
|
75
|
+
def _get_history_file_name(self):
|
|
76
|
+
return os.environ.get("HISTFILE",
|
|
77
|
+
os.path.expanduser('~/.zsh_history'))
|
|
78
|
+
|
|
79
|
+
def _get_history_line(self, command_script):
|
|
80
|
+
return u': {}:0;{}\n'.format(int(time()), command_script)
|
|
81
|
+
|
|
82
|
+
def _script_from_history(self, line):
|
|
83
|
+
if ';' in line:
|
|
84
|
+
return line.split(';', 1)[1]
|
|
85
|
+
else:
|
|
86
|
+
return ''
|
|
87
|
+
|
|
88
|
+
def how_to_configure(self):
|
|
89
|
+
return self._create_shell_configuration(
|
|
90
|
+
content=u'eval $(thefuck --alias)',
|
|
91
|
+
path='~/.zshrc',
|
|
92
|
+
reload='source ~/.zshrc')
|
|
93
|
+
|
|
94
|
+
def _get_version(self):
|
|
95
|
+
"""Returns the version of the current shell"""
|
|
96
|
+
proc = Popen(['zsh', '-c', 'echo $ZSH_VERSION'],
|
|
97
|
+
stdout=PIPE, stderr=DEVNULL)
|
|
98
|
+
return proc.stdout.read().decode('utf-8').strip()
|
|
File without changes
|
thefuck/specific/apt.py
ADDED