You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
6.1 KiB
183 lines
6.1 KiB
"""
|
|
Key binding handlers for displaying completions.
|
|
"""
|
|
from __future__ import unicode_literals
|
|
from prompt_toolkit.application.run_in_terminal import run_coroutine_in_terminal
|
|
from prompt_toolkit.completion import CompleteEvent, get_common_complete_suffix
|
|
from prompt_toolkit.key_binding.key_bindings import KeyBindings
|
|
from prompt_toolkit.keys import Keys
|
|
from prompt_toolkit.utils import get_cwidth
|
|
|
|
import math
|
|
|
|
__all__ = [
|
|
'generate_completions',
|
|
'display_completions_like_readline',
|
|
]
|
|
|
|
|
|
def generate_completions(event):
|
|
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):
|
|
"""
|
|
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, completions):
|
|
"""
|
|
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.shortcuts.prompt import create_confirm_session
|
|
from prompt_toolkit.formatted_text import to_formatted_text
|
|
assert isinstance(completions, list)
|
|
|
|
# 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):
|
|
# 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 = [] # FormattedText list: (style,text) tuples.
|
|
|
|
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.
|
|
def run_compl():
|
|
" Coroutine. "
|
|
if len(completions) > completions_per_page:
|
|
# Ask confirmation if it doesn't fit on the screen.
|
|
confirm = yield create_confirm_session(
|
|
'Display all {} possibilities?'.format(len(completions)),
|
|
).prompt(async_=True)
|
|
|
|
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 = yield _create_more_session('--MORE--').prompt(async_=True)
|
|
|
|
if not show_more:
|
|
return
|
|
else:
|
|
app.output.flush()
|
|
else:
|
|
# Display all completions.
|
|
display(0)
|
|
|
|
run_coroutine_in_terminal(run_compl, render_cli_done=True)
|
|
|
|
|
|
def _create_more_session(message='--MORE--'):
|
|
"""
|
|
Create a `PromptSession` object for displaying the "--MORE--".
|
|
"""
|
|
from prompt_toolkit.shortcuts import PromptSession
|
|
bindings = KeyBindings()
|
|
|
|
@bindings.add(' ')
|
|
@bindings.add('y')
|
|
@bindings.add('Y')
|
|
@bindings.add(Keys.ControlJ)
|
|
@bindings.add(Keys.ControlM)
|
|
@bindings.add(Keys.ControlI) # Tab.
|
|
def _(event):
|
|
event.app.exit(result=True)
|
|
|
|
@bindings.add('n')
|
|
@bindings.add('N')
|
|
@bindings.add('q')
|
|
@bindings.add('Q')
|
|
@bindings.add(Keys.ControlC)
|
|
def _(event):
|
|
event.app.exit(result=False)
|
|
|
|
@bindings.add(Keys.Any)
|
|
def _(event):
|
|
" Disable inserting of text. "
|
|
|
|
session = PromptSession(message,
|
|
key_bindings=bindings,
|
|
erase_when_done=True)
|
|
return session
|