diff --git a/README.md b/README.md index 386cb683..1a7efe1d 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,7 @@ following rules are enabled by default: * `docker_not_command` – fixes wrong docker commands like `docker tags`; * `docker_image_being_used_by_container` ‐ removes the container that is using the image before removing the image; * `dry` – fixes repetitions like `git git push`; +* `edit_filename` – fixes e.g. `vim file` to `vim file.ext` * `fab_command_not_found` – fix misspelled fabric commands; * `fix_alt_space` – replaces Alt+Space with Space character; * `fix_file` – opens a file with an error in your `$EDITOR`; diff --git a/tests/rules/test_edit_filename.py b/tests/rules/test_edit_filename.py new file mode 100644 index 00000000..7e9856d5 --- /dev/null +++ b/tests/rules/test_edit_filename.py @@ -0,0 +1,83 @@ +import os + +import pytest + +from thefuck.rules.edit_filename import EDITORS, get_new_command, match +from thefuck.types import Command + +parametrize_editor = pytest.mark.parametrize("editor", EDITORS) + + +def _edit_command(editor, path): + return Command(editor + " " + str(path), "") + + +@parametrize_editor +def test_correct_file_no_match(tmp_path, editor): + f = tmp_path / "module.py" + f.touch() + assert not match(_edit_command(editor, f)) + + +@parametrize_editor +def test_similar_correct_file(tmp_path, editor): + f = tmp_path / "module.py" + f2 = tmp_path / "module.html" + f.touch() + f2.touch() + assert not match(_edit_command(editor, f)) + + +@parametrize_editor +def test_match_for_nonexisting(tmp_path, editor): + f = tmp_path / "module.py" + edited = tmp_path / "module" + f.touch() + assert match(_edit_command(editor, edited)) + + +@parametrize_editor +def test_match_for_nonexisting_editor_flag(tmp_path, editor): + f = tmp_path / "module.py" + edited = tmp_path / "module" + f.touch() + assert match(_edit_command(editor + " --servername server", edited)) + + +def test_match_for_nonexisting_bad_editor(tmp_path): + f = tmp_path / "module.py" + edited = tmp_path / "module" + f.touch() + assert not match(_edit_command("cat", edited)) + + +@parametrize_editor +def test_get_new_command(tmp_path, editor): + f = tmp_path / "module.py" + edited = tmp_path / "module" + f.touch() + assert get_new_command(_edit_command(editor, edited)) == [ + _edit_command(editor, f).script + ] + + +@parametrize_editor +def test_get_new_command_multiple(tmp_path, editor): + f = tmp_path / "module.py" + f2 = tmp_path / "module.html" + edited = tmp_path / "module" + f.touch() + f2.touch() + assert sorted(get_new_command(_edit_command(editor, edited))) == sorted( + [_edit_command(editor, f).script, _edit_command(editor, f2).script] + ) + + +@parametrize_editor +def test_get_new_command_for_nonexisting_editor_flag(tmp_path, editor): + f = tmp_path / "module.py" + edited = tmp_path / "module" + f.touch() + assert get_new_command(_edit_command(editor + " --servername server", edited)) == [ + _edit_command(editor + " --servername server", f).script + ] diff --git a/thefuck/rules/edit_filename.py b/thefuck/rules/edit_filename.py new file mode 100644 index 00000000..a7339278 --- /dev/null +++ b/thefuck/rules/edit_filename.py @@ -0,0 +1,39 @@ +import re +from pathlib import Path + +from thefuck.shells import shell + +EDITORS = ["vim", "emacs", "nano", "vi", "joe", "neovim", "ne", "mc"] + + +def _get_fixed_edit_files(script): + if not re.match("({})".format("|".join(EDITORS)), script): + return + + parts = shell.split_command(script) + if len(parts) < 2: + return + + potential_filepath = Path(parts[-1]) + if potential_filepath.is_file(): + return + + for path in potential_filepath.parent.iterdir(): + if path.stem == potential_filepath.name: + yield str(path) + + +def match(command): + for _ in _get_fixed_edit_files(command.script): + return True + return False + + +def get_new_command(command): + command_parts = shell.split_command(command.script) + possible_files = _get_fixed_edit_files(command.script) + possible_commands = [] + for filepath in possible_files: + command_parts[-1] = filepath + possible_commands.append(" ".join(command_parts)) + return possible_commands