Files
local-ai-commit-reviewer/src/formatters/formatters.py
7000pctAUTO 58a45aa7bf
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled
fix: resolve CI/CD type errors and workflow issues
2026-02-05 06:48:08 +00:00

142 lines
4.9 KiB
Python

from abc import ABC, abstractmethod
from rich.console import Console
from rich.panel import Panel
from rich.style import Style
from rich.table import Table
from rich.text import Text
from ..core import Issue, IssueCategory, IssueSeverity, ReviewResult
class BaseFormatter(ABC):
@abstractmethod
def format(self, result: ReviewResult) -> str:
pass
class TerminalFormatter(BaseFormatter):
def __init__(self, theme: str = "auto", show_line_numbers: bool = True):
self.console = Console()
self.show_line_numbers = show_line_numbers
self.use_colors = theme != "dark" if theme == "auto" else theme == "dark"
def _get_severity_style(self, severity: IssueSeverity) -> Style:
styles = {
IssueSeverity.CRITICAL: Style(color="red", bold=True),
IssueSeverity.WARNING: Style(color="yellow"),
IssueSeverity.INFO: Style(color="blue"),
}
return styles.get(severity, Style())
def _get_category_icon(self, category: IssueCategory) -> str:
icons = {
IssueCategory.BUG: "[BUG]",
IssueCategory.SECURITY: "[SECURITY]",
IssueCategory.STYLE: "[STYLE]",
IssueCategory.PERFORMANCE: "[PERF]",
IssueCategory.DOCUMENTATION: "[DOC]",
}
return icons.get(category, "")
def _format_issue(self, issue: Issue) -> Text:
text = Text()
text.append(f"{issue.file}:{issue.line} ", style="dim")
text.append(f"[{issue.severity.value.upper()}] ", self._get_severity_style(issue.severity))
text.append(f"{self._get_category_icon(issue.category)} ")
text.append(issue.message)
if issue.suggestion:
text.append("\n Suggestion: ", style="dim")
text.append(issue.suggestion)
return text
def format(self, result: ReviewResult) -> str:
output: list[Panel | Table] = []
if result.error:
output.append(Panel(
f"[red]Error: {result.error}[/red]",
title="Review Failed",
expand=False
))
return "\n".join(str(p) for p in output)
summary = result.summary
summary_panel = Panel(
f"[bold]Files Reviewed:[/bold] {summary.files_reviewed}\n"
f"[bold]Lines Changed:[/bold] {summary.lines_changed}\n\n"
f"[red]Critical:[/red] {summary.critical_count} "
f"[yellow]Warnings:[/yellow] {summary.warning_count} "
f"[blue]Info:[/blue] {summary.info_count}\n\n"
f"[bold]Assessment:[/bold] {summary.overall_assessment}",
title="Review Summary",
expand=False
)
output.append(summary_panel)
if result.issues:
issues_table = Table(title="Issues Found", show_header=True)
issues_table.add_column("File", style="dim")
issues_table.add_column("Line", justify="right", style="dim")
issues_table.add_column("Severity", width=10)
issues_table.add_column("Category", width=12)
issues_table.add_column("Message")
for issue in result.issues:
issues_table.add_row(
issue.file,
str(issue.line),
f"[{issue.severity.value.upper()}]",
f"[{issue.category.value.upper()}]",
issue.message,
style=self._get_severity_style(issue.severity)
)
output.append(issues_table)
suggestions_panel = Panel(
"\n".join(
f"[bold]{issue.file}:{issue.line}[/bold]\n"
f" {issue.message}\n"
+ (f" [green]→ {issue.suggestion}[/green]\n" if issue.suggestion else "")
for issue in result.issues if issue.suggestion
),
title="Suggestions",
expand=False
)
output.append(suggestions_panel)
model_info = Panel(
f"[bold]Model:[/bold] {result.model_used}\n"
f"[bold]Tokens Used:[/bold] {result.tokens_used}\n"
f"[bold]Mode:[/bold] {result.review_mode}",
title="Review Info",
expand=False
)
output.append(model_info)
return "\n".join(str(o) for o in output)
class JSONFormatter(BaseFormatter):
def format(self, result: ReviewResult) -> str:
return result.to_json()
class MarkdownFormatter(BaseFormatter):
def format(self, result: ReviewResult) -> str:
return result.to_markdown()
def get_formatter(format_type: str = "terminal", **kwargs) -> BaseFormatter:
formatters: dict[str, type[BaseFormatter]] = {
"terminal": TerminalFormatter,
"json": JSONFormatter,
"markdown": MarkdownFormatter,
}
formatter_class = formatters.get(format_type, TerminalFormatter)
return formatter_class(**kwargs) # type: ignore[arg-type]