From d0f15fa829961da3280193e419e936e5672cedce Mon Sep 17 00:00:00 2001 From: Peter Baumann Date: Wed, 26 Nov 2025 20:44:00 +0100 Subject: [PATCH 1/2] feat: add inline summaries in write mode --- codespell_lib/_codespell.py | 55 +++++++++++++++++++++++++------ codespell_lib/tests/test_basic.py | 25 ++++++++++++++ 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py index 94ab65d068..998bae859f 100644 --- a/codespell_lib/_codespell.py +++ b/codespell_lib/_codespell.py @@ -882,6 +882,30 @@ def apply_uri_ignore_words( break return check_matches +def _format_colored_output( + filename: str, + colors: TermColors, + line_num: int, + wrong: str, + right: str, +) -> tuple[str, str, str, str]: + """Format colored strings for output. + + Args: + filename: The filename being processed. + colors: TermColors instance for color formatting. + line_num: Line number (1-based) where the misspelling was found. + wrong: The misspelled word. + right: The correct word. + + Returns: + Tuple of (filename, line_num, wrong_word, right_word) with color codes. + """ + cfilename = f"{colors.FILE}{filename}{colors.DISABLE}" + cline = f"{colors.FILE}{line_num}{colors.DISABLE}" + cwrongword = f"{colors.WWORD}{wrong}{colors.DISABLE}" + crightword = f"{colors.FWORD}{right}{colors.DISABLE}" + return cfilename, cline, cwrongword, crightword def parse_lines( fragment: tuple[bool, int, list[str]], @@ -897,9 +921,10 @@ def parse_lines( uri_ignore_words: set[str], context: Optional[tuple[int, int]], options: argparse.Namespace, -) -> tuple[int, bool]: +) -> tuple[int, bool, list[tuple[int, str, str]]]: bad_count = 0 changed = False + changes_made: list[tuple[int, str, str]] = [] _, fragment_line_number, lines = fragment @@ -985,6 +1010,7 @@ def parse_lines( changed = True lines[i] = re.sub(rf"\b{word}\b", fixword, lines[i]) fixed_words.add(word) + changes_made.append((line_number + 1, word, fixword)) continue # otherwise warning was explicitly set by interactive mode @@ -995,10 +1021,9 @@ def parse_lines( ): continue - cfilename = f"{colors.FILE}{filename}{colors.DISABLE}" - cline = f"{colors.FILE}{line_number + 1}{colors.DISABLE}" - cwrongword = f"{colors.WWORD}{word}{colors.DISABLE}" - crightword = f"{colors.FWORD}{fixword}{colors.DISABLE}" + cfilename, cline, cwrongword, crightword = _format_colored_output( + filename, colors, line_number + 1, word, fixword + ) reason = misspellings[lword].reason if reason: @@ -1028,7 +1053,7 @@ def parse_lines( f"==> {crightword}{creason}" ) - return bad_count, changed + return bad_count, changed, changes_made def parse_file( @@ -1068,9 +1093,9 @@ def parse_file( if summary and fix: summary.update(lword) - cfilename = f"{colors.FILE}{filename}{colors.DISABLE}" - cwrongword = f"{colors.WWORD}{word}{colors.DISABLE}" - crightword = f"{colors.FWORD}{fixword}{colors.DISABLE}" + cfilename, _, cwrongword, crightword = _format_colored_output( + filename, colors, 0, word, fixword + ) reason = misspellings[lword].reason if reason: @@ -1109,12 +1134,13 @@ def parse_file( # Parse lines. changed = False + changes_made: list[tuple[int, str, str]] = [] for fragment in fragments: ignore, _, _ = fragment if ignore: continue - bad_count_update, changed_update = parse_lines( + bad_count_update, changed_update, changes_made_update = parse_lines( fragment, filename, colors, @@ -1131,6 +1157,7 @@ def parse_file( ) bad_count += bad_count_update changed = changed or changed_update + changes_made.extend(changes_made_update) # Write out lines, if changed. if changed: @@ -1145,6 +1172,14 @@ def parse_file( f"{colors.FWORD}FIXED:{colors.DISABLE} {filename}", file=sys.stderr, ) + for line_num, wrong, right in changes_made: + cfilename, cline, cwrongword, crightword = _format_colored_output( + filename, colors, line_num, wrong, right + ) + print( + f" {cfilename}:{cline}: {cwrongword} ==> {crightword}", + file=sys.stderr, + ) with open(filename, "w", encoding=encoding, newline="") as f: for _, _, lines in fragments: f.writelines(lines) diff --git a/codespell_lib/tests/test_basic.py b/codespell_lib/tests/test_basic.py index 56a58942af..c8b078a738 100644 --- a/codespell_lib/tests/test_basic.py +++ b/codespell_lib/tests/test_basic.py @@ -169,6 +169,31 @@ def test_basic( assert cs.main(tmp_path) == 0 +def test_write_changes_lists_changes( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + """Test that -w flag shows list of changes made to file.""" + + fname = tmp_path / "misspelled.txt" + fname.write_text("This is abandonned\nAnd this is occured\nAlso teh typo\n") + + result = cs.main("-w", fname, std=True) + assert isinstance(result, tuple) + code, _, stderr = result + assert code == 0 + + assert "FIXED:" in stderr + + # Check that changes are listed with format: filename:line: wrong ==> right + assert "misspelled.txt:1: abandonned ==> abandoned" in stderr + assert "misspelled.txt:2: occured ==> occurred" in stderr + assert "misspelled.txt:3: teh ==> the" in stderr + + corrected = fname.read_text() + assert corrected == "This is abandoned\nAnd this is occurred\nAlso the typo\n" + + def test_default_word_parsing( tmp_path: Path, capsys: pytest.CaptureFixture[str], From 3ff86edc12e495e6ee6b0a067003334b1fed8a9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 20:23:40 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- codespell_lib/_codespell.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py index 998bae859f..341795b331 100644 --- a/codespell_lib/_codespell.py +++ b/codespell_lib/_codespell.py @@ -882,6 +882,7 @@ def apply_uri_ignore_words( break return check_matches + def _format_colored_output( filename: str, colors: TermColors, @@ -907,6 +908,7 @@ def _format_colored_output( crightword = f"{colors.FWORD}{right}{colors.DISABLE}" return cfilename, cline, cwrongword, crightword + def parse_lines( fragment: tuple[bool, int, list[str]], filename: str,