Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/completion.py: 19%
97 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1"""
2Key binding handlers for displaying completions.
3"""
4from __future__ import annotations
6import asyncio
7import math
8from typing import TYPE_CHECKING
10from prompt_toolkit.application.run_in_terminal import in_terminal
11from prompt_toolkit.completion import (
12 CompleteEvent,
13 Completion,
14 get_common_complete_suffix,
15)
16from prompt_toolkit.formatted_text import StyleAndTextTuples
17from prompt_toolkit.key_binding.key_bindings import KeyBindings
18from prompt_toolkit.key_binding.key_processor import KeyPressEvent
19from prompt_toolkit.keys import Keys
20from prompt_toolkit.utils import get_cwidth
22if TYPE_CHECKING:
23 from prompt_toolkit.application import Application
24 from prompt_toolkit.shortcuts import PromptSession
26__all__ = [
27 "generate_completions",
28 "display_completions_like_readline",
29]
31E = KeyPressEvent
34def generate_completions(event: E) -> None:
35 r"""
36 Tab-completion: where the first tab completes the common suffix and the
37 second tab lists all the completions.
38 """
39 b = event.current_buffer
41 # When already navigating through completions, select the next one.
42 if b.complete_state:
43 b.complete_next()
44 else:
45 b.start_completion(insert_common_part=True)
48def display_completions_like_readline(event: E) -> None:
49 """
50 Key binding handler for readline-style tab completion.
51 This is meant to be as similar as possible to the way how readline displays
52 completions.
54 Generate the completions immediately (blocking) and display them above the
55 prompt in columns.
57 Usage::
59 # Call this handler when 'Tab' has been pressed.
60 key_bindings.add(Keys.ControlI)(display_completions_like_readline)
61 """
62 # Request completions.
63 b = event.current_buffer
64 if b.completer is None:
65 return
66 complete_event = CompleteEvent(completion_requested=True)
67 completions = list(b.completer.get_completions(b.document, complete_event))
69 # Calculate the common suffix.
70 common_suffix = get_common_complete_suffix(b.document, completions)
72 # One completion: insert it.
73 if len(completions) == 1:
74 b.delete_before_cursor(-completions[0].start_position)
75 b.insert_text(completions[0].text)
76 # Multiple completions with common part.
77 elif common_suffix:
78 b.insert_text(common_suffix)
79 # Otherwise: display all completions.
80 elif completions:
81 _display_completions_like_readline(event.app, completions)
84def _display_completions_like_readline(
85 app: Application[object], completions: list[Completion]
86) -> asyncio.Task[None]:
87 """
88 Display the list of completions in columns above the prompt.
89 This will ask for a confirmation if there are too many completions to fit
90 on a single page and provide a paginator to walk through them.
91 """
92 from prompt_toolkit.formatted_text import to_formatted_text
93 from prompt_toolkit.shortcuts.prompt import create_confirm_session
95 # Get terminal dimensions.
96 term_size = app.output.get_size()
97 term_width = term_size.columns
98 term_height = term_size.rows
100 # Calculate amount of required columns/rows for displaying the
101 # completions. (Keep in mind that completions are displayed
102 # alphabetically column-wise.)
103 max_compl_width = min(
104 term_width, max(get_cwidth(c.display_text) for c in completions) + 1
105 )
106 column_count = max(1, term_width // max_compl_width)
107 completions_per_page = column_count * (term_height - 1)
108 page_count = int(math.ceil(len(completions) / float(completions_per_page)))
109 # Note: math.ceil can return float on Python2.
111 def display(page: int) -> None:
112 # Display completions.
113 page_completions = completions[
114 page * completions_per_page : (page + 1) * completions_per_page
115 ]
117 page_row_count = int(math.ceil(len(page_completions) / float(column_count)))
118 page_columns = [
119 page_completions[i * page_row_count : (i + 1) * page_row_count]
120 for i in range(column_count)
121 ]
123 result: StyleAndTextTuples = []
125 for r in range(page_row_count):
126 for c in range(column_count):
127 try:
128 completion = page_columns[c][r]
129 style = "class:readline-like-completions.completion " + (
130 completion.style or ""
131 )
133 result.extend(to_formatted_text(completion.display, style=style))
135 # Add padding.
136 padding = max_compl_width - get_cwidth(completion.display_text)
137 result.append((completion.style, " " * padding))
138 except IndexError:
139 pass
140 result.append(("", "\n"))
142 app.print_text(to_formatted_text(result, "class:readline-like-completions"))
144 # User interaction through an application generator function.
145 async def run_compl() -> None:
146 "Coroutine."
147 async with in_terminal(render_cli_done=True):
148 if len(completions) > completions_per_page:
149 # Ask confirmation if it doesn't fit on the screen.
150 confirm = await create_confirm_session(
151 f"Display all {len(completions)} possibilities?",
152 ).prompt_async()
154 if confirm:
155 # Display pages.
156 for page in range(page_count):
157 display(page)
159 if page != page_count - 1:
160 # Display --MORE-- and go to the next page.
161 show_more = await _create_more_session(
162 "--MORE--"
163 ).prompt_async()
165 if not show_more:
166 return
167 else:
168 app.output.flush()
169 else:
170 # Display all completions.
171 display(0)
173 return app.create_background_task(run_compl())
176def _create_more_session(message: str = "--MORE--") -> PromptSession[bool]:
177 """
178 Create a `PromptSession` object for displaying the "--MORE--".
179 """
180 from prompt_toolkit.shortcuts import PromptSession
182 bindings = KeyBindings()
184 @bindings.add(" ")
185 @bindings.add("y")
186 @bindings.add("Y")
187 @bindings.add(Keys.ControlJ)
188 @bindings.add(Keys.ControlM)
189 @bindings.add(Keys.ControlI) # Tab.
190 def _yes(event: E) -> None:
191 event.app.exit(result=True)
193 @bindings.add("n")
194 @bindings.add("N")
195 @bindings.add("q")
196 @bindings.add("Q")
197 @bindings.add(Keys.ControlC)
198 def _no(event: E) -> None:
199 event.app.exit(result=False)
201 @bindings.add(Keys.Any)
202 def _ignore(event: E) -> None:
203 "Disable inserting of text."
205 return PromptSession(message, key_bindings=bindings, erase_when_done=True)