ai-bot-test / venv /lib /python3.11 /site-packages /prompt_toolkit /key_binding /bindings /completion.py
| """ | |
| Key binding handlers for displaying completions. | |
| """ | |
| from __future__ import annotations | |
| import asyncio | |
| import math | |
| from typing import TYPE_CHECKING | |
| from prompt_toolkit.application.run_in_terminal import in_terminal | |
| from prompt_toolkit.completion import ( | |
| CompleteEvent, | |
| Completion, | |
| get_common_complete_suffix, | |
| ) | |
| from prompt_toolkit.formatted_text import StyleAndTextTuples | |
| from prompt_toolkit.key_binding.key_bindings import KeyBindings | |
| from prompt_toolkit.key_binding.key_processor import KeyPressEvent | |
| from prompt_toolkit.keys import Keys | |
| from prompt_toolkit.utils import get_cwidth | |
| if TYPE_CHECKING: | |
| from prompt_toolkit.application import Application | |
| from prompt_toolkit.shortcuts import PromptSession | |
| __all__ = [ | |
| "generate_completions", | |
| "display_completions_like_readline", | |
| ] | |
| E = KeyPressEvent | |
| def generate_completions(event: E) -> None: | |
| r""" | |
| Tab-completion: where the first tab completes the common suffix and the | |
| second tab lists all the completions. | |
| """ | |
| b = event.current_buffer | |
| # When already navigating through completions, select the next one. | |
| if b.complete_state: | |
| b.complete_next() | |
| else: | |
| b.start_completion(insert_common_part=True) | |
| def display_completions_like_readline(event: E) -> None: | |
| """ | |
| Key binding handler for readline-style tab completion. | |
| This is meant to be as similar as possible to the way how readline displays | |
| completions. | |
| Generate the completions immediately (blocking) and display them above the | |
| prompt in columns. | |
| Usage:: | |
| # Call this handler when 'Tab' has been pressed. | |
| key_bindings.add(Keys.ControlI)(display_completions_like_readline) | |
| """ | |
| # Request completions. | |
| b = event.current_buffer | |
| if b.completer is None: | |
| return | |
| complete_event = CompleteEvent(completion_requested=True) | |
| completions = list(b.completer.get_completions(b.document, complete_event)) | |
| # Calculate the common suffix. | |
| common_suffix = get_common_complete_suffix(b.document, completions) | |
| # One completion: insert it. | |
| if len(completions) == 1: | |
| b.delete_before_cursor(-completions[0].start_position) | |
| b.insert_text(completions[0].text) | |
| # Multiple completions with common part. | |
| elif common_suffix: | |
| b.insert_text(common_suffix) | |
| # Otherwise: display all completions. | |
| elif completions: | |
| _display_completions_like_readline(event.app, completions) | |
| def _display_completions_like_readline( | |
| app: Application[object], completions: list[Completion] | |
| ) -> asyncio.Task[None]: | |
| """ | |
| Display the list of completions in columns above the prompt. | |
| This will ask for a confirmation if there are too many completions to fit | |
| on a single page and provide a paginator to walk through them. | |
| """ | |
| from prompt_toolkit.formatted_text import to_formatted_text | |
| from prompt_toolkit.shortcuts.prompt import create_confirm_session | |
| # Get terminal dimensions. | |
| term_size = app.output.get_size() | |
| term_width = term_size.columns | |
| term_height = term_size.rows | |
| # Calculate amount of required columns/rows for displaying the | |
| # completions. (Keep in mind that completions are displayed | |
| # alphabetically column-wise.) | |
| max_compl_width = min( | |
| term_width, max(get_cwidth(c.display_text) for c in completions) + 1 | |
| ) | |
| column_count = max(1, term_width // max_compl_width) | |
| completions_per_page = column_count * (term_height - 1) | |
| page_count = int(math.ceil(len(completions) / float(completions_per_page))) | |
| # Note: math.ceil can return float on Python2. | |
| def display(page: int) -> None: | |
| # Display completions. | |
| page_completions = completions[ | |
| page * completions_per_page : (page + 1) * completions_per_page | |
| ] | |
| page_row_count = int(math.ceil(len(page_completions) / float(column_count))) | |
| page_columns = [ | |
| page_completions[i * page_row_count : (i + 1) * page_row_count] | |
| for i in range(column_count) | |
| ] | |
| result: StyleAndTextTuples = [] | |
| for r in range(page_row_count): | |
| for c in range(column_count): | |
| try: | |
| completion = page_columns[c][r] | |
| style = "class:readline-like-completions.completion " + ( | |
| completion.style or "" | |
| ) | |
| result.extend(to_formatted_text(completion.display, style=style)) | |
| # Add padding. | |
| padding = max_compl_width - get_cwidth(completion.display_text) | |
| result.append((completion.style, " " * padding)) | |
| except IndexError: | |
| pass | |
| result.append(("", "\n")) | |
| app.print_text(to_formatted_text(result, "class:readline-like-completions")) | |
| # User interaction through an application generator function. | |
| async def run_compl() -> None: | |
| "Coroutine." | |
| async with in_terminal(render_cli_done=True): | |
| if len(completions) > completions_per_page: | |
| # Ask confirmation if it doesn't fit on the screen. | |
| confirm = await create_confirm_session( | |
| f"Display all {len(completions)} possibilities?", | |
| ).prompt_async() | |
| if confirm: | |
| # Display pages. | |
| for page in range(page_count): | |
| display(page) | |
| if page != page_count - 1: | |
| # Display --MORE-- and go to the next page. | |
| show_more = await _create_more_session( | |
| "--MORE--" | |
| ).prompt_async() | |
| if not show_more: | |
| return | |
| else: | |
| app.output.flush() | |
| else: | |
| # Display all completions. | |
| display(0) | |
| return app.create_background_task(run_compl()) | |
| def _create_more_session(message: str = "--MORE--") -> PromptSession[bool]: | |
| """ | |
| Create a `PromptSession` object for displaying the "--MORE--". | |
| """ | |
| from prompt_toolkit.shortcuts import PromptSession | |
| bindings = KeyBindings() | |
| # Tab. | |
| def _yes(event: E) -> None: | |
| event.app.exit(result=True) | |
| def _no(event: E) -> None: | |
| event.app.exit(result=False) | |
| def _ignore(event: E) -> None: | |
| "Disable inserting of text." | |
| return PromptSession(message, key_bindings=bindings, erase_when_done=True) | |