From b4a043e759d94edfabddf6b37c6f97cbab53591e Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 20:15:31 +0000 Subject: [PATCH] Add source code files (detectors, generators, CLI) --- src/generators/pattern.py | 176 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/generators/pattern.py diff --git a/src/generators/pattern.py b/src/generators/pattern.py new file mode 100644 index 0000000..e1b4538 --- /dev/null +++ b/src/generators/pattern.py @@ -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, ""