Initial upload: Local Commit Message Generator v0.1.0
This commit is contained in:
148
app/local_commit_message_generator/src/templates.py
Normal file
148
app/local_commit_message_generator/src/templates.py
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
"""Template management for commit message formatting."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TemplateVariable:
|
||||||
|
"""Represents a template variable with its value."""
|
||||||
|
name: str
|
||||||
|
value: str
|
||||||
|
display_name: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateManager:
|
||||||
|
"""Manages commit message templates."""
|
||||||
|
|
||||||
|
DEFAULT_TEMPLATES = {
|
||||||
|
"simple": "{type}{scope}: {description}",
|
||||||
|
"detailed": "{type}({scope}): {description}\n\n{body}",
|
||||||
|
"conventional": "{type}{scope}: {description}\n\n{files}",
|
||||||
|
"verbose": "{type}{scope}: {description}\n\n{files}\n\n{body}",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, template: Optional[str] = None):
|
||||||
|
"""Initialize template manager.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template: Template string to use. If not provided, uses simple template.
|
||||||
|
"""
|
||||||
|
self.template = template or self.DEFAULT_TEMPLATES["simple"]
|
||||||
|
|
||||||
|
def render(
|
||||||
|
self,
|
||||||
|
type: str,
|
||||||
|
scope: str,
|
||||||
|
description: str,
|
||||||
|
body: str = "",
|
||||||
|
files: Optional[List[str]] = None,
|
||||||
|
**kwargs: Any
|
||||||
|
) -> str:
|
||||||
|
"""Render a commit message from template.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
type: Commit type (feat, fix, docs, etc.)
|
||||||
|
scope: Commit scope.
|
||||||
|
description: Commit description.
|
||||||
|
body: Optional extended body text.
|
||||||
|
files: Optional list of changed files.
|
||||||
|
**kwargs: Additional template variables.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Rendered commit message string.
|
||||||
|
"""
|
||||||
|
files_part = ""
|
||||||
|
if files:
|
||||||
|
files_str = "\n".join(f" - {f}" for f in files[:10])
|
||||||
|
if len(files) > 10:
|
||||||
|
files_str += f"\n ... and {len(files) - 10} more"
|
||||||
|
files_part = f"\n\nFiles changed:\n{files_str}"
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
"type": type,
|
||||||
|
"scope": scope,
|
||||||
|
"description": description,
|
||||||
|
"body": body,
|
||||||
|
"files": files_part,
|
||||||
|
**kwargs
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self.template
|
||||||
|
for key, value in variables.items():
|
||||||
|
placeholder = f"{{{key}}}"
|
||||||
|
result = result.replace(placeholder, str(value))
|
||||||
|
|
||||||
|
return result.strip()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_default_templates(cls) -> Dict[str, str]:
|
||||||
|
"""Get all default templates.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary of template name to template string.
|
||||||
|
"""
|
||||||
|
return cls.DEFAULT_TEMPLATES.copy()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_template(cls, template: str) -> tuple[bool, Optional[str]]:
|
||||||
|
"""Validate a template string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template: Template string to validate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (is_valid, error_message).
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
placeholders = re.findall(r"\{(\w+)\}", template)
|
||||||
|
valid_placeholders = {"type", "scope", "description", "body", "files"}
|
||||||
|
invalid = set(placeholders) - valid_placeholders
|
||||||
|
if invalid:
|
||||||
|
return False, f"Invalid placeholders: {', '.join(sorted(invalid))}"
|
||||||
|
return True, None
|
||||||
|
except re.error as e:
|
||||||
|
return False, f"Invalid regex in template: {e}"
|
||||||
|
|
||||||
|
def generate_suggestions(self, changes: List[str]) -> List[str]:
|
||||||
|
"""Generate description suggestions based on changed files.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
changes: List of changed file paths.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of suggested descriptions.
|
||||||
|
"""
|
||||||
|
suggestions = []
|
||||||
|
for path in changes[:5]:
|
||||||
|
filename = path.split("/")[-1]
|
||||||
|
base_name = ".".join(filename.split(".")[:-1])
|
||||||
|
if base_name:
|
||||||
|
suggestions.append(f"update {base_name}")
|
||||||
|
else:
|
||||||
|
suggestions.append(f"update {filename}")
|
||||||
|
return suggestions
|
||||||
|
|
||||||
|
|
||||||
|
def format_message(
|
||||||
|
type: str,
|
||||||
|
scope: str,
|
||||||
|
description: str,
|
||||||
|
template: Optional[str] = None,
|
||||||
|
files: Optional[List[str]] = None
|
||||||
|
) -> str:
|
||||||
|
"""Format a commit message.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
type: Commit type.
|
||||||
|
scope: Commit scope.
|
||||||
|
description: Commit description.
|
||||||
|
template: Optional custom template.
|
||||||
|
files: Optional list of changed files.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted commit message.
|
||||||
|
"""
|
||||||
|
manager = TemplateManager(template)
|
||||||
|
return manager.render(type, scope, description, files=files)
|
||||||
Reference in New Issue
Block a user