mirror of
https://github.com/nvbn/thefuck.git
synced 2025-11-18 07:46:11 +00:00
Compare commits
32 Commits
merge-cd-r
...
3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89f868c115 | ||
|
|
81f6a25abc | ||
|
|
cc9af78787 | ||
|
|
1fc3f1b5bf | ||
|
|
45574d06c9 | ||
|
|
dc23d67a42 | ||
|
|
959b96cf6e | ||
|
|
f20311fa89 | ||
|
|
a4c391096a | ||
|
|
e71a3e0cdb | ||
|
|
2d995d464f | ||
|
|
280751b36e | ||
|
|
0a6a3db65d | ||
|
|
ecfc180280 | ||
|
|
dae58211ba | ||
|
|
5e9b2c56da | ||
|
|
192ab0bfb0 | ||
|
|
346cb99217 | ||
|
|
bbfedb861f | ||
|
|
f5b0e96747 | ||
|
|
12a33f56bc | ||
|
|
590fdba2aa | ||
|
|
f374142bf8 | ||
|
|
540ff7e16d | ||
|
|
806dad18bf | ||
|
|
8b416f269f | ||
|
|
5e44fb22be | ||
|
|
5389d0c106 | ||
|
|
c970f190d2 | ||
|
|
8f25c95f06 | ||
|
|
4a48108c69 | ||
|
|
f5e8fe954e |
25
CONTRIBUTING.md
Normal file
25
CONTRIBUTING.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Report issues
|
||||
If you have any issue with The Fuck, sorry about that, but we will do what we
|
||||
can to fix that. Actually, maybe we already have, so first thing to do is to
|
||||
update The Fuck and see if the bug is still there.
|
||||
|
||||
If it is (sorry again), check if the problem has not already been reported and
|
||||
if not, just open an issue on [GitHub](https://github.com/nvbn/thefuck) with
|
||||
the following basic information:
|
||||
- the output of `thefuck --version` (something like `The Fuck 3.1 using
|
||||
Python 3.5.0`);
|
||||
- your shell and its version (`bash`, `zsh`, *Windows PowerShell*, etc.);
|
||||
- your system (Debian 7, ArchLinux, Windows, etc.);
|
||||
- how to reproduce the bug;
|
||||
- the output of The Fuck with `THEFUCK_DEBUG=true` exported (typically execute
|
||||
`export THEFUCK_DEBUG=true` in your shell before The Fuck);
|
||||
- if the bug only appears with a specific application, the output of that
|
||||
application and its version;
|
||||
- anything else you think is relevant.
|
||||
|
||||
It's only with enough information that we can do something to fix the problem.
|
||||
|
||||
# Make a pull request
|
||||
We gladly accept pull request on the [official
|
||||
repository](https://github.com/nvbn/thefuck) for new rules, new features, bug
|
||||
fixes, etc.
|
||||
13
README.md
13
README.md
@@ -141,8 +141,8 @@ using the matched rule and runs it. Rules enabled by default are as follows:
|
||||
|
||||
* `cargo` – runs `cargo build` instead of `cargo`;
|
||||
* `cargo_no_command` – fixes wrongs commands like `cargo buid`;
|
||||
* `cd_correction` – spellchecks and correct failed cd commands, when it's not possible
|
||||
creates directories before cd'ing into them;
|
||||
* `cd_correction` – spellchecks and correct failed cd commands;
|
||||
* `cd_mkdir` – creates directories before cd'ing into them;
|
||||
* `cd_parent` – changes `cd..` to `cd ..`;
|
||||
* `composer_not_command` – fixes composer command name;
|
||||
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
|
||||
@@ -167,6 +167,7 @@ creates directories before cd'ing into them;
|
||||
* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
|
||||
* `git_push_pull` – runs `git pull` when `push` was rejected;
|
||||
* `git_stash` – stashes you local modifications before rebasing or switching branch;
|
||||
* `git_two_dashes` – adds a missing dash to commands like `git commit -amend` or `git rebase -continue`;
|
||||
* `go_run` – appends `.go` extension when compiling/running Go programs
|
||||
* `grep_recursive` – adds `-r` when you trying to `grep` directory;
|
||||
* `gulp_not_task` – fixes misspelled `gulp` tasks;
|
||||
@@ -218,13 +219,13 @@ Enabled by default only on specific platforms:
|
||||
|
||||
Bundled, but not enabled by default:
|
||||
|
||||
* `git_push_force` – adds `--force` to a `git push` (may conflict with `git_push_pull`);
|
||||
* `git_push_force` – adds `--force-with-lease` to a `git push` (may conflict with `git_push_pull`);
|
||||
* `rm_root` – adds `--no-preserve-root` to `rm -rf /` command.
|
||||
|
||||
## Creating your own rules
|
||||
|
||||
For adding your own rule you should create `your-rule-name.py`
|
||||
in `~/.thefuck/rules`. The rule should contain two functions:
|
||||
in `~/.config/thefuck/rules`. The rule should contain two functions:
|
||||
|
||||
```python
|
||||
match(command: Command) -> bool
|
||||
@@ -241,7 +242,7 @@ and optional `enabled_by_default`, `requires_output` and `priority` variables.
|
||||
`Command` has three attributes: `script`, `stdout` and `stderr`.
|
||||
|
||||
*Rules api changed in 3.0:* For accessing settings in rule you need to import it with `from thefuck.conf import settings`.
|
||||
`settings` is a special object filled with `~/.thefuck/settings.py` and values from env ([see more below](#settings)).
|
||||
`settings` is a special object filled with `~/.config/thefuck/settings.py` and values from env ([see more below](#settings)).
|
||||
|
||||
Simple example of the rule for running script with `sudo`:
|
||||
|
||||
@@ -271,7 +272,7 @@ requires_output = True
|
||||
|
||||
## Settings
|
||||
|
||||
The Fuck has a few settings parameters which can be changed in `~/.thefuck/settings.py`:
|
||||
The Fuck has a few settings parameters which can be changed in `$XDG_CONFIG_HOME/thefuck/settings.py` (`$XDG_CONFIG_HOME` defaults to `~/.config`):
|
||||
|
||||
* `rules` – list of enabled rules, by default `thefuck.conf.DEFAULT_RULES`;
|
||||
* `exclude_rules` – list of disabled rules, by default `[]`;
|
||||
|
||||
80
install.sh
80
install.sh
@@ -8,50 +8,54 @@ installed () {
|
||||
hash $1 2>/dev/null
|
||||
}
|
||||
|
||||
# Install os dependencies:
|
||||
if installed apt-get; then
|
||||
# Debian/ubuntu:
|
||||
sudo apt-get update -yy
|
||||
sudo apt-get install -yy python-pip python-dev command-not-found
|
||||
install_thefuck () {
|
||||
# Install os dependencies:
|
||||
if installed apt-get; then
|
||||
# Debian/ubuntu:
|
||||
sudo apt-get update -yy
|
||||
sudo apt-get install -yy python-pip python-dev command-not-found
|
||||
|
||||
if [[ -n $(apt-cache search python-commandnotfound) ]]; then
|
||||
# In case of different python versions:
|
||||
sudo apt-get install -yy python-commandnotfound
|
||||
fi
|
||||
else
|
||||
if installed brew; then
|
||||
# OS X:
|
||||
brew update
|
||||
brew install python
|
||||
if [[ -n $(apt-cache search python-commandnotfound) ]]; then
|
||||
# In case of different python versions:
|
||||
sudo apt-get install -yy python-commandnotfound
|
||||
fi
|
||||
else
|
||||
# Genreic way:
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
sudo python get-pip.py
|
||||
rm get-pip.py
|
||||
if installed brew; then
|
||||
# OS X:
|
||||
brew update
|
||||
brew install python
|
||||
else
|
||||
# Genreic way:
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
sudo python get-pip.py
|
||||
rm get-pip.py
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# thefuck requires fresh versions of setuptools and pip:
|
||||
sudo pip install -U pip setuptools
|
||||
sudo pip install -U thefuck
|
||||
# thefuck requires fresh versions of setuptools and pip:
|
||||
sudo pip install -U pip setuptools
|
||||
sudo pip install -U thefuck
|
||||
|
||||
# Setup aliases:
|
||||
if should_add_alias ~/.bashrc; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.bashrc
|
||||
fi
|
||||
# Setup aliases:
|
||||
if should_add_alias ~/.bashrc; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.bashrc
|
||||
fi
|
||||
|
||||
if should_add_alias ~/.bash_profile; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.bash_profile
|
||||
fi
|
||||
if should_add_alias ~/.bash_profile; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.bash_profile
|
||||
fi
|
||||
|
||||
if should_add_alias ~/.zshrc; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.zshrc
|
||||
fi
|
||||
if should_add_alias ~/.zshrc; then
|
||||
echo 'eval $(thefuck --alias)' >> ~/.zshrc
|
||||
fi
|
||||
|
||||
if should_add_alias ~/.config/fish/config.fish; then
|
||||
thefuck --alias >> ~/.config/fish/config.fish
|
||||
fi
|
||||
if should_add_alias ~/.config/fish/config.fish; then
|
||||
thefuck --alias >> ~/.config/fish/config.fish
|
||||
fi
|
||||
|
||||
if should_add_alias ~/.tcshrc; then
|
||||
echo 'eval `thefuck --alias`' >> ~/.tcshrc
|
||||
fi
|
||||
if should_add_alias ~/.tcshrc; then
|
||||
echo 'eval `thefuck --alias`' >> ~/.tcshrc
|
||||
fi
|
||||
}
|
||||
|
||||
install_thefuck
|
||||
|
||||
2
setup.py
2
setup.py
@@ -20,7 +20,7 @@ elif (3, 0) < version < (3, 3):
|
||||
' ({}.{} detected).'.format(*version))
|
||||
sys.exit(-1)
|
||||
|
||||
VERSION = '3.1'
|
||||
VERSION = '3.2'
|
||||
|
||||
install_requires = ['psutil', 'colorama', 'six', 'decorator']
|
||||
extras_require = {':python_version<"3.4"': ['pathlib']}
|
||||
|
||||
@@ -31,8 +31,7 @@ def test_match(brew_no_available_formula, brew_already_installed,
|
||||
stderr=brew_no_available_formula))
|
||||
assert not match(Command('brew install git',
|
||||
stderr=brew_already_installed))
|
||||
assert not match(Command('brew install', stderr=brew_install_no_argument),
|
||||
None)
|
||||
assert not match(Command('brew install', stderr=brew_install_no_argument))
|
||||
|
||||
|
||||
@pytest.mark.skipif(_is_not_okay_to_test(),
|
||||
@@ -43,5 +42,5 @@ def test_get_new_command(brew_no_available_formula):
|
||||
== 'brew install elasticsearch'
|
||||
|
||||
assert get_new_command(Command('brew install aa',
|
||||
stderr=brew_no_available_formula),
|
||||
None) != 'brew install aha'
|
||||
stderr=brew_no_available_formula))\
|
||||
!= 'brew install aha'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import pytest
|
||||
from thefuck.rules.cd_correction import match, get_new_command
|
||||
from thefuck.rules.cd_mkdir import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import os
|
||||
import pytest
|
||||
import tarfile
|
||||
from thefuck.rules.dirty_untar import match, get_new_command, side_effect
|
||||
from thefuck.rules.dirty_untar import match, get_new_command, side_effect, \
|
||||
tar_extensions
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@@ -32,34 +33,40 @@ def tar_error(tmpdir):
|
||||
|
||||
return fixture
|
||||
|
||||
parametrize_filename = pytest.mark.parametrize('filename', [
|
||||
'foo.tar',
|
||||
'foo.tar.gz',
|
||||
'foo.tgz'])
|
||||
parametrize_extensions = pytest.mark.parametrize('ext', tar_extensions)
|
||||
|
||||
# (filename as typed by the user, unquoted filename, quoted filename as per shells.quote)
|
||||
parametrize_filename = pytest.mark.parametrize('filename, unquoted, quoted', [
|
||||
('foo{}', 'foo{}', 'foo{}'),
|
||||
('foo\ bar{}', 'foo bar{}', "'foo bar{}'"),
|
||||
('"foo bar{}"', 'foo bar{}', "'foo bar{}'")])
|
||||
|
||||
parametrize_script = pytest.mark.parametrize('script, fixed', [
|
||||
('tar xvf {}', 'mkdir -p foo && tar xvf {} -C foo'),
|
||||
('tar -xvf {}', 'mkdir -p foo && tar -xvf {} -C foo'),
|
||||
('tar --extract -f {}', 'mkdir -p foo && tar --extract -f {} -C foo')])
|
||||
('tar xvf {}', 'mkdir -p {dir} && tar xvf {filename} -C {dir}'),
|
||||
('tar -xvf {}', 'mkdir -p {dir} && tar -xvf {filename} -C {dir}'),
|
||||
('tar --extract -f {}', 'mkdir -p {dir} && tar --extract -f {filename} -C {dir}')])
|
||||
|
||||
|
||||
@parametrize_extensions
|
||||
@parametrize_filename
|
||||
@parametrize_script
|
||||
def test_match(tar_error, filename, script, fixed):
|
||||
tar_error(filename)
|
||||
assert match(Command(script=script.format(filename)))
|
||||
def test_match(ext, tar_error, filename, unquoted, quoted, script, fixed):
|
||||
tar_error(unquoted.format(ext))
|
||||
assert match(Command(script=script.format(filename.format(ext))))
|
||||
|
||||
|
||||
@parametrize_extensions
|
||||
@parametrize_filename
|
||||
@parametrize_script
|
||||
def test_side_effect(tar_error, filename, script, fixed):
|
||||
tar_error(filename)
|
||||
side_effect(Command(script=script.format(filename)), None)
|
||||
assert set(os.listdir('.')) == {filename, 'd'}
|
||||
|
||||
def test_side_effect(ext, tar_error, filename, unquoted, quoted, script, fixed):
|
||||
tar_error(unquoted.format(ext))
|
||||
side_effect(Command(script=script.format(filename.format(ext))), None)
|
||||
assert set(os.listdir('.')) == {unquoted.format(ext), 'd'}
|
||||
|
||||
@parametrize_extensions
|
||||
@parametrize_filename
|
||||
@parametrize_script
|
||||
def test_get_new_command(tar_error, filename, script, fixed):
|
||||
tar_error(filename)
|
||||
assert get_new_command(Command(script=script.format(filename))) == fixed.format(filename)
|
||||
def test_get_new_command(ext, tar_error, filename, unquoted, quoted, script, fixed):
|
||||
tar_error(unquoted.format(ext))
|
||||
assert (get_new_command(Command(script=script.format(filename.format(ext))))
|
||||
== fixed.format(dir=quoted.format(''), filename=filename.format(ext)))
|
||||
|
||||
@@ -43,6 +43,8 @@ def test_side_effect(zip_error, script):
|
||||
|
||||
@pytest.mark.parametrize('script,fixed', [
|
||||
('unzip foo', 'unzip foo -d foo'),
|
||||
(R"unzip foo\ bar.zip", R"unzip foo\ bar.zip -d 'foo bar'"),
|
||||
(R"unzip 'foo bar.zip'", R"unzip 'foo bar.zip' -d 'foo bar'"),
|
||||
('unzip foo.zip', 'unzip foo.zip -d foo')])
|
||||
def test_get_new_command(zip_error, script, fixed):
|
||||
assert get_new_command(Command(script=script)) == fixed
|
||||
|
||||
@@ -45,8 +45,8 @@ def test_not_match(command):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, output', [
|
||||
(Command(script='git push', stderr=git_err), 'git push --force'),
|
||||
(Command(script='git push nvbn', stderr=git_err), 'git push --force nvbn'),
|
||||
(Command(script='git push nvbn master', stderr=git_err), 'git push --force nvbn master')])
|
||||
(Command(script='git push', stderr=git_err), 'git push --force-with-lease'),
|
||||
(Command(script='git push nvbn', stderr=git_err), 'git push --force-with-lease nvbn'),
|
||||
(Command(script='git push nvbn master', stderr=git_err), 'git push --force-with-lease nvbn master')])
|
||||
def test_get_new_command(command, output):
|
||||
assert get_new_command(command) == output
|
||||
|
||||
47
tests/rules/test_git_two_dashes.py
Normal file
47
tests/rules/test_git_two_dashes.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import pytest
|
||||
from thefuck.rules.git_two_dashes import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stderr(meant):
|
||||
return 'error: did you mean `%s` (with two dashes ?)' % meant
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='git add -patch', stderr=stderr('--patch')),
|
||||
Command(script='git checkout -patch', stderr=stderr('--patch')),
|
||||
Command(script='git commit -amend', stderr=stderr('--amend')),
|
||||
Command(script='git push -tags', stderr=stderr('--tags')),
|
||||
Command(script='git rebase -continue', stderr=stderr('--continue'))])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='git add --patch'),
|
||||
Command(script='git checkout --patch'),
|
||||
Command(script='git commit --amend'),
|
||||
Command(script='git push --tags'),
|
||||
Command(script='git rebase --continue')])
|
||||
def test_not_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, output', [
|
||||
(Command(script='git add -patch', stderr=stderr('--patch')),
|
||||
'git add --patch'),
|
||||
(Command(script='git checkout -patch', stderr=stderr('--patch')),
|
||||
'git checkout --patch'),
|
||||
(Command(script='git checkout -patch', stderr=stderr('--patch')),
|
||||
'git checkout --patch'),
|
||||
(Command(script='git init -bare', stderr=stderr('--bare')),
|
||||
'git init --bare'),
|
||||
(Command(script='git commit -amend', stderr=stderr('--amend')),
|
||||
'git commit --amend'),
|
||||
(Command(script='git push -tags', stderr=stderr('--tags')),
|
||||
'git push --tags'),
|
||||
(Command(script='git rebase -continue', stderr=stderr('--continue')),
|
||||
'git rebase --continue')])
|
||||
def test_get_new_command(command, output):
|
||||
assert get_new_command(command) == output
|
||||
@@ -1,17 +1,18 @@
|
||||
from mock import Mock, patch
|
||||
from mock import patch
|
||||
from thefuck.rules.has_exists_script import match, get_new_command
|
||||
from ..utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
with patch('os.path.exists', return_value=True):
|
||||
assert match(Mock(script='main', stderr='main: command not found'))
|
||||
assert match(Mock(script='main --help',
|
||||
assert match(Command(script='main', stderr='main: command not found'))
|
||||
assert match(Command(script='main --help',
|
||||
stderr='main: command not found'))
|
||||
assert not match(Mock(script='main', stderr=''))
|
||||
assert not match(Command(script='main', stderr=''))
|
||||
|
||||
with patch('os.path.exists', return_value=False):
|
||||
assert not match(Mock(script='main', stderr='main: command not found'))
|
||||
assert not match(Command(script='main', stderr='main: command not found'))
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
assert get_new_command(Mock(script='main --help')) == './main --help'
|
||||
assert get_new_command(Command(script='main --help')) == './main --help'
|
||||
|
||||
@@ -26,7 +26,7 @@ ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
|
||||
'THEFUCK_PRIORITY': 'priority',
|
||||
'THEFUCK_DEBUG': 'debug'}
|
||||
|
||||
SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file
|
||||
SETTINGS_HEADER = u"""# The Fuck settings file
|
||||
#
|
||||
# The rules are defined as in the example bellow:
|
||||
#
|
||||
@@ -71,9 +71,21 @@ class Settings(dict):
|
||||
for setting in DEFAULT_SETTINGS.items():
|
||||
settings_file.write(u'# {} = {}\n'.format(*setting))
|
||||
|
||||
def _get_user_dir_path(self):
|
||||
# for backward compatibility, use `~/.thefuck` if it exists
|
||||
legacy_user_dir = Path(os.path.expanduser('~/.thefuck'))
|
||||
|
||||
if legacy_user_dir.is_dir():
|
||||
return legacy_user_dir
|
||||
else:
|
||||
default_xdg_config_dir = os.path.expanduser("~/.config")
|
||||
xdg_config_dir = os.getenv("XDG_CONFIG_HOME", default_xdg_config_dir)
|
||||
return Path(os.path.join(xdg_config_dir, 'thefuck'))
|
||||
|
||||
def _setup_user_dir(self):
|
||||
"""Returns user config dir, create it when it doesn't exist."""
|
||||
user_dir = Path(os.path.expanduser('~/.thefuck'))
|
||||
user_dir = self._get_user_dir_path()
|
||||
|
||||
rules_dir = user_dir.joinpath('rules')
|
||||
if not rules_dir.is_dir():
|
||||
rules_dir.mkdir(parents=True)
|
||||
|
||||
@@ -28,29 +28,29 @@ def exception(title, exc_info):
|
||||
|
||||
|
||||
def rule_failed(rule, exc_info):
|
||||
exception('Rule {}'.format(rule.name), exc_info)
|
||||
exception(u'Rule {}'.format(rule.name), exc_info)
|
||||
|
||||
|
||||
def failed(msg):
|
||||
sys.stderr.write('{red}{msg}{reset}\n'.format(
|
||||
sys.stderr.write(u'{red}{msg}{reset}\n'.format(
|
||||
msg=msg,
|
||||
red=color(colorama.Fore.RED),
|
||||
reset=color(colorama.Style.RESET_ALL)))
|
||||
|
||||
|
||||
def show_corrected_command(corrected_command):
|
||||
sys.stderr.write('{bold}{script}{reset}{side_effect}\n'.format(
|
||||
sys.stderr.write(u'{bold}{script}{reset}{side_effect}\n'.format(
|
||||
script=corrected_command.script,
|
||||
side_effect=' (+side effect)' if corrected_command.side_effect else '',
|
||||
side_effect=u' (+side effect)' if corrected_command.side_effect else u'',
|
||||
bold=color(colorama.Style.BRIGHT),
|
||||
reset=color(colorama.Style.RESET_ALL)))
|
||||
|
||||
|
||||
def confirm_text(corrected_command):
|
||||
sys.stderr.write(
|
||||
('{clear}{bold}{script}{reset}{side_effect} '
|
||||
'[{green}enter{reset}/{blue}↑{reset}/{blue}↓{reset}'
|
||||
'/{red}ctrl+c{reset}]').format(
|
||||
(u'{clear}{bold}{script}{reset}{side_effect} '
|
||||
u'[{green}enter{reset}/{blue}↑{reset}/{blue}↓{reset}'
|
||||
u'/{red}ctrl+c{reset}]').format(
|
||||
script=corrected_command.script,
|
||||
side_effect=' (+side effect)' if corrected_command.side_effect else '',
|
||||
clear='\033[1K\r',
|
||||
|
||||
@@ -2,14 +2,14 @@ import re
|
||||
from thefuck.utils import replace_argument, for_app
|
||||
|
||||
|
||||
@for_app('cargo')
|
||||
@for_app('cargo', at_least=1)
|
||||
def match(command):
|
||||
return ('No such subcommand' in command.stderr
|
||||
and 'Did you mean' in command.stderr)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
broken = command.script.split()[1]
|
||||
broken = command.script_parts[1]
|
||||
fix = re.findall(r'Did you mean `([^`]*)`', command.stderr)[0]
|
||||
|
||||
return replace_argument(command.script, broken, fix)
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
import os
|
||||
from difflib import get_close_matches
|
||||
import re
|
||||
from thefuck.specific.sudo import sudo_support
|
||||
from thefuck.rules import cd_mkdir
|
||||
from thefuck.utils import for_app
|
||||
from thefuck import shells
|
||||
|
||||
__author__ = "mmussomele"
|
||||
|
||||
@@ -34,7 +33,7 @@ def get_new_command(command):
|
||||
defaults to the rules of cd_mkdir.
|
||||
Change sensitivity by changing MAX_ALLOWED_DIFF. Default value is 0.6
|
||||
"""
|
||||
dest = command.script.split()[1].split(os.sep)
|
||||
dest = command.script_parts[1].split(os.sep)
|
||||
if dest[-1] == '':
|
||||
dest = dest[:-1]
|
||||
cwd = os.getcwd()
|
||||
@@ -44,13 +43,11 @@ def get_new_command(command):
|
||||
elif directory == "..":
|
||||
cwd = os.path.split(cwd)[0]
|
||||
continue
|
||||
best_matches = get_close_matches(
|
||||
directory, _get_sub_dirs(cwd), cutoff=MAX_ALLOWED_DIFF)
|
||||
best_matches = get_close_matches(directory, _get_sub_dirs(cwd), cutoff=MAX_ALLOWED_DIFF)
|
||||
if best_matches:
|
||||
cwd = os.path.join(cwd, best_matches[0])
|
||||
else:
|
||||
repl = shells.and_('mkdir -p \\1', 'cd \\1')
|
||||
return re.sub(r'^cd (.*)', repl, command.script)
|
||||
return cd_mkdir.get_new_command(command)
|
||||
return 'cd "{0}"'.format(cwd)
|
||||
|
||||
|
||||
|
||||
17
thefuck/rules/cd_mkdir.py
Normal file
17
thefuck/rules/cd_mkdir.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import re
|
||||
from thefuck import shells
|
||||
from thefuck.utils import for_app
|
||||
from thefuck.specific.sudo import sudo_support
|
||||
|
||||
|
||||
@sudo_support
|
||||
@for_app('cd')
|
||||
def match(command):
|
||||
return (('no such file or directory' in command.stderr.lower()
|
||||
or 'cd: can\'t cd to' in command.stderr.lower()))
|
||||
|
||||
|
||||
@sudo_support
|
||||
def get_new_command(command):
|
||||
repl = shells.and_('mkdir -p \\1', 'cd \\1')
|
||||
return re.sub(r'^cd (.*)', repl, command.script)
|
||||
@@ -1,7 +1,7 @@
|
||||
from thefuck.utils import for_app
|
||||
|
||||
|
||||
@for_app(['g++', 'clang++'])
|
||||
@for_app('g++', 'clang++')
|
||||
def match(command):
|
||||
return ('This file requires compiler and library support for the '
|
||||
'ISO C++ 2011 standard.' in command.stderr or
|
||||
|
||||
@@ -4,6 +4,11 @@ from thefuck import shells
|
||||
from thefuck.utils import for_app
|
||||
|
||||
|
||||
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
|
||||
'.tar.lzma', '.tar.xz', '.taz', '.tb2', '.tbz', '.tbz2',
|
||||
'.tgz', '.tlz', '.txz', '.tz')
|
||||
|
||||
|
||||
def _is_tar_extract(cmd):
|
||||
if '--extract' in cmd:
|
||||
return True
|
||||
@@ -14,11 +19,8 @@ def _is_tar_extract(cmd):
|
||||
|
||||
|
||||
def _tar_file(cmd):
|
||||
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
|
||||
'.tar.lzma', '.tar.xz', '.taz', '.tb2', '.tbz', '.tbz2',
|
||||
'.tgz', '.tlz', '.txz', '.tz')
|
||||
|
||||
for c in cmd.split():
|
||||
for c in cmd:
|
||||
for ext in tar_extensions:
|
||||
if c.endswith(ext):
|
||||
return (c, c[0:len(c) - len(ext)])
|
||||
@@ -28,16 +30,17 @@ def _tar_file(cmd):
|
||||
def match(command):
|
||||
return ('-C' not in command.script
|
||||
and _is_tar_extract(command.script)
|
||||
and _tar_file(command.script) is not None)
|
||||
and _tar_file(command.script_parts) is not None)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
dir = shells.quote(_tar_file(command.script_parts)[1])
|
||||
return shells.and_('mkdir -p {dir}', '{cmd} -C {dir}') \
|
||||
.format(dir=_tar_file(command.script)[1], cmd=command.script)
|
||||
.format(dir=dir, cmd=command.script)
|
||||
|
||||
|
||||
def side_effect(old_cmd, command):
|
||||
with tarfile.TarFile(_tar_file(old_cmd.script)[0]) as archive:
|
||||
with tarfile.TarFile(_tar_file(old_cmd.script_parts)[0]) as archive:
|
||||
for file in archive.getnames():
|
||||
try:
|
||||
os.remove(file)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import zipfile
|
||||
from thefuck.utils import for_app
|
||||
from thefuck.shells import quote
|
||||
|
||||
|
||||
def _is_bad_zip(file):
|
||||
@@ -13,7 +14,7 @@ def _zip_file(command):
|
||||
# unzip [-flags] file[.zip] [file(s) ...] [-x file(s) ...]
|
||||
# ^ ^ files to unzip from the archive
|
||||
# archive to unzip
|
||||
for c in command.script.split()[1:]:
|
||||
for c in command.script_parts[1:]:
|
||||
if not c.startswith('-'):
|
||||
if c.endswith('.zip'):
|
||||
return c
|
||||
@@ -28,7 +29,7 @@ def match(command):
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
return '{} -d {}'.format(command.script, _zip_file(command)[:-4])
|
||||
return '{} -d {}'.format(command.script, quote(_zip_file(command)[:-4]))
|
||||
|
||||
|
||||
def side_effect(old_cmd, command):
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
def match(command):
|
||||
split_command = command.script.split()
|
||||
split_command = command.script_parts
|
||||
|
||||
return len(split_command) >= 2 and split_command[0] == split_command[1]
|
||||
return (split_command
|
||||
and len(split_command) >= 2
|
||||
and split_command[0] == split_command[1])
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
return command.script[command.script.find(' ')+1:]
|
||||
return ' '.join(command.script_parts[1:])
|
||||
|
||||
# it should be rare enough to actually have to type twice the same word, so
|
||||
# this rule can have a higher priority to come before things like "cd cd foo"
|
||||
|
||||
@@ -5,7 +5,8 @@ from thefuck.specific.git import git_support
|
||||
@git_support
|
||||
def match(command):
|
||||
# catches "git branch list" in place of "git branch"
|
||||
return command.script.split()[1:] == 'branch list'.split()
|
||||
return (command.script_parts
|
||||
and command.script_parts[1:] == 'branch list'.split())
|
||||
|
||||
|
||||
@git_support
|
||||
|
||||
@@ -5,9 +5,8 @@ from thefuck.specific.git import git_support
|
||||
|
||||
@git_support
|
||||
def match(command):
|
||||
splited_script = command.script.split()
|
||||
if len(splited_script) > 1:
|
||||
return (splited_script[1] == 'stash'
|
||||
if command.script_parts and len(command.script_parts) > 1:
|
||||
return (command.script_parts[1] == 'stash'
|
||||
and 'usage:' in command.stderr)
|
||||
else:
|
||||
return False
|
||||
@@ -26,12 +25,12 @@ stash_commands = (
|
||||
|
||||
@git_support
|
||||
def get_new_command(command):
|
||||
stash_cmd = command.script.split()[2]
|
||||
stash_cmd = command.script_parts[2]
|
||||
fixed = utils.get_closest(stash_cmd, stash_commands, fallback_to_first=False)
|
||||
|
||||
if fixed is not None:
|
||||
return replace_argument(command.script, stash_cmd, fixed)
|
||||
else:
|
||||
cmd = command.script.split()
|
||||
cmd = command.script_parts[:]
|
||||
cmd.insert(2, 'save')
|
||||
return ' '.join(cmd)
|
||||
|
||||
@@ -12,7 +12,7 @@ def match(command):
|
||||
|
||||
@git_support
|
||||
def get_new_command(command):
|
||||
return replace_argument(command.script, 'push', 'push --force')
|
||||
return replace_argument(command.script, 'push', 'push --force-with-lease')
|
||||
|
||||
|
||||
enabled_by_default = False
|
||||
|
||||
14
thefuck/rules/git_two_dashes.py
Normal file
14
thefuck/rules/git_two_dashes.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from thefuck.utils import replace_argument
|
||||
from thefuck.specific.git import git_support
|
||||
|
||||
|
||||
@git_support
|
||||
def match(command):
|
||||
return ('error: did you mean `' in command.stderr
|
||||
and '` (with two dashes ?)' in command.stderr)
|
||||
|
||||
|
||||
@git_support
|
||||
def get_new_command(command):
|
||||
to = command.stderr.split('`')[1]
|
||||
return replace_argument(command.script, to[1:], to)
|
||||
@@ -4,7 +4,7 @@ from thefuck.specific.sudo import sudo_support
|
||||
|
||||
@sudo_support
|
||||
def match(command):
|
||||
return os.path.exists(command.script.split()[0]) \
|
||||
return command.script_parts and os.path.exists(command.script_parts[0]) \
|
||||
and 'command not found' in command.stderr
|
||||
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ from thefuck.utils import for_app
|
||||
|
||||
@for_app('ls')
|
||||
def match(command):
|
||||
return 'ls -' not in command.script
|
||||
return command.script_parts and 'ls -' not in command.script
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
command = command.script.split(' ')
|
||||
command = command.script_parts[:]
|
||||
command[0] = 'ls -lah'
|
||||
return ' '.join(command)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
from thefuck.utils import for_app
|
||||
|
||||
|
||||
@for_app('man', at_least=1)
|
||||
def match(command):
|
||||
return command.script.strip().startswith('man ')
|
||||
return True
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
@@ -8,7 +12,7 @@ def get_new_command(command):
|
||||
if '2' in command.script:
|
||||
return command.script.replace("2", "3")
|
||||
|
||||
split_cmd2 = command.script.split()
|
||||
split_cmd2 = command.script_parts
|
||||
split_cmd3 = split_cmd2[:]
|
||||
|
||||
split_cmd2.insert(1, ' 2 ')
|
||||
|
||||
@@ -21,7 +21,7 @@ def match(command):
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
script = command.script.split(' ')
|
||||
script = command.script_parts[:]
|
||||
possibilities = extract_possibilities(command)
|
||||
script[1] = get_closest(script[1], possibilities)
|
||||
return ' '.join(script)
|
||||
|
||||
@@ -5,16 +5,17 @@ from thefuck.specific.sudo import sudo_support
|
||||
|
||||
@sudo_support
|
||||
def match(command):
|
||||
return 'not found' in command.stderr and \
|
||||
bool(get_close_matches(command.script.split(' ')[0],
|
||||
get_all_executables()))
|
||||
return (command.script_parts
|
||||
and 'not found' in command.stderr
|
||||
and bool(get_close_matches(command.script_parts[0],
|
||||
get_all_executables())))
|
||||
|
||||
|
||||
@sudo_support
|
||||
def get_new_command(command):
|
||||
old_command = command.script.split(' ')[0]
|
||||
old_command = command.script_parts[0]
|
||||
new_cmds = get_close_matches(old_command, get_all_executables(), cutoff=0.1)
|
||||
return [' '.join([new_command] + command.script.split(' ')[1:])
|
||||
return [' '.join([new_command] + command.script_parts[1:])
|
||||
for new_command in new_cmds]
|
||||
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ from thefuck.specific.archlinux import get_pkgfile, archlinux_env
|
||||
|
||||
|
||||
def match(command):
|
||||
return (command.script.startswith(('pacman', 'sudo pacman', 'yaourt'))
|
||||
return (command.script_parts
|
||||
and (command.script_parts[0] in ('pacman', 'yaourt')
|
||||
or command.script_parts[0:2] == ['sudo', 'pacman'])
|
||||
and 'error: target not found:' in command.stderr)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
pgr = command.script.split()[-1]
|
||||
pgr = command.script_parts[-1]
|
||||
|
||||
return replace_command(command, pgr, get_pkgfile(pgr))
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ from thefuck.specific.sudo import sudo_support
|
||||
|
||||
@sudo_support
|
||||
def match(command):
|
||||
toks = command.script.split()
|
||||
return (len(toks) > 0
|
||||
toks = command.script_parts
|
||||
return (toks
|
||||
and toks[0].endswith('.py')
|
||||
and ('Permission denied' in command.stderr or
|
||||
'command not found' in command.stderr))
|
||||
|
||||
@@ -5,7 +5,8 @@ enabled_by_default = False
|
||||
|
||||
@sudo_support
|
||||
def match(command):
|
||||
return ({'rm', '/'}.issubset(command.script.split())
|
||||
return (command.script_parts
|
||||
and {'rm', '/'}.issubset(command.script_parts)
|
||||
and '--no-preserve-root' not in command.script
|
||||
and '--no-preserve-root' in command.stderr)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import shlex
|
||||
from thefuck.utils import quote, for_app
|
||||
from thefuck.shells import quote
|
||||
from thefuck.utils import for_app
|
||||
|
||||
|
||||
@for_app('sed')
|
||||
|
||||
@@ -11,9 +11,11 @@ source_layouts = [u'''йцукенгшщзхъфывапролджэячсмит
|
||||
|
||||
@memoize
|
||||
def _get_matched_layout(command):
|
||||
# don't use command.split_script here because a layout mismatch will likely
|
||||
# result in a non-splitable sript as per shlex
|
||||
cmd = command.script.split(' ')
|
||||
for source_layout in source_layouts:
|
||||
if all([ch in source_layout or ch in '-_'
|
||||
for ch in command.script.split(' ')[0]]):
|
||||
if all([ch in source_layout or ch in '-_' for ch in cmd[0]]):
|
||||
return source_layout
|
||||
|
||||
|
||||
|
||||
@@ -8,15 +8,15 @@ from thefuck.utils import for_app
|
||||
@sudo_support
|
||||
@for_app('systemctl')
|
||||
def match(command):
|
||||
# Catches 'Unknown operation 'service'.' when executing systemctl with
|
||||
# Catches "Unknown operation 'service'." when executing systemctl with
|
||||
# misordered arguments
|
||||
cmd = command.script.split()
|
||||
return ('Unknown operation \'' in command.stderr and
|
||||
cmd = command.script_parts
|
||||
return (cmd and 'Unknown operation \'' in command.stderr and
|
||||
len(cmd) - cmd.index('systemctl') == 3)
|
||||
|
||||
|
||||
@sudo_support
|
||||
def get_new_command(command):
|
||||
cmd = command.script.split()
|
||||
cmd = command.script_parts
|
||||
cmd[-1], cmd[-2] = cmd[-2], cmd[-1]
|
||||
return ' '.join(cmd)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import re
|
||||
from thefuck import shells
|
||||
from thefuck.utils import for_app
|
||||
from thefuck.shells import and_
|
||||
|
||||
|
||||
@for_app('touch')
|
||||
@@ -10,4 +10,4 @@ def match(command):
|
||||
|
||||
def get_new_command(command):
|
||||
path = re.findall(r"touch: cannot touch '(.+)/.+':", command.stderr)[0]
|
||||
return and_(u'mkdir -p {}'.format(path), command.script)
|
||||
return shells.and_(u'mkdir -p {}'.format(path), command.script)
|
||||
|
||||
@@ -8,13 +8,13 @@ def match(command):
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
cmds = command.script.split(' ')
|
||||
cmds = command.script_parts
|
||||
machine = None
|
||||
if len(cmds) >= 3:
|
||||
machine = cmds[2]
|
||||
|
||||
startAllInstances = shells.and_("vagrant up", command.script)
|
||||
if machine is None:
|
||||
if machine is None:
|
||||
return startAllInstances
|
||||
else:
|
||||
return [ shells.and_("vagrant up " + machine, command.script), startAllInstances]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
from six.moves.urllib.parse import urlparse
|
||||
from thefuck.utils import for_app
|
||||
|
||||
|
||||
@for_app('whois', at_least=1)
|
||||
def match(command):
|
||||
"""
|
||||
What the `whois` command returns depends on the 'Whois server' it contacted
|
||||
@@ -19,11 +21,11 @@ def match(command):
|
||||
- www.google.fr → subdomain: www, domain: 'google.fr';
|
||||
- google.co.uk → subdomain: None, domain; 'google.co.uk'.
|
||||
"""
|
||||
return 'whois ' in command.script.strip()
|
||||
return True
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
url = command.script.split()[1]
|
||||
url = command.script_parts[1]
|
||||
|
||||
if '/' in command.script:
|
||||
return 'whois ' + urlparse(url).netloc
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Module with shell specific actions, each shell class should
|
||||
implement `from_shell`, `to_shell`, `app_alias`, `put_to_history` and `get_aliases`
|
||||
methods.
|
||||
implement `from_shell`, `to_shell`, `app_alias`, `put_to_history` and
|
||||
`get_aliases` methods.
|
||||
|
||||
"""
|
||||
from collections import defaultdict
|
||||
@@ -9,6 +9,8 @@ from subprocess import Popen, PIPE
|
||||
from time import time
|
||||
import io
|
||||
import os
|
||||
import shlex
|
||||
import six
|
||||
from .utils import DEVNULL, memoize, cache
|
||||
|
||||
|
||||
@@ -75,6 +77,20 @@ class Generic(object):
|
||||
def how_to_configure(self):
|
||||
return
|
||||
|
||||
def split_command(self, command):
|
||||
"""Split the command using shell-like syntax."""
|
||||
return shlex.split(command)
|
||||
|
||||
def quote(self, s):
|
||||
"""Return a shell-escaped version of the string s."""
|
||||
|
||||
if six.PY2:
|
||||
from pipes import quote
|
||||
else:
|
||||
from shlex import quote
|
||||
|
||||
return quote(s)
|
||||
|
||||
|
||||
class Bash(Generic):
|
||||
def app_alias(self, fuck):
|
||||
@@ -126,19 +142,20 @@ class Fish(Generic):
|
||||
return ['cd', 'grep', 'ls', 'man', 'open']
|
||||
|
||||
def app_alias(self, fuck):
|
||||
return ("set TF_ALIAS {0}\n"
|
||||
"function {0} -d 'Correct your previous console command'\n"
|
||||
" set -l exit_code $status\n"
|
||||
" set -l eval_script"
|
||||
" (mktemp 2>/dev/null ; or mktemp -t 'thefuck')\n"
|
||||
" set -l fucked_up_command $history[1]\n"
|
||||
" thefuck $fucked_up_command > $eval_script\n"
|
||||
" . $eval_script\n"
|
||||
" /bin/rm $eval_script\n"
|
||||
" if test $exit_code -ne 0\n"
|
||||
" history --delete $fucked_up_command\n"
|
||||
" end\n"
|
||||
"end").format(fuck)
|
||||
return ('function {0} -d "Correct your previous console command"\n'
|
||||
' set -l exit_code $status\n'
|
||||
' set -x TF_ALIAS {0}\n'
|
||||
' set -l fucked_up_command $history[1]\n'
|
||||
' thefuck $fucked_up_command | read -l unfucked_command\n'
|
||||
' if [ "$unfucked_command" != "" ]\n'
|
||||
' eval $unfucked_command\n'
|
||||
' if test $exit_code -ne 0\n'
|
||||
' history --delete $fucked_up_command\n'
|
||||
' history --merge ^ /dev/null\n'
|
||||
' return 0\n'
|
||||
' end\n'
|
||||
' end\n'
|
||||
'end').format(fuck)
|
||||
|
||||
@memoize
|
||||
def get_aliases(self):
|
||||
@@ -284,9 +301,18 @@ def get_aliases():
|
||||
return list(_get_shell().get_aliases().keys())
|
||||
|
||||
|
||||
def split_command(command):
|
||||
return _get_shell().split_command(command)
|
||||
|
||||
|
||||
def quote(s):
|
||||
return _get_shell().quote(s)
|
||||
|
||||
|
||||
@memoize
|
||||
def get_history():
|
||||
return list(_get_shell().get_history())
|
||||
|
||||
|
||||
def how_to_configure():
|
||||
return _get_shell().how_to_configure()
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import re
|
||||
from shlex import split
|
||||
from decorator import decorator
|
||||
from ..types import Command
|
||||
from ..utils import quote, is_app
|
||||
from ..utils import is_app
|
||||
from ..shells import quote, split_command
|
||||
|
||||
|
||||
@decorator
|
||||
@@ -24,7 +23,7 @@ def git_support(fn, command):
|
||||
# 'commit' '--amend'
|
||||
# which is surprising and does not allow to easily test for
|
||||
# eg. 'git commit'
|
||||
expansion = ' '.join(map(quote, split(search.group(2))))
|
||||
expansion = ' '.join(map(quote, split_command(search.group(2))))
|
||||
new_script = command.script.replace(alias, expansion)
|
||||
|
||||
command = command.update(script=new_script)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from imp import load_source
|
||||
import os
|
||||
from subprocess import Popen, PIPE
|
||||
import os
|
||||
import sys
|
||||
from psutil import Process, TimeoutExpired
|
||||
import six
|
||||
from .conf import settings, DEFAULT_PRIORITY, ALL_ENABLED
|
||||
from .utils import compatibility_call
|
||||
from .exceptions import EmptyCommand
|
||||
from psutil import Process, TimeoutExpired
|
||||
from . import logs, shells
|
||||
from .conf import settings, DEFAULT_PRIORITY, ALL_ENABLED
|
||||
from .exceptions import EmptyCommand
|
||||
from .utils import compatibility_call
|
||||
|
||||
|
||||
class Command(object):
|
||||
@@ -25,6 +25,17 @@ class Command(object):
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
|
||||
@property
|
||||
def script_parts(self):
|
||||
if not hasattr(self, '_script_parts'):
|
||||
try:
|
||||
self._script_parts = shells.split_command(self.script)
|
||||
except Exception:
|
||||
logs.debug("Can't split command script {} because:\n {}".format(
|
||||
self, sys.exc_info()))
|
||||
self._script_parts = None
|
||||
return self._script_parts
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Command):
|
||||
return (self.script, self.stdout, self.stderr) \
|
||||
|
||||
@@ -12,16 +12,10 @@ from inspect import getargspec
|
||||
|
||||
from pathlib import Path
|
||||
import pkg_resources
|
||||
import six
|
||||
from .conf import settings
|
||||
|
||||
DEVNULL = open(os.devnull, 'w')
|
||||
|
||||
if six.PY2:
|
||||
from pipes import quote
|
||||
else:
|
||||
from shlex import quote
|
||||
|
||||
|
||||
def memoize(fn):
|
||||
"""Caches previous calls to the function."""
|
||||
@@ -144,19 +138,23 @@ def replace_command(command, broken, matched):
|
||||
|
||||
|
||||
@memoize
|
||||
def is_app(command, *app_names):
|
||||
def is_app(command, *app_names, **kwargs):
|
||||
"""Returns `True` if command is call to one of passed app names."""
|
||||
for name in app_names:
|
||||
if command.script == name \
|
||||
or command.script.startswith(u'{} '.format(name)):
|
||||
return True
|
||||
|
||||
at_least = kwargs.pop('at_least', 0)
|
||||
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:
|
||||
return command.script_parts[0] in app_names
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def for_app(*app_names):
|
||||
def for_app(*app_names, **kwargs):
|
||||
"""Specifies that matching script is for on of app names."""
|
||||
def _for_app(fn, command):
|
||||
if is_app(command, *app_names):
|
||||
if is_app(command, *app_names, **kwargs):
|
||||
return fn(command)
|
||||
else:
|
||||
return False
|
||||
@@ -180,17 +178,32 @@ def cache(*depends_on):
|
||||
except OSError:
|
||||
return '0'
|
||||
|
||||
def _get_cache_path():
|
||||
default_xdg_cache_dir = os.path.expanduser("~/.cache")
|
||||
cache_dir = os.getenv("XDG_CACHE_HOME", default_xdg_cache_dir)
|
||||
cache_path = Path(cache_dir).joinpath('thefuck').as_posix()
|
||||
|
||||
# Ensure the cache_path exists, Python 2 does not have the exist_ok
|
||||
# parameter
|
||||
try:
|
||||
os.makedirs(cache_dir)
|
||||
except OSError:
|
||||
if not os.path.isdir(cache_dir):
|
||||
raise
|
||||
|
||||
return cache_path
|
||||
|
||||
@decorator
|
||||
def _cache(fn, *args, **kwargs):
|
||||
if cache.disabled:
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
cache_path = settings.user_dir.joinpath('.thefuck-cache').as_posix()
|
||||
# A bit obscure, but simplest way to generate unique key for
|
||||
# functions and methods in python 2 and 3:
|
||||
key = '{}.{}'.format(fn.__module__, repr(fn).split('at')[0])
|
||||
|
||||
etag = '.'.join(_get_mtime(name) for name in depends_on)
|
||||
cache_path = _get_cache_path()
|
||||
|
||||
with closing(shelve.open(cache_path)) as db:
|
||||
if db.get(key, {}).get('etag') == etag:
|
||||
|
||||
Reference in New Issue
Block a user