"""Diff generation for i18n fixes.""" import difflib from dataclasses import dataclass, field from pathlib import Path from typing import List, Optional from rich.console import Console from rich.syntax import Syntax @dataclass class DiffChange: """Represents a single change in a diff.""" file_path: Path original_line: int original_text: str new_text: str new_line: Optional[int] = None @dataclass class DiffResult: """Result of a diff operation.""" changes: List[DiffChange] = field(default_factory=list) files_modified: int = 0 total_changes: int = 0 class DiffGenerator: """Generate diffs for i18n fixes.""" def __init__(self, color: bool = True) -> None: self.color = color self.console = Console() def generate_diff( self, file_path: Path, old_content: str, new_content: str, ) -> str: """Generate a unified diff between old and new content.""" diff = difflib.unified_diff( old_content.splitlines(keepends=True), new_content.splitlines(keepends=True), fromfile=str(file_path), tofile=str(file_path), n=3, ) return "".join(diff) def preview_diff(self, diff: str) -> None: """Preview a diff using Rich.""" if self.color: self.console.print(Syntax(diff, "diff", line_numbers=True)) else: self.console.print(diff) def apply_diff( self, file_path: Path, diff: str, backup: bool = True, ) -> bool: """Apply a diff to a file.""" try: if backup: backup_path = file_path.with_suffix(".bak") backup_path.write_text(file_path.read_text(encoding="utf-8"), encoding="utf-8") lines = file_path.read_text(encoding="utf-8").splitlines(keepends=True) diff_lines = diff.splitlines(keepends=True) patched_lines = self._apply_diff_lines(lines, diff_lines) new_content = "".join(patched_lines) file_path.write_text(new_content, encoding="utf-8") return True except (OSError, UnicodeDecodeError) as e: print(f"Error applying diff: {e}") return False def _apply_diff_lines( self, lines: List[str], diff_lines: List[str], ) -> List[str]: """Apply diff lines to document.""" result = [] i = 0 j = 0 while j < len(diff_lines): diff_line = diff_lines[j] if diff_line.startswith("---") or diff_line.startswith("+++"): j += 1 continue if diff_line.startswith("@@"): j += 1 continue if diff_line.startswith("-"): original_line = diff_line[1:] added_content = "" if "+" in original_line: parts = original_line.split("+", 1) original_line = parts[0] added_content = parts[1] if len(parts) > 1 else "" while i < len(lines) and lines[i].rstrip("\n") != original_line.rstrip("\n"): result.append(lines[i]) i += 1 if i < len(lines): i += 1 if added_content: result.append(added_content) j += 1 continue if diff_line.startswith("+"): result.append(diff_line[1:]) j += 1 continue if diff_line.startswith(" "): result.append(lines[i] if i < len(lines) else diff_line[1:]) i += 1 j += 1 continue j += 1 while i < len(lines): result.append(lines[i]) i += 1 return result def create_replacement( self, original_text: str, replacement: str, i18n_function: str = "t", ) -> str: """Create a replacement string with i18n function call.""" escaped = original_text.replace("\\", "\\\\").replace('"', '\\"') return f'{i18n_function}("{escaped}")' def preview_change( self, file_path: Path, original_text: str, replacement: str, line_number: int, ) -> None: """Preview a single change.""" diff = self.generate_diff( file_path, original_text, original_text.replace(original_text, replacement, 1), ) self.preview_diff(diff)