Add source files: models and parser
This commit is contained in:
108
src/gdiffer/models.py
Normal file
108
src/gdiffer/models.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""Data models for git diff parsing and analysis."""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class DiffHunk:
|
||||
"""Represents a single hunk (chunk) of changes in a diff."""
|
||||
old_start: int
|
||||
old_lines: int
|
||||
new_start: int
|
||||
new_lines: int
|
||||
old_lines_content: list[str] = field(default_factory=list)
|
||||
new_lines_content: list[str] = field(default_factory=list)
|
||||
header: str = ""
|
||||
|
||||
def get_added_lines(self) -> list[tuple[int, str]]:
|
||||
result = []
|
||||
for i, line in enumerate(self.new_lines_content):
|
||||
if line.startswith('+') and not line.startswith('+++'):
|
||||
result.append((self.new_start + i, line[1:]))
|
||||
return result
|
||||
|
||||
def get_removed_lines(self) -> list[tuple[int, str]]:
|
||||
result = []
|
||||
for i, line in enumerate(self.old_lines_content):
|
||||
if line.startswith('-') and not line.startswith('---'):
|
||||
result.append((self.old_start + i, line[1:]))
|
||||
return result
|
||||
|
||||
def get_modified_lines(self) -> list[tuple[int, str, str]]:
|
||||
result = []
|
||||
added = self.get_added_lines()
|
||||
removed = self.get_removed_lines()
|
||||
for i, (old_line, old_content) in enumerate(removed):
|
||||
if i < len(added):
|
||||
new_line, new_content = added[i]
|
||||
if old_content != new_content:
|
||||
result.append((old_line, old_content, new_content))
|
||||
return result
|
||||
|
||||
|
||||
@dataclass
|
||||
class DiffFile:
|
||||
"""Represents a file in the diff with its changes."""
|
||||
old_path: Optional[str]
|
||||
new_path: Optional[str]
|
||||
new_file_mode: Optional[str] = None
|
||||
deleted_file_mode: Optional[str] = None
|
||||
similarity_index: Optional[str] = None
|
||||
rename_from: Optional[str] = None
|
||||
rename_to: Optional[str] = None
|
||||
hunks: list[DiffHunk] = field(default_factory=list)
|
||||
change_type: str = "modify"
|
||||
|
||||
@property
|
||||
def filename(self) -> str:
|
||||
if self.new_path:
|
||||
return self.new_path
|
||||
return self.old_path or ""
|
||||
|
||||
@property
|
||||
def is_new(self) -> bool:
|
||||
return self.new_file_mode is not None or self.old_path in [None, "/dev/null"]
|
||||
|
||||
@property
|
||||
def is_deleted(self) -> bool:
|
||||
return self.deleted_file_mode is not None
|
||||
|
||||
@property
|
||||
def is_rename(self) -> bool:
|
||||
return self.rename_from is not None
|
||||
|
||||
@property
|
||||
def extension(self) -> str:
|
||||
filename = self.filename
|
||||
if '.' in filename:
|
||||
return filename.rsplit('.', 1)[-1].lower()
|
||||
return ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class CodeChange:
|
||||
"""Represents a code change with context."""
|
||||
file: DiffFile
|
||||
hunk: Optional[DiffHunk]
|
||||
old_code: str
|
||||
new_code: str
|
||||
language: str = "unknown"
|
||||
summary: str = ""
|
||||
issues: list[dict] = field(default_factory=list)
|
||||
suggestions: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DiffAnalysis:
|
||||
"""Complete analysis result for a diff."""
|
||||
files: list[DiffFile] = field(default_factory=list)
|
||||
total_files: int = 0
|
||||
files_added: int = 0
|
||||
files_deleted: int = 0
|
||||
files_modified: int = 0
|
||||
files_renamed: int = 0
|
||||
total_changes: int = 0
|
||||
language_breakdown: dict[str, int] = field(default_factory=dict)
|
||||
all_issues: list[dict] = field(default_factory=list)
|
||||
all_suggestions: list[str] = field(default_factory=list)
|
||||
Reference in New Issue
Block a user