Skip to content

Commit d0f15fa

Browse files
committed
feat: add inline summaries in write mode
1 parent 075d076 commit d0f15fa

File tree

2 files changed

+70
-10
lines changed

2 files changed

+70
-10
lines changed

codespell_lib/_codespell.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,30 @@ def apply_uri_ignore_words(
882882
break
883883
return check_matches
884884

885+
def _format_colored_output(
886+
filename: str,
887+
colors: TermColors,
888+
line_num: int,
889+
wrong: str,
890+
right: str,
891+
) -> tuple[str, str, str, str]:
892+
"""Format colored strings for output.
893+
894+
Args:
895+
filename: The filename being processed.
896+
colors: TermColors instance for color formatting.
897+
line_num: Line number (1-based) where the misspelling was found.
898+
wrong: The misspelled word.
899+
right: The correct word.
900+
901+
Returns:
902+
Tuple of (filename, line_num, wrong_word, right_word) with color codes.
903+
"""
904+
cfilename = f"{colors.FILE}{filename}{colors.DISABLE}"
905+
cline = f"{colors.FILE}{line_num}{colors.DISABLE}"
906+
cwrongword = f"{colors.WWORD}{wrong}{colors.DISABLE}"
907+
crightword = f"{colors.FWORD}{right}{colors.DISABLE}"
908+
return cfilename, cline, cwrongword, crightword
885909

886910
def parse_lines(
887911
fragment: tuple[bool, int, list[str]],
@@ -897,9 +921,10 @@ def parse_lines(
897921
uri_ignore_words: set[str],
898922
context: Optional[tuple[int, int]],
899923
options: argparse.Namespace,
900-
) -> tuple[int, bool]:
924+
) -> tuple[int, bool, list[tuple[int, str, str]]]:
901925
bad_count = 0
902926
changed = False
927+
changes_made: list[tuple[int, str, str]] = []
903928

904929
_, fragment_line_number, lines = fragment
905930

@@ -985,6 +1010,7 @@ def parse_lines(
9851010
changed = True
9861011
lines[i] = re.sub(rf"\b{word}\b", fixword, lines[i])
9871012
fixed_words.add(word)
1013+
changes_made.append((line_number + 1, word, fixword))
9881014
continue
9891015

9901016
# otherwise warning was explicitly set by interactive mode
@@ -995,10 +1021,9 @@ def parse_lines(
9951021
):
9961022
continue
9971023

998-
cfilename = f"{colors.FILE}{filename}{colors.DISABLE}"
999-
cline = f"{colors.FILE}{line_number + 1}{colors.DISABLE}"
1000-
cwrongword = f"{colors.WWORD}{word}{colors.DISABLE}"
1001-
crightword = f"{colors.FWORD}{fixword}{colors.DISABLE}"
1024+
cfilename, cline, cwrongword, crightword = _format_colored_output(
1025+
filename, colors, line_number + 1, word, fixword
1026+
)
10021027

10031028
reason = misspellings[lword].reason
10041029
if reason:
@@ -1028,7 +1053,7 @@ def parse_lines(
10281053
f"==> {crightword}{creason}"
10291054
)
10301055

1031-
return bad_count, changed
1056+
return bad_count, changed, changes_made
10321057

10331058

10341059
def parse_file(
@@ -1068,9 +1093,9 @@ def parse_file(
10681093
if summary and fix:
10691094
summary.update(lword)
10701095

1071-
cfilename = f"{colors.FILE}{filename}{colors.DISABLE}"
1072-
cwrongword = f"{colors.WWORD}{word}{colors.DISABLE}"
1073-
crightword = f"{colors.FWORD}{fixword}{colors.DISABLE}"
1096+
cfilename, _, cwrongword, crightword = _format_colored_output(
1097+
filename, colors, 0, word, fixword
1098+
)
10741099

10751100
reason = misspellings[lword].reason
10761101
if reason:
@@ -1109,12 +1134,13 @@ def parse_file(
11091134

11101135
# Parse lines.
11111136
changed = False
1137+
changes_made: list[tuple[int, str, str]] = []
11121138
for fragment in fragments:
11131139
ignore, _, _ = fragment
11141140
if ignore:
11151141
continue
11161142

1117-
bad_count_update, changed_update = parse_lines(
1143+
bad_count_update, changed_update, changes_made_update = parse_lines(
11181144
fragment,
11191145
filename,
11201146
colors,
@@ -1131,6 +1157,7 @@ def parse_file(
11311157
)
11321158
bad_count += bad_count_update
11331159
changed = changed or changed_update
1160+
changes_made.extend(changes_made_update)
11341161

11351162
# Write out lines, if changed.
11361163
if changed:
@@ -1145,6 +1172,14 @@ def parse_file(
11451172
f"{colors.FWORD}FIXED:{colors.DISABLE} {filename}",
11461173
file=sys.stderr,
11471174
)
1175+
for line_num, wrong, right in changes_made:
1176+
cfilename, cline, cwrongword, crightword = _format_colored_output(
1177+
filename, colors, line_num, wrong, right
1178+
)
1179+
print(
1180+
f" {cfilename}:{cline}: {cwrongword} ==> {crightword}",
1181+
file=sys.stderr,
1182+
)
11481183
with open(filename, "w", encoding=encoding, newline="") as f:
11491184
for _, _, lines in fragments:
11501185
f.writelines(lines)

codespell_lib/tests/test_basic.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,31 @@ def test_basic(
169169
assert cs.main(tmp_path) == 0
170170

171171

172+
def test_write_changes_lists_changes(
173+
tmp_path: Path,
174+
capsys: pytest.CaptureFixture[str],
175+
) -> None:
176+
"""Test that -w flag shows list of changes made to file."""
177+
178+
fname = tmp_path / "misspelled.txt"
179+
fname.write_text("This is abandonned\nAnd this is occured\nAlso teh typo\n")
180+
181+
result = cs.main("-w", fname, std=True)
182+
assert isinstance(result, tuple)
183+
code, _, stderr = result
184+
assert code == 0
185+
186+
assert "FIXED:" in stderr
187+
188+
# Check that changes are listed with format: filename:line: wrong ==> right
189+
assert "misspelled.txt:1: abandonned ==> abandoned" in stderr
190+
assert "misspelled.txt:2: occured ==> occurred" in stderr
191+
assert "misspelled.txt:3: teh ==> the" in stderr
192+
193+
corrected = fname.read_text()
194+
assert corrected == "This is abandoned\nAnd this is occurred\nAlso the typo\n"
195+
196+
172197
def test_default_word_parsing(
173198
tmp_path: Path,
174199
capsys: pytest.CaptureFixture[str],

0 commit comments

Comments
 (0)