Add source code files (detectors, generators, CLI)
This commit is contained in:
176
src/generators/pattern.py
Normal file
176
src/generators/pattern.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
"""Pattern generator for creating .gitignore content."""
|
||||||
|
|
||||||
|
from typing import List, Optional, Set
|
||||||
|
|
||||||
|
from src.data.patterns import (
|
||||||
|
FRAMEWORK_PATTERNS,
|
||||||
|
IDE_PATTERNS,
|
||||||
|
LANGUAGE_PATTERNS,
|
||||||
|
OS_PATTERNS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PatternGenerator:
|
||||||
|
"""Generates .gitignore patterns based on detected stack."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.language_patterns = LANGUAGE_PATTERNS
|
||||||
|
self.framework_patterns = FRAMEWORK_PATTERNS
|
||||||
|
self.ide_patterns = IDE_PATTERNS
|
||||||
|
self.os_patterns = OS_PATTERNS
|
||||||
|
|
||||||
|
def generate(
|
||||||
|
self,
|
||||||
|
languages: Optional[List[str]] = None,
|
||||||
|
frameworks: Optional[List[str]] = None,
|
||||||
|
ides: Optional[List[str]] = None,
|
||||||
|
os_list: Optional[List[str]] = None,
|
||||||
|
custom_patterns: Optional[List[str]] = None,
|
||||||
|
) -> str:
|
||||||
|
"""Generate .gitignore content.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
languages: List of detected languages.
|
||||||
|
frameworks: List of detected frameworks.
|
||||||
|
ides: List of detected IDEs.
|
||||||
|
os_list: List of operating systems.
|
||||||
|
custom_patterns: User-provided custom patterns.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Complete .gitignore content as string.
|
||||||
|
"""
|
||||||
|
lines: List[str] = []
|
||||||
|
seen_patterns: Set[str] = set()
|
||||||
|
|
||||||
|
os_order = ["macos", "windows", "linux"]
|
||||||
|
ide_order = ["jetbrains", "vscode", "pycharm", "webstorm", "intellij", "eclipse", "netbeans", "sublime", "vim", "emacs", "atom", "spacemacs"]
|
||||||
|
language_order = ["python", "nodejs", "go", "java", "rust", "csharp", "cpp", "ruby", "php", "dart", "swift", "kotlin", "scala", "perl", "r", "elixir", "clojure", "lua", "haskell"]
|
||||||
|
framework_order = ["django", "flask", "fastapi", "react", "vue", "angular", "express", "nextjs", "nuxt", "svelte", "gatsby", "astro", "gin", "spring", "rails", "laravel", "dotnet", "quasar", "sveltekit", "remix", "vite", "nestjs"]
|
||||||
|
|
||||||
|
os_list = os_list or []
|
||||||
|
ides = ides or []
|
||||||
|
languages = languages or []
|
||||||
|
frameworks = frameworks or []
|
||||||
|
|
||||||
|
if not os_list and not ides and not languages and not frameworks:
|
||||||
|
os_list = ["macos", "windows", "linux"]
|
||||||
|
|
||||||
|
for os_name in os_order:
|
||||||
|
if os_name in os_list:
|
||||||
|
self._add_patterns(lines, seen_patterns, self.os_patterns.get(os_name, []), f"=== {os_name.upper()} ===")
|
||||||
|
|
||||||
|
for ide_name in ide_order:
|
||||||
|
if ide_name in ides:
|
||||||
|
self._add_patterns(lines, seen_patterns, self.ide_patterns.get(ide_name, []), f"=== {ide_name.upper()} ===")
|
||||||
|
|
||||||
|
for lang_name in language_order:
|
||||||
|
if lang_name in languages:
|
||||||
|
self._add_patterns(lines, seen_patterns, self.language_patterns.get(lang_name, []), f"=== {lang_name.upper()} ===")
|
||||||
|
|
||||||
|
for fw_name in framework_order:
|
||||||
|
if fw_name in frameworks:
|
||||||
|
self._add_patterns(lines, seen_patterns, self.framework_patterns.get(fw_name, []), f"=== {fw_name.upper()} ===")
|
||||||
|
|
||||||
|
if custom_patterns:
|
||||||
|
self._add_patterns(lines, seen_patterns, custom_patterns, "=== CUSTOM ===")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
def _add_patterns(
|
||||||
|
self,
|
||||||
|
lines: List[str],
|
||||||
|
seen_patterns: Set[str],
|
||||||
|
patterns: List[str],
|
||||||
|
header: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
"""Add patterns to lines list, avoiding duplicates.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
lines: List to append patterns to.
|
||||||
|
seen_patterns: Set of already added patterns.
|
||||||
|
patterns: Patterns to add.
|
||||||
|
header: Optional section header.
|
||||||
|
"""
|
||||||
|
if not patterns:
|
||||||
|
return
|
||||||
|
|
||||||
|
section_patterns: List[str] = []
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
if pattern and pattern not in seen_patterns:
|
||||||
|
normalized = pattern.strip()
|
||||||
|
if normalized and normalized not in seen_patterns:
|
||||||
|
seen_patterns.add(normalized)
|
||||||
|
section_patterns.append(normalized)
|
||||||
|
|
||||||
|
if section_patterns:
|
||||||
|
if header:
|
||||||
|
lines.append(f"# {header}")
|
||||||
|
lines.extend(section_patterns)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
def get_supported_languages(self) -> List[str]:
|
||||||
|
"""Get list of supported languages.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of language names.
|
||||||
|
"""
|
||||||
|
return list(self.language_patterns.keys())
|
||||||
|
|
||||||
|
def get_supported_frameworks(self) -> List[str]:
|
||||||
|
"""Get list of supported frameworks.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of framework names.
|
||||||
|
"""
|
||||||
|
return list(self.framework_patterns.keys())
|
||||||
|
|
||||||
|
def get_supported_ides(self) -> List[str]:
|
||||||
|
"""Get list of supported IDEs.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of IDE names.
|
||||||
|
"""
|
||||||
|
return list(self.ide_patterns.keys())
|
||||||
|
|
||||||
|
def get_supported_os(self) -> List[str]:
|
||||||
|
"""Get list of supported operating systems.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of OS names.
|
||||||
|
"""
|
||||||
|
return list(self.os_patterns.keys())
|
||||||
|
|
||||||
|
def validate_pattern(self, pattern: str) -> tuple[bool, str]:
|
||||||
|
"""Validate a single gitignore pattern.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: Pattern to validate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (is_valid, error_message).
|
||||||
|
"""
|
||||||
|
if not pattern or not pattern.strip():
|
||||||
|
return False, "Pattern cannot be empty"
|
||||||
|
|
||||||
|
pattern = pattern.strip()
|
||||||
|
|
||||||
|
if pattern.startswith("#"):
|
||||||
|
return True, ""
|
||||||
|
|
||||||
|
if pattern.startswith("!"):
|
||||||
|
if len(pattern) < 2:
|
||||||
|
return False, "Negation pattern must have content after '!'"
|
||||||
|
return True, ""
|
||||||
|
|
||||||
|
if pattern.startswith("/"):
|
||||||
|
if len(pattern) < 2:
|
||||||
|
return False, "Root pattern must have content after '/'"
|
||||||
|
return True, ""
|
||||||
|
|
||||||
|
invalid_chars = ["\n", "\r", "\t"]
|
||||||
|
for char in invalid_chars:
|
||||||
|
if char in pattern:
|
||||||
|
return False, f"Pattern contains invalid character: {repr(char)}"
|
||||||
|
|
||||||
|
return True, ""
|
||||||
Reference in New Issue
Block a user