1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-01-31 02:01:13 +00:00

#591: Add path_from_history rule

This commit is contained in:
Vladimir Iakovlev 2017-03-13 19:05:34 +01:00
parent 350be285b8
commit 2379573cf2
4 changed files with 100 additions and 1 deletions

View File

@ -234,6 +234,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `python_command` – prepends `python` when you trying to run not executable/without `./` python script;
* `python_execute` – appends missing `.py` when executing Python files;
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
* `path_from_history` – replaces not found path with similar absolute path from history;
* `react_native_command_unrecognized` – fixes unrecognized `react-native` commands;
* `remove_trailing_cedilla` – remove trailling cedillas `ç`, a common typo for european keyboard layouts;
* `rm_dir` – adds `-rf` when you trying to remove directory;

View File

@ -0,0 +1,43 @@
import pytest
from thefuck.rules.path_from_history import match, get_new_command
from tests.utils import Command
@pytest.fixture(autouse=True)
def history(mocker):
return mocker.patch(
'thefuck.rules.path_from_history.get_valid_history_without_current',
return_value=['cd /opt/java', 'ls ~/work/project/'])
@pytest.fixture(autouse=True)
def path_exists(mocker):
path_mock = mocker.patch('thefuck.rules.path_from_history.Path')
exists_mock = path_mock.return_value.expanduser.return_value.exists
exists_mock.return_value = True
return exists_mock
@pytest.mark.parametrize('script, stderr', [
('ls project', 'no such file or directory: project'),
('cd project', "can't cd to project"),
])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr', [
('myapp cats', 'no such file or directory: project'),
('cd project', ""),
])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr, result', [
('ls project', 'no such file or directory: project', 'ls ~/work/project'),
('cd java', "can't cd to java", 'cd /opt/java'),
])
def test_get_new_command(script, stderr, result):
new_command = get_new_command(Command(script, stderr=stderr))
assert new_command[0] == result

View File

@ -0,0 +1,53 @@
from collections import Counter
import re
from thefuck.system import Path
from thefuck.utils import (get_valid_history_without_current,
memoize, replace_argument)
from thefuck.shells import shell
patterns = [r'no such file or directory: (.*)$',
r"cannot access '(.*)': No such file or directory",
r': (.*): No such file or directory',
r"can't cd to (.*)$"]
@memoize
def _get_destination(command):
for pattern in patterns:
found = re.findall(pattern, command.stderr)
if found:
if found[0] in command.script_parts:
return found[0]
def match(command):
return bool(_get_destination(command))
def _get_all_absolute_paths_from_history(command):
counter = Counter()
for line in get_valid_history_without_current(command):
splitted = shell.split_command(line)
for param in splitted[1:]:
if param.startswith('/') or param.startswith('~'):
if param.endswith('/'):
param = param[:-1]
counter[param] += 1
return (path for path, _ in counter.most_common(None))
def get_new_command(command):
destination = _get_destination(command)
paths = _get_all_absolute_paths_from_history(command)
return [replace_argument(command.script, destination, path)
for path in paths if path.endswith(destination)
and Path(path).expanduser().exists()]
priority = 800

View File

@ -268,7 +268,9 @@ def get_valid_history_without_current(command):
from thefuck.shells import shell
history = shell.get_history()
tf_alias = get_alias()
executables = set(get_all_executables())
executables = set(get_all_executables())\
.union(shell.get_builtin_commands())
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]