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
thefuck/logs.py
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
import unicodedata
|
|
9
|
+
from traceback import format_exception
|
|
10
|
+
import colorama
|
|
11
|
+
try:
|
|
12
|
+
from shutil import get_terminal_size
|
|
13
|
+
except ImportError: # pragma: no cover - Python < 3.3
|
|
14
|
+
from backports.shutil_get_terminal_size import get_terminal_size
|
|
15
|
+
from .conf import settings
|
|
16
|
+
from . import const
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def color(color_):
|
|
20
|
+
"""Utility for ability to disabling colored output."""
|
|
21
|
+
if settings.no_colors:
|
|
22
|
+
return ''
|
|
23
|
+
else:
|
|
24
|
+
return color_
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_ANSI_RE = re.compile(r'\x1b\[[0-?]*[ -/]*[@-~]')
|
|
28
|
+
_last_confirm_lines = 0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _strip_ansi(text):
|
|
32
|
+
return _ANSI_RE.sub('', text)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _terminal_columns():
|
|
36
|
+
try:
|
|
37
|
+
return os.get_terminal_size(sys.stderr.fileno()).columns or 80
|
|
38
|
+
except Exception:
|
|
39
|
+
cols = get_terminal_size((80, 20)).columns or 80
|
|
40
|
+
return cols
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _text_width(text):
|
|
44
|
+
width = 0
|
|
45
|
+
for ch in text:
|
|
46
|
+
if unicodedata.combining(ch):
|
|
47
|
+
continue
|
|
48
|
+
if unicodedata.east_asian_width(ch) in ('W', 'F'):
|
|
49
|
+
width += 2
|
|
50
|
+
else:
|
|
51
|
+
width += 1
|
|
52
|
+
return width
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _calc_prompt_lines(text):
|
|
56
|
+
cols = _terminal_columns()
|
|
57
|
+
if cols <= 0:
|
|
58
|
+
cols = 80
|
|
59
|
+
visible = _strip_ansi(text.replace(const.USER_COMMAND_MARK, ''))
|
|
60
|
+
if not visible:
|
|
61
|
+
return 1
|
|
62
|
+
total = 0
|
|
63
|
+
for line in visible.splitlines() or ['']:
|
|
64
|
+
width = _text_width(line)
|
|
65
|
+
total += max(1, (width - 1) // cols + 1)
|
|
66
|
+
return total
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _clear_previous_confirm():
|
|
70
|
+
if _last_confirm_lines <= 0:
|
|
71
|
+
return ''
|
|
72
|
+
seq = '\r'
|
|
73
|
+
for idx in range(_last_confirm_lines):
|
|
74
|
+
seq += '\033[2K'
|
|
75
|
+
if idx < _last_confirm_lines - 1:
|
|
76
|
+
seq += '\033[1A'
|
|
77
|
+
return seq + '\r'
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def reset_confirm_text():
|
|
81
|
+
global _last_confirm_lines
|
|
82
|
+
_last_confirm_lines = 0
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def warn(title):
|
|
86
|
+
sys.stderr.write(u'{warn}[WARN] {title}{reset}\n'.format(
|
|
87
|
+
warn=color(colorama.Back.RED + colorama.Fore.WHITE
|
|
88
|
+
+ colorama.Style.BRIGHT),
|
|
89
|
+
reset=color(colorama.Style.RESET_ALL),
|
|
90
|
+
title=title))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def exception(title, exc_info):
|
|
94
|
+
sys.stderr.write(
|
|
95
|
+
u'{warn}[WARN] {title}:{reset}\n{trace}'
|
|
96
|
+
u'{warn}----------------------------{reset}\n\n'.format(
|
|
97
|
+
warn=color(colorama.Back.RED + colorama.Fore.WHITE
|
|
98
|
+
+ colorama.Style.BRIGHT),
|
|
99
|
+
reset=color(colorama.Style.RESET_ALL),
|
|
100
|
+
title=title,
|
|
101
|
+
trace=''.join(format_exception(*exc_info))))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def rule_failed(rule, exc_info):
|
|
105
|
+
exception(u'Rule {}'.format(rule.name), exc_info)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def failed(msg):
|
|
109
|
+
sys.stderr.write(u'{red}{msg}{reset}\n'.format(
|
|
110
|
+
msg=msg,
|
|
111
|
+
red=color(colorama.Fore.RED),
|
|
112
|
+
reset=color(colorama.Style.RESET_ALL)))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def show_corrected_command(corrected_command):
|
|
116
|
+
desc = getattr(corrected_command, 'desc', '') or ''
|
|
117
|
+
desc = u' '.join(desc.split())
|
|
118
|
+
desc_output = ''
|
|
119
|
+
if desc:
|
|
120
|
+
desc_output = u' — {desc_color}{desc}{reset}'.format(
|
|
121
|
+
desc_color=color(colorama.Fore.YELLOW),
|
|
122
|
+
desc=desc,
|
|
123
|
+
reset=color(colorama.Style.RESET_ALL))
|
|
124
|
+
sys.stderr.write(
|
|
125
|
+
u'{prefix}{cmd_color}{script}{reset}{desc_output}{side_effect}\n'.format(
|
|
126
|
+
prefix=const.USER_COMMAND_MARK,
|
|
127
|
+
script=corrected_command.script,
|
|
128
|
+
desc_output=desc_output,
|
|
129
|
+
side_effect=u' (+side effect)' if corrected_command.side_effect else u'',
|
|
130
|
+
cmd_color=color(colorama.Style.BRIGHT + colorama.Fore.CYAN),
|
|
131
|
+
reset=color(colorama.Style.RESET_ALL)))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def confirm_text(corrected_command):
|
|
135
|
+
global _last_confirm_lines
|
|
136
|
+
desc = getattr(corrected_command, 'desc', '') or ''
|
|
137
|
+
desc = u' '.join(desc.split())
|
|
138
|
+
desc_output = ''
|
|
139
|
+
if desc:
|
|
140
|
+
desc_output = u' — {desc_color}{desc}{reset}'.format(
|
|
141
|
+
desc_color=color(colorama.Fore.YELLOW),
|
|
142
|
+
desc=desc,
|
|
143
|
+
reset=color(colorama.Style.RESET_ALL))
|
|
144
|
+
prompt = (u'{prefix}{cmd_color}{script}{reset}{desc_output}{side_effect} '
|
|
145
|
+
u'[{green}enter{reset}/{blue}↑{reset}/{blue}↓{reset}'
|
|
146
|
+
u'/{blue}tab{reset}/{red}ctrl+c{reset}]').format(
|
|
147
|
+
prefix=const.USER_COMMAND_MARK,
|
|
148
|
+
script=corrected_command.script,
|
|
149
|
+
desc_output=desc_output,
|
|
150
|
+
side_effect=' (+side effect)' if corrected_command.side_effect else '',
|
|
151
|
+
cmd_color=color(colorama.Style.BRIGHT + colorama.Fore.CYAN),
|
|
152
|
+
green=color(colorama.Fore.GREEN),
|
|
153
|
+
red=color(colorama.Fore.RED),
|
|
154
|
+
reset=color(colorama.Style.RESET_ALL),
|
|
155
|
+
blue=color(colorama.Fore.BLUE))
|
|
156
|
+
sys.stderr.write(_clear_previous_confirm())
|
|
157
|
+
sys.stderr.write(prompt)
|
|
158
|
+
sys.stderr.flush()
|
|
159
|
+
_last_confirm_lines = _calc_prompt_lines(prompt)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def ai_choose_header():
|
|
163
|
+
sys.stderr.write(u'\n{label}Choose{reset}\n'.format(
|
|
164
|
+
label=color(colorama.Style.BRIGHT + colorama.Fore.GREEN),
|
|
165
|
+
reset=color(colorama.Style.RESET_ALL)))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def confirm_choice(corrected_command):
|
|
169
|
+
global _last_confirm_lines
|
|
170
|
+
index = getattr(corrected_command, '_tf_index', None)
|
|
171
|
+
if not index:
|
|
172
|
+
return confirm_text(corrected_command)
|
|
173
|
+
prompt = (u'{prefix}{bold}{index}{reset} '
|
|
174
|
+
u'[{green}enter{reset}/{blue}↑{reset}/{blue}↓{reset}'
|
|
175
|
+
u'/{blue}tab{reset}/{red}ctrl+c{reset}]').format(
|
|
176
|
+
prefix=const.USER_COMMAND_MARK,
|
|
177
|
+
index=index,
|
|
178
|
+
bold=color(colorama.Style.BRIGHT),
|
|
179
|
+
green=color(colorama.Fore.GREEN),
|
|
180
|
+
red=color(colorama.Fore.RED),
|
|
181
|
+
reset=color(colorama.Style.RESET_ALL),
|
|
182
|
+
blue=color(colorama.Fore.BLUE))
|
|
183
|
+
sys.stderr.write(_clear_previous_confirm())
|
|
184
|
+
sys.stderr.write(prompt)
|
|
185
|
+
sys.stderr.flush()
|
|
186
|
+
_last_confirm_lines = _calc_prompt_lines(prompt)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def debug(msg):
|
|
190
|
+
if settings.debug:
|
|
191
|
+
sys.stderr.write(u'{blue}{bold}DEBUG:{reset} {msg}\n'.format(
|
|
192
|
+
msg=msg,
|
|
193
|
+
reset=color(colorama.Style.RESET_ALL),
|
|
194
|
+
blue=color(colorama.Fore.BLUE),
|
|
195
|
+
bold=color(colorama.Style.BRIGHT)))
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@contextmanager
|
|
199
|
+
def debug_time(msg):
|
|
200
|
+
started = datetime.now()
|
|
201
|
+
try:
|
|
202
|
+
yield
|
|
203
|
+
finally:
|
|
204
|
+
debug(u'{} took: {}'.format(msg, datetime.now() - started))
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def how_to_configure_alias(configuration_details):
|
|
208
|
+
print(u"Seems like {bold}fuck{reset} alias isn't configured!".format(
|
|
209
|
+
bold=color(colorama.Style.BRIGHT),
|
|
210
|
+
reset=color(colorama.Style.RESET_ALL)))
|
|
211
|
+
|
|
212
|
+
if configuration_details:
|
|
213
|
+
print(
|
|
214
|
+
u"Please put {bold}{content}{reset} in your "
|
|
215
|
+
u"{bold}{path}{reset} and apply "
|
|
216
|
+
u"changes with {bold}{reload}{reset} or restart your shell.".format(
|
|
217
|
+
bold=color(colorama.Style.BRIGHT),
|
|
218
|
+
reset=color(colorama.Style.RESET_ALL),
|
|
219
|
+
**configuration_details._asdict()))
|
|
220
|
+
|
|
221
|
+
if configuration_details.can_configure_automatically:
|
|
222
|
+
print(
|
|
223
|
+
u"Or run {bold}fuck{reset} a second time to configure"
|
|
224
|
+
u" it automatically.".format(
|
|
225
|
+
bold=color(colorama.Style.BRIGHT),
|
|
226
|
+
reset=color(colorama.Style.RESET_ALL)))
|
|
227
|
+
|
|
228
|
+
print(u'More details - https://github.com/nvbn/thefuck#manual-installation')
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def already_configured(configuration_details):
|
|
232
|
+
print(
|
|
233
|
+
u"Seems like {bold}fuck{reset} alias already configured!\n"
|
|
234
|
+
u"For applying changes run {bold}{reload}{reset}"
|
|
235
|
+
u" or restart your shell.".format(
|
|
236
|
+
bold=color(colorama.Style.BRIGHT),
|
|
237
|
+
reset=color(colorama.Style.RESET_ALL),
|
|
238
|
+
reload=configuration_details.reload))
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def configured_successfully(configuration_details):
|
|
242
|
+
print(
|
|
243
|
+
u"{bold}fuck{reset} alias configured successfully!\n"
|
|
244
|
+
u"For applying changes run {bold}{reload}{reset}"
|
|
245
|
+
u" or restart your shell.".format(
|
|
246
|
+
bold=color(colorama.Style.BRIGHT),
|
|
247
|
+
reset=color(colorama.Style.RESET_ALL),
|
|
248
|
+
reload=configuration_details.reload))
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def version(thefuck_version, python_version, shell_info):
|
|
252
|
+
sys.stderr.write(
|
|
253
|
+
u'The Fuck {} using Python {} and {}\n'.format(thefuck_version,
|
|
254
|
+
python_version,
|
|
255
|
+
shell_info))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from ..conf import settings
|
|
2
|
+
from . import read_log, rerun, shell_logger
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_output(script, expanded):
|
|
6
|
+
"""Get output of the script.
|
|
7
|
+
|
|
8
|
+
:param script: Console script.
|
|
9
|
+
:type script: str
|
|
10
|
+
:param expanded: Console script with expanded aliases.
|
|
11
|
+
:type expanded: str
|
|
12
|
+
:rtype: str
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
if shell_logger.is_available():
|
|
16
|
+
return shell_logger.get_output(script)
|
|
17
|
+
if settings.instant_mode:
|
|
18
|
+
return read_log.get_output(script)
|
|
19
|
+
else:
|
|
20
|
+
return rerun.get_output(script, expanded)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shlex
|
|
3
|
+
import mmap
|
|
4
|
+
import re
|
|
5
|
+
try:
|
|
6
|
+
from shutil import get_terminal_size
|
|
7
|
+
except ImportError:
|
|
8
|
+
from backports.shutil_get_terminal_size import get_terminal_size
|
|
9
|
+
import six
|
|
10
|
+
import pyte
|
|
11
|
+
from ..exceptions import ScriptNotInLog
|
|
12
|
+
from .. import const, logs
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _group_by_calls(log):
|
|
16
|
+
ps1 = os.environ['PS1']
|
|
17
|
+
ps1_newlines = ps1.count('\\n') + ps1.count('\n')
|
|
18
|
+
ps1_counter = 0
|
|
19
|
+
|
|
20
|
+
script_line = None
|
|
21
|
+
lines = []
|
|
22
|
+
for line in log:
|
|
23
|
+
if const.USER_COMMAND_MARK in line or ps1_counter > 0:
|
|
24
|
+
if script_line and ps1_counter == 0:
|
|
25
|
+
yield script_line, lines
|
|
26
|
+
|
|
27
|
+
if ps1_newlines > 0:
|
|
28
|
+
if ps1_counter <= 0:
|
|
29
|
+
ps1_counter = ps1_newlines
|
|
30
|
+
else:
|
|
31
|
+
ps1_counter -= 1
|
|
32
|
+
|
|
33
|
+
script_line = line
|
|
34
|
+
lines = [line]
|
|
35
|
+
elif script_line is not None:
|
|
36
|
+
lines.append(line)
|
|
37
|
+
|
|
38
|
+
if script_line:
|
|
39
|
+
yield script_line, lines
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _get_script_group_lines(grouped, script):
|
|
43
|
+
if six.PY2:
|
|
44
|
+
script = script.encode('utf-8')
|
|
45
|
+
|
|
46
|
+
parts = shlex.split(script)
|
|
47
|
+
|
|
48
|
+
for script_line, lines in reversed(grouped):
|
|
49
|
+
if all(part in script_line for part in parts):
|
|
50
|
+
return lines
|
|
51
|
+
|
|
52
|
+
raise ScriptNotInLog
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _get_output_lines(script, log_file):
|
|
56
|
+
data = log_file.read().decode()
|
|
57
|
+
data = re.sub(r'\x00+$', '', data)
|
|
58
|
+
lines = data.split('\n')
|
|
59
|
+
grouped = list(_group_by_calls(lines))
|
|
60
|
+
script_lines = _get_script_group_lines(grouped, script)
|
|
61
|
+
screen = pyte.Screen(get_terminal_size().columns, len(script_lines))
|
|
62
|
+
stream = pyte.Stream(screen)
|
|
63
|
+
stream.feed('\n'.join(script_lines))
|
|
64
|
+
return screen.display
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _skip_old_lines(log_file):
|
|
68
|
+
size = os.path.getsize(os.environ['THEFUCK_OUTPUT_LOG'])
|
|
69
|
+
if size > const.LOG_SIZE_IN_BYTES:
|
|
70
|
+
log_file.seek(size - const.LOG_SIZE_IN_BYTES)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_output(script):
|
|
74
|
+
"""Reads script output from log.
|
|
75
|
+
|
|
76
|
+
:type script: str
|
|
77
|
+
:rtype: str | None
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
if six.PY2:
|
|
81
|
+
logs.warn('Experimental instant mode is Python 3+ only')
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
if 'THEFUCK_OUTPUT_LOG' not in os.environ:
|
|
85
|
+
logs.warn("Output log isn't specified")
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
if const.USER_COMMAND_MARK not in os.environ.get('PS1', ''):
|
|
89
|
+
logs.warn(
|
|
90
|
+
"PS1 doesn't contain user command mark, please ensure "
|
|
91
|
+
"that PS1 is not changed after The Fuck alias initialization")
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
with logs.debug_time(u'Read output from log'):
|
|
96
|
+
fd = os.open(os.environ['THEFUCK_OUTPUT_LOG'], os.O_RDONLY)
|
|
97
|
+
buffer = mmap.mmap(fd, const.LOG_SIZE_IN_BYTES, mmap.MAP_SHARED, mmap.PROT_READ)
|
|
98
|
+
_skip_old_lines(buffer)
|
|
99
|
+
lines = _get_output_lines(script, buffer)
|
|
100
|
+
output = '\n'.join(lines).strip()
|
|
101
|
+
logs.debug(u'Received output: {}'.format(output))
|
|
102
|
+
return output
|
|
103
|
+
except OSError:
|
|
104
|
+
logs.warn("Can't read output log")
|
|
105
|
+
return None
|
|
106
|
+
except ScriptNotInLog:
|
|
107
|
+
logs.warn("Script not found in output log")
|
|
108
|
+
return None
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shlex
|
|
3
|
+
import six
|
|
4
|
+
from subprocess import Popen, PIPE, STDOUT
|
|
5
|
+
from psutil import AccessDenied, Process, TimeoutExpired
|
|
6
|
+
from .. import logs
|
|
7
|
+
from ..conf import settings
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _kill_process(proc):
|
|
11
|
+
"""Tries to kill the process otherwise just logs a debug message, the
|
|
12
|
+
process will be killed when thefuck terminates.
|
|
13
|
+
|
|
14
|
+
:type proc: Process
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
try:
|
|
18
|
+
proc.kill()
|
|
19
|
+
except AccessDenied:
|
|
20
|
+
logs.debug(u'Rerun: process PID {} ({}) could not be terminated'.format(
|
|
21
|
+
proc.pid, proc.exe()))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _wait_output(popen, is_slow):
|
|
25
|
+
"""Returns `True` if we can get output of the command in the
|
|
26
|
+
`settings.wait_command` time.
|
|
27
|
+
|
|
28
|
+
Command will be killed if it wasn't finished in the time.
|
|
29
|
+
|
|
30
|
+
:type popen: Popen
|
|
31
|
+
:rtype: bool
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
proc = Process(popen.pid)
|
|
35
|
+
try:
|
|
36
|
+
proc.wait(settings.wait_slow_command if is_slow
|
|
37
|
+
else settings.wait_command)
|
|
38
|
+
return True
|
|
39
|
+
except TimeoutExpired:
|
|
40
|
+
for child in proc.children(recursive=True):
|
|
41
|
+
_kill_process(child)
|
|
42
|
+
_kill_process(proc)
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_output(script, expanded):
|
|
47
|
+
"""Runs the script and obtains stdin/stderr.
|
|
48
|
+
|
|
49
|
+
:type script: str
|
|
50
|
+
:type expanded: str
|
|
51
|
+
:rtype: str | None
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
env = dict(os.environ)
|
|
55
|
+
env.update(settings.env)
|
|
56
|
+
|
|
57
|
+
if six.PY2:
|
|
58
|
+
expanded = expanded.encode('utf-8')
|
|
59
|
+
|
|
60
|
+
split_expand = shlex.split(expanded)
|
|
61
|
+
is_slow = split_expand[0] in settings.slow_commands if split_expand else False
|
|
62
|
+
with logs.debug_time(u'Call: {}; with env: {}; is slow: {}'.format(
|
|
63
|
+
script, env, is_slow)):
|
|
64
|
+
result = Popen(expanded, shell=True, stdin=PIPE,
|
|
65
|
+
stdout=PIPE, stderr=STDOUT, env=env)
|
|
66
|
+
if _wait_output(result, is_slow):
|
|
67
|
+
output = result.stdout.read().decode('utf-8', errors='replace')
|
|
68
|
+
logs.debug(u'Received output: {}'.format(output))
|
|
69
|
+
return output
|
|
70
|
+
else:
|
|
71
|
+
logs.debug(u'Execution timed out!')
|
|
72
|
+
return None
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import socket
|
|
4
|
+
try:
|
|
5
|
+
from shutil import get_terminal_size
|
|
6
|
+
except ImportError:
|
|
7
|
+
from backports.shutil_get_terminal_size import get_terminal_size
|
|
8
|
+
import pyte
|
|
9
|
+
from .. import const, logs
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_socket_path():
|
|
13
|
+
return os.environ.get(const.SHELL_LOGGER_SOCKET_ENV)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_available():
|
|
17
|
+
"""Returns `True` if shell logger socket available.
|
|
18
|
+
|
|
19
|
+
:rtype: book
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
path = _get_socket_path()
|
|
23
|
+
if not path:
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
return os.path.exists(path)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_last_n(n):
|
|
30
|
+
with socket.socket(socket.AF_UNIX) as client:
|
|
31
|
+
client.connect(_get_socket_path())
|
|
32
|
+
request = json.dumps({
|
|
33
|
+
"type": "list",
|
|
34
|
+
"count": n,
|
|
35
|
+
}) + '\n'
|
|
36
|
+
client.sendall(request.encode('utf-8'))
|
|
37
|
+
response = client.makefile().readline()
|
|
38
|
+
return json.loads(response)['commands']
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _get_output_lines(output):
|
|
42
|
+
lines = output.split('\n')
|
|
43
|
+
screen = pyte.Screen(get_terminal_size().columns, len(lines))
|
|
44
|
+
stream = pyte.Stream(screen)
|
|
45
|
+
stream.feed('\n'.join(lines))
|
|
46
|
+
return screen.display
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_output(script):
|
|
50
|
+
"""Gets command output from shell logger."""
|
|
51
|
+
with logs.debug_time(u'Read output from external shell logger'):
|
|
52
|
+
commands = _get_last_n(const.SHELL_LOGGER_LIMIT)
|
|
53
|
+
for command in commands:
|
|
54
|
+
if command['command'] == script:
|
|
55
|
+
lines = _get_output_lines(command['output'])
|
|
56
|
+
output = '\n'.join(lines).strip()
|
|
57
|
+
return output
|
|
58
|
+
else:
|
|
59
|
+
logs.warn("Output isn't available in shell logger")
|
|
60
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from thefuck.utils import is_app, get_closest, replace_argument
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
_ADB_COMMANDS = (
|
|
5
|
+
'backup',
|
|
6
|
+
'bugreport',
|
|
7
|
+
'connect',
|
|
8
|
+
'devices',
|
|
9
|
+
'disable-verity',
|
|
10
|
+
'disconnect',
|
|
11
|
+
'enable-verity',
|
|
12
|
+
'emu',
|
|
13
|
+
'forward',
|
|
14
|
+
'get-devpath',
|
|
15
|
+
'get-serialno',
|
|
16
|
+
'get-state',
|
|
17
|
+
'install',
|
|
18
|
+
'install-multiple',
|
|
19
|
+
'jdwp',
|
|
20
|
+
'keygen',
|
|
21
|
+
'kill-server',
|
|
22
|
+
'logcat',
|
|
23
|
+
'pull',
|
|
24
|
+
'push',
|
|
25
|
+
'reboot',
|
|
26
|
+
'reconnect',
|
|
27
|
+
'restore',
|
|
28
|
+
'reverse',
|
|
29
|
+
'root',
|
|
30
|
+
'run-as',
|
|
31
|
+
'shell',
|
|
32
|
+
'sideload',
|
|
33
|
+
'start-server',
|
|
34
|
+
'sync',
|
|
35
|
+
'tcpip',
|
|
36
|
+
'uninstall',
|
|
37
|
+
'unroot',
|
|
38
|
+
'usb',
|
|
39
|
+
'wait-for',
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def match(command):
|
|
44
|
+
return (is_app(command, 'adb')
|
|
45
|
+
and command.output.startswith('Android Debug Bridge version'))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_new_command(command):
|
|
49
|
+
for idx, arg in enumerate(command.script_parts[1:]):
|
|
50
|
+
# allowed params to ADB are a/d/e/s/H/P/L where s, H, P and L take additional args
|
|
51
|
+
# for example 'adb -s 111 logcat' or 'adb -e logcat'
|
|
52
|
+
if not arg[0] == '-' and not command.script_parts[idx] in ('-s', '-H', '-P', '-L'):
|
|
53
|
+
adb_cmd = get_closest(arg, _ADB_COMMANDS)
|
|
54
|
+
return replace_argument(command.script, arg, adb_cmd)
|
thefuck/rules/apt_get.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from types import ModuleType
|
|
2
|
+
from thefuck.specific.apt import apt_available
|
|
3
|
+
from thefuck.utils import memoize, which
|
|
4
|
+
from thefuck.shells import shell
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from CommandNotFound import CommandNotFound
|
|
8
|
+
|
|
9
|
+
enabled_by_default = apt_available
|
|
10
|
+
|
|
11
|
+
if isinstance(CommandNotFound, ModuleType):
|
|
12
|
+
# For ubuntu 18.04+
|
|
13
|
+
_get_packages = CommandNotFound.CommandNotFound().get_packages
|
|
14
|
+
else:
|
|
15
|
+
# For older versions
|
|
16
|
+
_get_packages = CommandNotFound().getPackages
|
|
17
|
+
except ImportError:
|
|
18
|
+
enabled_by_default = False
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_executable(command):
|
|
22
|
+
if command.script_parts[0] == 'sudo':
|
|
23
|
+
return command.script_parts[1]
|
|
24
|
+
else:
|
|
25
|
+
return command.script_parts[0]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@memoize
|
|
29
|
+
def get_package(executable):
|
|
30
|
+
try:
|
|
31
|
+
packages = _get_packages(executable)
|
|
32
|
+
return packages[0][0]
|
|
33
|
+
except IndexError:
|
|
34
|
+
# IndexError is thrown when no matching package is found
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def match(command):
|
|
39
|
+
if 'not found' in command.output or 'not installed' in command.output:
|
|
40
|
+
executable = _get_executable(command)
|
|
41
|
+
return not which(executable) and get_package(executable)
|
|
42
|
+
else:
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_new_command(command):
|
|
47
|
+
executable = _get_executable(command)
|
|
48
|
+
name = get_package(executable)
|
|
49
|
+
formatme = shell.and_('sudo apt-get install {}', '{}')
|
|
50
|
+
return formatme.format(name, command.script)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from thefuck.specific.apt import apt_available
|
|
3
|
+
from thefuck.utils import for_app
|
|
4
|
+
|
|
5
|
+
enabled_by_default = apt_available
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@for_app('apt-get')
|
|
9
|
+
def match(command):
|
|
10
|
+
return command.script.startswith('apt-get search')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_new_command(command):
|
|
14
|
+
return re.sub(r'^apt-get', 'apt-cache', command.script)
|