YCode / Code-agent-main /ycode /ui /prompts.py
Yazhiniyan's picture
Upload 190 files
5ef3875 verified
Raw History Blame Contribute Delete
3.18 kB
"""User input: prompt_toolkit session with history and multiline editing."""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Iterable
PROMPT = "ycode> "
class InputReader:
"""Reads one request at a time.
* Enter submits; Esc+Enter (or Alt+Enter) inserts a newline.
* A line ending in a backslash continues on the next line.
* Up/Down walk through persistent history (~/.ycode/history).
Falls back to plain ``input()`` when stdin is not a terminal.
"""
def __init__(self, history_path: Path | None = None, commands: Iterable[str] = ()) -> None:
self._session = None
if sys.stdin.isatty() and sys.stdout.isatty():
try:
self._session = self._build_session(history_path, list(commands))
except Exception: # noqa: BLE001 - unusual terminals: degrade to input()
self._session = None
@staticmethod
def _build_session(history_path: Path | None, commands: list[str]):
from prompt_toolkit import PromptSession
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.history import FileHistory, InMemoryHistory
from prompt_toolkit.key_binding import KeyBindings
bindings = KeyBindings()
@bindings.add("escape", "enter")
def _newline(event) -> None: # noqa: ANN001
event.current_buffer.insert_text("\n")
@bindings.add("enter")
def _submit(event) -> None: # noqa: ANN001
buffer = event.current_buffer
if buffer.text.endswith("\\"):
buffer.delete_before_cursor(1)
buffer.insert_text("\n")
else:
buffer.validate_and_handle()
history = InMemoryHistory()
if history_path is not None:
try:
history_path.parent.mkdir(parents=True, exist_ok=True)
history = FileHistory(str(history_path))
except OSError:
pass
completer = WordCompleter(commands, sentence=True) if commands else None
return PromptSession(
history=history,
auto_suggest=AutoSuggestFromHistory(),
completer=completer,
complete_while_typing=True,
reserve_space_for_menu=4,
key_bindings=bindings,
multiline=True,
prompt_continuation=lambda width, line_number, is_soft_wrap: "." * (width - 1) + " ",
)
def read(self) -> str:
"""Return the next request. Raises EOFError (Ctrl+D) / KeyboardInterrupt (Ctrl+C)."""
if self._session is not None:
from prompt_toolkit.formatted_text import HTML
return self._session.prompt(HTML("<ansicyan><b>ycode&gt;</b></ansicyan> "))
lines = []
prompt = PROMPT
while True:
line = input(prompt)
if line.endswith("\\"):
lines.append(line[:-1])
prompt = "... "
continue
lines.append(line)
return "\n".join(lines)