Files
gitignore-generator/gitignore_generator/cli/interactive.py
7000pctAUTO 161c68b748
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / build (push) Has been cancelled
fix: resolve CI lint and build failures
- Fixed lint path in CI workflow from src/gdiffer/ to gitignore_generator/
- Fixed verification command from gdiffer --version to gitignore --version
- Removed unused imports (pathlib.Path, typing.List, os, sys, pytest, unittest.mock.patch)
- Removed unused variable assignments (category, index)
- Fixed f-strings without placeholders
- Renamed ambiguous variable 'l' to 'line' and 'prev_line'
- Added type assertion for type narrowing in template_loader.py
2026-02-02 16:06:16 +00:00

204 lines
6.2 KiB
Python

"""Interactive mode for gitignore-generator."""
from typing import List, Optional, Tuple
import click
from gitignore_generator.template_loader import template_loader
from gitignore_generator.validator import validator
def show_preview(content: str) -> None:
"""Show preview of gitignore content."""
click.echo("\n--- Preview ---")
lines = content.splitlines()
for i, line in enumerate(lines[:30], 1):
click.echo(f"{i:3} | {line}")
if len(lines) > 30:
click.echo(f"... and {len(lines) - 30} more lines")
click.echo("-------------\n")
def prompt_category() -> str:
"""Prompt user to select template category."""
categories = template_loader.get_templates_by_category()
category_options = []
for cat in ["languages", "ides"]:
if categories.get(cat):
category_options.append(cat.capitalize())
category_options.append("Custom")
click.echo("\nSelect category:")
for i, cat in enumerate(category_options, 1):
click.echo(f" {i}. {cat}")
choice = click.prompt("Enter choice", type=int, default=1)
if 1 <= choice <= len(category_options):
selected = category_options[choice - 1]
if selected == "Custom":
return "custom"
return selected.lower()
return "languages"
def prompt_templates(category: str) -> List[str]:
"""Prompt user to select templates."""
templates = template_loader.get_available_templates(category)
if not templates:
return []
click.echo(f"\n{category.capitalize()} templates (select multiple, comma-separated):")
for i, template in enumerate(templates[:20], 1):
click.echo(f" {i}. {template}")
if len(templates) > 20:
click.echo(f" ... and {len(templates) - 20} more")
click.echo(" 0. None/Skip")
choices = click.prompt("Enter template numbers", type=str, default="")
selected = []
if choices.strip():
try:
nums = [int(x.strip()) for x in choices.split(",")]
for num in nums:
if 1 <= num <= len(templates):
selected.append(templates[num - 1])
except ValueError:
pass
return selected
def prompt_custom_patterns() -> List[str]:
"""Prompt user for custom patterns."""
patterns = []
click.echo("\nAdd custom patterns (one per line, empty to finish):")
while True:
pattern = click.prompt("Pattern", default="", show_default=False).strip()
if not pattern:
break
if pattern.startswith("#"):
click.echo(" Skipping comment line")
continue
issue = validator.validate_pattern(pattern)
if issue:
click.echo(f" Warning: {issue.message}")
if not click.confirm("Add anyway?"):
continue
patterns.append(pattern)
return patterns
def prompt_output_file() -> str:
"""Prompt user for output file name."""
return click.prompt("Output file", default=".gitignore", show_default=True)
def run_interactive_wizard() -> Optional[Tuple[str, str]]:
"""Run the interactive wizard."""
try:
click.echo("=== Gitignore Generator - Interactive Mode ===")
category = prompt_category()
selected_templates = []
if category != "custom":
selected_templates = prompt_templates(category)
custom_patterns = prompt_custom_patterns()
if not selected_templates and not custom_patterns:
click.echo("\nNo templates or patterns selected. Aborting.")
return None
content_parts = []
for template_name in selected_templates:
template_content = template_loader.get_template_content(template_name)
if template_content:
content_parts.append(f"# --- {template_name} ---\n")
content_parts.append(template_content)
content_parts.append("\n")
if custom_patterns:
content_parts.append("# --- Custom Patterns ---\n")
content_parts.extend(custom_patterns)
content_parts.append("\n")
final_content = "".join(content_parts).rstrip() + "\n"
show_preview(final_content)
if click.confirm("Do you want to edit the patterns?"):
final_content = edit_content_interactive(final_content)
output_file = prompt_output_file()
return final_content, output_file
except KeyboardInterrupt:
click.echo("\n\nAborted by user.")
return None
except EOFError:
click.echo("\n\nInput stream closed. Exiting.")
return None
def edit_content_interactive(content: str) -> str:
"""Allow interactive editing of content."""
lines = content.splitlines()
click.echo("\nEdit mode (empty line number to finish editing):")
click.echo(" Commands: 'add <pattern>', 'delete <line_num>', 'show', 'save'")
while True:
cmd = click.prompt("Edit command", default="save", show_default=False).strip().lower()
if cmd == "save" or not cmd:
break
if cmd.startswith("add "):
pattern = cmd[4:].strip()
if pattern:
lines.append(pattern)
click.echo(f"Added: {pattern}")
elif cmd.startswith("delete "):
try:
line_num = int(cmd[7:].strip())
if 1 <= line_num <= len(lines):
deleted = lines.pop(line_num - 1)
click.echo(f"Deleted line {line_num}: {deleted}")
else:
click.echo("Invalid line number")
except ValueError:
click.echo("Invalid command")
elif cmd == "show":
show_preview("\n".join(lines))
else:
click.echo("Unknown command. Use: add <pattern>, delete <num>, show, save")
return "\n".join(lines) + "\n"
def run_simple_interactive() -> None:
"""Run a simple interactive session."""
result = run_interactive_wizard()
if result:
content, output_file = result
try:
with open(output_file, "w") as f:
f.write(content)
click.echo(f"\nSuccessfully wrote .gitignore to '{output_file}'")
except IOError as e:
click.echo(f"\nError writing file: {e}")