Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ Several *The Fuck* parameters can be changed in the file `$XDG_CONFIG_HOME/thefu
* `wait_slow_command` – max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `slow_commands` – list of slow commands;
* `num_close_matches` – maximum number of close matches to suggest, by default `3`.
* `excluded_search_path_prefixes` – path prefixes to ignore when searching for commands, by default `[]`.

An example of `settings.py`:

Expand Down Expand Up @@ -466,6 +467,7 @@ rule with lower `priority` will be matched first;
* `THEFUCK_WAIT_SLOW_COMMAND` – max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `THEFUCK_SLOW_COMMANDS` – list of slow commands, like `lein:gradle`;
* `THEFUCK_NUM_CLOSE_MATCHES` – maximum number of close matches to suggest, like `5`.
* `THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES` – path prefixes to ignore when searching for commands, by default `[]`.

For example:

Expand Down
4 changes: 3 additions & 1 deletion tests/test_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def test_from_env(self, os_environ, settings):
'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15',
'THEFUCK_WAIT_SLOW_COMMAND': '999',
'THEFUCK_SLOW_COMMANDS': 'lein:react-native:./gradlew',
'THEFUCK_NUM_CLOSE_MATCHES': '359'})
'THEFUCK_NUM_CLOSE_MATCHES': '359',
'THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES': '/media/:/mnt/'})
settings.init()
assert settings.rules == ['bash', 'lisp']
assert settings.exclude_rules == ['git', 'vim']
Expand All @@ -65,6 +66,7 @@ def test_from_env(self, os_environ, settings):
assert settings.wait_slow_command == 999
assert settings.slow_commands == ['lein', 'react-native', './gradlew']
assert settings.num_close_matches == 359
assert settings.excluded_search_path_prefixes == ['/media/', '/mnt/']

def test_from_env_with_DEFAULT(self, os_environ, settings):
os_environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})
Expand Down
14 changes: 14 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,20 @@ def test_get_all_executables_pathsep(path, pathsep):
Path_mock.assert_has_calls([call(p) for p in path.split(pathsep)], True)


@pytest.mark.usefixtures('no_memoize', 'os_environ_pathsep')
@pytest.mark.parametrize('path, pathsep, excluded', [
('/foo:/bar:/baz:/foo/bar:/mnt/foo', ':', '/mnt/foo'),
(r'C:\\foo;C:\\bar;C:\\baz;C:\\foo\\bar;Z:\\foo', ';', r'Z:\\foo')])
def test_get_all_executables_exclude_paths(path, pathsep, excluded, settings):
settings.init()
settings.excluded_search_path_prefixes = [excluded]
with patch('thefuck.utils.Path') as Path_mock:
get_all_executables()
path_list = path.split(pathsep)
assert call(path_list[-1]) not in Path_mock.mock_calls
assert all(call(p) in Path_mock.mock_calls for p in path_list[:-1])


@pytest.mark.parametrize('args, result', [
(('apt-get instol vim', 'instol', 'install'), 'apt-get install vim'),
(('git brnch', 'brnch', 'branch'), 'git branch')])
Expand Down
2 changes: 1 addition & 1 deletion thefuck/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def _val_from_env(self, env, attr):
elif attr in ('require_confirmation', 'no_colors', 'debug',
'alter_history', 'instant_mode'):
return val.lower() == 'true'
elif attr == 'slow_commands':
elif attr in ('slow_commands', 'excluded_search_path_prefixes'):
return val.split(':')
else:
return val
Expand Down
6 changes: 4 additions & 2 deletions thefuck/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def __repr__(self):
'repeat': False,
'instant_mode': False,
'num_close_matches': 3,
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}}
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'},
'excluded_search_path_prefixes': []}

ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
'THEFUCK_EXCLUDE_RULES': 'exclude_rules',
Expand All @@ -58,7 +59,8 @@ def __repr__(self):
'THEFUCK_SLOW_COMMANDS': 'slow_commands',
'THEFUCK_REPEAT': 'repeat',
'THEFUCK_INSTANT_MODE': 'instant_mode',
'THEFUCK_NUM_CLOSE_MATCHES': 'num_close_matches'}
'THEFUCK_NUM_CLOSE_MATCHES': 'num_close_matches',
'THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES': 'excluded_search_path_prefixes'}

SETTINGS_HEADER = u"""# The Fuck settings file
#
Expand Down
5 changes: 5 additions & 0 deletions thefuck/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ def get_close_matches(word, possibilities, n=None, cutoff=0.6):
return difflib_get_close_matches(word, possibilities, n, cutoff)


def include_path_in_search(path):
return not any(path.startswith(x) for x in settings.excluded_search_path_prefixes)


@memoize
def get_all_executables():
from thefuck.shells import shell
Expand All @@ -119,6 +123,7 @@ def _safe(fn, fallback):

bins = [exe.name.decode('utf8') if six.PY2 else exe.name
for path in os.environ.get('PATH', '').split(os.pathsep)
if include_path_in_search(path)
for exe in _safe(lambda: list(Path(path).iterdir()), [])
if not _safe(exe.is_dir, True)
and exe.name not in tf_entry_points]
Expand Down