Merge branch 'master' into flake8

This commit is contained in:
Joseph Frazier
2017-03-08 12:21:54 -05:00
63 changed files with 1060 additions and 113 deletions
+5 -4
View File
@@ -83,13 +83,14 @@ def how_to_configure_alias(configuration_details):
print("Seems like {bold}fuck{reset} alias isn't configured!".format(
bold=color(colorama.Style.BRIGHT),
reset=color(colorama.Style.RESET_ALL)))
if configuration_details:
content, path = configuration_details
print(
"Please put {bold}{content}{reset} in your "
"{bold}{path}{reset}.".format(
"{bold}{path}{reset} and apply "
"changes with {bold}{reload}{reset} or restart your shell.".format(
bold=color(colorama.Style.BRIGHT),
reset=color(colorama.Style.RESET_ALL),
path=path,
content=content))
**configuration_details))
print('More details - https://github.com/nvbn/thefuck#manual-installation')
+10
View File
@@ -0,0 +1,10 @@
from thefuck.utils import for_app
@for_app('ag')
def match(command):
return command.stderr.endswith('run ag with -Q\n')
def get_new_command(command):
return command.script.replace('ag', 'ag -Q', 1)
+1 -1
View File
@@ -29,7 +29,7 @@ def get_package(executable):
def match(command):
if 'not found' in command.stderr:
if 'not found' in command.stderr or 'not installed' in command.stderr:
executable = _get_executable(command)
return not which(executable) and get_package(executable)
else:
+5 -4
View File
@@ -8,7 +8,8 @@ def match(command):
def get_new_command(command):
command.script_parts[1] = 'link'
command.script_parts.insert(2, '--overwrite')
command.script_parts.insert(3, '--dry-run')
return ' '.join(command.script_parts)
command_parts = command.script_parts[:]
command_parts[1] = 'link'
command_parts.insert(2, '--overwrite')
command_parts.insert(3, '--dry-run')
return ' '.join(command_parts)
+4 -3
View File
@@ -8,6 +8,7 @@ def match(command):
def get_new_command(command):
command.script_parts[1] = 'uninstall'
command.script_parts.insert(2, '--force')
return ' '.join(command.script_parts)
command_parts = command.script_parts[:]
command_parts[1] = 'uninstall'
command_parts.insert(2, '--force')
return ' '.join(command_parts)
+32
View File
@@ -0,0 +1,32 @@
import re
import subprocess
from thefuck.utils import for_app, eager, replace_command
@for_app('gem')
def match(command):
return ('ERROR: While executing gem ... (Gem::CommandLineError)'
in command.stderr
and 'Unknown command' in command.stderr)
def _get_unknown_command(command):
return re.findall(r'Unknown command (.*)$', command.stderr)[0]
@eager
def _get_all_commands():
proc = subprocess.Popen(['gem', 'help', 'commands'],
stdout=subprocess.PIPE)
for line in proc.stdout.readlines():
line = line.decode()
if line.startswith(' '):
yield line.strip().split(' ')[0]
def get_new_command(command):
unknown_command = _get_unknown_command(command)
all_commands = _get_all_commands()
return replace_command(command, unknown_command, all_commands)
+13
View File
@@ -0,0 +1,13 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('add' in command.script_parts
and 'Use -f if you really want to add them.' in command.stderr)
@git_support
def get_new_command(command):
return replace_argument(command.script, 'add', 'add --force')
+30
View File
@@ -0,0 +1,30 @@
import re
from thefuck.specific.git import git_support
error_pattern = "fatal: bad flag '(.*?)' used after filename"
@git_support
def match(command):
return re.search(error_pattern, command.stderr)
@git_support
def get_new_command(command):
command_parts = command.script_parts[:]
# find the bad flag
bad_flag = re.search(error_pattern, command.stderr).group(1)
bad_flag_index = command_parts.index(bad_flag)
# find the filename
for index in reversed(range(bad_flag_index)):
if command_parts[index][0] != '-':
filename_index = index
break
# swap them
command_parts[bad_flag_index], command_parts[filename_index] = \
command_parts[filename_index], command_parts[bad_flag_index] # noqa: E122
return u' '.join(command_parts)
+19 -13
View File
@@ -8,23 +8,29 @@ def match(command):
and 'set-upstream' in command.stderr)
def _get_upstream_option_index(command_parts):
if '--set-upstream' in command_parts:
return command_parts.index('--set-upstream')
elif '-u' in command_parts:
return command_parts.index('-u')
else:
return None
@git_support
def get_new_command(command):
# If --set-upstream or -u are passed, remove it and its argument. This is
# because the remaining arguments are concatenated onto the command suggested
# by git, which includes --set-upstream and its argument
upstream_option_index = -1
try:
upstream_option_index = command.script_parts.index('--set-upstream')
except ValueError:
pass
try:
upstream_option_index = command.script_parts.index('-u')
except ValueError:
pass
if upstream_option_index is not -1:
command.script_parts.pop(upstream_option_index)
command.script_parts.pop(upstream_option_index)
command_parts = command.script_parts[:]
upstream_option_index = _get_upstream_option_index(command_parts)
if upstream_option_index is not None:
command_parts.pop(upstream_option_index)
# In case of `git push -u` we don't have next argument:
if len(command_parts) > upstream_option_index:
command_parts.pop(upstream_option_index)
push_upstream = command.stderr.split('\n')[-3].strip().partition('git ')[2]
return replace_argument(" ".join(command.script_parts), 'push', push_upstream)
return replace_argument(" ".join(command_parts), 'push', push_upstream)
+17
View File
@@ -0,0 +1,17 @@
from difflib import get_close_matches
from thefuck.specific.git import git_support
@git_support
def match(command):
return (' rebase' in command.script and
'It seems that there is already a rebase-merge directory' in command.stderr and
'I wonder if you are in the middle of another rebase' in command.stderr)
@git_support
def get_new_command(command):
command_list = ['git rebase --continue', 'git rebase --abort', 'git rebase --skip']
rm_cmd = command.stderr.split('\n')[-4]
command_list.append(rm_cmd.strip())
return get_close_matches(command.script, command_list, 4, 0)
@@ -0,0 +1,19 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return (' rm ' in command.script and
'error: the following file has local modifications' in command.stderr and
'use --cached to keep the file, or -f to force removal' in command.stderr)
@git_support
def get_new_command(command):
command_parts = command.script_parts[:]
index = command_parts.index('rm') + 1
command_parts.insert(index, '--cached')
command_list = [u' '.join(command_parts)]
command_parts[index] = '-f'
command_list.append(u' '.join(command_parts))
return command_list
+4 -3
View File
@@ -10,6 +10,7 @@ def match(command):
@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)
command_parts = command.script_parts[:]
index = command_parts.index('rm') + 1
command_parts.insert(index, '-r')
return u' '.join(command_parts)
+19
View File
@@ -0,0 +1,19 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return (' rm ' in command.script and
'error: the following file has changes staged in the index' in command.stderr and
'use --cached to keep the file, or -f to force removal' in command.stderr)
@git_support
def get_new_command(command):
command_parts = command.script_parts[:]
index = command_parts.index('rm') + 1
command_parts.insert(index, '--cached')
command_list = [u' '.join(command_parts)]
command_parts[index] = '-f'
command_list.append(u' '.join(command_parts))
return command_list
+18
View File
@@ -0,0 +1,18 @@
from thefuck.shells import shell
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('stash' in command.script
and 'pop' in command.script
and 'Your local changes to the following files would be overwritten by merge' in command.stderr)
@git_support
def get_new_command(command):
return shell.and_('git add .', 'git stash pop', 'git reset .')
# make it come before the other applicable rules
priority = 900
+13
View File
@@ -0,0 +1,13 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('tag' in command.script_parts
and 'already exists' in command.stderr)
@git_support
def get_new_command(command):
return replace_argument(command.script, 'tag', 'tag --force')
@@ -0,0 +1,25 @@
import subprocess
from thefuck.utils import for_app, replace_command, eager
import sys
@for_app('ifconfig')
def match(command):
return 'error fetching interface information: Device not found' \
in command.stderr
@eager
def _get_possible_interfaces():
proc = subprocess.Popen(['ifconfig', '-a'], stdout=subprocess.PIPE)
for line in proc.stdout.readlines():
line = line.decode()
if line and line != '\n' and not line.startswith(' '):
yield line.split(' ')[0]
def get_new_command(command):
interface = command.stderr.split(' ')[0][:-1]
possible_interfaces = _get_possible_interfaces()
return replace_command(command, interface, possible_interfaces)
+10
View File
@@ -0,0 +1,10 @@
from thefuck.utils import for_app
@for_app('ls')
def match(command):
return command.stdout.strip() == ''
def get_new_command(command):
return ' '.join(['ls', '-A'] + command.script_parts[1:])
+13 -1
View File
@@ -12,10 +12,22 @@ def get_new_command(command):
if '2' in command.script:
return command.script.replace("2", "3")
last_arg = command.script_parts[-1]
help_command = last_arg + ' --help'
# If there are no man pages for last_arg, suggest `last_arg --help` instead.
# Otherwise, suggest `--help` after suggesting other man page sections.
if command.stderr.strip() == 'No manual entry for ' + last_arg:
return [help_command]
split_cmd2 = command.script_parts
split_cmd3 = split_cmd2[:]
split_cmd2.insert(1, ' 2 ')
split_cmd3.insert(1, ' 3 ')
return ["".join(split_cmd3), "".join(split_cmd2)]
return [
"".join(split_cmd3),
"".join(split_cmd2),
help_command,
]
+2 -3
View File
@@ -6,9 +6,8 @@ from thefuck.specific.sudo import sudo_support
@sudo_support
def match(command):
toks = command.script_parts
return (toks
and toks[0].endswith('.py')
return (command.script_parts
and command.script_parts[0].endswith('.py')
and ('Permission denied' in command.stderr or
'command not found' in command.stderr))
+32
View File
@@ -0,0 +1,32 @@
from thefuck.utils import for_app, memoize
from thefuck.system import Path
path_to_scm = {
'.git': 'git',
'.hg': 'hg',
}
wrong_scm_patterns = {
'git': 'fatal: Not a git repository',
'hg': 'abort: no repository found',
}
@memoize
def _get_actual_scm():
for path, scm in path_to_scm.items():
if Path(path).is_dir():
return scm
@for_app(*wrong_scm_patterns.keys())
def match(command):
scm = command.script_parts[0]
pattern = wrong_scm_patterns[scm]
return pattern in command.stderr and _get_actual_scm()
def get_new_command(command):
scm = _get_actual_scm()
return u' '.join([scm] + command.script_parts[1:])
@@ -0,0 +1,21 @@
import re
from thefuck.utils import for_app, which, replace_argument
def _get_command_name(command):
found = re.findall(r'sudo: (.*): command not found', command.stderr)
if found:
return found[0]
@for_app('sudo')
def match(command):
if 'command not found' in command.stderr:
command_name = _get_command_name(command)
return which(command_name)
def get_new_command(command):
command_name = _get_command_name(command)
return replace_argument(command.script, command_name,
u'env "PATH=$PATH" {}'.format(command_name))
+1 -1
View File
@@ -17,6 +17,6 @@ def match(command):
@sudo_support
def get_new_command(command):
cmd = command.script_parts
cmd = command.script_parts[:]
cmd[-1], cmd[-2] = cmd[-2], cmd[-1]
return ' '.join(cmd)
+14
View File
@@ -0,0 +1,14 @@
import re
from thefuck.utils import replace_argument, for_app
@for_app('yarn', at_least=1)
def match(command):
return ('Did you mean' in command.stderr)
def get_new_command(command):
broken = command.script_parts[1]
fix = re.findall(r'Did you mean `yarn ([^`]*)`', command.stderr)[0]
return replace_argument(command.script, broken, fix)
+31
View File
@@ -0,0 +1,31 @@
import re
from subprocess import Popen, PIPE
from thefuck.utils import for_app, eager, replace_command
regex = re.compile(r'error Command "(.*)" not found.')
@for_app('yarn')
def match(command):
return regex.findall(command.stderr)
@eager
def _get_all_tasks():
proc = Popen(['yarn', '--help'], stdout=PIPE)
should_yield = False
for line in proc.stdout.readlines():
line = line.decode().strip()
if 'Commands:' in line:
should_yield = True
continue
if should_yield and '- ' in line:
yield line.split(' ')[-1]
def get_new_command(command):
misspelled_task = regex.findall(command.stderr)[0]
tasks = _get_all_tasks()
return replace_command(command, misspelled_task, tasks)
+7 -2
View File
@@ -14,7 +14,7 @@ class Bash(Generic):
" eval $TF_CMD".format(fuck)
if settings.alter_history:
return alias + " && history -s $TF_CMD'"
return alias + "; history -s $TF_CMD'"
else:
return alias + "'"
@@ -44,4 +44,9 @@ class Bash(Generic):
config = '~/.bashrc'
else:
config = 'bash config'
return 'eval $(thefuck --alias)', config
return {
'content': 'eval $(thefuck --alias)',
'path': config,
'reload': u'source {}'.format(config),
}
+8 -4
View File
@@ -20,8 +20,9 @@ class Fish(Generic):
def app_alias(self, fuck):
if settings.alter_history:
alter_history = (' history --delete $fucked_up_command\n'
' history --merge ^ /dev/null\n')
alter_history = (' builtin history delete --exact'
' --case-sensitive -- $fucked_up_command\n'
' builtin history merge ^ /dev/null\n')
else:
alter_history = ''
# It is VERY important to have the variables declared WITHIN the alias
@@ -66,8 +67,11 @@ class Fish(Generic):
return u'; and '.join(commands)
def how_to_configure(self):
return (r"eval (thefuck --alias | tr '\n' ';')",
'~/.config/fish/config.fish')
return {
'content': r"eval (thefuck --alias | tr '\n' ';')",
'path': '~/.config/fish/config.fish',
'reload': 'fish',
}
def put_to_history(self, command):
try:
+12 -2
View File
@@ -65,9 +65,19 @@ class Generic(object):
def split_command(self, command):
"""Split the command using shell-like syntax."""
encoded = self.encode_utf8(command)
splitted = shlex.split(encoded)
return self.decode_utf8(splitted)
def encode_utf8(self, command):
if six.PY2:
return [s.decode('utf8') for s in shlex.split(command.encode('utf8'))]
return shlex.split(command)
return command.encode('utf8')
return command
def decode_utf8(self, command_parts):
if six.PY2:
return [s.decode('utf8') for s in command_parts]
return command_parts
def quote(self, s):
"""Return a shell-escaped version of the string s."""
+13 -6
View File
@@ -3,11 +3,14 @@ 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' \
return 'function ' + fuck + ' {\n' \
' $history = (Get-History -Count 1).CommandLine;\n' \
' if (-not [string]::IsNullOrWhiteSpace($history)) {\n' \
' $fuck = $(thefuck $history);\n' \
' if (-not [string]::IsNullOrWhiteSpace($fuck)) {\n' \
' if ($fuck.StartsWith("echo")) { $fuck = $fuck.Substring(5); }\n' \
' else { iex "$fuck"; }\n' \
' }\n' \
' }\n' \
'}\n'
@@ -15,4 +18,8 @@ class Powershell(Generic):
return u' -and '.join('({0})'.format(c) for c in commands)
def how_to_configure(self):
return 'iex "thefuck --alias"', '$profile'
return {
'content': 'iex "thefuck --alias"',
'path': '$profile',
'reload': '& $profile',
}
+5 -1
View File
@@ -31,4 +31,8 @@ class Tcsh(Generic):
return u'#+{}\n{}\n'.format(int(time()), command_script)
def how_to_configure(self):
return 'eval `thefuck --alias`', '~/.tcshrc'
return {
'content': 'eval `thefuck --alias`',
'path': '~/.tcshrc',
'reload': 'tcsh',
}
+6 -2
View File
@@ -15,7 +15,7 @@ class Zsh(Generic):
" eval $TF_CMD".format(alias_name)
if settings.alter_history:
return alias + " && print -s $TF_CMD'"
return alias + " ; test -n \"$TF_CMD\" && print -s $TF_CMD'"
else:
return alias + "'"
@@ -45,4 +45,8 @@ class Zsh(Generic):
return ''
def how_to_configure(self):
return 'eval $(thefuck --alias)', '~/.zshrc'
return {
'content': 'eval $(thefuck --alias)',
'path': '~/.zshrc',
'reload': 'source ~/.zshrc',
}
+2 -1
View File
@@ -34,7 +34,8 @@ class Command(object):
except Exception:
logs.debug(u"Can't split command script {} because:\n {}".format(
self, sys.exc_info()))
self._script_parts = None
self._script_parts = []
return self._script_parts
def __eq__(self, other):
+3 -2
View File
@@ -12,9 +12,10 @@ def read_actions():
while True:
key = get_key()
if key in (const.KEY_UP, 'k'):
# Handle arrows, j/k (qwerty), and n/e (colemak)
if key in (const.KEY_UP, 'k', 'e'):
yield const.ACTION_PREVIOUS
elif key in (const.KEY_DOWN, 'j'):
elif key in (const.KEY_DOWN, 'j', 'n'):
yield const.ACTION_NEXT
elif key in (const.KEY_CTRL_C, 'q'):
yield const.ACTION_ABORT
+2 -2
View File
@@ -159,7 +159,7 @@ def is_app(command, *app_names, **kwargs):
if kwargs:
raise TypeError("got an unexpected keyword argument '{}'".format(kwargs.keys()))
if command.script_parts is not None and len(command.script_parts) > at_least:
if len(command.script_parts) > at_least:
return command.script_parts[0] in app_names
return False
@@ -264,7 +264,7 @@ def get_valid_history_without_current(command):
from thefuck.shells import shell
history = shell.get_history()
tf_alias = get_alias()
executables = get_all_executables()
executables = set(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]