diff --git a/README.md b/README.md index 68daf358f..94fff4610 100644 --- a/README.md +++ b/README.md @@ -405,6 +405,25 @@ export THEFUCK_PRIORITY='no_command=9999:apt_get=100' export THEFUCK_HISTORY_LIMIT='2000' ``` +## Third-party packages with rules + +If you want to make very specific rules or rules, that you don't want to make public, +but share with other people. +You can create a special package with name `thefuck_contrib_*` with following structure: + +``` +thefuck_contrib_foo + thefuck_contrib_foo + rules + __init__.py + *third-party rules* + __init__.py + *third-party-utils* + setup.py +``` + +And thefuck will find all rules from `rules` module. + ## Experimental instant mode By default The Fuck reruns a previous command and that takes time, diff --git a/thefuck/corrector.py b/thefuck/corrector.py index 42df2ea50..222a1359a 100644 --- a/thefuck/corrector.py +++ b/thefuck/corrector.py @@ -1,3 +1,4 @@ +import sys from .conf import settings from .types import Rule from .system import Path @@ -18,17 +19,33 @@ def get_loaded_rules(rules_paths): yield rule +def get_rules_import_paths(): + """Yields all rules import paths. + + :rtype: Iterable[Path] + + """ + # Bundled rules: + yield Path(__file__).parent.joinpath('rules') + # Rules defined by user: + yield settings.user_dir.joinpath('rules') + # Packages with third-party rules: + for path in sys.path: + for contrib_module in Path(path).glob('thefuck_contrib_*'): + contrib_rules = contrib_module.joinpath('rules') + if contrib_rules.is_dir(): + yield contrib_rules + + def get_rules(): """Returns all enabled rules. :rtype: [Rule] """ - bundled = Path(__file__).parent \ - .joinpath('rules') \ - .glob('*.py') - user = settings.user_dir.joinpath('rules').glob('*.py') - return sorted(get_loaded_rules(sorted(bundled) + sorted(user)), + paths = [rule_path for path in get_rules_import_paths() + for rule_path in sorted(path.glob('*.py'))] + return sorted(get_loaded_rules(paths), key=lambda rule: rule.priority)