Vortttxx / multi_file_editor.py
RDS777's picture
Upload 7 files
09842c9 verified
Raw
History Blame Contribute Delete
5.32 kB
#!/usr/bin/env python3
"""
multi_file_editor.py — ATYPIK v24.0
Edition multi-fichiers avec patches unidiff et edits semantiques.
"""
import re
import ast
from pathlib import Path
from typing import Tuple, List, Dict
from dataclasses import dataclass
@dataclass
class EditResult:
success: bool
file: str
error: str = ""
original: str = ""
modified: str = ""
class MultiFileEditor:
def __init__(self, repo_path: Path):
self.repo_path = Path(repo_path)
self.edits: List[EditResult] = []
def read_file(self, filepath: str) -> str:
fpath = self.repo_path / filepath
if not fpath.exists():
return ""
return fpath.read_text(encoding="utf-8")
def write_file(self, filepath: str, content: str) -> bool:
try:
fpath = self.repo_path / filepath
fpath.parent.mkdir(parents=True, exist_ok=True)
fpath.write_text(content, encoding="utf-8")
return True
except Exception:
return False
def apply_edit(self, filepath: str, old_string: str, new_string: str) -> EditResult:
content = self.read_file(filepath)
if old_string not in content:
normalized_old = re.sub(r'\s+', ' ', old_string).strip()
normalized_content = re.sub(r'\s+', ' ', content)
if normalized_old not in normalized_content:
return EditResult(False, filepath, f"old_string not found in {filepath}")
new_content = content.replace(old_string, new_string, 1)
if filepath.endswith(".py"):
try:
ast.parse(new_content)
except SyntaxError as e:
return EditResult(False, filepath, f"Syntax error: {e}")
if self.write_file(filepath, new_content):
result = EditResult(True, filepath, "", content, new_content)
self.edits.append(result)
return result
return EditResult(False, filepath, "Write failed")
def apply_patch_unidiff(self, patch_content: str) -> List[EditResult]:
results = []
lines = patch_content.split("\n")
current_file = None
file_lines = []
hunk_old_start = 0
i = 0
while i < len(lines):
line = lines[i]
if line.startswith("diff --git"):
if current_file and file_lines:
result = self._apply_hunk(current_file, file_lines, hunk_old_start)
results.append(result)
match = re.search(r'b/(.+)$', line)
current_file = match.group(1) if match else None
file_lines = []
i += 1
continue
if line.startswith("@@"):
match = re.match(r'@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@', line)
if match:
hunk_old_start = int(match.group(1))
i += 1
continue
if current_file and (line.startswith('+') or line.startswith('-') or line.startswith(' ') or line == ''):
file_lines.append(line)
i += 1
if current_file and file_lines:
result = self._apply_hunk(current_file, file_lines, hunk_old_start)
results.append(result)
return results
def _apply_hunk(self, filepath: str, hunk_lines: List[str], old_start: int) -> EditResult:
content = self.read_file(filepath)
if not content:
return EditResult(False, filepath, "File not found")
lines = content.split("\n")
new_lines = []
hunk_idx = 0
for i, line in enumerate(lines, 1):
if i < old_start:
new_lines.append(line)
continue
if hunk_idx >= len(hunk_lines):
new_lines.append(line)
continue
hunk_line = hunk_lines[hunk_idx]
if hunk_line.startswith('+'):
new_lines.append(hunk_line[1:])
hunk_idx += 1
elif hunk_line.startswith('-'):
if line.strip() == hunk_line[1:].strip():
hunk_idx += 1
else:
new_lines.append(line)
elif hunk_line.startswith(' '):
new_lines.append(hunk_line[1:])
hunk_idx += 1
else:
new_lines.append(line)
while hunk_idx < len(hunk_lines):
hl = hunk_lines[hunk_idx]
if hl.startswith('+'):
new_lines.append(hl[1:])
hunk_idx += 1
new_content = "\n".join(new_lines)
if filepath.endswith(".py"):
try:
ast.parse(new_content)
except SyntaxError as e:
return EditResult(False, filepath, f"Syntax error: {e}")
if self.write_file(filepath, new_content):
return EditResult(True, filepath)
return EditResult(False, filepath, "Write failed")
def get_summary(self) -> Dict:
return {
"total": len(self.edits),
"successful": sum(1 for e in self.edits if e.success),
"failed": sum(1 for e in self.edits if not e.success),
"files_modified": list(set(e.file for e in self.edits if e.success))
}