Files
regex-humanizer-cli/regex_humanizer/interactive.py
7000pctAUTO 814b52ca26
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled
Add remaining source files and tests
2026-02-06 01:13:08 +00:00

292 lines
8.9 KiB
Python

"""Interactive REPL mode for exploring regex patterns."""
import sys
import os
from typing import Optional
from .parser import parse_regex
from .translator import translate_regex
from .test_generator import generate_test_cases
from .flavors import get_flavor_manager
def format_output(text: str, use_color: bool = True) -> str:
"""Format output with optional color."""
if not use_color or not sys.stdout.isatty():
return text
try:
from pygments import highlight
from pygments.lexers import RegexLexer
from pygments.formatters import TerminalFormatter
lexer = RegexLexer()
formatter = TerminalFormatter()
return highlight(text, lexer, formatter)
except ImportError:
return text
class InteractiveSession:
"""Interactive session for regex exploration."""
def __init__(self, flavor: str = "pcre", use_color: bool = True):
self.flavor = flavor
self.use_color = use_color
self.history: list[str] = []
self.history_file = os.path.expanduser("~/.regex_humanizer_history")
self._load_history()
def _load_history(self):
"""Load command history from file."""
if os.path.exists(self.history_file):
try:
with open(self.history_file, 'r') as f:
self.history = [line.strip() for line in f if line.strip()]
except Exception:
self.history = []
def _save_history(self):
"""Save command history to file."""
try:
os.makedirs(os.path.dirname(self.history_file), exist_ok=True)
with open(self.history_file, 'w') as f:
for cmd in self.history[-1000:]:
f.write(cmd + '\\n')
except Exception:
pass
def run(self):
"""Run the interactive session."""
print("\\nRegex Humanizer - Interactive Mode")
print("Type 'help' for available commands, 'quit' to exit.\\n")
while True:
try:
import click
user_input = click.prompt(
"regex> ",
type=str,
default="",
show_default=False
)
if not user_input.strip():
continue
self.history.append(user_input)
self._save_history()
self._process_command(user_input.strip())
except (KeyboardInterrupt, EOFError):
print("\\nGoodbye!")
break
def _process_command(self, command: str):
"""Process a user command."""
parts = command.split(None, 1)
cmd = parts[0].lower()
args = parts[1] if len(parts) > 1 else ""
commands = {
"help": self._cmd_help,
"quit": self._cmd_quit,
"exit": self._cmd_quit,
"explain": self._cmd_explain,
"test": self._cmd_test,
"flavor": self._cmd_flavor,
"set": self._cmd_flavor,
"load": self._cmd_load,
"save": self._cmd_save,
"history": self._cmd_history,
"clear": self._cmd_clear,
"example": self._cmd_example,
}
handler = commands.get(cmd)
if handler:
handler(args)
else:
print(f"Unknown command: {cmd}")
print("Type 'help' for available commands.")
def _cmd_help(self, args: str):
"""Show help message."""
help_text = """
Available Commands:
explain <pattern> - Explain a regex pattern in English
test <pattern> - Generate test cases for a pattern
flavor <name> - Set the regex flavor (pcre, javascript, python)
set <name> - Same as 'flavor'
load <filename> - Load a pattern from a file
save <filename> - Save the last pattern to a file
history - Show command history
example - Show an example pattern
clear - Clear the screen
quit / exit - Exit the interactive mode
Examples:
explain ^\\d{3}-\\d{4}$
test [a-z]+
flavor javascript
"""
print(help_text)
def _cmd_quit(self, args: str):
"""Exit the session."""
print("Goodbye!")
sys.exit(0)
def _cmd_explain(self, args: str):
"""Explain a regex pattern."""
if not args:
print("Usage: explain <pattern>")
return
try:
pattern = self._expand_pattern(args)
result = translate_regex(pattern, self.flavor)
header = f"Pattern: {pattern}"
print("\\n" + "=" * (len(header)))
print(header)
print("=" * (len(header)))
print("\\nEnglish Explanation:")
print("-" * (len(header)))
print(result)
print()
except Exception as e:
print(f"Error parsing pattern: {e}")
def _cmd_test(self, args: str):
"""Generate test cases for a pattern."""
if not args:
print("Usage: test <pattern>")
return
try:
pattern = self._expand_pattern(args)
result = generate_test_cases(pattern, self.flavor, 3, 3)
header = f"Pattern: {pattern}"
print("\\n" + "=" * (len(header)))
print(header)
print("=" * (len(header)))
print(f"\\nFlavor: {self.flavor}")
print("\\nMatching strings:")
print("-" * (len(header)))
for i, s in enumerate(result["matching"], 1):
print(f" {i}. {s}")
print("\\nNon-matching strings:")
print("-" * (len(header)))
for i, s in enumerate(result["non_matching"], 1):
print(f" {i}. {s}")
print()
except Exception as e:
print(f"Error generating tests: {e}")
def _cmd_flavor(self, args: str):
"""Set the current flavor."""
if not args:
manager = get_flavor_manager()
flavors = manager.list_flavors()
print("Available flavors:")
for name, desc in flavors:
marker = " (current)" if name == self.flavor else ""
print(f" {name}{marker}: {desc}")
return
flavor_name = args.strip().lower()
manager = get_flavor_manager()
if manager.get_flavor(flavor_name):
self.flavor = flavor_name
print(f"Flavor set to: {flavor_name}")
else:
print(f"Unknown flavor: {flavor_name}")
print("Available flavors: pcre, javascript, python")
def _cmd_load(self, args: str):
"""Load a pattern from a file."""
if not args:
print("Usage: load <filename>")
return
filename = args.strip()
if not os.path.exists(filename):
print(f"File not found: {filename}")
return
try:
with open(filename, 'r') as f:
pattern = f.read().strip()
print(f"Loaded pattern: {pattern}")
if hasattr(self, '_last_pattern'):
pass
self._last_pattern = pattern
except Exception as e:
print(f"Error reading file: {e}")
def _cmd_save(self, args: str):
"""Save a pattern to a file."""
if not args:
print("Usage: save <filename>")
return
pattern = getattr(self, '_last_pattern', None)
if not pattern:
print("No pattern to save. Use 'explain' or 'test' first.")
return
try:
with open(args.strip(), 'w') as f:
f.write(pattern)
print(f"Saved pattern to: {args.strip()}")
except Exception as e:
print(f"Error writing file: {e}")
def _cmd_history(self, args: str):
"""Show command history."""
print("Command history:")
for i, cmd in enumerate(self.history[-50:], 1):
print(f" {i:3}. {cmd}")
def _cmd_clear(self, args: str):
"""Clear the screen."""
os.system('cls' if os.name == 'nt' else 'clear')
def _cmd_example(self, args: str):
"""Show an example pattern."""
examples = [
r"^\\d{3}-\\d{4}$",
r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
r"^(?:http|https)://[^\\s]+$",
r"\\b\\d{4}-\\d{2}-\\d{2}\\b",
r"(?i)(hello|hi|greetings)\\s+world!?",
]
import random
example = random.choice(examples)
print(f"\\nExample pattern: {example}")
print("\\nType: explain " + example)
print("Type: test " + example)
print()
def _expand_pattern(self, pattern: str) -> str:
"""Expand a pattern from history or args."""
return pattern
def start_interactive_mode(flavor: str = "pcre"):
"""Start the interactive mode."""
session = InteractiveSession(flavor=flavor)
session.run()