From 7a8cab5fa74ea9415a69d0377f04927c5c9e8ecc Mon Sep 17 00:00:00 2001 From: herr kaste Date: Mon, 11 Jul 2022 17:09:43 +0200 Subject: [PATCH] Implement inline ignores Fixes #1212 Following @rob-miller's suggestion implement inline ignore hints. E.g. ``` crate // codespell:ignore crate abandonned abondon abilty # codespell:ignore abondon,abilty abandonned abondon abilty # codespell:ignore # to ignore all ``` --- codespell_lib/_codespell.py | 13 ++++++++++- codespell_lib/tests/test_basic.py | 36 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py index b0462ebd207..e8524849a18 100644 --- a/codespell_lib/_codespell.py +++ b/codespell_lib/_codespell.py @@ -36,6 +36,8 @@ "(\\b(?:https?|[ts]?ftp|file|git|smb)://[^\\s]+(?=$|\\s)|" "\\b[\\w.%+-]+@[\\w.-]+\\b)" ) +inline_ignore_regex = re.compile( + r"[^\w\s] codespell:ignore(?:(?:\s|$)([\w,]*))") encodings = ("utf-8", "iso-8859-1") USAGE = """ \t%prog [OPTIONS] [file1 file2 ... fileN] @@ -846,6 +848,15 @@ def parse_file( if line in exclude_lines: continue + extra_words_to_ignore = set() + match = inline_ignore_regex.search(line) + if match: + extra_words_to_ignore = set( + filter(None, (match.group(1) or "").split(",")) + ) + if not extra_words_to_ignore: + continue + fixed_words = set() asked_for = set() @@ -868,7 +879,7 @@ def parse_file( for word in check_words: lword = word.lower() - if lword in misspellings: + if lword in misspellings and lword not in extra_words_to_ignore: context_shown = False fix = misspellings[lword].fix fixword = fix_case(word, misspellings[lword].data) diff --git a/codespell_lib/tests/test_basic.py b/codespell_lib/tests/test_basic.py index cb000afd5f8..e206c323942 100644 --- a/codespell_lib/tests/test_basic.py +++ b/codespell_lib/tests/test_basic.py @@ -328,6 +328,42 @@ def test_ignore_word_list( assert cs.main("-Labandonned,someword", "-Labilty", d) == 1 +@pytest.mark.parametrize('content, expected_error_count', [ + # recommended form + ('abandonned abondon abilty # codespell:ignore abondon', 2), + ('abandonned abondon abilty // codespell:ignore abondon,abilty', 1), + ('abandonned abondon abilty /* codespell:ignore abandonned,abondon,abilty', 0), # noqa: E501 + # wildcard form + ('abandonned abondon abilty # codespell:ignore ', 0), + ('abandonned abondon abilty # codespell:ignore', 0), + ('abandonned abondon abilty # codespell:ignore\n', 0), + ('abandonned abondon abilty # codespell:ignore # noqa: E501\n', 0), + ('abandonned abondon abilty # codespell:ignore # noqa: E501\n', 0), + # ignore these for safety + ('abandonned abondon abilty # codespell:ignore# noqa: E501\n', 3), + ('abandonned abondon abilty # codespell:ignore, noqa: E501\n', 3), + ('abandonned abondon abilty # codespell:ignore/noqa: E501\n', 3), + ('abandonned abondon abilty # codespell:ignorenoqa: E501\n', 3), + ('abandonned abondon abilty #codespell:ignore\n', 3), + ('abandonned abondon abilty codespell:ignore\n', 3), + ('abandonned abondon abilty codespell:ignore\n', 3), + # showcase different comment markers + ("abandonned abondon abilty ' codespell:ignore\n", 0), + ('abandonned abondon abilty " codespell:ignore\n', 0), + ('abandonned abondon abilty ;; codespell:ignore\n', 0), +]) +def test_inline_ignores( + tmpdir: pytest.TempPathFactory, + capsys: pytest.CaptureFixture[str], + content: str, + expected_error_count: int +) -> None: + d = str(tmpdir) + with open(op.join(d, 'bad.txt'), 'w') as f: + f.write(content) + assert cs.main(d) == expected_error_count + + def test_custom_regex( tmpdir: pytest.TempPathFactory, capsys: pytest.CaptureFixture[str],