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
16 changes: 16 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,22 @@ Words should be separated by a comma.
def wrod(wrods) # codespell:ignore
pass

3. ignore the following line (useful when a formatter pushes comments to a new line):

.. code-block:: python

# codespell:ignore-next-line wrod
def wrod():
pass

Use the bare form to ignore every misspelling on the next line:

.. code-block:: python

# codespell:ignore-next-line
def wrod(wrods):
pass

Using a config file
-------------------

Expand Down
31 changes: 29 additions & 2 deletions codespell_lib/_codespell.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@
r"(\b(?:https?|[ts]?ftp|file|git|smb)://[^\s]+(?=$|\s)|\b[\w.%+-]+@[\w.-]+\b)"
)
codespell_ignore_tag = "codespell:ignore"
codespell_ignore_next_line_tag = "codespell:ignore-next-line"
inline_ignore_regex = re.compile(
rf"[^\w\s]\s*{codespell_ignore_tag}\b(\s+(?P<words>[\w,]*))?"
rf"[^\w\s]\s*{codespell_ignore_tag}(?!-)\b(\s+(?P<words>[\w,]*))?"
)
ignore_next_line_regex = re.compile(
rf"[^\w\s]\s*{codespell_ignore_next_line_tag}\b(\s+(?P<words>[\w,]*))?"
)
USAGE = """
\t%prog [OPTIONS] [file1 file2 ... fileN]
Expand Down Expand Up @@ -955,13 +959,28 @@ def parse_lines(

_, fragment_line_number, lines = fragment

next_line_ignore_words: Optional[set[str]] = None

for i, line in enumerate(lines):
line = line.rstrip()
# Apply any ignore-next-line directive carried from the previous line.
pending_next_line_ignore = next_line_ignore_words
next_line_ignore_words = None

directive_words: set[str] = set()
if codespell_ignore_next_line_tag in line:
nl_match = ignore_next_line_regex.search(line)
if nl_match:
directive_words = set(
filter(None, (nl_match.group("words") or "").split(","))
)
next_line_ignore_words = directive_words

if not line or line in exclude_lines:
continue
line_number = fragment_line_number + i

extra_words_to_ignore = set()
extra_words_to_ignore: set[str] = set()
match = (
inline_ignore_regex.search(line) if codespell_ignore_tag in line else None
)
Expand All @@ -972,6 +991,14 @@ def parse_lines(
if not extra_words_to_ignore:
continue

# Words named in an ignore-next-line directive are ignored on its own line too.
extra_words_to_ignore |= directive_words

if pending_next_line_ignore is not None:
if not pending_next_line_ignore:
continue
extra_words_to_ignore |= pending_next_line_ignore

fixed_words = set()
asked_for = set()

Expand Down
44 changes: 44 additions & 0 deletions codespell_lib/tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,50 @@ def test_inline_ignores(
assert cs.main(d) == expected_error_count


@pytest.mark.parametrize(
("content", "expected_error_count"),
[
# wildcard form: ignore all misspellings on the next line
("# codespell:ignore-next-line\nabandonned abondon abilty\n", 0),
("// codespell:ignore-next-line\nabandonned abondon abilty\n", 0),
# specific word form: ignore only listed words on the next line
(
"# codespell:ignore-next-line abondon\nabandonned abondon abilty\n",
2,
),
(
"# codespell:ignore-next-line abondon,abilty\nabandonned abondon abilty\n",
1,
),
# the directive does not affect the line it is on or subsequent lines
(
"abandonned # codespell:ignore-next-line\nabondon\nabilty\n",
2,
),
# listing an unused ignore word still triggers a skip
(
"# codespell:ignore-next-line nomenklatur\nabandonned abondon abilty\n",
3,
),
# invalid directives are not honored
("# codespell:ignore-next-lin\nabandonned\n", 1),
("codespell:ignore-next-line\nabandonned\n", 1),
# directive followed by a blank line still consumes the directive
("# codespell:ignore-next-line\n\nabandonned\n", 1),
],
)
def test_ignore_next_line(
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", encoding="utf-8") as f:
f.write(content)
assert cs.main(d) == expected_error_count


def test_custom_regex(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
Expand Down
Loading