"""Gitignore generation from templates.""" import os from pathlib import Path from typing import Dict, List, Optional, Set class GitignoreGenerator: """Generate .gitignore content from templates.""" TEMPLATE_MAP: Dict[str, str] = { "python": "languages/python.gitignore", "javascript": "languages/javascript.gitignore", "typescript": "languages/typescript.gitignore", "java": "languages/java.gitignore", "go": "languages/go.gitignore", "rust": "languages/rust.gitignore", "dotnet": "languages/dotnet.gitignore", "php": "languages/php.gitignore", "ruby": "languages/ruby.gitignore", "django": "frameworks/django.gitignore", "flask": "frameworks/flask.gitignore", "react": "frameworks/react.gitignore", "vue": "frameworks/vue.gitignore", "angular": "frameworks/angular.gitignore", "rails": "frameworks/rails.gitignore", "laravel": "frameworks/laravel.gitignore", "spring": "frameworks/spring.gitignore", "vscode": "ide/vscode.gitignore", "jetbrains": "ide/jetbrains.gitignore", "visualstudiocode": "ide/visualstudiocode.gitignore", "linux": "os/linux.gitignore", "macos": "os/macos.gitignore", "windows": "os/windows.gitignore", "docker": "tools/docker.gitignore", "gradle": "tools/gradle.gitignore", "maven": "tools/maven.gitignore", "jupyter": "tools/jupyter.gitignore", "terraform": "tools/terraform.gitignore", } def __init__(self, config: Optional["Config"] = None) -> None: """Initialize generator with optional configuration.""" self.template_dir = Path(__file__).parent / "templates" self.selected_templates: Set[str] = set() self.config = config def add_template(self, template_type: str) -> None: """Add a template type to include.""" if template_type in self.TEMPLATE_MAP: self.selected_templates.add(template_type) def remove_template(self, template_type: str) -> None: """Remove a template type from selection.""" self.selected_templates.discard(template_type) def get_template_path(self, template_type: str) -> Optional[Path]: """Get the path to a template file.""" template_rel = self.TEMPLATE_MAP.get(template_type) if template_rel: template_path = self.template_dir / template_rel if template_path.exists(): return template_path return None def load_template(self, template_type: str) -> str: """Load template content.""" template_path = self.get_template_path(template_type) if template_path: try: with open(template_path, "r") as f: return f.read() except OSError: return "" return "" def load_template_lines(self, template_type: str) -> List[str]: """Load template content as list of lines.""" content = self.load_template(template_type) lines = content.split("\n") while lines and not lines[-1]: lines.pop() while lines and not lines[0]: lines.pop(0) return lines def deduplicate_entries( self, entries: List[str], comments: List[str] = None ) -> List[str]: """Remove duplicate entries while preserving order.""" seen: Set[str] = set() unique_entries: List[str] = [] for entry in entries: normalized = entry.strip().lower() if normalized and normalized not in seen: seen.add(normalized) unique_entries.append(entry) if comments: unique_comments = [] seen_comments = set() for comment in comments: if comment not in seen_comments: seen_comments.add(comment) unique_comments.append(comment) return unique_comments + unique_entries return unique_entries def generate(self) -> str: """Generate combined .gitignore content.""" all_entries: List[str] = [] all_comments: List[str] = [] used_categories: Set[str] = set() template_order = [ "language", "framework", "ide", "os", "tools", ] category_map = { "python": "language", "javascript": "language", "typescript": "language", "java": "language", "go": "language", "rust": "language", "dotnet": "language", "php": "language", "ruby": "language", "django": "framework", "flask": "framework", "react": "framework", "vue": "framework", "angular": "framework", "rails": "framework", "laravel": "framework", "spring": "framework", "vscode": "ide", "jetbrains": "ide", "visualstudiocode": "ide", "linux": "os", "macos": "os", "windows": "os", "docker": "tools", "gradle": "tools", "maven": "tools", "jupyter": "tools", "terraform": "tools", } for template_type in sorted(self.selected_templates): template_path = self.get_template_path(template_type) if template_path: try: category = category_map.get(template_type, "other") header = f"# {template_type.upper()}" if header not in all_comments: all_comments.append(header) lines = self.load_template_lines(template_type) all_entries.extend(lines) except OSError: pass if not all_entries: return "" combined = self.deduplicate_entries(all_entries, all_comments) result = "\n".join(combined) if result and not result.endswith("\n"): result += "\n" return result def generate_for_types( self, types: List[str], deduplicate: bool = True ) -> str: """Generate .gitignore for specific types.""" self.selected_templates = set() for t in types: self.add_template(t) return self.generate() def get_available_templates(self) -> List[str]: """Get list of available template types.""" available = [] for template_type, template_rel in self.TEMPLATE_MAP.items(): template_path = self.template_dir / template_rel if template_path.exists(): available.append(template_type) return sorted(available) def validate_template_type(self, template_type: str) -> bool: """Validate if a template type is valid.""" return template_type in self.TEMPLATE_MAP def get_template_categories(self) -> Dict[str, List[str]]: """Get templates organized by category.""" categories: Dict[str, List[str]] = { "language": [], "framework": [], "ide": [], "os": [], "tools": [], } category_map = { "python": "language", "javascript": "language", "typescript": "language", "java": "language", "go": "language", "rust": "language", "dotnet": "language", "php": "language", "ruby": "language", "django": "framework", "flask": "framework", "react": "framework", "vue": "framework", "angular": "framework", "rails": "framework", "laravel": "framework", "spring": "framework", "vscode": "ide", "jetbrains": "ide", "visualstudiocode": "ide", "linux": "os", "macos": "os", "windows": "os", "docker": "tools", "gradle": "tools", "maven": "tools", "jupyter": "tools", "terraform": "tools", } for template_type in self.TEMPLATE_MAP: category = category_map.get(template_type, "other") if category in categories: categories[category].append(template_type) return categories