diff --git a/promptshell/main.py b/promptshell/main.py index 54ac648..ceeb287 100644 --- a/promptshell/main.py +++ b/promptshell/main.py @@ -1,5 +1,5 @@ from .ai_terminal_assistant import AITerminalAssistant -from .readline_setup import setup_readline +from .readline_setup import setup_readline, clear_screen import platform import os import sys @@ -26,6 +26,7 @@ def main(): enable_ansi_support() setup_readline() + custom_input = setup_readline() model_name = get_active_model() assistant = AITerminalAssistant(config=config, model_name=model_name) @@ -46,6 +47,22 @@ def main(): if user_input.lower() in ('quit', 'exit'): print(format_text('red', bold=True) + "\nTerminating..." + reset_format()) break + + # Use custom input if available (from prompt_toolkit), otherwise use standard input + if custom_input: + user_input = custom_input(prompt).strip() + else: + user_input = input(prompt).strip() + + # Handle Ctrl+L clear screen command + if user_input == "clear_screen": + clear_screen() + continue + + # Handle clear/cls commands + if user_input.lower() in ('clear', 'cls'): + clear_screen() + continue if user_input.lower() == "--config": setup_wizard() diff --git a/promptshell/readline_setup.py b/promptshell/readline_setup.py index c4d92f7..3cd04a9 100644 --- a/promptshell/readline_setup.py +++ b/promptshell/readline_setup.py @@ -1,37 +1,131 @@ import sys -import glob import os +import atexit +import glob +import platform + +# Define internal commands for tab completion +INTERNAL_COMMANDS = [ + '--help', '--config', '--tutorial', + 'alias', 'exit', 'quit', 'clear', 'cls' +] + +def clear_screen(): + """Clear the screen using ANSI escape sequences.""" + sys.stdout.write("\033[2J\033[H") + sys.stdout.flush() def setup_readline(): - """Configures tab completion and history support.""" + """Configures tab completion, history support, and keyboard shortcuts.""" + histfile = os.path.join(os.path.expanduser("~"), ".promptshell_history") + custom_prompt = None try: - import readline # Works on Unix-like systems + # Unix-like systems + import readline + + # Load existing history + try: + readline.read_history_file(histfile) + except FileNotFoundError: + open(histfile, 'a').close() + + # Set history length and save on exit + readline.set_history_length(1000) + atexit.register(readline.write_history_file, histfile) + + # Configure tab completion + readline.set_completer(completer) + readline.set_completer_delims(" \t\n;") + readline.parse_and_bind("tab: complete") + + # Bind Ctrl+L to clear screen + readline.parse_and_bind("Control-l: clear_screen") + print("Using readline for enhanced input features.") + except ImportError: if sys.platform == "win32": try: - import pyreadline3 as readline # Use pyreadline3 on Windows + # Windows with pyreadline + import pyreadline3 as readline + + # Load existing history + try: + readline.read_history_file(histfile) + except FileNotFoundError: + open(histfile, 'a').close() + + # Set history length and save on exit + readline.set_history_length(1000) + atexit.register(readline.write_history_file, histfile) + + # Configure tab completion + readline.set_completer(completer) + readline.parse_and_bind("tab: complete") + + # Bind Ctrl+L to clear screen + readline.parse_and_bind("Control-l: clear_screen") + print("Using pyreadline for enhanced input features.") + except ImportError: + # Fallback to prompt_toolkit try: - import prompt_toolkit # Alternative for better Windows support - from prompt_toolkit.completion import PathCompleter - from prompt_toolkit.shortcuts import prompt - - def complete_path(): - return prompt(">>> ", completer=PathCompleter()) - - print("Using prompt_toolkit for tab completion.") - return complete_path # Return prompt-based tab completion + import prompt_toolkit + from prompt_toolkit.completion import Completer, Completion + from prompt_toolkit.shortcuts import prompt as pt_prompt + from prompt_toolkit.keys import Keys + from prompt_toolkit.key_binding import KeyBindings + from prompt_toolkit.history import FileHistory + + print("Using prompt_toolkit for enhanced input features.") + + # Set up history + history = FileHistory(histfile) + + # Custom completer class + class InternalCompleter(Completer): + def get_completions(self, document, complete_event): + text = document.text_before_cursor + # Internal commands + for cmd in INTERNAL_COMMANDS: + if cmd.startswith(text): + yield Completion(cmd, start_position=-len(text)) + # File paths + for path in glob.glob(os.path.expanduser(text) + "*"): + yield Completion(path, start_position=-len(text)) + + # Key bindings for clear screen (Ctrl+L) + bindings = KeyBindings() + + @bindings.add(Keys.ControlL) + def clear_screen(event): + event.app.renderer.clear() + + def custom_prompt(prompt_str): + return pt_prompt( + prompt_str, + history=history, + completer=InternalCompleter(), + key_bindings=bindings + ) + + return custom_prompt + except ImportError: print("Warning: No readline or pyreadline3 found. Tab completion will not work.") - return - - # Configure readline for tab completion - readline.parse_and_bind("tab: complete") - - def complete(text, state): - matches = glob.glob(os.path.expanduser(text) + "*") + [None] - return matches[state] + else: + print("Warning: readline not available. Tab completion will not work.") + + return custom_prompt - readline.set_completer(complete) - readline.set_completer_delims(" \t\n;") \ No newline at end of file +def completer(text, state): + """Tab completion function that completes both internal commands and file paths.""" + # First check internal commands + command_matches = [cmd for cmd in INTERNAL_COMMANDS if cmd.startswith(text)] + + # Then check file paths + file_matches = glob.glob(os.path.expanduser(text) + "*") + + # Combine both types of matches + matches = command_matches + file_matches + return matches[state] if state < len(matches) else None \ No newline at end of file