Repository: cleder/crepr
Issue category: Feature Request

Issue:
Sparse output of the changes.
For the `add` and `remove` commands:
Without the `--diff` or `--inline` switch, only the proposed changes should be presented on the screen, with the name of the class the `__repr__` is generated or removed from.


Hints:

Patch:
diff --git a/crepr/crepr.py b/crepr/crepr.py
index f6d3f98..c3a4109 100644
--- a/crepr/crepr.py
+++ b/crepr/crepr.py
@@ -12,6 +12,7 @@
 import inspect
 import pathlib
 import uuid
+from collections.abc import Callable
 from collections.abc import Iterable
 from collections.abc import Iterator
 from types import MappingProxyType
@@ -353,6 +354,32 @@ def get_modules(
         yield module, file_path
 
 
+def print_changes(changes: dict[int, Change], action: str) -> None:
+    """Print changes for each class."""
+    for change in changes.values():
+        typer.echo(f"__repr__ {action} for class: {change['class_name']}")
+        typer.echo("\n".join(change["lines"]))
+        typer.echo("")
+
+
+def apply_changes(
+    module: ModuleType,
+    changes: dict[int, Change],
+    file_path: pathlib.Path,
+    diff: bool,  # noqa: FBT001
+    change_func: Callable[[ModuleType, dict[int, Change]], list[str]],
+) -> None:
+    """Apply changes to the module and handle diff or file writing."""
+    src = change_func(module, changes)
+    if diff:
+        before = inspect.getsource(module).splitlines()
+        _diff = difflib.unified_diff(before, src, lineterm="")
+        typer.echo("\n".join(_diff))
+    else:
+        with file_path.open(mode="w", encoding="UTF-8") as f:
+            f.write("\n".join(src))
+
+
 @app.command()
 def add(
     files: Annotated[list[pathlib.Path], file_arg],
@@ -365,17 +392,17 @@ def add(
         changes = create_repr(module, kwarg_splat, ignore_existing)
         if not changes:
             continue
-        src = insert_changes(module, changes)
+
         if diff is None:
-            typer.echo("\n".join(src))
-        elif diff:
-            before = inspect.getsource(module).splitlines()
-            _diff = difflib.unified_diff(before, src, lineterm="")
-            typer.echo("\n".join(_diff))
-            continue
+            print_changes(changes, "generated")
         else:
-            with file_path.open(mode="w", encoding="UTF-8") as f:
-                f.write("\n".join(src))
+            apply_changes(
+                module,
+                changes,
+                file_path,
+                diff=diff,
+                change_func=insert_changes,
+            )
 
 
 @app.command()
@@ -388,17 +415,17 @@ def remove(
         changes = remove_repr(module)
         if not changes:
             continue
-        src = remove_changes(module, changes)
+
         if diff is None:
-            typer.echo("\n".join(src))
-        elif diff:
-            before = inspect.getsource(module).splitlines()
-            _diff = difflib.unified_diff(before, src, lineterm="")
-            typer.echo("\n".join(_diff))
-            continue
+            print_changes(changes, "removed")
         else:
-            with file_path.open(mode="w", encoding="UTF-8") as f:
-                f.write("\n".join(src))
+            apply_changes(
+                module,
+                changes,
+                file_path,
+                diff=diff,
+                change_func=remove_changes,
+            )
 
 
 @app.command()


Test patch:
diff --git a/tests/run_test.py b/tests/run_test.py
index f1582b5..307aced 100644
--- a/tests/run_test.py
+++ b/tests/run_test.py
@@ -188,8 +188,9 @@ def test_show() -> None:
     result = runner.invoke(crepr.app, ["add", "tests/classes/kw_only_test.py"])
 
     assert result.exit_code == 0
+    assert "__repr__ generated for class: KwOnly" in result.stdout
     assert "Create a string (c)representation for KwOnly" in result.stdout
-    assert len(result.stdout.splitlines()) == 20
+    assert len(result.stdout.splitlines()) == 10
 
 
 def test_show_no_init() -> None:
@@ -275,8 +276,8 @@ def test_show_remove() -> None:
     result = runner.invoke(crepr.app, ["remove", "tests/remove/kw_only_test.py"])
 
     assert result.exit_code == 0
-    assert "__repr__" not in result.stdout
-    assert len(result.stdout.splitlines()) == 13
+    assert "__repr__ removed for class: KwOnly" in result.stdout
+    assert len(result.stdout.splitlines()) == 10
 
 
 def test_show_remove_no_repr() -> None:
@@ -366,3 +367,35 @@ def test_add_ignore_existing_true() -> None:
         assert "Create a string (c)representation for ExistingRepr" not in content
 
     pathlib.Path.unlink(temp_file_path)
+
+
+def test_add_show_only_changes() -> None:
+    """Test that only proposed changes are shown without --diff or --inline."""
+    result = runner.invoke(crepr.app, ["add", "tests/classes/kw_only_test.py"])
+
+    assert result.exit_code == 0
+    assert "__repr__ generated for class: KwOnly" in result.stdout
+    assert "def __repr__(self) -> str:" in result.stdout
+    assert (
+        "return (f'{self.__class__.__module__}.{self.__class__.__name__}("
+        in result.stdout
+    )
+    assert "f'name={self.name!r}, '" in result.stdout
+    assert "f'age={self.age!r}, '" in result.stdout
+    assert "')')" in result.stdout
+
+
+def test_remove_show_only_changes() -> None:
+    """Test that only proposed removals are shown without --diff or --inline."""
+    result = runner.invoke(crepr.app, ["remove", "tests/remove/kw_only_test.py"])
+
+    assert result.exit_code == 0
+    assert "__repr__ removed for class: KwOnly" in result.stdout
+    assert "def __repr__(self: Self) -> str:" in result.stdout
+    assert '"""Create a string (c)representation for KwOnly."""' in result.stdout
+    assert "return (" in result.stdout
+    assert 'f"{self.__class__.__module__}.{self.__class__.__name__}("' in result.stdout
+    assert 'f"name={self.name!r}, "' in result.stdout
+    assert 'f"age={self.age!r}, "' in result.stdout
+    assert '")"\n' in result.stdout
+    assert ")" in result.stdout